From 555570c15e6180d7617d2be8200f0bfc0b02ceb4 Mon Sep 17 00:00:00 2001 From: AurelienUoU Date: Thu, 23 May 2019 16:03:08 -0600 Subject: [PATCH] Update Yosys from version 0.7 to version 0.8 --- yosys/Brewfile | 8 + yosys/CHANGELOG | 136 ++ yosys/COPYING | 13 + yosys/CodingReadme | 98 +- yosys/Makefile | 321 ++- yosys/README.md | 490 ++++ yosys/backends/aiger/Makefile.inc | 3 + yosys/backends/aiger/aiger.cc | 788 +++++++ yosys/backends/blif/blif.cc | 82 +- yosys/backends/btor/README | 23 - yosys/backends/btor/btor.cc | 2072 +++++++++-------- yosys/backends/btor/test_cells.sh | 30 + yosys/backends/btor/verilog2btor.sh | 37 - yosys/backends/edif/edif.cc | 230 +- yosys/backends/edif/runtest.py | 121 + yosys/backends/firrtl/.gitignore | 2 + yosys/backends/firrtl/Makefile.inc | 3 + yosys/backends/firrtl/firrtl.cc | 677 ++++++ yosys/backends/firrtl/test.sh | 25 + yosys/backends/firrtl/test.v | 63 + yosys/backends/ilang/ilang_backend.cc | 10 +- yosys/backends/intersynth/intersynth.cc | 4 +- yosys/backends/json/json.cc | 18 +- yosys/backends/protobuf/.gitignore | 2 + yosys/backends/protobuf/Makefile.inc | 8 + yosys/backends/protobuf/protobuf.cc | 370 +++ yosys/backends/simplec/.gitignore | 2 + yosys/backends/simplec/Makefile.inc | 3 + yosys/backends/simplec/simplec.cc | 810 +++++++ yosys/backends/simplec/test00.sh | 5 + yosys/backends/simplec/test00_tb.c | 93 + yosys/backends/simplec/test00_uut.v | 14 + yosys/backends/smt2/Makefile.inc | 2 +- yosys/backends/smt2/smt2.cc | 732 ++++-- yosys/backends/smt2/smtbmc.py | 1073 ++++++++- yosys/backends/smt2/smtio.py | 475 +++- yosys/backends/smv/smv.cc | 97 +- yosys/backends/spice/spice.cc | 4 +- yosys/backends/table/Makefile.inc | 3 + yosys/backends/table/table.cc | 120 + yosys/backends/verilog/verilog_backend.cc | 280 ++- yosys/examples/aiger/.gitignore | 5 + yosys/examples/aiger/README | 22 + yosys/examples/aiger/demo.sh | 14 + yosys/examples/aiger/demo.v | 12 + yosys/examples/basys3/example.xdc | 3 + yosys/examples/basys3/run_prog.tcl | 1 + yosys/examples/cmos/counter.ys | 10 +- yosys/examples/cmos/counter_tb.v | 4 +- yosys/examples/cmos/testbench_digital.sh | 2 +- yosys/examples/cxx-api/evaldemo.cc | 2 +- yosys/examples/gowin/.gitignore | 8 + yosys/examples/gowin/README | 17 + yosys/examples/gowin/demo.cst | 41 + yosys/examples/gowin/demo.sdc | 1 + yosys/examples/gowin/demo.v | 12 + yosys/examples/gowin/run.sh | 12 + yosys/examples/gowin/testbench.v | 40 + yosys/examples/igloo2/.gitignore | 3 + yosys/examples/igloo2/example.v | 22 + yosys/examples/igloo2/example.ys | 3 + yosys/examples/igloo2/libero.tcl | 35 + yosys/examples/igloo2/runme.sh | 5 + .../intel/DE2i-150/quartus_compile/de2i.qpf | 4 + .../intel/DE2i-150/quartus_compile/de2i.qsf | 1099 +++++++++ .../DE2i-150/quartus_compile/runme_quartus | 7 + yosys/examples/intel/DE2i-150/run_cycloneiv | 2 + yosys/examples/intel/DE2i-150/sevenseg.v | 25 + yosys/examples/intel/DE2i-150/top.v | 15 + yosys/examples/intel/MAX10/run_max10 | 1 + yosys/examples/intel/MAX10/runme_postsynth | 5 + yosys/examples/intel/MAX10/sevenseg.v | 25 + yosys/examples/intel/MAX10/top.v | 15 + yosys/examples/intel/asicworld_lfsr/README | 6 + .../intel/asicworld_lfsr/lfsr_updown.v | 35 + .../intel/asicworld_lfsr/lfsr_updown_tb.v | 34 + .../intel/asicworld_lfsr/run_cycloneiv | 2 + yosys/examples/intel/asicworld_lfsr/run_max10 | 2 + .../intel/asicworld_lfsr/runme_postsynth | 5 + .../intel/asicworld_lfsr/runme_presynth | 5 + yosys/examples/osu035/.gitignore | 3 + yosys/examples/osu035/Makefile | 13 + yosys/examples/osu035/example.constr | 2 + yosys/examples/osu035/example.v | 3 + yosys/examples/osu035/example.ys | 11 + yosys/examples/smtbmc/.gitignore | 2 + yosys/examples/smtbmc/Makefile | 11 +- yosys/examples/smtbmc/demo2.v | 2 +- yosys/examples/smtbmc/demo8.v | 12 + yosys/frontends/ast/ast.cc | 376 ++- yosys/frontends/ast/ast.h | 46 +- yosys/frontends/ast/genrtlil.cc | 248 +- yosys/frontends/ast/simplify.cc | 528 +++-- yosys/frontends/blif/blifparse.cc | 159 +- yosys/frontends/blif/blifparse.h | 3 +- yosys/frontends/ilang/.gitignore | 2 +- yosys/frontends/ilang/Makefile.inc | 7 +- yosys/frontends/ilang/ilang_frontend.cc | 34 +- yosys/frontends/ilang/ilang_frontend.h | 2 + yosys/frontends/ilang/ilang_lexer.l | 2 +- yosys/frontends/ilang/ilang_parser.y | 42 +- yosys/frontends/json/Makefile.inc | 3 + yosys/frontends/json/jsonparse.cc | 541 +++++ yosys/frontends/liberty/liberty.cc | 88 +- yosys/frontends/verific/Makefile.inc | 3 + yosys/frontends/verific/README | 33 + yosys/frontends/verific/build_amd64.txt | 30 - yosys/frontends/verific/example.sby | 16 + yosys/frontends/verific/example.sv | 18 + yosys/frontends/verific/test_navre.ys | 18 - yosys/frontends/verific/verific.cc | 1978 +++++++++++++--- yosys/frontends/verific/verific.h | 109 + yosys/frontends/verific/verificsva.cc | 1815 +++++++++++++++ yosys/frontends/verilog/.gitignore | 2 +- yosys/frontends/verilog/Makefile.inc | 7 +- yosys/frontends/verilog/const2ast.cc | 8 +- yosys/frontends/verilog/preproc.cc | 189 +- yosys/frontends/verilog/verilog_frontend.cc | 144 +- yosys/frontends/verilog/verilog_frontend.h | 12 +- yosys/frontends/verilog/verilog_lexer.l | 68 +- yosys/frontends/verilog/verilog_parser.y | 636 ++++- yosys/frontends/vhdl2verilog/Makefile.inc | 1 - yosys/frontends/vhdl2verilog/vhdl2verilog.cc | 183 -- yosys/kernel/calc.cc | 4 +- yosys/kernel/cellaigs.cc | 16 +- yosys/kernel/celledges.h | 6 +- yosys/kernel/celltypes.h | 36 +- yosys/kernel/consteval.h | 18 +- yosys/kernel/cost.h | 30 +- yosys/kernel/driver.cc | 175 +- yosys/kernel/hashlib.h | 9 +- yosys/kernel/log.cc | 151 +- yosys/kernel/log.h | 19 +- yosys/kernel/modtools.h | 10 +- yosys/kernel/register.cc | 15 +- yosys/kernel/register.h | 14 +- yosys/kernel/rtlil.cc | 360 ++- yosys/kernel/rtlil.h | 308 +-- yosys/kernel/satgen.h | 39 +- yosys/kernel/yosys.cc | 126 +- yosys/kernel/yosys.h | 16 +- yosys/libs/ezsat/ezminisat.cc | 6 +- yosys/libs/ezsat/ezminisat.h | 2 +- .../minisat/00_PATCH_no_fpu_control.patch | 43 + yosys/libs/minisat/00_PATCH_remove_zlib.patch | 17 + yosys/libs/minisat/00_PATCH_typofixes.patch | 20 + yosys/libs/minisat/00_UPDATE.sh | 6 +- yosys/libs/minisat/Dimacs.h | 8 +- yosys/libs/minisat/Options.cc | 4 + yosys/libs/minisat/SimpSolver.cc | 4 + yosys/libs/minisat/Solver.cc | 4 + yosys/libs/minisat/System.cc | 15 +- yosys/libs/minisat/System.h | 7 - yosys/libs/subcircuit/README | 6 +- yosys/libs/subcircuit/subcircuit.h | 2 +- yosys/manual/CHAPTER_CellLib.tex | 51 +- yosys/manual/CHAPTER_Overview.tex | 4 +- yosys/manual/CHAPTER_Prog/stubnets.cc | 2 +- yosys/manual/PRESENTATION_Prog/my_cmd.cc | 6 +- yosys/manual/clean.sh | 2 +- yosys/manual/command-reference-manual.tex | 1336 +++++++++-- yosys/misc/create_vcxsrc.sh | 8 +- yosys/misc/yosys.proto | 175 ++ yosys/passes/cmds/Makefile.inc | 6 +- yosys/passes/cmds/add.cc | 4 +- yosys/passes/cmds/blackbox.cc | 81 + yosys/passes/cmds/bugpoint.cc | 369 +++ yosys/passes/cmds/check.cc | 89 +- yosys/passes/cmds/chformal.cc | 282 +++ yosys/passes/cmds/chtype.cc | 83 + yosys/passes/cmds/connect.cc | 10 +- yosys/passes/cmds/connwrappers.cc | 6 +- yosys/passes/cmds/copy.cc | 4 +- yosys/passes/cmds/cover.cc | 6 +- yosys/passes/cmds/delete.cc | 4 +- yosys/passes/cmds/design.cc | 142 +- yosys/passes/cmds/edgetypes.cc | 4 +- yosys/passes/cmds/logcmd.cc | 4 +- yosys/passes/cmds/ltp.cc | 185 ++ yosys/passes/cmds/plugin.cc | 4 +- yosys/passes/cmds/qwp.cc | 5 +- yosys/passes/cmds/rename.cc | 120 +- yosys/passes/cmds/scatter.cc | 4 +- yosys/passes/cmds/scc.cc | 78 +- yosys/passes/cmds/select.cc | 115 +- yosys/passes/cmds/setattr.cc | 12 +- yosys/passes/cmds/setundef.cc | 269 ++- yosys/passes/cmds/show.cc | 38 +- yosys/passes/cmds/splice.cc | 4 +- yosys/passes/cmds/splitnets.cc | 14 +- yosys/passes/cmds/stat.cc | 15 +- yosys/passes/cmds/tee.cc | 9 +- yosys/passes/cmds/torder.cc | 4 +- yosys/passes/cmds/trace.cc | 17 +- yosys/passes/cmds/write_file.cc | 5 +- yosys/passes/equiv/Makefile.inc | 2 +- yosys/passes/equiv/equiv_add.cc | 4 +- yosys/passes/equiv/equiv_induct.cc | 4 +- yosys/passes/equiv/equiv_make.cc | 94 +- yosys/passes/equiv/equiv_mark.cc | 4 +- yosys/passes/equiv/equiv_miter.cc | 4 +- yosys/passes/equiv/equiv_opt.cc | 157 ++ yosys/passes/equiv/equiv_purge.cc | 8 +- yosys/passes/equiv/equiv_remove.cc | 4 +- yosys/passes/equiv/equiv_simple.cc | 59 +- yosys/passes/equiv/equiv_status.cc | 4 +- yosys/passes/equiv/equiv_struct.cc | 4 +- yosys/passes/fsm/fsm.cc | 4 +- yosys/passes/fsm/fsm_detect.cc | 16 +- yosys/passes/fsm/fsm_expand.cc | 34 +- yosys/passes/fsm/fsm_export.cc | 4 +- yosys/passes/fsm/fsm_extract.cc | 8 +- yosys/passes/fsm/fsm_info.cc | 4 +- yosys/passes/fsm/fsm_map.cc | 7 +- yosys/passes/fsm/fsm_opt.cc | 4 +- yosys/passes/fsm/fsm_recode.cc | 4 +- yosys/passes/hierarchy/Makefile.inc | 2 +- yosys/passes/hierarchy/hierarchy.cc | 389 +++- yosys/passes/hierarchy/submod.cc | 5 +- .../hierarchy/{singleton.cc => uniquify.cc} | 43 +- yosys/passes/memory/Makefile.inc | 1 + yosys/passes/memory/memory.cc | 4 +- yosys/passes/memory/memory_bram.cc | 22 +- yosys/passes/memory/memory_collect.cc | 7 +- yosys/passes/memory/memory_dff.cc | 24 +- yosys/passes/memory/memory_map.cc | 4 +- yosys/passes/memory/memory_memx.cc | 4 +- yosys/passes/memory/memory_nordff.cc | 121 + yosys/passes/memory/memory_share.cc | 4 +- yosys/passes/memory/memory_unpack.cc | 4 +- yosys/passes/opt/Makefile.inc | 3 + yosys/passes/opt/opt.cc | 4 +- yosys/passes/opt/opt_clean.cc | 107 +- yosys/passes/opt/opt_demorgan.cc | 202 ++ yosys/passes/opt/opt_expr.cc | 354 ++- yosys/passes/opt/opt_lut.cc | 607 +++++ yosys/passes/opt/opt_merge.cc | 19 +- yosys/passes/opt/opt_muxtree.cc | 22 +- yosys/passes/opt/opt_reduce.cc | 4 +- yosys/passes/opt/opt_rmdff.cc | 246 +- yosys/passes/opt/rmports.cc | 187 ++ yosys/passes/opt/share.cc | 12 +- yosys/passes/opt/wreduce.cc | 9 +- yosys/passes/proc/proc.cc | 4 +- yosys/passes/proc/proc_arst.cc | 4 +- yosys/passes/proc/proc_clean.cc | 42 +- yosys/passes/proc/proc_dff.cc | 9 +- yosys/passes/proc/proc_dlatch.cc | 20 +- yosys/passes/proc/proc_init.cc | 4 +- yosys/passes/proc/proc_mux.cc | 4 +- yosys/passes/proc/proc_rmdead.cc | 4 +- yosys/passes/sat/Makefile.inc | 2 + yosys/passes/sat/assertpmux.cc | 4 +- yosys/passes/sat/async2sync.cc | 147 ++ yosys/passes/sat/clk2fflogic.cc | 114 +- yosys/passes/sat/eval.cc | 4 +- yosys/passes/sat/expose.cc | 57 +- yosys/passes/sat/freduce.cc | 7 +- yosys/passes/sat/miter.cc | 4 +- yosys/passes/sat/sat.cc | 5 +- yosys/passes/sat/sim.cc | 862 +++++++ yosys/passes/techmap/Makefile.inc | 6 +- yosys/passes/techmap/abc.cc | 620 +++-- yosys/passes/techmap/aigmap.cc | 4 +- yosys/passes/techmap/alumacc.cc | 19 +- yosys/passes/techmap/attrmap.cc | 12 +- yosys/passes/techmap/attrmvcp.cc | 4 +- yosys/passes/techmap/deminout.cc | 39 +- yosys/passes/techmap/dff2dffe.cc | 53 +- yosys/passes/techmap/dff2dffs.cc | 142 ++ yosys/passes/techmap/dffinit.cc | 51 +- yosys/passes/techmap/dfflibmap.cc | 24 +- yosys/passes/techmap/dffsr2dff.cc | 4 +- yosys/passes/techmap/extract.cc | 4 +- .../techmap/extract_counter.cc} | 425 +++- yosys/passes/techmap/extract_fa.cc | 605 +++++ yosys/passes/techmap/extract_reduce.cc | 324 +++ yosys/passes/techmap/flowmap.cc | 954 ++++++++ yosys/passes/techmap/hilomap.cc | 4 +- yosys/passes/techmap/insbuf.cc | 4 +- yosys/passes/techmap/iopadmap.cc | 61 +- yosys/passes/techmap/libparse.cc | 39 +- yosys/passes/techmap/lut2mux.cc | 6 +- yosys/passes/techmap/maccmap.cc | 4 +- yosys/passes/techmap/muxcover.cc | 4 +- yosys/passes/techmap/nlutmap.cc | 6 +- yosys/passes/techmap/pmuxtree.cc | 4 +- yosys/passes/techmap/shregmap.cc | 4 +- yosys/passes/techmap/simplemap.cc | 4 +- yosys/passes/techmap/techmap.cc | 45 +- yosys/passes/techmap/tribuf.cc | 4 +- yosys/passes/techmap/zinit.cc | 4 +- yosys/passes/tests/flowmap/flow.v | 22 + yosys/passes/tests/flowmap/flowp.v | 16 + yosys/passes/tests/test_abcloop.cc | 4 +- yosys/passes/tests/test_autotb.cc | 4 +- yosys/passes/tests/test_cell.cc | 6 +- yosys/techlibs/achronix/Makefile.inc | 6 + .../achronix/speedster22i/cells_arith.v | 65 + .../achronix/speedster22i/cells_map.v | 72 + .../achronix/speedster22i/cells_sim.v | 80 + yosys/techlibs/achronix/synth_achronix.cc | 188 ++ yosys/techlibs/anlogic/Makefile.inc | 12 + .../anlogic/anlogic_determine_init.cc | 72 + yosys/techlibs/anlogic/anlogic_eqn.cc | 113 + yosys/techlibs/anlogic/arith_map.v | 84 + yosys/techlibs/anlogic/cells_map.v | 61 + yosys/techlibs/anlogic/cells_sim.v | 103 + yosys/techlibs/anlogic/dram_init_16x4.vh | 16 + yosys/techlibs/anlogic/drams.txt | 16 + yosys/techlibs/anlogic/drams_map.v | 22 + yosys/techlibs/anlogic/eagle_bb.v | 1028 ++++++++ yosys/techlibs/anlogic/synth_anlogic.cc | 213 ++ yosys/techlibs/common/Makefile.inc | 4 +- yosys/techlibs/common/cmp2lut.v | 105 + yosys/techlibs/common/dff2ff.v | 14 + yosys/techlibs/common/gate2lut.v | 87 + yosys/techlibs/common/prep.cc | 34 +- yosys/techlibs/common/simcells.v | 260 ++- yosys/techlibs/common/simlib.v | 62 +- yosys/techlibs/common/synth.cc | 67 +- yosys/techlibs/coolrunner2/Makefile.inc | 8 + yosys/techlibs/coolrunner2/cells_latch.v | 19 + yosys/techlibs/coolrunner2/cells_sim.v | 310 +++ yosys/techlibs/coolrunner2/coolrunner2_sop.cc | 320 +++ .../techlibs/coolrunner2/synth_coolrunner2.cc | 202 ++ yosys/techlibs/coolrunner2/tff_extract.v | 41 + yosys/techlibs/coolrunner2/xc2_dff.lib | 31 + yosys/techlibs/easic/Makefile.inc | 3 + yosys/techlibs/easic/synth_easic.cc | 188 ++ yosys/techlibs/ecp5/.gitignore | 9 + yosys/techlibs/ecp5/Makefile.inc | 44 + yosys/techlibs/ecp5/arith_map.v | 79 + yosys/techlibs/ecp5/bram.txt | 29 + yosys/techlibs/ecp5/brams_connect.py | 46 + yosys/techlibs/ecp5/brams_init.py | 22 + yosys/techlibs/ecp5/brams_map.v | 115 + yosys/techlibs/ecp5/cells_bb.v | 514 ++++ yosys/techlibs/ecp5/cells_map.v | 135 ++ yosys/techlibs/ecp5/cells_sim.v | 549 +++++ yosys/techlibs/ecp5/dram.txt | 17 + yosys/techlibs/ecp5/drams_map.v | 28 + yosys/techlibs/ecp5/latches_map.v | 11 + yosys/techlibs/ecp5/synth_ecp5.cc | 325 +++ yosys/techlibs/gowin/Makefile.inc | 1 + yosys/techlibs/gowin/arith_map.v | 59 + yosys/techlibs/gowin/cells_sim.v | 54 +- yosys/techlibs/gowin/synth_gowin.cc | 41 +- yosys/techlibs/greenpak4/Makefile.inc | 6 +- yosys/techlibs/greenpak4/cells_blackbox.v | 18 + yosys/techlibs/greenpak4/cells_latch.v | 15 + yosys/techlibs/greenpak4/cells_map.v | 165 ++ yosys/techlibs/greenpak4/cells_sim.v | 462 +--- yosys/techlibs/greenpak4/cells_sim_ams.v | 110 + yosys/techlibs/greenpak4/cells_sim_digital.v | 794 +++++++ yosys/techlibs/greenpak4/cells_sim_wip.v | 136 ++ yosys/techlibs/greenpak4/greenpak4_dffinv.cc | 25 +- yosys/techlibs/greenpak4/synth_greenpak4.cc | 17 +- yosys/techlibs/ice40/Makefile.inc | 1 + yosys/techlibs/ice40/cells_map.v | 2 + yosys/techlibs/ice40/cells_sim.v | 478 +++- yosys/techlibs/ice40/ice40_ffinit.cc | 4 +- yosys/techlibs/ice40/ice40_ffssr.cc | 7 +- yosys/techlibs/ice40/ice40_opt.cc | 53 +- yosys/techlibs/ice40/ice40_unlut.cc | 106 + yosys/techlibs/ice40/synth_ice40.cc | 126 +- yosys/techlibs/intel/Makefile.inc | 24 + yosys/techlibs/intel/a10gx/cells_arith.v | 65 + yosys/techlibs/intel/a10gx/cells_map.v | 53 + yosys/techlibs/intel/a10gx/cells_sim.v | 59 + yosys/techlibs/intel/common/altpll_bb.v | 366 +++ yosys/techlibs/intel/common/brams.txt | 33 + yosys/techlibs/intel/common/brams_map.v | 93 + yosys/techlibs/intel/common/m9k_bb.v | 70 + yosys/techlibs/intel/cyclone10/cells_arith.v | 65 + yosys/techlibs/intel/cyclone10/cells_map.v | 109 + yosys/techlibs/intel/cyclone10/cells_sim.v | 137 ++ yosys/techlibs/intel/cycloneiv/cells_arith.v | 97 + yosys/techlibs/intel/cycloneiv/cells_map.v | 93 + yosys/techlibs/intel/cycloneiv/cells_sim.v | 299 +++ yosys/techlibs/intel/cycloneive/arith_map.v | 99 + yosys/techlibs/intel/cycloneive/cells_map.v | 109 + yosys/techlibs/intel/cycloneive/cells_sim.v | 292 +++ yosys/techlibs/intel/cyclonev/cells_arith.v | 65 + yosys/techlibs/intel/cyclonev/cells_map.v | 162 ++ yosys/techlibs/intel/cyclonev/cells_sim.v | 144 ++ yosys/techlibs/intel/max10/cells_arith.v | 65 + yosys/techlibs/intel/max10/cells_map.v | 93 + yosys/techlibs/intel/max10/cells_sim.v | 292 +++ yosys/techlibs/intel/synth_intel.cc | 270 +++ yosys/techlibs/sf2/Makefile.inc | 7 + yosys/techlibs/sf2/arith_map.v | 21 + yosys/techlibs/sf2/cells_map.v | 68 + yosys/techlibs/sf2/cells_sim.v | 75 + yosys/techlibs/sf2/synth_sf2.cc | 206 ++ yosys/techlibs/xilinx/Makefile.inc | 2 +- yosys/techlibs/xilinx/brams_init.py | 16 +- yosys/techlibs/xilinx/cells_map.v | 2 + yosys/techlibs/xilinx/cells_sim.v | 30 + yosys/techlibs/xilinx/cells_xtra.sh | 5 +- yosys/techlibs/xilinx/cells_xtra.v | 636 +++++ yosys/techlibs/xilinx/drams_bb.v | 20 - yosys/techlibs/xilinx/lut2lut.v | 65 + yosys/techlibs/xilinx/synth_xilinx.cc | 42 +- yosys/tests/errors/syntax_err01.v | 4 + yosys/tests/errors/syntax_err02.v | 7 + yosys/tests/errors/syntax_err03.v | 7 + yosys/tests/errors/syntax_err04.v | 4 + yosys/tests/errors/syntax_err05.v | 4 + yosys/tests/errors/syntax_err06.v | 6 + yosys/tests/errors/syntax_err07.v | 6 + yosys/tests/errors/syntax_err08.v | 6 + yosys/tests/errors/syntax_err09.v | 3 + yosys/tests/errors/syntax_err10.v | 3 + yosys/tests/errors/syntax_err11.v | 3 + yosys/tests/errors/syntax_err12.v | 7 + yosys/tests/errors/syntax_err13.v | 4 + yosys/tests/lut/.gitignore | 1 + yosys/tests/lut/check_map.ys | 6 + yosys/tests/lut/map_and.v | 5 + yosys/tests/lut/map_cmp.v | 29 + yosys/tests/lut/map_mux.v | 5 + yosys/tests/lut/map_not.v | 5 + yosys/tests/lut/map_or.v | 5 + yosys/tests/lut/map_xor.v | 5 + yosys/tests/lut/run-test.sh | 6 + yosys/tests/opt/.gitignore | 1 + yosys/tests/opt/opt_expr_cmp.v | 40 + yosys/tests/opt/opt_expr_cmp.ys | 4 + yosys/tests/opt/opt_lut.v | 18 + yosys/tests/opt/opt_lut.ys | 4 + yosys/tests/opt/opt_lut_elim.il | 19 + yosys/tests/opt/opt_lut_elim.ys | 3 + yosys/tests/opt/opt_lut_port.il | 18 + yosys/tests/opt/opt_lut_port.ys | 3 + yosys/tests/opt/run-test.sh | 6 + yosys/tests/sat/sizebits.sv | 32 + yosys/tests/sat/sizebits.ys | 2 + yosys/tests/simple/arraycells.v | 2 +- yosys/tests/simple/graphtest.v | 2 +- yosys/tests/simple/hierdefparam.v | 23 + yosys/tests/simple/specify.v | 31 + yosys/tests/sva/.gitignore | 7 + yosys/tests/sva/Makefile | 13 + yosys/tests/sva/basic00.sv | 12 + yosys/tests/sva/basic01.sv | 16 + yosys/tests/sva/basic02.sv | 20 + yosys/tests/sva/basic03.sv | 12 + yosys/tests/sva/basic04.sv | 10 + yosys/tests/sva/basic04.vhd | 26 + yosys/tests/sva/basic05.sv | 19 + yosys/tests/sva/basic05.vhd | 26 + yosys/tests/sva/counter.sv | 30 + yosys/tests/sva/runtest.sh | 72 + yosys/tests/sva/sva_not.sv | 34 + yosys/tests/sva/sva_range.sv | 19 + yosys/tests/sva/sva_throughout.sv | 19 + yosys/tests/svinterfaces/.gitignore | 8 + yosys/tests/svinterfaces/run-test.sh | 6 + yosys/tests/svinterfaces/runone.sh | 44 + yosys/tests/svinterfaces/svinterface1.sv | 117 + yosys/tests/svinterfaces/svinterface1_ref.v | 107 + yosys/tests/svinterfaces/svinterface1_tb.v | 57 + .../tests/svinterfaces/svinterface_at_top.sv | 125 + .../svinterfaces/svinterface_at_top_ref.v | 120 + .../svinterfaces/svinterface_at_top_tb.v | 68 + .../svinterface_at_top_tb_wrapper.v | 68 + .../svinterfaces/svinterface_at_top_wrapper.v | 33 + yosys/tests/tools/autotest.sh | 4 +- yosys/tests/unit/Makefile | 35 + yosys/tests/unit/kernel/logTest.cc | 14 + yosys/tests/unit/kernel/rtlilTest.cc | 14 + yosys/tests/various/reg_wire_error.sv | 74 + yosys/tests/various/reg_wire_error.ys | 1 + 474 files changed, 40577 insertions(+), 4915 deletions(-) create mode 100644 yosys/Brewfile create mode 100644 yosys/COPYING create mode 100644 yosys/README.md create mode 100644 yosys/backends/aiger/Makefile.inc create mode 100644 yosys/backends/aiger/aiger.cc delete mode 100644 yosys/backends/btor/README create mode 100644 yosys/backends/btor/test_cells.sh delete mode 100755 yosys/backends/btor/verilog2btor.sh create mode 100644 yosys/backends/edif/runtest.py create mode 100644 yosys/backends/firrtl/.gitignore create mode 100644 yosys/backends/firrtl/Makefile.inc create mode 100644 yosys/backends/firrtl/firrtl.cc create mode 100644 yosys/backends/firrtl/test.sh create mode 100644 yosys/backends/firrtl/test.v create mode 100644 yosys/backends/protobuf/.gitignore create mode 100644 yosys/backends/protobuf/Makefile.inc create mode 100644 yosys/backends/protobuf/protobuf.cc create mode 100644 yosys/backends/simplec/.gitignore create mode 100644 yosys/backends/simplec/Makefile.inc create mode 100644 yosys/backends/simplec/simplec.cc create mode 100644 yosys/backends/simplec/test00.sh create mode 100644 yosys/backends/simplec/test00_tb.c create mode 100644 yosys/backends/simplec/test00_uut.v create mode 100644 yosys/backends/table/Makefile.inc create mode 100644 yosys/backends/table/table.cc create mode 100644 yosys/examples/aiger/.gitignore create mode 100644 yosys/examples/aiger/README create mode 100644 yosys/examples/aiger/demo.sh create mode 100644 yosys/examples/aiger/demo.v create mode 100644 yosys/examples/gowin/.gitignore create mode 100644 yosys/examples/gowin/README create mode 100644 yosys/examples/gowin/demo.cst create mode 100644 yosys/examples/gowin/demo.sdc create mode 100644 yosys/examples/gowin/demo.v create mode 100644 yosys/examples/gowin/run.sh create mode 100644 yosys/examples/gowin/testbench.v create mode 100644 yosys/examples/igloo2/.gitignore create mode 100644 yosys/examples/igloo2/example.v create mode 100644 yosys/examples/igloo2/example.ys create mode 100644 yosys/examples/igloo2/libero.tcl create mode 100644 yosys/examples/igloo2/runme.sh create mode 100644 yosys/examples/intel/DE2i-150/quartus_compile/de2i.qpf create mode 100644 yosys/examples/intel/DE2i-150/quartus_compile/de2i.qsf create mode 100644 yosys/examples/intel/DE2i-150/quartus_compile/runme_quartus create mode 100644 yosys/examples/intel/DE2i-150/run_cycloneiv create mode 100644 yosys/examples/intel/DE2i-150/sevenseg.v create mode 100644 yosys/examples/intel/DE2i-150/top.v create mode 100644 yosys/examples/intel/MAX10/run_max10 create mode 100644 yosys/examples/intel/MAX10/runme_postsynth create mode 100644 yosys/examples/intel/MAX10/sevenseg.v create mode 100644 yosys/examples/intel/MAX10/top.v create mode 100644 yosys/examples/intel/asicworld_lfsr/README create mode 100644 yosys/examples/intel/asicworld_lfsr/lfsr_updown.v create mode 100644 yosys/examples/intel/asicworld_lfsr/lfsr_updown_tb.v create mode 100755 yosys/examples/intel/asicworld_lfsr/run_cycloneiv create mode 100755 yosys/examples/intel/asicworld_lfsr/run_max10 create mode 100755 yosys/examples/intel/asicworld_lfsr/runme_postsynth create mode 100755 yosys/examples/intel/asicworld_lfsr/runme_presynth create mode 100644 yosys/examples/osu035/.gitignore create mode 100644 yosys/examples/osu035/Makefile create mode 100644 yosys/examples/osu035/example.constr create mode 100644 yosys/examples/osu035/example.v create mode 100644 yosys/examples/osu035/example.ys create mode 100644 yosys/examples/smtbmc/demo8.v create mode 100644 yosys/frontends/json/Makefile.inc create mode 100644 yosys/frontends/json/jsonparse.cc create mode 100644 yosys/frontends/verific/README delete mode 100644 yosys/frontends/verific/build_amd64.txt create mode 100644 yosys/frontends/verific/example.sby create mode 100644 yosys/frontends/verific/example.sv delete mode 100644 yosys/frontends/verific/test_navre.ys create mode 100644 yosys/frontends/verific/verific.h create mode 100644 yosys/frontends/verific/verificsva.cc delete mode 100644 yosys/frontends/vhdl2verilog/Makefile.inc delete mode 100644 yosys/frontends/vhdl2verilog/vhdl2verilog.cc create mode 100644 yosys/libs/minisat/00_PATCH_no_fpu_control.patch create mode 100644 yosys/libs/minisat/00_PATCH_typofixes.patch mode change 100644 => 100755 yosys/libs/minisat/00_UPDATE.sh create mode 100644 yosys/misc/yosys.proto create mode 100644 yosys/passes/cmds/blackbox.cc create mode 100644 yosys/passes/cmds/bugpoint.cc create mode 100644 yosys/passes/cmds/chformal.cc create mode 100644 yosys/passes/cmds/chtype.cc create mode 100644 yosys/passes/cmds/ltp.cc create mode 100644 yosys/passes/equiv/equiv_opt.cc rename yosys/passes/hierarchy/{singleton.cc => uniquify.cc} (68%) create mode 100644 yosys/passes/memory/memory_nordff.cc create mode 100644 yosys/passes/opt/opt_demorgan.cc create mode 100644 yosys/passes/opt/opt_lut.cc create mode 100644 yosys/passes/opt/rmports.cc create mode 100644 yosys/passes/sat/async2sync.cc create mode 100644 yosys/passes/sat/sim.cc create mode 100644 yosys/passes/techmap/dff2dffs.cc rename yosys/{techlibs/greenpak4/greenpak4_counters.cc => passes/techmap/extract_counter.cc} (54%) create mode 100644 yosys/passes/techmap/extract_fa.cc create mode 100644 yosys/passes/techmap/extract_reduce.cc create mode 100644 yosys/passes/techmap/flowmap.cc create mode 100644 yosys/passes/tests/flowmap/flow.v create mode 100644 yosys/passes/tests/flowmap/flowp.v create mode 100755 yosys/techlibs/achronix/Makefile.inc create mode 100755 yosys/techlibs/achronix/speedster22i/cells_arith.v create mode 100755 yosys/techlibs/achronix/speedster22i/cells_map.v create mode 100755 yosys/techlibs/achronix/speedster22i/cells_sim.v create mode 100755 yosys/techlibs/achronix/synth_achronix.cc create mode 100644 yosys/techlibs/anlogic/Makefile.inc create mode 100644 yosys/techlibs/anlogic/anlogic_determine_init.cc create mode 100644 yosys/techlibs/anlogic/anlogic_eqn.cc create mode 100644 yosys/techlibs/anlogic/arith_map.v create mode 100644 yosys/techlibs/anlogic/cells_map.v create mode 100644 yosys/techlibs/anlogic/cells_sim.v create mode 100644 yosys/techlibs/anlogic/dram_init_16x4.vh create mode 100644 yosys/techlibs/anlogic/drams.txt create mode 100644 yosys/techlibs/anlogic/drams_map.v create mode 100644 yosys/techlibs/anlogic/eagle_bb.v create mode 100644 yosys/techlibs/anlogic/synth_anlogic.cc create mode 100644 yosys/techlibs/common/cmp2lut.v create mode 100644 yosys/techlibs/common/dff2ff.v create mode 100644 yosys/techlibs/common/gate2lut.v create mode 100644 yosys/techlibs/coolrunner2/Makefile.inc create mode 100644 yosys/techlibs/coolrunner2/cells_latch.v create mode 100644 yosys/techlibs/coolrunner2/cells_sim.v create mode 100644 yosys/techlibs/coolrunner2/coolrunner2_sop.cc create mode 100644 yosys/techlibs/coolrunner2/synth_coolrunner2.cc create mode 100644 yosys/techlibs/coolrunner2/tff_extract.v create mode 100644 yosys/techlibs/coolrunner2/xc2_dff.lib create mode 100644 yosys/techlibs/easic/Makefile.inc create mode 100644 yosys/techlibs/easic/synth_easic.cc create mode 100644 yosys/techlibs/ecp5/.gitignore create mode 100644 yosys/techlibs/ecp5/Makefile.inc create mode 100644 yosys/techlibs/ecp5/arith_map.v create mode 100644 yosys/techlibs/ecp5/bram.txt create mode 100755 yosys/techlibs/ecp5/brams_connect.py create mode 100755 yosys/techlibs/ecp5/brams_init.py create mode 100644 yosys/techlibs/ecp5/brams_map.v create mode 100644 yosys/techlibs/ecp5/cells_bb.v create mode 100644 yosys/techlibs/ecp5/cells_map.v create mode 100644 yosys/techlibs/ecp5/cells_sim.v create mode 100644 yosys/techlibs/ecp5/dram.txt create mode 100644 yosys/techlibs/ecp5/drams_map.v create mode 100644 yosys/techlibs/ecp5/latches_map.v create mode 100644 yosys/techlibs/ecp5/synth_ecp5.cc create mode 100644 yosys/techlibs/gowin/arith_map.v create mode 100644 yosys/techlibs/greenpak4/cells_blackbox.v create mode 100644 yosys/techlibs/greenpak4/cells_latch.v create mode 100644 yosys/techlibs/greenpak4/cells_sim_ams.v create mode 100644 yosys/techlibs/greenpak4/cells_sim_digital.v create mode 100644 yosys/techlibs/greenpak4/cells_sim_wip.v create mode 100644 yosys/techlibs/ice40/ice40_unlut.cc create mode 100644 yosys/techlibs/intel/Makefile.inc create mode 100644 yosys/techlibs/intel/a10gx/cells_arith.v create mode 100644 yosys/techlibs/intel/a10gx/cells_map.v create mode 100644 yosys/techlibs/intel/a10gx/cells_sim.v create mode 100644 yosys/techlibs/intel/common/altpll_bb.v create mode 100644 yosys/techlibs/intel/common/brams.txt create mode 100644 yosys/techlibs/intel/common/brams_map.v create mode 100644 yosys/techlibs/intel/common/m9k_bb.v create mode 100644 yosys/techlibs/intel/cyclone10/cells_arith.v create mode 100644 yosys/techlibs/intel/cyclone10/cells_map.v create mode 100644 yosys/techlibs/intel/cyclone10/cells_sim.v create mode 100644 yosys/techlibs/intel/cycloneiv/cells_arith.v create mode 100644 yosys/techlibs/intel/cycloneiv/cells_map.v create mode 100644 yosys/techlibs/intel/cycloneiv/cells_sim.v create mode 100644 yosys/techlibs/intel/cycloneive/arith_map.v create mode 100644 yosys/techlibs/intel/cycloneive/cells_map.v create mode 100644 yosys/techlibs/intel/cycloneive/cells_sim.v create mode 100644 yosys/techlibs/intel/cyclonev/cells_arith.v create mode 100644 yosys/techlibs/intel/cyclonev/cells_map.v create mode 100644 yosys/techlibs/intel/cyclonev/cells_sim.v create mode 100644 yosys/techlibs/intel/max10/cells_arith.v create mode 100644 yosys/techlibs/intel/max10/cells_map.v create mode 100644 yosys/techlibs/intel/max10/cells_sim.v create mode 100644 yosys/techlibs/intel/synth_intel.cc create mode 100644 yosys/techlibs/sf2/Makefile.inc create mode 100644 yosys/techlibs/sf2/arith_map.v create mode 100644 yosys/techlibs/sf2/cells_map.v create mode 100644 yosys/techlibs/sf2/cells_sim.v create mode 100644 yosys/techlibs/sf2/synth_sf2.cc delete mode 100644 yosys/techlibs/xilinx/drams_bb.v create mode 100644 yosys/techlibs/xilinx/lut2lut.v create mode 100644 yosys/tests/errors/syntax_err01.v create mode 100644 yosys/tests/errors/syntax_err02.v create mode 100644 yosys/tests/errors/syntax_err03.v create mode 100644 yosys/tests/errors/syntax_err04.v create mode 100644 yosys/tests/errors/syntax_err05.v create mode 100644 yosys/tests/errors/syntax_err06.v create mode 100644 yosys/tests/errors/syntax_err07.v create mode 100644 yosys/tests/errors/syntax_err08.v create mode 100644 yosys/tests/errors/syntax_err09.v create mode 100644 yosys/tests/errors/syntax_err10.v create mode 100644 yosys/tests/errors/syntax_err11.v create mode 100644 yosys/tests/errors/syntax_err12.v create mode 100644 yosys/tests/errors/syntax_err13.v create mode 100644 yosys/tests/lut/.gitignore create mode 100644 yosys/tests/lut/check_map.ys create mode 100644 yosys/tests/lut/map_and.v create mode 100644 yosys/tests/lut/map_cmp.v create mode 100644 yosys/tests/lut/map_mux.v create mode 100644 yosys/tests/lut/map_not.v create mode 100644 yosys/tests/lut/map_or.v create mode 100644 yosys/tests/lut/map_xor.v create mode 100755 yosys/tests/lut/run-test.sh create mode 100644 yosys/tests/opt/.gitignore create mode 100644 yosys/tests/opt/opt_expr_cmp.v create mode 100644 yosys/tests/opt/opt_expr_cmp.ys create mode 100644 yosys/tests/opt/opt_lut.v create mode 100644 yosys/tests/opt/opt_lut.ys create mode 100644 yosys/tests/opt/opt_lut_elim.il create mode 100644 yosys/tests/opt/opt_lut_elim.ys create mode 100644 yosys/tests/opt/opt_lut_port.il create mode 100644 yosys/tests/opt/opt_lut_port.ys create mode 100755 yosys/tests/opt/run-test.sh create mode 100644 yosys/tests/sat/sizebits.sv create mode 100644 yosys/tests/sat/sizebits.ys create mode 100644 yosys/tests/simple/hierdefparam.v create mode 100644 yosys/tests/simple/specify.v create mode 100644 yosys/tests/sva/.gitignore create mode 100644 yosys/tests/sva/Makefile create mode 100644 yosys/tests/sva/basic00.sv create mode 100644 yosys/tests/sva/basic01.sv create mode 100644 yosys/tests/sva/basic02.sv create mode 100644 yosys/tests/sva/basic03.sv create mode 100644 yosys/tests/sva/basic04.sv create mode 100644 yosys/tests/sva/basic04.vhd create mode 100644 yosys/tests/sva/basic05.sv create mode 100644 yosys/tests/sva/basic05.vhd create mode 100644 yosys/tests/sva/counter.sv create mode 100644 yosys/tests/sva/runtest.sh create mode 100644 yosys/tests/sva/sva_not.sv create mode 100644 yosys/tests/sva/sva_range.sv create mode 100644 yosys/tests/sva/sva_throughout.sv create mode 100644 yosys/tests/svinterfaces/.gitignore create mode 100755 yosys/tests/svinterfaces/run-test.sh create mode 100755 yosys/tests/svinterfaces/runone.sh create mode 100644 yosys/tests/svinterfaces/svinterface1.sv create mode 100644 yosys/tests/svinterfaces/svinterface1_ref.v create mode 100644 yosys/tests/svinterfaces/svinterface1_tb.v create mode 100644 yosys/tests/svinterfaces/svinterface_at_top.sv create mode 100644 yosys/tests/svinterfaces/svinterface_at_top_ref.v create mode 100644 yosys/tests/svinterfaces/svinterface_at_top_tb.v create mode 100644 yosys/tests/svinterfaces/svinterface_at_top_tb_wrapper.v create mode 100644 yosys/tests/svinterfaces/svinterface_at_top_wrapper.v create mode 100644 yosys/tests/unit/Makefile create mode 100644 yosys/tests/unit/kernel/logTest.cc create mode 100644 yosys/tests/unit/kernel/rtlilTest.cc create mode 100644 yosys/tests/various/reg_wire_error.sv create mode 100644 yosys/tests/various/reg_wire_error.ys diff --git a/yosys/Brewfile b/yosys/Brewfile new file mode 100644 index 000000000..0c58ce161 --- /dev/null +++ b/yosys/Brewfile @@ -0,0 +1,8 @@ +brew "bison" +brew "flex" +brew "gawk" +brew "libffi" +brew "git" +brew "graphviz" +brew "pkg-config" +brew "python3" diff --git a/yosys/CHANGELOG b/yosys/CHANGELOG index bfea999a6..42e01645e 100644 --- a/yosys/CHANGELOG +++ b/yosys/CHANGELOG @@ -3,6 +3,142 @@ List of major changes and improvements between releases ======================================================= +Yosys 0.8 .. Yosys 0.8-dev +-------------------------- + + * Various + - Added $changed support to read_verilog + - Added "write_edif -attrprop" + - Added "ice40_unlut" pass + - Added "opt_lut" pass + - Added "synth_ice40 -relut" + - Added "synth_ice40 -noabc" + - Added "gate2lut.v" techmap rule + - Added "rename -src" + - Added "equiv_opt" pass + + +Yosys 0.7 .. Yosys 0.8 +---------------------- + + * Various + - Many bugfixes and small improvements + - Strip debug symbols from installed binary + - Replace -ignore_redef with -[no]overwrite in front-ends + - Added write_verilog hex dump support, add -nohex option + - Added "write_verilog -decimal" + - Added "scc -set_attr" + - Added "verilog_defines" command + - Remeber defines from one read_verilog to next + - Added support for hierarchical defparam + - Added FIRRTL back-end + - Improved ABC default scripts + - Added "design -reset-vlog" + - Added "yosys -W regex", "yosys -w regex", and "yosys -e regex" + - Added Verilog $rtoi and $itor support + - Added "check -initdrv" + - Added "read_blif -wideports" + - Added support for systemVerilog "++" and "--" operators + - Added support for SystemVerilog unique, unique0, and priority case + - Added "write_edif" options for edif "flavors" + - Added support for resetall compiler directive + - Added simple C beck-end (bitwise combinatorical only atm) + - Added $_ANDNOT_ and $_ORNOT_ cell types + - Added cell library aliases to "abc -g" + - Added "setundef -anyseq" + - Added "chtype" command + - Added "design -import" + - Added "write_table" command + - Added "read_json" command + - Added "sim" command + - Added "extract_fa" and "extract_reduce" commands + - Added "extract_counter" command + - Added "opt_demorgan" command + - Added support for $size and $bits SystemVerilog functions + - Added "blackbox" command + - Added "ltp" command + - Added support for editline as replacement for readline + - Added warnings for driver-driver conflicts between FFs (and other cells) and constants + - Added "yosys -E" for creating Makefile dependencies files + - Added "synth -noshare" + - Added "memory_nordff" + - Added "setundef -undef -expose -anyconst" + - Added "expose -input" + - Added specify/specparam parser support (simply ignore them) + - Added "write_blif -inames -iattr" + - Added "hierarchy -simcheck" + - Added an option to statically link abc into yosys + - Added protobuf back-end + - Added BLIF parsing support for .conn and .cname + - Added read_verilog error checking for reg/wire/logic misuse + - Added "make coverage" and ENABLE_GCOV build option + + * Changes in Yosys APIs + - Added ConstEval defaultval feature + - Added {get,set}_src_attribute() methods on RTLIL::AttrObject + - Added SigSpec::is_fully_ones() and Const::is_fully_ones() + - Added log_file_warning() and log_file_error() functions + + * Formal Verification + - Added "write_aiger" + - Added "yosys-smtbmc --aig" + - Added "always " to .smtc format + - Added $cover cell type and support for cover properties + - Added $fair/$live cell type and support for liveness properties + - Added smtbmc support for memory vcd dumping + - Added "chformal" command + - Added "write_smt2 -stbv" and "write_smt2 -stdt" + - Fix equiv_simple, old behavior now available with "equiv_simple -short" + - Change to Yices2 as default SMT solver (it is GPL now) + - Added "yosys-smtbmc --presat" (now default in SymbiYosys) + - Added "yosys-smtbmc --smtc-init --smtc-top --noinit" + - Added a brand new "write_btor" command for BTOR2 + - Added clk2fflogic memory support and other improvements + - Added "async memory write" support to write_smt2 + - Simulate clock toggling in yosys-smtbmc VCD output + - Added $allseq/$allconst cells for EA-solving + - Make -nordff the default in "prep" + - Added (* gclk *) attribute + - Added "async2sync" pass for single-clock designs with async resets + + * Verific support + - Many improvements in Verific front-end + - Added proper handling of concurent SVA properties + - Map "const" and "rand const" to $anyseq/$anyconst + - Added "verific -import -flatten" and "verific -import -extnets" + - Added "verific -vlog-incdir -vlog-define -vlog-libdir" + - Remove PSL support (because PSL has been removed in upstream Verific) + - Improve integration with "hierarchy" command design elaboration + - Added YOSYS_NOVERIFIC for running non-verific test cases with verific bin + - Added simpilied "read" command that automatically uses verific if available + - Added "verific -set- .." + - Added "verific -work " + + * New back-ends + - Added initial Coolrunner-II support + - Added initial eASIC support + - Added initial ECP5 support + + * GreenPAK Support + - Added support for GP_DLATCH, GP_SPI, GP_DCMx, GP_COUNTx, etc. + + * iCE40 Support + - Add "synth_ice40 -vpr" + - Add "synth_ice40 -nodffe" + - Add "synth_ice40 -json" + - Add Support for UltraPlus cells + + * MAX10 and Cyclone IV Support + - Added initial version of metacommand "synth_intel". + - Improved write_verilog command to produce VQM netlist for Quartus Prime. + - Added support for MAX10 FPGA family synthesis. + - Added support for Cyclone IV family synthesis. + - Added example of implementation for DE2i-150 board. + - Added example of implementation for MAX10 development kit. + - Added LFSR example from Asic World. + - Added "dffinit -highlow" for mapping to Intel primitives + + Yosys 0.6 .. Yosys 0.7 ---------------------- diff --git a/yosys/COPYING b/yosys/COPYING new file mode 100644 index 000000000..a121cdfe9 --- /dev/null +++ b/yosys/COPYING @@ -0,0 +1,13 @@ +Copyright (C) 2012 - 2018 Clifford Wolf + +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. diff --git a/yosys/CodingReadme b/yosys/CodingReadme index cbe1fb8be..b64e79178 100644 --- a/yosys/CodingReadme +++ b/yosys/CodingReadme @@ -21,7 +21,7 @@ Here is a the C++ code for a "hello_world" Yosys command (hello.cc): struct HelloWorldPass : public Pass { HelloWorldPass() : Pass("hello_world") { } - virtual void execute(vector, Design*) { + void execute(vector, Design*) override { log("Hello World!\n"); } } HelloWorldPass; @@ -373,6 +373,7 @@ Finally run all tests with "make config-{clang,gcc,gcc-4.8}": cd ~yosys make clean make test + make ystests make vloghtb make install @@ -411,3 +412,98 @@ Updating the website: git commit -am update make push + + +Cross-Building for Windows with MXE +=================================== + +Check http://mxe.cc/#requirements and install all missing requirements. + +As root (or other user with write access to /usr/local/src): + + cd /usr/local/src + git clone https://github.com/mxe/mxe.git + cd mxe + + make -j$(nproc) MXE_PLUGIN_DIRS="plugins/tcl.tk" \ + MXE_TARGETS="i686-w64-mingw32.static" \ + gcc tcl readline + +Then as regular user in some directory where you build stuff: + + git clone https://github.com/cliffordwolf/yosys.git yosys-win32 + cd yosys-win32 + make config-mxe + make -j$(nproc) mxebin + + + +How to add unit test +==================== + +Unit test brings some advantages, briefly, we can list some of them (reference +[1](https://en.wikipedia.org/wiki/Unit_testing)): + +* Tests reduce bugs in new features; +* Tests reduce bugs in existing features; +* Tests are good documentation; +* Tests reduce the cost of change; +* Tests allow refactoring; + +With those advantages in mind, it was required to choose a framework which fits +well with C/C++ code. Hence, it was chosen (google test) +[https://github.com/google/googletest], because it is largely used and it is +relatively easy learn. + +Install and configure google test (manually) +-------------------------------------------- + +In this section, you will see a brief description of how to install google +test. However, it is strongly recommended that you take a look to the official +repository (https://github.com/google/googletest) and refers to that if you +have any problem to install it. Follow the steps below: + +* Install: cmake and pthread +* Clone google test project from: https://github.com/google/googletest and + enter in the project directory +* Inside project directory, type: + +``` +cmake -DBUILD_SHARED_LIBS=ON . +make +``` + +* After compilation, copy all "*.so" inside directory "googlemock" and + "googlemock/gtest" to "/usr/lib/" +* Done! Now you can compile your tests. + +If you have any problem, go to the official repository to find help. + +Ps.: Some distros already have googletest packed. If your distro supports it, +you can use it instead of compile. + +Create new unit test +-------------------- + +If you want to add new unit tests for Yosys, just follow the steps below: + +* Go to directory "yosys/test/unit/" +* In this directory you can find something similar Yosys's directory structure. + To create your unit test file you have to follow this pattern: + fileNameToImplementUnitTest + Test.cc. E.g.: if you want to implement the + unit test for kernel/celledges.cc, you will need to create a file like this: + tests/unit/kernel/celledgesTest.cc; +* Implement your unit test + +Run unit test +------------- + +To compile and run all unit tests, just go to yosys root directory and type: +``` +make unit-test +``` + +If you want to remove all unit test files, type: +``` +make clean-unit-test +``` diff --git a/yosys/Makefile b/yosys/Makefile index aa3386375..d2794c57e 100644 --- a/yosys/Makefile +++ b/yosys/Makefile @@ -5,19 +5,29 @@ # CONFIG := emcc # CONFIG := mxe # CONFIG := msys2 +# CONFIG := msys2-64 # features (the more the better) ENABLE_TCL := 1 ENABLE_ABC := 1 ENABLE_PLUGINS := 1 ENABLE_READLINE := 1 +ENABLE_EDITLINE := 0 ENABLE_VERIFIC := 0 ENABLE_COVER := 1 ENABLE_LIBYOSYS := 0 +ENABLE_PROTOBUF := 0 # other configuration flags +ENABLE_GCOV := 0 ENABLE_GPROF := 0 +ENABLE_DEBUG := 0 ENABLE_NDEBUG := 0 +LINK_CURSES := 0 +LINK_TERMCAP := 0 +LINK_ABC := 0 +# Needed for environments that don't have proper thread support (i.e. emscripten) +DISABLE_ABC_THREADS := 0 # clang sanitizers SANITIZER = @@ -27,6 +37,7 @@ SANITIZER = # SANITIZER = cfi +OS := $(shell uname -s) PREFIX ?= /usr/local INSTALL_SUDO := @@ -44,35 +55,52 @@ TARGETS = yosys$(EXE) yosys-config PRETTY = 1 SMALL = 0 +# Unit test +UNITESTPATH := tests/unit + all: top-all YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST))) VPATH := $(YOSYS_SRC) -CXXFLAGS += -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include -LDFLAGS += -L$(LIBDIR) -LDLIBS = -lstdc++ -lm +CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -D_YOSYS_ -fPIC -I$(PREFIX)/include +LDFLAGS := $(LDFLAGS) -L$(LIBDIR) +LDLIBS := $(LDLIBS) -lstdc++ -lm +PLUGIN_LDFLAGS := -PKG_CONFIG = pkg-config -SED = sed -BISON = bison +PKG_CONFIG ?= pkg-config +SED ?= sed +BISON ?= bison +STRIP ?= strip +AWK ?= awk -ifeq (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 +ifeq ($(OS), Darwin) +PLUGIN_LDFLAGS += -undefined dynamic_lookup + +# homebrew search paths +ifneq ($(shell which brew),) +BREW_PREFIX := $(shell brew --prefix)/opt +CXXFLAGS += -I$(BREW_PREFIX)/readline/include +LDFLAGS += -L$(BREW_PREFIX)/readline/lib +PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH) +PKG_CONFIG_PATH := $(BREW_PREFIX)/tcl-tk/lib/pkgconfig:$(PKG_CONFIG_PATH) +export PATH := $(BREW_PREFIX)/bison/bin:$(BREW_PREFIX)/gettext/bin:$(BREW_PREFIX)/flex/bin:$(PATH) + +# macports search paths +else ifneq ($(shell which port),) +PORT_PREFIX := $(patsubst %/bin/port,%,$(shell which port)) +CXXFLAGS += -I$(PORT_PREFIX)/include +LDFLAGS += -L$(PORT_PREFIX)/lib +PKG_CONFIG_PATH := $(PORT_PREFIX)/lib/pkgconfig:$(PKG_CONFIG_PATH) +export PATH := $(PORT_PREFIX)/bin:$(PATH) endif -YOSYS_VER := 0.7 +else +LDFLAGS += -rdynamic +LDLIBS += -lrt +endif + +YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. | wc -l; }) GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN) OBJS = kernel/version_$(GIT_REV).o @@ -82,7 +110,7 @@ OBJS = kernel/version_$(GIT_REV).o # is just a symlink to your actual ABC working directory, as 'make mrproper' # will remove the 'abc' directory and you do not want to accidentally # delete your work on ABC.. -ABCREV = ae6716b +ABCREV = default ABCPULL = 1 ABCURL ?= https://github.com/berkeley-abc/abc ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 @@ -105,6 +133,7 @@ ifeq ($(CONFIG),clang) CXX = clang LD = clang++ CXXFLAGS += -std=c++11 -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" ifneq ($(SANITIZER),) $(info [Clang Sanitizer] $(SANITIZER)) @@ -127,16 +156,37 @@ else ifeq ($(CONFIG),gcc) CXX = gcc LD = gcc CXXFLAGS += -std=c++11 -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" + +else ifeq ($(CONFIG),gcc-static) +LD = $(CXX) +LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -static +LDLIBS := $(filter-out -lrt,$(LDLIBS)) +CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) +CXXFLAGS += -std=c++11 -Os +ABCMKARGS = CC="$(CC)" CXX="$(CXX)" LD="$(LD)" ABC_USE_LIBSTDCXX=1 LIBS="-lm -lpthread -static" OPTFLAGS="-O" \ + ARCHFLAGS="-DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING=1 -Wno-unused-but-set-variable $(ARCHFLAGS)" ABC_USE_NO_READLINE=1 +ifeq ($(DISABLE_ABC_THREADS),1) +ABCMKARGS += "ABC_USE_NO_PTHREADS=1" +endif else ifeq ($(CONFIG),gcc-4.8) CXX = gcc-4.8 LD = gcc-4.8 CXXFLAGS += -std=c++11 -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" + +else ifeq ($(CONFIG),cygwin) +CXX = gcc +LD = gcc +CXXFLAGS += -std=gnu++11 -Os +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H" else ifeq ($(CONFIG),emcc) CXX = emcc LD = emcc CXXFLAGS := -std=c++11 $(filter-out -fPIC -ggdb,$(CXXFLAGS)) +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DABC_MEMALIGN=8" EMCCFLAGS := -Os -Wno-warn-absolute-paths EMCCFLAGS += --memory-init-file 0 --embed-file share -s NO_EXIT_RUNTIME=1 EMCCFLAGS += -s EXPORTED_FUNCTIONS="['_main','_run','_prompt','_errmsg']" @@ -150,6 +200,11 @@ EXE = .js TARGETS := $(filter-out yosys-config,$(TARGETS)) EXTRA_TARGETS += yosysjs-$(YOSYS_VER).zip +ifeq ($(ENABLE_ABC),1) +LINK_ABC := 1 +DISABLE_ABC_THREADS := 1 +endif + viz.js: wget -O viz.js.part https://github.com/mdaines/viz.js/releases/download/0.0.3/viz.js mv viz.js.part viz.js @@ -164,29 +219,41 @@ yosys.html: misc/yosys.html $(P) cp misc/yosys.html yosys.html else ifeq ($(CONFIG),mxe) -CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc -LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc -CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE +PKG_CONFIG = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-pkg-config +CXX = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ +LD = /usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-g++ +CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_MXE_HACKS -Wno-attributes CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDLIBS := $(filter-out -lrt,$(LDLIBS)) -ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w" -ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="$(CXX)" CXX="$(CXX)" +ABCMKARGS += ARCHFLAGS="-DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" +ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=1 CC="/usr/local/src/mxe/usr/bin/i686-w64-mingw32.static-gcc" EXE = .exe else ifeq ($(CONFIG),msys2) -CXX = i686-w64-mingw32-gcc -LD = i686-w64-mingw32-gcc +CXX = i686-w64-mingw32-g++ +LD = i686-w64-mingw32-g++ CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s LDLIBS := $(filter-out -lrt,$(LDLIBS)) -ABCMKARGS += ARCHFLAGS="-DSIZEOF_VOID_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -x c++ -fpermissive -w" -ABCMKARGS += LIBS="lib/x86/pthreadVC2.lib -s" ABC_USE_NO_READLINE=0 CC="$(CXX)" CXX="$(CXX)" +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" +ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="i686-w64-mingw32-gcc" CXX="$(CXX)" +EXE = .exe + +else ifeq ($(CONFIG),msys2-64) +CXX = x86_64-w64-mingw32-g++ +LD = x86_64-w64-mingw32-g++ +CXXFLAGS += -std=c++11 -Os -D_POSIX_SOURCE -DYOSYS_WIN32_UNIX_DIR +CXXFLAGS := $(filter-out -fPIC,$(CXXFLAGS)) +LDFLAGS := $(filter-out -rdynamic,$(LDFLAGS)) -s +LDLIBS := $(filter-out -lrt,$(LDLIBS)) +ABCMKARGS += ARCHFLAGS="-DABC_USE_STDINT_H -DWIN32_NO_DLL -DHAVE_STRUCT_TIMESPEC -fpermissive -w" +ABCMKARGS += LIBS="-lpthread -s" ABC_USE_NO_READLINE=0 CC="x86_64-w64-mingw32-gcc" CXX="$(CXX)" EXE = .exe else ifneq ($(CONFIG),none) -$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2) +$(error Invalid CONFIG setting '$(CONFIG)'. Valid values: clang, gcc, gcc-4.8, emcc, mxe, msys2, msys2-64) endif ifeq ($(ENABLE_LIBYOSYS),1) @@ -195,22 +262,67 @@ endif ifeq ($(ENABLE_READLINE),1) CXXFLAGS += -DYOSYS_ENABLE_READLINE +ifeq ($(OS), FreeBSD) +CXXFLAGS += -I/usr/local/include +endif LDLIBS += -lreadline +ifeq ($(LINK_CURSES),1) +LDLIBS += -lcurses +ABCMKARGS += "ABC_READLINE_LIBRARIES=-lcurses -lreadline" +endif +ifeq ($(LINK_TERMCAP),1) +LDLIBS += -ltermcap +ABCMKARGS += "ABC_READLINE_LIBRARIES=-lreadline -ltermcap" +endif ifeq ($(CONFIG),mxe) -LDLIBS += -lpdcurses +LDLIBS += -ltermcap +endif +else +ifeq ($(ENABLE_EDITLINE),1) +CXXFLAGS += -DYOSYS_ENABLE_EDITLINE +LDLIBS += -ledit -ltinfo -lbsd +else +ABCMKARGS += "ABC_USE_NO_READLINE=1" endif endif +ifeq ($(DISABLE_ABC_THREADS),1) +ABCMKARGS += "ABC_USE_NO_PTHREADS=1" +endif + ifeq ($(ENABLE_PLUGINS),1) -CXXFLAGS += -DYOSYS_ENABLE_PLUGINS $(shell $(PKG_CONFIG) --silence-errors --cflags libffi) -LDLIBS += $(shell $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) -ldl +CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags libffi) -DYOSYS_ENABLE_PLUGINS +LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs libffi || echo -lffi) +ifneq ($(OS), FreeBSD) +LDLIBS += -ldl +endif endif ifeq ($(ENABLE_TCL),1) TCL_VERSION ?= tcl$(shell bash -c "tclsh <(echo 'puts [info tclversion]')") +ifeq ($(OS), FreeBSD) +TCL_INCLUDE ?= /usr/local/include/$(TCL_VERSION) +else TCL_INCLUDE ?= /usr/include/$(TCL_VERSION) -CXXFLAGS += -I$(TCL_INCLUDE) -DYOSYS_ENABLE_TCL -LDLIBS += -l$(TCL_VERSION) +endif + +ifeq ($(CONFIG),mxe) +CXXFLAGS += -DYOSYS_ENABLE_TCL +LDLIBS += -ltcl86 -lwsock32 -lws2_32 -lnetapi32 -lz +else +CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --cflags tcl || echo -I$(TCL_INCLUDE)) -DYOSYS_ENABLE_TCL +ifeq ($(OS), FreeBSD) +# FreeBSD uses tcl8.6, but lib is named "libtcl86" +LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION) | tr -d '.') +else +LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) $(PKG_CONFIG) --silence-errors --libs tcl || echo -l$(TCL_VERSION)) +endif +endif +endif + +ifeq ($(ENABLE_GCOV),1) +CXXFLAGS += --coverage +LDFLAGS += --coverage endif ifeq ($(ENABLE_GPROF),1) @@ -219,21 +331,44 @@ LDFLAGS += -pg endif ifeq ($(ENABLE_NDEBUG),1) -CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os,$(CXXFLAGS)) +CXXFLAGS := -O3 -DNDEBUG $(filter-out -Os -ggdb,$(CXXFLAGS)) +endif + +ifeq ($(ENABLE_DEBUG),1) +ifeq ($(CONFIG),clang) +CXXFLAGS := -O0 -DDEBUG $(filter-out -Os,$(CXXFLAGS)) +else +CXXFLAGS := -Og -DDEBUG $(filter-out -Os,$(CXXFLAGS)) +endif endif ifeq ($(ENABLE_ABC),1) CXXFLAGS += -DYOSYS_ENABLE_ABC +ifeq ($(LINK_ABC),1) +CXXFLAGS += -DYOSYS_LINK_ABC +ifeq ($(DISABLE_ABC_THREADS),0) +LDLIBS += -lpthread +endif +else ifeq ($(ABCEXTERNAL),) TARGETS += yosys-abc$(EXE) endif endif +endif ifeq ($(ENABLE_VERIFIC),1) -VERIFIC_DIR ?= /usr/local/src/verific_lib_eval -VERIFIC_COMPONENTS ?= verilog vhdl database util containers sdf +VERIFIC_DIR ?= /usr/local/src/verific_lib +VERIFIC_COMPONENTS ?= verilog vhdl database util containers hier_tree CXXFLAGS += $(patsubst %,-I$(VERIFIC_DIR)/%,$(VERIFIC_COMPONENTS)) -DYOSYS_ENABLE_VERIFIC -LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) +ifeq ($(OS), Darwin) +LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-mac.a,$(VERIFIC_COMPONENTS)) -lz +else +LDLIBS += $(patsubst %,$(VERIFIC_DIR)/%/*-linux.a,$(VERIFIC_COMPONENTS)) -lz +endif +endif + +ifeq ($(ENABLE_PROTOBUF),1) +LDLIBS += $(shell pkg-config --cflags --libs protobuf) endif ifeq ($(ENABLE_COVER),1) @@ -261,8 +396,8 @@ endef ifeq ($(PRETTY), 1) P_STATUS = 0 P_OFFSET = 0 -P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | gawk 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }')) -P_SHOW = [$(shell gawk "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%] +P_UPDATE = $(eval P_STATUS=$(shell echo $(OBJS) yosys$(EXE) | $(AWK) 'BEGIN { RS = " "; I = $(P_STATUS)+0; } $$1 == "$@" && NR > I { I = NR; } END { print I; }')) +P_SHOW = [$(shell $(AWK) "BEGIN { N=$(words $(OBJS) yosys$(EXE)); printf \"%3d\", $(P_OFFSET)+90*$(P_STATUS)/N; exit; }")%] P = @echo "$(if $(findstring $@,$(TARGETS) $(EXTRA_TARGETS)),$(eval P_OFFSET = 10))$(call P_UPDATE)$(call P_SHOW) Building $@"; Q = @ S = -s @@ -347,6 +482,10 @@ include techlibs/common/Makefile.inc endif +ifeq ($(LINK_ABC),1) +OBJS += yosys-libabc.a +endif + top-all: $(TARGETS) $(EXTRA_TARGETS) @echo "" @echo " Build successful." @@ -377,19 +516,30 @@ kernel/version_$(GIT_REV).cc: $(YOSYS_SRC)/Makefile $(P) rm -f kernel/version_*.o kernel/version_*.d kernel/version_*.cc $(Q) mkdir -p kernel && echo "namespace Yosys { extern const char *yosys_version_str; const char *yosys_version_str=\"$(YOSYS_VER_STR)\"; }" > kernel/version_$(GIT_REV).cc +ifeq ($(ENABLE_VERIFIC),1) +CXXFLAGS_NOVERIFIC = $(foreach v,$(CXXFLAGS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) +LDLIBS_NOVERIFIC = $(foreach v,$(LDLIBS),$(if $(findstring $(VERIFIC_DIR),$(v)),,$(v))) +else +CXXFLAGS_NOVERIFIC = $(CXXFLAGS) +LDLIBS_NOVERIFIC = $(LDLIBS) +endif + yosys-config: misc/yosys-config.in - $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(CXXFLAGS))#;' \ - -e 's#@CXX@#$(CXX)#;' -e 's#@LDFLAGS@#$(LDFLAGS)#;' -e 's#@LDLIBS@#$(LDLIBS)#;' \ - -e 's#@BINDIR@#$(BINDIR)#;' -e 's#@DATDIR@#$(DATDIR)#;' < $< > yosys-config + $(P) $(SED) -e 's#@CXXFLAGS@#$(subst -I. -I"$(YOSYS_SRC)",-I"$(DATDIR)/include",$(strip $(CXXFLAGS_NOVERIFIC)))#;' \ + -e 's#@CXX@#$(strip $(CXX))#;' -e 's#@LDFLAGS@#$(strip $(LDFLAGS) $(PLUGIN_LDFLAGS))#;' -e 's#@LDLIBS@#$(strip $(LDLIBS_NOVERIFIC))#;' \ + -e 's#@BINDIR@#$(strip $(BINDIR))#;' -e 's#@DATDIR@#$(strip $(DATDIR))#;' < $< > yosys-config $(Q) chmod +x yosys-config abc/abc-$(ABCREV)$(EXE) abc/libabc-$(ABCREV).a: $(P) ifneq ($(ABCREV),default) - $(Q) if ( cd abc 2> /dev/null && hg identify; ) | grep -q +; then \ + $(Q) if test -d abc/.hg; then \ + echo 'REEBE: NOP qverpgbel vf n ut jbexvat pbcl! Erzbir nop/ naq er-eha "znxr".' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ + fi + $(Q) if ( cd abc 2> /dev/null && ! git diff-index --quiet HEAD; ); then \ echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \ fi - $(Q) if test "`cd abc 2> /dev/null && hg identify | cut -f1 -d' '`" != "$(ABCREV)"; then \ + $(Q) if test "`cd abc 2> /dev/null && git rev-parse --short HEAD`" != "$(ABCREV)"; then \ test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \ echo "Pulling ABC from $(ABCURL):"; set -x; \ test -d abc || git clone $(ABCURL) abc; \ @@ -407,6 +557,9 @@ endif yosys-abc$(EXE): abc/abc-$(ABCREV)$(EXE) $(P) cp abc/abc-$(ABCREV)$(EXE) yosys-abc$(EXE) +yosys-libabc.a: abc/libabc-$(ABCREV).a + $(P) cp abc/libabc-$(ABCREV).a yosys-libabc.a + ifneq ($(SEED),) SEEDOPT="-S $(SEED)" else @@ -425,6 +578,8 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/bram && bash run-test.sh $(SEEDOPT) +cd tests/various && bash run-test.sh +cd tests/sat && bash run-test.sh + +cd tests/svinterfaces && bash run-test.sh $(SEEDOPT) + +cd tests/opt && bash run-test.sh @echo "" @echo " Passed \"make test\"." @echo "" @@ -443,18 +598,44 @@ vloghtb: $(TARGETS) $(EXTRA_TARGETS) @echo " Passed \"make vloghtb\"." @echo "" +ystests: $(TARGETS) $(EXTRA_TARGETS) + rm -rf tests/ystests + git clone https://github.com/YosysHQ/yosys-tests.git tests/ystests + +$(MAKE) PATH="$$PWD:$$PATH" -C tests/ystests + @echo "" + @echo " Finished \"make ystests\"." + @echo "" + +# Unit test +unit-test: libyosys.so + @$(MAKE) -C $(UNITESTPATH) CXX="$(CXX)" CPPFLAGS="$(CPPFLAGS)" \ + CXXFLAGS="$(CXXFLAGS)" LDLIBS="$(LDLIBS)" ROOTPATH="$(CURDIR)" + +clean-unit-test: + @$(MAKE) -C $(UNITESTPATH) clean + install: $(TARGETS) $(EXTRA_TARGETS) $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(BINDIR) - $(INSTALL_SUDO) install $(TARGETS) $(DESTDIR)$(BINDIR) + $(INSTALL_SUDO) cp $(TARGETS) $(DESTDIR)$(BINDIR) +ifneq ($(filter yosys,$(TARGETS)),) + $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(BINDIR)/yosys +endif +ifneq ($(filter yosys-abc,$(TARGETS)),) + $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/yosys-abc +endif +ifneq ($(filter yosys-filterlib,$(TARGETS)),) + $(INSTALL_SUDO) $(STRIP) $(DESTDIR)$(BINDIR)/yosys-filterlib +endif $(INSTALL_SUDO) mkdir -p $(DESTDIR)$(DATDIR) $(INSTALL_SUDO) cp -r share/. $(DESTDIR)$(DATDIR)/. ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) cp libyosys.so $(DESTDIR)$(LIBDIR) + $(INSTALL_SUDO) $(STRIP) -S $(DESTDIR)$(LIBDIR)/libyosys.so $(INSTALL_SUDO) ldconfig endif uninstall: - $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR),$(notdir $(TARGETS))) + $(INSTALL_SUDO) rm -vf $(addprefix $(DESTDIR)$(BINDIR)/,$(notdir $(TARGETS))) $(INSTALL_SUDO) rm -rvf $(DESTDIR)$(DATDIR) ifeq ($(ENABLE_LIBYOSYS),1) $(INSTALL_SUDO) rm -vf $(DESTDIR)$(LIBDIR)/libyosys.so @@ -472,16 +653,31 @@ clean: rm -rf share if test -d manual; then cd manual && sh clean.sh; fi rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS) - rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* + rm -f kernel/version_*.o kernel/version_*.cc abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a rm -f libs/*/*.d frontends/*/*.d passes/*/*.d backends/*/*.d kernel/*.d techlibs/*/*.d + rm -rf tests/asicworld/*.out tests/asicworld/*.log + rm -rf tests/hana/*.out tests/hana/*.log + rm -rf tests/simple/*.out tests/simple/*.log + rm -rf tests/memories/*.out tests/memories/*.log tests/memories/*.dmp + rm -rf tests/sat/*.log tests/techmap/*.log tests/various/*.log + rm -rf tests/bram/temp tests/fsm/temp tests/realmath/temp tests/share/temp tests/smv/temp + rm -rf vloghtb/Makefile vloghtb/refdat vloghtb/rtl vloghtb/scripts vloghtb/spec vloghtb/check_yosys vloghtb/vloghammer_tb.tar.bz2 vloghtb/temp vloghtb/log_test_* + rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff + rm -f tests/tools/cmp_tbdata clean-abc: $(MAKE) -C abc DEP= clean - rm -f yosys-abc$(EXE) abc/abc-[0-9a-f]* + rm -f yosys-abc$(EXE) yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a mrproper: clean git clean -xdf +coverage: + ./yosys -qp 'help; help -all' + rm -rf coverage.info coverage_html + lcov --capture -d . --no-external -o coverage.info + genhtml coverage.info --output-directory coverage_html + qtcreator: { for file in $(basename $(OBJS)); do \ for prefix in cc y l; do if [ -f $${file}.$${prefix} ]; then echo $$file.$${prefix}; fi; done \ @@ -521,6 +717,12 @@ config-clang: clean config-gcc: clean echo 'CONFIG := gcc' > Makefile.conf +config-gcc-static: clean + echo 'CONFIG := gcc-static' > Makefile.conf + echo 'ENABLE_PLUGINS := 0' >> Makefile.conf + echo 'ENABLE_READLINE := 0' >> Makefile.conf + echo 'ENABLE_TCL := 0' >> Makefile.conf + config-gcc-4.8: clean echo 'CONFIG := gcc-4.8' > Makefile.conf @@ -533,13 +735,22 @@ config-emcc: clean config-mxe: clean echo 'CONFIG := mxe' > Makefile.conf - echo 'ENABLE_TCL := 0' >> Makefile.conf echo 'ENABLE_PLUGINS := 0' >> Makefile.conf - echo 'ENABLE_READLINE := 0' >> Makefile.conf config-msys2: clean echo 'CONFIG := msys2' > Makefile.conf +config-msys2-64: clean + echo 'CONFIG := msys2-64' > Makefile.conf + +config-cygwin: clean + echo 'CONFIG := cygwin' > Makefile.conf + +config-gcov: clean + echo 'CONFIG := gcc' > Makefile.conf + echo 'ENABLE_GCOV := 1' >> Makefile.conf + echo 'ENABLE_DEBUG := 1' >> Makefile.conf + config-gprof: clean echo 'CONFIG := gcc' > Makefile.conf echo 'ENABLE_GPROF := 1' >> Makefile.conf @@ -560,6 +771,6 @@ echo-git-rev: -include kernel/*.d -include techlibs/*/*.d -.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator -.PHONY: config-clean config-clang config-gcc config-gcc-4.8 config-gprof config-sudo +.PHONY: all top-all abc test install install-abc manual clean mrproper qtcreator coverage vcxsrc mxebin +.PHONY: config-clean config-clang config-gcc config-gcc-static config-gcc-4.8 config-gprof config-sudo diff --git a/yosys/README.md b/yosys/README.md new file mode 100644 index 000000000..883f2b0a5 --- /dev/null +++ b/yosys/README.md @@ -0,0 +1,490 @@ +``` +yosys -- Yosys Open SYnthesis Suite + +Copyright (C) 2012 - 2018 Clifford Wolf + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + + +yosys – Yosys Open SYnthesis Suite +=================================== + +This is a framework for RTL synthesis tools. It currently has +extensive Verilog-2005 support and provides a basic set of +synthesis algorithms for various application domains. + +Yosys can be adapted to perform any synthesis job by combining +the existing passes (algorithms) using synthesis scripts and +adding additional passes as needed by extending the yosys C++ +code base. + +Yosys is free software licensed under the ISC license (a GPL +compatible license that is similar in terms to the MIT license +or the 2-clause BSD license). + + +Web Site +======== + +More information and documentation can be found on the Yosys web site: +http://www.clifford.at/yosys/ + +Setup +====== + +You need a C++ compiler with C++11 support (up-to-date CLANG or GCC is +recommended) and some standard tools such as GNU Flex, GNU Bison, and GNU Make. +TCL, readline and libffi are optional (see ``ENABLE_*`` settings in Makefile). +Xdot (graphviz) is used by the ``show`` command in yosys to display schematics. + +For example on Ubuntu Linux 16.04 LTS the following commands will install all +prerequisites for building yosys: + + $ sudo apt-get install build-essential clang bison flex \ + libreadline-dev gawk tcl-dev libffi-dev git \ + graphviz xdot pkg-config python3 + +Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies: + + $ brew tap Homebrew/bundle && brew bundle + $ sudo port install bison flex readline gawk libffi \ + git graphviz pkgconfig python36 + +On FreeBSD use the following command to install all prerequisites: + + # pkg install bison flex readline gawk libffi\ + git graphviz pkgconfig python3 python36 tcl-wrapper + +On FreeBSD system use gmake instead of make. To run tests use: + % MAKE=gmake CC=cc gmake test + +For Cygwin use the following command to install all prerequisites, or select these additional packages: + + setup-x86_64.exe -q --packages=bison,flex,gcc-core,gcc-g++,git,libffi-devel,libreadline-devel,make,pkg-config,python3,tcl-devel + +There are also pre-compiled Yosys binary packages for Ubuntu and Win32 as well +as a source distribution for Visual Studio. Visit the Yosys download page for +more information: http://www.clifford.at/yosys/download.html + +To configure the build system to use a specific compiler, use one of + + $ make config-clang + $ make config-gcc + +For other compilers and build configurations it might be +necessary to make some changes to the config section of the +Makefile. + + $ vi Makefile # ..or.. + $ vi Makefile.conf + +To build Yosys simply type 'make' in this directory. + + $ make + $ make test + $ sudo make install + +Note that this also downloads, builds and installs ABC (using yosys-abc +as executable name). + +Getting Started +=============== + +Yosys can be used with the interactive command shell, with +synthesis scripts or with command line arguments. Let's perform +a simple synthesis job using the interactive command shell: + + $ ./yosys + yosys> + +the command ``help`` can be used to print a list of all available +commands and ``help `` to print details on the specified command: + + yosys> help help + +reading the design using the Verilog frontend: + + yosys> read_verilog tests/simple/fiedler-cooley.v + +writing the design to the console in Yosys's internal format: + + yosys> write_ilang + +elaborate design hierarchy: + + yosys> hierarchy + +convert processes (``always`` blocks) to netlist elements and perform +some simple optimizations: + + yosys> proc; opt + +display design netlist using ``xdot``: + + yosys> show + +the same thing using ``gv`` as postscript viewer: + + yosys> show -format ps -viewer gv + +translating netlist to gate logic and perform some simple optimizations: + + yosys> techmap; opt + +write design netlist to a new Verilog file: + + yosys> write_verilog synth.v + +a similar synthesis can be performed using yosys command line options only: + + $ ./yosys -o synth.v -p hierarchy -p proc -p opt \ + -p techmap -p opt tests/simple/fiedler-cooley.v + +or using a simple synthesis script: + + $ cat synth.ys + read_verilog tests/simple/fiedler-cooley.v + hierarchy; proc; opt; techmap; opt + write_verilog synth.v + + $ ./yosys synth.ys + +It is also possible to only have the synthesis commands but not the read/write +commands in the synthesis script: + + $ cat synth.ys + hierarchy; proc; opt; techmap; opt + + $ ./yosys -o synth.v tests/simple/fiedler-cooley.v synth.ys + +The following very basic synthesis script should work well with all designs: + + # check design hierarchy + hierarchy + + # translate processes (always blocks) + proc; opt + + # detect and optimize FSM encodings + fsm; opt + + # implement memories (arrays) + memory; opt + + # convert to gate logic + techmap; opt + +If ABC is enabled in the Yosys build configuration and a cell library is given +in the liberty file ``mycells.lib``, the following synthesis script will +synthesize for the given cell library: + + # the high-level stuff + hierarchy; proc; fsm; opt; memory; opt + + # mapping to internal cell library + techmap; opt + + # mapping flip-flops to mycells.lib + dfflibmap -liberty mycells.lib + + # mapping logic to mycells.lib + abc -liberty mycells.lib + + # cleanup + clean + +If you do not have a liberty file but want to test this synthesis script, +you can use the file ``examples/cmos/cmos_cells.lib`` from the yosys sources. + +Liberty file downloads for and information about free and open ASIC standard +cell libraries can be found here: + +- http://www.vlsitechnology.org/html/libraries.html +- http://www.vlsitechnology.org/synopsys/vsclib013.lib + +The command ``synth`` provides a good default synthesis script (see +``help synth``). If possible a synthesis script should borrow from ``synth``. +For example: + + # the high-level stuff + hierarchy + synth -run coarse + + # mapping to internal cells + techmap; opt -fast + dfflibmap -liberty mycells.lib + abc -liberty mycells.lib + clean + +Yosys is under construction. A more detailed documentation will follow. + + +Unsupported Verilog-2005 Features +================================= + +The following Verilog-2005 features are not supported by +Yosys and there are currently no plans to add support +for them: + +- Non-synthesizable language features as defined in + IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002 + +- The ``tri``, ``triand``, ``trior``, ``wand`` and ``wor`` net types + +- The ``config`` keyword and library map files + +- The ``disable``, ``primitive`` and ``specify`` statements + +- Latched logic (is synthesized as logic with feedback loops) + + +Verilog Attributes and non-standard features +============================================ + +- The ``full_case`` attribute on case statements is supported + (also the non-standard ``// synopsys full_case`` directive) + +- The ``parallel_case`` attribute on case statements is supported + (also the non-standard ``// synopsys parallel_case`` directive) + +- The ``// synopsys translate_off`` and ``// synopsys translate_on`` + directives are also supported (but the use of ``` `ifdef .. `endif ``` + is strongly recommended instead). + +- The ``nomem2reg`` attribute on modules or arrays prohibits the + automatic early conversion of arrays to separate registers. This + is potentially dangerous. Usually the front-end has good reasons + for converting an array to a list of registers. Prohibiting this + step will likely result in incorrect synthesis results. + +- The ``mem2reg`` attribute on modules or arrays forces the early + conversion of arrays to separate registers. + +- The ``nomeminit`` attribute on modules or arrays prohibits the + creation of initialized memories. This effectively puts ``mem2reg`` + on all memories that are written to in an ``initial`` block and + are not ROMs. + +- The ``nolatches`` attribute on modules or always-blocks + prohibits the generation of logic-loops for latches. Instead + all not explicitly assigned values default to x-bits. This does + not affect clocked storage elements such as flip-flops. + +- The ``nosync`` attribute on registers prohibits the generation of a + storage element. The register itself will always have all bits set + to 'x' (undefined). The variable may only be used as blocking assigned + temporary variable within an always block. This is mostly used internally + by Yosys to synthesize Verilog functions and access arrays. + +- The ``onehot`` attribute on wires mark them as one-hot state register. This + is used for example for memory port sharing and set by the fsm_map pass. + +- The ``blackbox`` attribute on modules is used to mark empty stub modules + that have the same ports as the real thing but do not contain information + on the internal configuration. This modules are only used by the synthesis + passes to identify input and output ports of cells. The Verilog backend + also does not output blackbox modules on default. + +- The ``keep`` attribute on cells and wires is used to mark objects that should + never be removed by the optimizer. This is used for example for cells that + have hidden connections that are not part of the netlist, such as IO pads. + Setting the ``keep`` attribute on a module has the same effect as setting it + on all instances of the module. + +- The ``keep_hierarchy`` attribute on cells and modules keeps the ``flatten`` + command from flattening the indicated cells and modules. + +- The ``init`` attribute on wires is set by the frontend when a register is + initialized "FPGA-style" with ``reg foo = val``. It can be used during + synthesis to add the necessary reset logic. + +- The ``top`` attribute on a module marks this module as the top of the + design hierarchy. The ``hierarchy`` command sets this attribute when called + with ``-top``. Other commands, such as ``flatten`` and various backends + use this attribute to determine the top module. + +- The ``src`` attribute is set on cells and wires created by to the string + ``:`` 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 ``:`` 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 ``'s?[bodh]``) support constant + expressions as . If the expression is not a simple identifier, it + must be put in parentheses. Examples: ``WIDTH'd42``, ``(4+2)'b101010`` + +- The system tasks ``$finish`` and ``$display`` are supported in initial blocks + in an unconditional context (only if/case statements on parameters + and constant values). The intended use for this is synthesis-time DRC. + + +Non-standard or SystemVerilog features for formal verification +============================================================== + +- Support for ``assert``, ``assume``, ``restrict``, and ``cover`` is enabled + when ``read_verilog`` is called with ``-formal``. + +- The system task ``$initstate`` evaluates to 1 in the initial state and + to 0 otherwise. + +- The system function ``$anyconst`` evaluates to any constant value. This is + equivalent to declaring a reg as ``rand const``, but also works outside + of checkers. (Yosys also supports ``rand const`` outside checkers.) + +- The system function ``$anyseq`` evaluates to any value, possibly a different + value in each cycle. This is equivalent to declaring a reg as ``rand``, + but also works outside of checkers. (Yosys also supports ``rand`` + variables outside checkers.) + +- The system functions ``$allconst`` and ``$allseq`` can be used to construct + formal exist-forall problems. Assumptions only hold if the trace satisfies + the assumption for all ``$allconst/$allseq`` values. For assertions and cover + statements it is sufficient if just one ``$allconst/$allseq`` value triggers + the property (similar to ``$anyconst/$anyseq``). + +- Wires/registers declared using the ``anyconst/anyseq/allconst/allseq`` attribute + (for example ``(* anyconst *) reg [7:0] foobar;``) will behave as if driven + by a ``$anyconst/$anyseq/$allconst/$allseq`` function. + +- The SystemVerilog tasks ``$past``, ``$stable``, ``$rose`` and ``$fell`` are + supported in any clocked block. + +- The syntax ``@($global_clock)`` can be used to create FFs that have no + explicit clock input ($ff cells). The same can be achieved by using + ``@(posedge )`` or ``@(negedge )`` when ```` + is marked with the ``(* gclk *)`` Verilog attribute. + + +Supported features from SystemVerilog +===================================== + +When ``read_verilog`` is called with ``-sv``, it accepts some language features +from SystemVerilog: + +- The ``assert`` statement from SystemVerilog is supported in its most basic + form. In module context: ``assert property ();`` and within an + always block: ``assert();``. It is transformed to a $assert cell. + +- The ``assume``, ``restrict``, and ``cover`` statements from SystemVerilog are + also supported. The same limitations as with the ``assert`` statement apply. + +- The keywords ``always_comb``, ``always_ff`` and ``always_latch``, ``logic`` + and ``bit`` are supported. + +- Declaring free variables with ``rand`` and ``rand const`` is supported. + +- Checkers without a port list that do not need to be instantiated (but instead + behave like a named block) are supported. + +- SystemVerilog packages are supported. Once a SystemVerilog file is read + into a design with ``read_verilog``, all its packages are available to + SystemVerilog files being read into the same design afterwards. + +- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether + ports are inputs or outputs are supported. + + +Building the documentation +========================== + +Note that there is no need to build the manual if you just want to read it. +Simply download the PDF from http://www.clifford.at/yosys/documentation.html +instead. + +On Ubuntu, texlive needs these packages to be able to build the manual: + + sudo apt-get install texlive-binaries + sudo apt-get install texlive-science # install algorithm2e.sty + sudo apt-get install texlive-bibtex-extra # gets multibib.sty + sudo apt-get install texlive-fonts-extra # gets skull.sty and dsfont.sty + sudo apt-get install texlive-publishers # IEEEtran.cls + +Also the non-free font luximono should be installed, there is unfortunately +no Ubuntu package for this so it should be installed separately using +`getnonfreefonts`: + + wget https://tug.org/fonts/getnonfreefonts/install-getnonfreefonts + sudo texlua install-getnonfreefonts # will install to /usr/local by default, can be changed by editing BINDIR at MANDIR at the top of the script + getnonfreefonts luximono # installs to /home/user/texmf + +Then execute, from the root of the repository: + + make manual + +Notes: + +- To run `make manual` you need to have installed Yosys with `make install`, + otherwise it will fail on finding `kernel/yosys.h` while building + `PRESENTATION_Prog`. diff --git a/yosys/backends/aiger/Makefile.inc b/yosys/backends/aiger/Makefile.inc new file mode 100644 index 000000000..0fc37e95c --- /dev/null +++ b/yosys/backends/aiger/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/aiger/aiger.o + diff --git a/yosys/backends/aiger/aiger.cc b/yosys/backends/aiger/aiger.cc new file mode 100644 index 000000000..dfe506c66 --- /dev/null +++ b/yosys/backends/aiger/aiger.cc @@ -0,0 +1,788 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void aiger_encode(std::ostream &f, int x) +{ + log_assert(x >= 0); + + while (x & ~0x7f) { + f.put((x & 0x7f) | 0x80); + x = x >> 7; + } + + f.put(x); +} + +struct AigerWriter +{ + Module *module; + bool zinit_mode; + SigMap sigmap; + + dict init_map; + pool input_bits, output_bits; + dict not_map, ff_map, alias_map; + dict> and_map; + vector> asserts, assumes; + vector> liveness, fairness; + pool initstate_bits; + + vector> aig_gates; + vector aig_latchin, aig_latchinit, aig_outputs; + int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; + int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0; + + dict aig_map; + dict ordered_outputs; + dict ordered_latches; + + dict init_inputs; + int initstate_ff = 0; + + int mkgate(int a0, int a1) + { + aig_m++, aig_a++; + aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); + return 2*aig_m; + } + + int bit2aig(SigBit bit) + { + if (aig_map.count(bit) == 0) + { + aig_map[bit] = -1; + + if (initstate_bits.count(bit)) { + log_assert(initstate_ff > 0); + aig_map[bit] = initstate_ff; + } else + if (not_map.count(bit)) { + int a = bit2aig(not_map.at(bit)) ^ 1; + aig_map[bit] = a; + } else + if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + aig_map[bit] = mkgate(a0, a1); + } else + if (alias_map.count(bit)) { + aig_map[bit] = bit2aig(alias_map.at(bit)); + } + + if (bit == State::Sx || bit == State::Sz) + log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); + } + + log_assert(aig_map.at(bit) >= 0); + return aig_map.at(bit); + } + + AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module) + { + pool undriven_bits; + pool unused_bits; + + // promote public wires + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + sigmap.add(wire); + + // promote input wires + for (auto wire : module->wires()) + if (wire->port_input) + sigmap.add(wire); + + // promote output wires + for (auto wire : module->wires()) + if (wire->port_output) + sigmap.add(wire); + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init")) { + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(wire) && i < GetSize(initval); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + init_map[initsig[i]] = initval[i] == State::S1; + } + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wirebit(wire, i); + SigBit bit = sigmap(wirebit); + + if (bit.wire == nullptr) { + if (wire->port_output) { + aig_map[wirebit] = (bit == State::S1) ? 1 : 0; + output_bits.insert(wirebit); + } + continue; + } + + undriven_bits.insert(bit); + unused_bits.insert(bit); + + if (wire->port_input) + input_bits.insert(bit); + + if (wire->port_output) { + if (bit != wirebit) + alias_map[wirebit] = bit; + output_bits.insert(wirebit); + } + } + } + + for (auto bit : input_bits) + undriven_bits.erase(bit); + + for (auto bit : output_bits) + unused_bits.erase(bit); + + for (auto cell : module->cells()) + { + if (cell->type == "$_NOT_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + undriven_bits.erase(Y); + not_map[Y] = A; + continue; + } + + if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_")) + { + SigBit D = sigmap(cell->getPort("\\D").as_bit()); + SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); + unused_bits.erase(D); + undriven_bits.erase(Q); + ff_map[Q] = D; + continue; + } + + if (cell->type == "$_AND_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit B = sigmap(cell->getPort("\\B").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + unused_bits.erase(B); + undriven_bits.erase(Y); + and_map[Y] = make_pair(A, B); + continue; + } + + if (cell->type == "$initstate") + { + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + undriven_bits.erase(Y); + initstate_bits.insert(Y); + continue; + } + + if (cell->type == "$assert") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit EN = sigmap(cell->getPort("\\EN").as_bit()); + unused_bits.erase(A); + unused_bits.erase(EN); + asserts.push_back(make_pair(A, EN)); + continue; + } + + if (cell->type == "$assume") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit EN = sigmap(cell->getPort("\\EN").as_bit()); + unused_bits.erase(A); + unused_bits.erase(EN); + assumes.push_back(make_pair(A, EN)); + continue; + } + + if (cell->type == "$live") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit EN = sigmap(cell->getPort("\\EN").as_bit()); + unused_bits.erase(A); + unused_bits.erase(EN); + liveness.push_back(make_pair(A, EN)); + continue; + } + + if (cell->type == "$fair") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit EN = sigmap(cell->getPort("\\EN").as_bit()); + unused_bits.erase(A); + unused_bits.erase(EN); + fairness.push_back(make_pair(A, EN)); + continue; + } + + if (cell->type == "$anyconst") + { + for (auto bit : sigmap(cell->getPort("\\Y"))) { + undriven_bits.erase(bit); + ff_map[bit] = bit; + } + continue; + } + + if (cell->type == "$anyseq") + { + for (auto bit : sigmap(cell->getPort("\\Y"))) { + undriven_bits.erase(bit); + input_bits.insert(bit); + } + continue; + } + + log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + } + + for (auto bit : unused_bits) + undriven_bits.erase(bit); + + if (!undriven_bits.empty()) { + undriven_bits.sort(); + for (auto bit : undriven_bits) { + log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); + input_bits.insert(bit); + } + log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); + } + + init_map.sort(); + input_bits.sort(); + output_bits.sort(); + not_map.sort(); + ff_map.sort(); + and_map.sort(); + + aig_map[State::S0] = 0; + aig_map[State::S1] = 1; + + for (auto bit : input_bits) { + aig_m++, aig_i++; + aig_map[bit] = 2*aig_m; + } + + if (imode && input_bits.empty()) { + aig_m++, aig_i++; + } + + if (zinit_mode) + { + for (auto it : ff_map) { + if (init_map.count(it.first)) + continue; + aig_m++, aig_i++; + init_inputs[it.first] = 2*aig_m; + } + } + + int fair_live_inputs_cnt = GetSize(liveness); + int fair_live_inputs_m = aig_m; + + aig_m += fair_live_inputs_cnt; + aig_i += fair_live_inputs_cnt; + + for (auto it : ff_map) { + aig_m++, aig_l++; + aig_map[it.first] = 2*aig_m; + ordered_latches[it.first] = aig_l-1; + if (init_map.count(it.first) == 0) + aig_latchinit.push_back(2); + else + aig_latchinit.push_back(init_map.at(it.first) ? 1 : 0); + } + + if (!initstate_bits.empty() || !init_inputs.empty()) { + aig_m++, aig_l++; + initstate_ff = 2*aig_m+1; + aig_latchinit.push_back(0); + } + + int fair_live_latches_cnt = GetSize(fairness) + 2*GetSize(liveness); + int fair_live_latches_m = aig_m; + int fair_live_latches_l = aig_l; + + aig_m += fair_live_latches_cnt; + aig_l += fair_live_latches_cnt; + + for (int i = 0; i < fair_live_latches_cnt; i++) + aig_latchinit.push_back(0); + + if (zinit_mode) + { + for (auto it : ff_map) + { + int l = ordered_latches[it.first]; + + if (aig_latchinit.at(l) == 1) + aig_map[it.first] ^= 1; + + if (aig_latchinit.at(l) == 2) + { + int gated_ffout = mkgate(aig_map[it.first], initstate_ff^1); + int gated_initin = mkgate(init_inputs[it.first], initstate_ff); + aig_map[it.first] = mkgate(gated_ffout^1, gated_initin^1)^1; + } + } + } + + for (auto it : ff_map) { + int a = bit2aig(it.second); + int l = ordered_latches[it.first]; + if (zinit_mode && aig_latchinit.at(l) == 1) + aig_latchin.push_back(a ^ 1); + else + aig_latchin.push_back(a); + } + + if (!initstate_bits.empty() || !init_inputs.empty()) + aig_latchin.push_back(1); + + for (auto bit : output_bits) { + aig_o++; + ordered_outputs[bit] = aig_o-1; + aig_outputs.push_back(bit2aig(bit)); + } + + if (omode && output_bits.empty()) { + aig_o++; + aig_outputs.push_back(0); + } + + for (auto it : asserts) { + aig_b++; + int bit_a = bit2aig(it.first); + int bit_en = bit2aig(it.second); + aig_outputs.push_back(mkgate(bit_a^1, bit_en)); + } + + if (bmode && asserts.empty()) { + aig_b++; + aig_outputs.push_back(0); + } + + for (auto it : assumes) { + aig_c++; + int bit_a = bit2aig(it.first); + int bit_en = bit2aig(it.second); + aig_outputs.push_back(mkgate(bit_a^1, bit_en)^1); + } + + for (auto it : liveness) + { + int input_m = ++fair_live_inputs_m; + int latch_m1 = ++fair_live_latches_m; + int latch_m2 = ++fair_live_latches_m; + + log_assert(GetSize(aig_latchin) == fair_live_latches_l); + fair_live_latches_l += 2; + + int bit_a = bit2aig(it.first); + int bit_en = bit2aig(it.second); + int bit_s = 2*input_m; + int bit_q1 = 2*latch_m1; + int bit_q2 = 2*latch_m2; + + int bit_d1 = mkgate(mkgate(bit_s, bit_en)^1, bit_q1^1)^1; + int bit_d2 = mkgate(mkgate(bit_d1, bit_a)^1, bit_q2^1)^1; + + aig_j++; + aig_latchin.push_back(bit_d1); + aig_latchin.push_back(bit_d2); + aig_outputs.push_back(mkgate(bit_q1, bit_q2^1)); + } + + for (auto it : fairness) + { + int latch_m = ++fair_live_latches_m; + + log_assert(GetSize(aig_latchin) == fair_live_latches_l); + fair_live_latches_l += 1; + + int bit_a = bit2aig(it.first); + int bit_en = bit2aig(it.second); + int bit_q = 2*latch_m; + + aig_f++; + aig_latchin.push_back(mkgate(mkgate(bit_q^1, bit_en^1)^1, bit_a^1)); + aig_outputs.push_back(bit_q^1); + } + } + + void write_aiger(std::ostream &f, bool ascii_mode, bool miter_mode, bool symbols_mode) + { + int aig_obc = aig_o + aig_b + aig_c; + int aig_obcj = aig_obc + aig_j; + int aig_obcjf = aig_obcj + aig_f; + + log_assert(aig_m == aig_i + aig_l + aig_a); + log_assert(aig_l == GetSize(aig_latchin)); + log_assert(aig_l == GetSize(aig_latchinit)); + log_assert(aig_obcjf == GetSize(aig_outputs)); + + if (miter_mode) { + if (aig_b || aig_c || aig_j || aig_f) + log_error("Running AIGER back-end in -miter mode, but design contains $assert, $assume, $live and/or $fair cells!\n"); + f << stringf("%s %d %d %d 0 %d %d\n", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_a, aig_o); + } else { + f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); + if (aig_b || aig_c || aig_j || aig_f) + f << stringf(" %d %d %d %d", aig_b, aig_c, aig_j, aig_f); + f << stringf("\n"); + } + + if (ascii_mode) + { + for (int i = 0; i < aig_i; i++) + f << stringf("%d\n", 2*i+2); + + for (int i = 0; i < aig_l; i++) { + if (zinit_mode || aig_latchinit.at(i) == 0) + f << stringf("%d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 1) + f << stringf("%d %d 1\n", 2*(aig_i+i)+2, aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 2) + f << stringf("%d %d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i), 2*(aig_i+i)+2); + } + + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) + f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); + } + else + { + for (int i = 0; i < aig_l; i++) { + if (zinit_mode || aig_latchinit.at(i) == 0) + f << stringf("%d\n", aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 1) + f << stringf("%d 1\n", aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 2) + f << stringf("%d %d\n", aig_latchin.at(i), 2*(aig_i+i)+2); + } + + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) { + int lhs = 2*(aig_i+aig_l+i)+2; + int rhs0 = aig_gates.at(i).first; + int rhs1 = aig_gates.at(i).second; + int delta0 = lhs - rhs0; + int delta1 = rhs0 - rhs1; + aiger_encode(f, delta0); + aiger_encode(f, delta1); + } + } + + if (symbols_mode) + { + dict> symbols; + + for (auto wire : module->wires()) + { + if (wire->name[0] == '$') + continue; + + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(wire); i++) + { + if (sig[i].wire == nullptr) { + if (wire->port_output) + sig[i] = SigBit(wire, i); + else + continue; + } + + if (wire->port_input) { + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + if (GetSize(wire) != 1) + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i)); + else + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire))); + } + + if (wire->port_output) { + int o = ordered_outputs.at(SigSpec(wire, i)); + if (GetSize(wire) != 1) + symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i)); + else + symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire))); + } + + if (init_inputs.count(sig[i])) { + int a = init_inputs.at(sig[i]); + log_assert((a & 1) == 0); + if (GetSize(wire) != 1) + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i)); + else + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire))); + } + + if (ordered_latches.count(sig[i])) { + int l = ordered_latches.at(sig[i]); + const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : ""; + if (GetSize(wire) != 1) + symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i)); + else + symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire))); + } + } + } + + symbols.sort(); + + for (auto &sym : symbols) { + f << sym.first; + std::sort(sym.second.begin(), sym.second.end()); + for (auto &s : sym.second) + f << " " << s; + f << std::endl; + } + } + + f << stringf("c\nGenerated by %s\n", yosys_version_str); + } + + void write_map(std::ostream &f, bool verbose_map) + { + dict input_lines; + dict init_lines; + dict output_lines; + dict latch_lines; + dict wire_lines; + + for (auto wire : module->wires()) + { + if (!verbose_map && wire->name[0] == '$') + continue; + + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(wire); i++) + { + if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr) + continue; + + int a = aig_map.at(sig[i]); + + if (verbose_map) + wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire)); + + if (wire->port_input) { + log_assert((a & 1) == 0); + input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); + } + + if (wire->port_output) { + int o = ordered_outputs.at(sig[i]); + output_lines[o] += stringf("output %d %d %s\n", o, i, log_id(wire)); + } + + if (init_inputs.count(sig[i])) { + int a = init_inputs.at(sig[i]); + log_assert((a & 1) == 0); + init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, i, log_id(wire)); + } + + if (ordered_latches.count(sig[i])) { + int l = ordered_latches.at(sig[i]); + if (zinit_mode && (aig_latchinit.at(l) == 1)) + latch_lines[l] += stringf("invlatch %d %d %s\n", l, i, log_id(wire)); + else + latch_lines[l] += stringf("latch %d %d %s\n", l, i, log_id(wire)); + } + } + } + + input_lines.sort(); + for (auto &it : input_lines) + f << it.second; + + init_lines.sort(); + for (auto &it : init_lines) + f << it.second; + + output_lines.sort(); + for (auto &it : output_lines) + f << it.second; + + latch_lines.sort(); + for (auto &it : latch_lines) + f << it.second; + + wire_lines.sort(); + for (auto &it : wire_lines) + f << it.second; + } +}; + +struct AigerBackend : public Backend { + AigerBackend() : Backend("aiger", "write design to AIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_aiger [options] [filename]\n"); + log("\n"); + log("Write the current design to an AIGER file. The design must be flattened and\n"); + log("must not contain any cell types except $_AND_, $_NOT_, simple FF types,\n"); + log("$assert and $assume cells, and $initstate cells.\n"); + log("\n"); + log("$assert and $assume cells are converted to AIGER bad state properties and\n"); + log("invariant constraints.\n"); + log("\n"); + log(" -ascii\n"); + log(" write ASCII version of AGIER format\n"); + log("\n"); + log(" -zinit\n"); + log(" convert FFs to zero-initialized FFs, adding additional inputs for\n"); + log(" uninitialized FFs.\n"); + log("\n"); + log(" -miter\n"); + log(" design outputs are AIGER bad state properties\n"); + log("\n"); + log(" -symbols\n"); + log(" include a symbol table in the generated AIGER file\n"); + log("\n"); + log(" -map \n"); + log(" write an extra file with port and latch symbols\n"); + log("\n"); + log(" -vmap \n"); + log(" like -map, but more verbose\n"); + log("\n"); + log(" -I, -O, -B\n"); + log(" If the design contains no input/output/assert then create one\n"); + log(" dummy input/output/bad_state pin to make the tools reading the\n"); + log(" AIGER file happy.\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + bool ascii_mode = false; + bool zinit_mode = false; + bool miter_mode = false; + bool symbols_mode = false; + bool verbose_map = false; + bool imode = false; + bool omode = false; + bool bmode = false; + std::string map_filename; + + log_header(design, "Executing AIGER backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ascii") { + ascii_mode = true; + continue; + } + if (args[argidx] == "-zinit") { + zinit_mode = true; + continue; + } + if (args[argidx] == "-miter") { + miter_mode = true; + continue; + } + if (args[argidx] == "-symbols") { + symbols_mode = true; + continue; + } + if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + verbose_map = true; + continue; + } + if (args[argidx] == "-I") { + imode = true; + continue; + } + if (args[argidx] == "-O") { + omode = true; + continue; + } + if (args[argidx] == "-B") { + bmode = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + Module *top_module = design->top_module(); + + if (top_module == nullptr) + log_error("Can't find top module in current design!\n"); + + AigerWriter writer(top_module, zinit_mode, imode, omode, bmode); + writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); + + if (!map_filename.empty()) { + std::ofstream mapf; + mapf.open(map_filename.c_str(), std::ofstream::trunc); + if (mapf.fail()) + log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); + writer.write_map(mapf, verbose_map); + } + } +} AigerBackend; + +PRIVATE_NAMESPACE_END diff --git a/yosys/backends/blif/blif.cc b/yosys/backends/blif/blif.cc index d9d0cc177..0db5ff27c 100644 --- a/yosys/backends/blif/blif.cc +++ b/yosys/backends/blif/blif.cc @@ -38,8 +38,10 @@ struct BlifDumperConfig bool impltf_mode; bool gates_mode; bool cname_mode; + bool iname_mode; bool param_mode; bool attr_mode; + bool iattr_mode; bool blackbox_mode; bool noalias_mode; @@ -48,7 +50,8 @@ struct BlifDumperConfig std::string true_type, true_out, false_type, false_out, undef_type, undef_out; BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), - cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false), noalias_mode(false) { } + cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false), + blackbox_mode(false), noalias_mode(false) { } }; struct BlifDumper @@ -112,7 +115,7 @@ struct BlifDumper str[i] = '?'; if (sig.wire->width != 1) - str += stringf("[%d]", sig.offset); + str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset); cstr_buf.push_back(str); return cstr_buf.back().c_str(); @@ -240,106 +243,118 @@ struct BlifDumper if (!config->icells_mode && cell->type == "$_NOT_") { f << stringf(".names %s %s\n0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_AND_") { f << stringf(".names %s %s %s\n11 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_OR_") { f << stringf(".names %s %s %s\n1- 1\n-1 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_XOR_") { f << stringf(".names %s %s %s\n10 1\n01 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_NAND_") { f << stringf(".names %s %s %s\n0- 1\n-0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_NOR_") { f << stringf(".names %s %s %s\n00 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_XNOR_") { f << stringf(".names %s %s %s\n11 1\n00 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; + } + + if (!config->icells_mode && cell->type == "$_ANDNOT_") { + f << stringf(".names %s %s %s\n10 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); + goto internal_cell; + } + + if (!config->icells_mode && cell->type == "$_ORNOT_") { + f << stringf(".names %s %s %s\n1- 1\n-0 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); + goto internal_cell; } if (!config->icells_mode && cell->type == "$_AOI3_") { f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_OAI3_") { f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_AOI4_") { f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_OAI4_") { f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_MUX_") { f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_FF_") { f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DFF_N_") { f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DFF_P_") { f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DLATCH_N_") { f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DLATCH_P_") { f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$lut") { @@ -361,7 +376,7 @@ struct BlifDumper } f << " 1\n"; } - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$sop") { @@ -389,7 +404,7 @@ struct BlifDumper } f << " 1\n"; } - continue; + goto internal_cell; } f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type)); @@ -409,6 +424,14 @@ struct BlifDumper dump_params(".attr", cell->attributes); if (config->param_mode) dump_params(".param", cell->parameters); + + if (0) { + internal_cell: + if (config->iname_mode) + f << stringf(".cname %s\n", cstr(cell->name)); + if (config->iattr_mode) + dump_params(".attr", cell->attributes); + } } for (auto &conn : module->connections()) @@ -441,7 +464,7 @@ struct BlifDumper struct BlifBackend : public Backend { BlifBackend() : Backend("blif", "write design to BLIF file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -499,6 +522,11 @@ struct BlifBackend : public Backend { log(" -cname\n"); log(" use the non-standard .cname statement to write cell names\n"); log("\n"); + log(" -iname, -iattr\n"); + log(" enable -cname and -attr functionality for .names statements\n"); + log(" (the .cname and .attr statements will be included in the BLIF\n"); + log(" output after the truth table for the .names statement)\n"); + log("\n"); log(" -blackbox\n"); log(" write blackbox cells with .blackbox statement.\n"); log("\n"); @@ -506,7 +534,7 @@ struct BlifBackend : public Backend { log(" do not write definitions for the $true, $false and $undef wires.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string top_module_name; std::string buf_type, buf_in, buf_out; @@ -575,6 +603,14 @@ struct BlifBackend : public Backend { config.attr_mode = true; continue; } + if (args[argidx] == "-iname") { + config.iname_mode = true; + continue; + } + if (args[argidx] == "-iattr") { + config.iattr_mode = true; + continue; + } if (args[argidx] == "-blackbox") { config.blackbox_mode = true; continue; diff --git a/yosys/backends/btor/README b/yosys/backends/btor/README deleted file mode 100644 index efcf0d8f5..000000000 --- a/yosys/backends/btor/README +++ /dev/null @@ -1,23 +0,0 @@ - -This is the Yosys BTOR backend. -It is developed by Ahmed Irfan - 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/yosys/backends/btor/btor.cc b/yosys/backends/btor/btor.cc index bbe90e85f..53359bd7b 100644 --- a/yosys/backends/btor/btor.cc +++ b/yosys/backends/btor/btor.cc @@ -2,7 +2,6 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf - * Copyright (C) 2014 Ahmed Irfan * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,1093 +17,1152 @@ * */ -// [[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 -#include 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 +struct BtorWorker { std::ostream &f; - RTLIL::Module *module; - RTLIL::Design *design; - BtorDumperConfig *config; - CellTypes ct; - SigMap sigmap; - std::map> inter_wire_map;// for mapping the intermediate wires that are output of some cell - std::map line_ref;//mapping of ids to line_num of the btor file - std::map 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 basic_wires;//input wires and registers - RTLIL::IdString curr_cell; //current cell being dumped - std::map cell_type_translation, s_cell_type_translation; //RTLIL to BTOR translation - std::map>> 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) + RTLIL::Module *module; + bool verbose; + bool single_bad; + + int next_nid = 1; + int initstate_nid = -1; + + // => + dict sorts_bv; + + // (, ) => + dict, int> sorts_mem; + + // SigBit => (, ) + dict> bit_nid; + + // => + dict nid_width; + + // SigSpec => + dict sig_nid; + + // bit to driving cell + dict bit_cell; + + // nids for constants + dict consts; + + // ff inputs that need to be evaluated (, ) + vector> ff_todo; + + pool cell_recursion_guard; + vector bad_properties; + dict initbits; + pool statewires; + string indent; + + void btorf(const char *fmt, ...) { - line_num=0; - str.clear(); - for(auto it=module->wires_.begin(); it!=module->wires_.end(); ++it) + va_list ap; + va_start(ap, fmt); + f << indent << vstringf(fmt, ap); + va_end(ap); + } + + void btorf_push(const string &id) + { + if (verbose) { + f << indent << stringf(" ; begin %s\n", id.c_str()); + indent += " "; + } + } + + void btorf_pop(const string &id) + { + if (verbose) { + indent = indent.substr(4); + f << indent << stringf(" ; end %s\n", id.c_str()); + } + } + + int get_bv_sid(int width) + { + if (sorts_bv.count(width) == 0) { + int nid = next_nid++; + btorf("%d sort bitvec %d\n", nid, width); + sorts_bv[width] = nid; + } + return sorts_bv.at(width); + } + + int get_mem_sid(int abits, int dbits) + { + pair key(abits, dbits); + if (sorts_mem.count(key) == 0) { + int addr_sid = get_bv_sid(abits); + int data_sid = get_bv_sid(dbits); + int nid = next_nid++; + btorf("%d sort array %d %d\n", nid, addr_sid, data_sid); + sorts_mem[key] = nid; + } + return sorts_mem.at(key); + } + + void add_nid_sig(int nid, const SigSpec &sig) + { + if (verbose) + f << indent << stringf("; %d %s\n", nid, log_signal(sig)); + + for (int i = 0; i < GetSize(sig); i++) + bit_nid[sig[i]] = make_pair(nid, i); + + sig_nid[sig] = nid; + nid_width[nid] = GetSize(sig); + } + + void export_cell(Cell *cell) + { + log_assert(cell_recursion_guard.count(cell) == 0); + cell_recursion_guard.insert(cell); + btorf_push(log_id(cell)); + + if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx", + "$concat", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) { - if(it->second->port_input) + string btor_op; + if (cell->type == "$add") btor_op = "add"; + if (cell->type == "$sub") btor_op = "sub"; + if (cell->type == "$mul") btor_op = "mul"; + if (cell->type.in("$shl", "$sshl")) btor_op = "sll"; + if (cell->type == "$shr") btor_op = "srl"; + if (cell->type == "$sshr") btor_op = "sra"; + if (cell->type.in("$shift", "$shiftx")) btor_op = "shift"; + if (cell->type.in("$and", "$_AND_")) btor_op = "and"; + if (cell->type.in("$or", "$_OR_")) btor_op = "or"; + if (cell->type.in("$xor", "$_XOR_")) btor_op = "xor"; + if (cell->type == "$concat") btor_op = "concat"; + if (cell->type == "$_NAND_") btor_op = "nand"; + if (cell->type == "$_NOR_") btor_op = "nor"; + if (cell->type.in("$xnor", "$_XNOR_")) btor_op = "xnor"; + log_assert(!btor_op.empty()); + + int width = GetSize(cell->getPort("\\Y")); + width = std::max(width, GetSize(cell->getPort("\\A"))); + width = std::max(width, GetSize(cell->getPort("\\B"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false; + + if (btor_op == "shift" && !b_signed) + btor_op = "srl"; + + if (cell->type.in("$shl", "$sshl", "$shr", "$sshr")) + b_signed = false; + + if (cell->type == "$sshr" && !a_signed) + btor_op = "srl"; + + int sid = get_bv_sid(width); + int nid; + + if (btor_op == "shift") { - basic_wires[it->first]=true; + int nid_a = get_sig_nid(cell->getPort("\\A"), width, false); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + int nid_r = next_nid++; + btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b); + + int nid_b_neg = next_nid++; + btorf("%d neg %d %d\n", nid_b_neg, sid, nid_b); + + int nid_l = next_nid++; + btorf("%d sll %d %d %d\n", nid_l, sid, nid_a, nid_b_neg); + + int sid_bit = get_bv_sid(1); + int nid_zero = get_sig_nid(Const(0, width)); + int nid_b_ltz = next_nid++; + btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero); + + nid = next_nid++; + btorf("%d ite %d %d %d %d\n", nid, sid, nid_b_ltz, nid_l, nid_r); } else { - basic_wires[it->first]=false; + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + nid = next_nid++; + btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b); } - 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"; + SigSpec sig = sigmap(cell->getPort("\\Y")); - } - - vector 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; + if (GetSize(sig) < width) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); + nid = nid2; } - else return it->second; + + add_nid_sig(nid, sig); + goto okay; } - else // case when the wire is not basic wire + + if (cell->type.in("$div", "$mod")) { - log("case of non-basic wire - %s\n", cstr(wire->name)); - auto it = line_ref.find(wire->name); - if(it==std::end(line_ref)) + string btor_op; + if (cell->type == "$div") btor_op = "div"; + if (cell->type == "$mod") btor_op = "rem"; + log_assert(!btor_op.empty()); + + int width = GetSize(cell->getPort("\\Y")); + width = std::max(width, GetSize(cell->getPort("\\A"))); + width = std::max(width, GetSize(cell->getPort("\\B"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false; + + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + int sid = get_bv_sid(width); + int nid = next_nid++; + btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b); + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) < width) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; + } + + if (cell->type.in("$_ANDNOT_", "$_ORNOT_")) + { + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = get_sig_nid(cell->getPort("\\B")); + + int nid1 = next_nid++; + int nid2 = next_nid++; + + if (cell->type == "$_ANDNOT_") { + btorf("%d not %d %d\n", nid1, sid, nid_b); + btorf("%d and %d %d %d\n", nid2, sid, nid_a, nid1); + } + + if (cell->type == "$_ORNOT_") { + btorf("%d not %d %d\n", nid1, sid, nid_b); + btorf("%d or %d %d %d\n", nid2, sid, nid_a, nid1); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + add_nid_sig(nid2, sig); + goto okay; + } + + if (cell->type.in("$_OAI3_", "$_AOI3_")) + { + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = get_sig_nid(cell->getPort("\\B")); + int nid_c = get_sig_nid(cell->getPort("\\C")); + + int nid1 = next_nid++; + int nid2 = next_nid++; + int nid3 = next_nid++; + + if (cell->type == "$_OAI3_") { + btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c); + btorf("%d not %d %d\n", nid3, sid, nid2); + } + + if (cell->type == "$_AOI3_") { + btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c); + btorf("%d not %d %d\n", nid3, sid, nid2); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + add_nid_sig(nid3, sig); + goto okay; + } + + if (cell->type.in("$_OAI4_", "$_AOI4_")) + { + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = get_sig_nid(cell->getPort("\\B")); + int nid_c = get_sig_nid(cell->getPort("\\C")); + int nid_d = get_sig_nid(cell->getPort("\\D")); + + int nid1 = next_nid++; + int nid2 = next_nid++; + int nid3 = next_nid++; + int nid4 = next_nid++; + + if (cell->type == "$_OAI4_") { + btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d); + btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2); + btorf("%d not %d %d\n", nid4, sid, nid3); + } + + if (cell->type == "$_AOI4_") { + btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d); + btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2); + btorf("%d not %d %d\n", nid4, sid, nid3); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + add_nid_sig(nid4, sig); + goto okay; + } + + if (cell->type.in("$lt", "$le", "$eq", "$eqx", "$ne", "$nex", "$ge", "$gt")) + { + string btor_op; + if (cell->type == "$lt") btor_op = "lt"; + if (cell->type == "$le") btor_op = "lte"; + if (cell->type.in("$eq", "$eqx")) btor_op = "eq"; + if (cell->type.in("$ne", "$nex")) btor_op = "ne"; + if (cell->type == "$ge") btor_op = "gte"; + if (cell->type == "$gt") btor_op = "gt"; + log_assert(!btor_op.empty()); + + int width = 1; + width = std::max(width, GetSize(cell->getPort("\\A"))); + width = std::max(width, GetSize(cell->getPort("\\B"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false; + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + int nid = next_nid++; + if (cell->type.in("$lt", "$le", "$ge", "$gt")) { + btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b); + } else { + btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) > 1) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d uext %d %d %d\n", nid2, sid, nid, GetSize(sig) - 1); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; + } + + if (cell->type.in("$not", "$neg", "$_NOT_")) + { + string btor_op; + if (cell->type.in("$not", "$_NOT_")) btor_op = "not"; + if (cell->type == "$neg") btor_op = "neg"; + log_assert(!btor_op.empty()); + + int width = GetSize(cell->getPort("\\Y")); + width = std::max(width, GetSize(cell->getPort("\\A"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + + int sid = get_bv_sid(width); + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + + int nid = next_nid++; + btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a); + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) < width) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; + } + + if (cell->type.in("$logic_and", "$logic_or", "$logic_not")) + { + string btor_op; + if (cell->type == "$logic_and") btor_op = "and"; + if (cell->type == "$logic_or") btor_op = "or"; + if (cell->type == "$logic_not") btor_op = "not"; + log_assert(!btor_op.empty()); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = btor_op != "not" ? get_sig_nid(cell->getPort("\\B")) : 0; + + if (GetSize(cell->getPort("\\A")) > 1) { + int nid_red_a = next_nid++; + btorf("%d redor %d %d\n", nid_red_a, sid, nid_a); + nid_a = nid_red_a; + } + + if (btor_op != "not" && GetSize(cell->getPort("\\B")) > 1) { + int nid_red_b = next_nid++; + btorf("%d redor %d %d\n", nid_red_b, sid, nid_b); + nid_b = nid_red_b; + } + + int nid = next_nid++; + if (btor_op != "not") + btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b); + else + btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a); + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) > 1) { + int sid = get_bv_sid(GetSize(sig)); + int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); + int nid2 = next_nid++; + btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; + } + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor")) + { + string btor_op; + if (cell->type == "$reduce_and") btor_op = "redand"; + if (cell->type.in("$reduce_or", "$reduce_bool")) btor_op = "redor"; + if (cell->type.in("$reduce_xor", "$reduce_xnor")) btor_op = "redxor"; + log_assert(!btor_op.empty()); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + + int nid = next_nid++; + btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a); + + if (cell->type == "$reduce_xnor") { + int nid2 = next_nid++; + btorf("%d not %d %d %d\n", nid2, sid, nid); + nid = nid2; + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) > 1) { + int sid = get_bv_sid(GetSize(sig)); + int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); + int nid2 = next_nid++; + btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; + } + + if (cell->type.in("$mux", "$_MUX_")) + { + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + int nid_a = get_sig_nid(sig_a); + int nid_b = get_sig_nid(sig_b); + int nid_s = get_sig_nid(sig_s); + + int sid = get_bv_sid(GetSize(sig_y)); + int nid = next_nid++; + btorf("%d ite %d %d %d %d\n", nid, sid, nid_s, nid_b, nid_a); + + add_nid_sig(nid, sig_y); + goto okay; + } + + if (cell->type == "$pmux") + { + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + int width = GetSize(sig_a); + int sid = get_bv_sid(width); + int nid = get_sig_nid(sig_a); + + for (int i = 0; i < GetSize(sig_s); i++) { + int nid_b = get_sig_nid(sig_b.extract(i*width, width)); + int nid_s = get_sig_nid(sig_s.extract(i)); + int nid2 = next_nid++; + btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid); + nid = nid2; + } + + add_nid_sig(nid, sig_y); + goto okay; + } + + if (cell->type.in("$dff", "$ff", "$_DFF_P_", "$_DFF_N", "$_FF_")) + { + SigSpec sig_d = sigmap(cell->getPort("\\D")); + SigSpec sig_q = sigmap(cell->getPort("\\Q")); + + IdString symbol; + + if (sig_q.is_wire()) { + Wire *w = sig_q.as_wire(); + if (w->port_id == 0) { + statewires.insert(w); + symbol = w->name; + } + } + + Const initval; + for (int i = 0; i < GetSize(sig_q); i++) + if (initbits.count(sig_q[i])) + initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0); + else + initval.bits.push_back(State::Sx); + + int nid_init_val = -1; + + if (!initval.is_fully_undef()) + nid_init_val = get_sig_nid(initval); + + int sid = get_bv_sid(GetSize(sig_q)); + int nid = next_nid++; + + if (symbol.empty()) + btorf("%d state %d\n", nid, sid); + else + btorf("%d state %d %s\n", nid, sid, log_id(symbol)); + + if (nid_init_val >= 0) { + int nid_init = next_nid++; + if (verbose) + btorf("; initval = %s\n", log_signal(initval)); + btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); + } + + ff_todo.push_back(make_pair(nid, cell)); + add_nid_sig(nid, sig_q); + goto okay; + } + + if (cell->type.in("$anyconst", "$anyseq")) + { + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + int sid = get_bv_sid(GetSize(sig_y)); + int nid = next_nid++; + + btorf("%d state %d\n", nid, sid); + + if (cell->type == "$anyconst") { + int nid2 = next_nid++; + btorf("%d next %d %d %d\n", nid2, sid, nid, nid); + } + + add_nid_sig(nid, sig_y); + goto okay; + } + + if (cell->type == "$initstate") + { + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + if (initstate_nid < 0) { - std::set& 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) + int sid = get_bv_sid(1); + int one_nid = get_sig_nid(Const(1, 1)); + int zero_nid = get_sig_nid(Const(0, 1)); + initstate_nid = next_nid++; + btorf("%d state %d\n", initstate_nid, sid); + btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid); + btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid); + } + + add_nid_sig(initstate_nid, sig_y); + goto okay; + } + + if (cell->type == "$mem") + { + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int rdports = cell->getParam("\\RD_PORTS").as_int(); + int wrports = cell->getParam("\\WR_PORTS").as_int(); + + Const wr_clk_en = cell->getParam("\\WR_CLK_ENABLE"); + Const rd_clk_en = cell->getParam("\\RD_CLK_ENABLE"); + + bool asyncwr = wr_clk_en.is_fully_zero(); + + if (!asyncwr && !wr_clk_en.is_fully_ones()) + log_error("Memory %s.%s has mixed async/sync write ports.\n", + log_id(module), log_id(cell)); + + if (!rd_clk_en.is_fully_zero()) + log_error("Memory %s.%s has sync read ports.\n", + log_id(module), log_id(cell)); + + SigSpec sig_rd_addr = sigmap(cell->getPort("\\RD_ADDR")); + SigSpec sig_rd_data = sigmap(cell->getPort("\\RD_DATA")); + + SigSpec sig_wr_addr = sigmap(cell->getPort("\\WR_ADDR")); + SigSpec sig_wr_data = sigmap(cell->getPort("\\WR_DATA")); + SigSpec sig_wr_en = sigmap(cell->getPort("\\WR_EN")); + + int data_sid = get_bv_sid(width); + int bool_sid = get_bv_sid(1); + int sid = get_mem_sid(abits, width); + int nid = next_nid++; + int nid_head = nid; + + if (cell->name[0] == '$') + btorf("%d state %d\n", nid, sid); + else + btorf("%d state %d %s\n", nid, sid, log_id(cell)); + + if (asyncwr) + { + for (int port = 0; port < wrports; port++) { - 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); + SigSpec wa = sig_wr_addr.extract(port*abits, abits); + SigSpec wd = sig_wr_data.extract(port*width, width); + SigSpec we = sig_wr_en.extract(port*width, width); - if(dep_set.size()==1 && wire->width == cell_output->size()) + int wa_nid = get_sig_nid(wa); + int wd_nid = get_sig_nid(wd); + int we_nid = get_sig_nid(we); + + int nid2 = next_nid++; + btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); + + int nid3 = next_nid++; + btorf("%d not %d %d\n", nid3, data_sid, we_nid); + + int nid4 = next_nid++; + btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); + + int nid5 = next_nid++; + btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); + + int nid6 = next_nid++; + btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); + + int nid7 = next_nid++; + btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); + + int nid8 = next_nid++; + btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); + + int nid9 = next_nid++; + btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); + + nid_head = nid9; + } + } + + for (int port = 0; port < rdports; port++) + { + SigSpec ra = sig_rd_addr.extract(port*abits, abits); + SigSpec rd = sig_rd_data.extract(port*width, width); + + int ra_nid = get_sig_nid(ra); + int rd_nid = next_nid++; + + btorf("%d read %d %d %d\n", rd_nid, data_sid, nid_head, ra_nid); + + add_nid_sig(rd_nid, rd); + } + + if (!asyncwr) + { + ff_todo.push_back(make_pair(nid, cell)); + } + else + { + int nid2 = next_nid++; + btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head); + } + + goto okay; + } + + log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + + okay: + btorf_pop(log_id(cell)); + cell_recursion_guard.erase(cell); + } + + int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false) + { + int nid = -1; + sigmap.apply(sig); + + for (auto bit : sig) + if (bit == State::Sx) + goto has_undef_bits; + + if (0) + { + has_undef_bits: + SigSpec sig_mask_undef, sig_noundef; + int first_undef = -1; + + for (int i = 0; i < GetSize(sig); i++) + if (sig[i] == State::Sx) { + if (first_undef < 0) + first_undef = i; + sig_mask_undef.append(State::S1); + sig_noundef.append(State::S0); + } else { + sig_mask_undef.append(State::S0); + sig_noundef.append(sig[i]); + } + + if (to_width < 0 || first_undef < to_width) + { + int sid = get_bv_sid(GetSize(sig)); + + int nid_input = next_nid++; + btorf("%d input %d\n", nid_input, sid); + + int nid_masked_input; + if (sig_mask_undef.is_fully_ones()) { + nid_masked_input = nid_input; + } else { + int nid_mask_undef = get_sig_nid(sig_mask_undef); + nid_masked_input = next_nid++; + btorf("%d and %d %d %d\n", nid_masked_input, sid, nid_input, nid_mask_undef); + } + + if (sig_noundef.is_fully_zero()) { + nid = nid_masked_input; + } else { + int nid_noundef = get_sig_nid(sig_noundef); + nid = next_nid++; + btorf("%d or %d %d %d\n", nid, sid, nid_masked_input, nid_noundef); + } + + goto extend_or_trim; + } + + sig = sig_noundef; + } + + if (sig_nid.count(sig) == 0) + { + // , + vector> nidbits; + + // collect all bits + for (int i = 0; i < GetSize(sig); i++) + { + SigBit bit = sig[i]; + + if (bit_nid.count(bit) == 0) + { + if (bit.wire == nullptr) { - wire_line = cell_line; - break; + Const c(bit.data); + + while (i+GetSize(c) < GetSize(sig) && sig[i+GetSize(c)].wire == nullptr) + c.bits.push_back(sig[i+GetSize(c)].data); + + if (consts.count(c) == 0) { + int sid = get_bv_sid(GetSize(c)); + int nid = next_nid++; + btorf("%d const %d %s\n", nid, sid, c.as_string().c_str()); + consts[c] = nid; + nid_width[nid] = GetSize(c); + } + + int nid = consts.at(c); + + for (int j = 0; j < GetSize(c); j++) + nidbits.push_back(make_pair(nid, j)); + + i += GetSize(c)-1; + continue; } else { - int prev_wire_line=0; //previously dumped wire line - int start_bit=0; - for(unsigned j=0; jchunks().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 (bit_cell.count(bit) == 0) + log_error("No driver for signal bit %s.\n", log_signal(bit)); + export_cell(bit_cell.at(bit)); + log_assert(bit_nid.count(bit)); } } - 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; + + nidbits.push_back(bit_nid.at(bit)); + } + + int width = 0; + int nid = -1; + + // group bits and emit slice-concat chain + for (int i = 0; i < GetSize(nidbits); i++) + { + int nid2 = nidbits[i].first; + int lower = nidbits[i].second; + int upper = lower; + + while (i+1 < GetSize(nidbits) && nidbits[i+1].first == nidbits[i].first && + nidbits[i+1].second == nidbits[i].second+1) + upper++, i++; + + int nid3 = nid2; + + if (lower != 0 || upper+1 != nid_width.at(nid2)) { + int sid = get_bv_sid(upper-lower+1); + nid3 = next_nid++; + btorf("%d slice %d %d %d %d\n", nid3, sid, nid2, upper, lower); } - line_ref[wire->name]=wire_line; - return wire_line; + + int nid4 = nid3; + + if (nid >= 0) { + int sid = get_bv_sid(width+upper-lower+1); + nid4 = next_nid++; + btorf("%d concat %d %d %d\n", nid4, sid, nid3, nid); + } + + width += upper-lower+1; + nid = nid4; + } + + sig_nid[sig] = nid; + nid_width[nid] = width; + } + + nid = sig_nid.at(sig); + + extend_or_trim: + if (to_width >= 0 && to_width != GetSize(sig)) + { + if (to_width < GetSize(sig)) + { + int sid = get_bv_sid(to_width); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, to_width-1); + nid = nid2; } else { - log(" -- already processed wire\n"); - return it->second; + int sid = get_bv_sid(to_width); + int nid2 = next_nid++; + btorf("%d %s %d %d %d\n", nid2, is_signed ? "sext" : "uext", + sid, nid, to_width - GetSize(sig)); + nid = nid2; } } - log_abort(); - return -1; + + return nid; } - int dump_memory(const RTLIL::Memory* memory) + BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad) : + f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad) { - log("writing memory %s\n", cstr(memory->name)); - auto it = line_ref.find(memory->name); - if(it==std::end(line_ref)) + btorf_push("inputs"); + + for (auto wire : module->wires()) { - ++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>& 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; + if (wire->attributes.count("\\init")) { + Const attrval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(wire) && i < GetSize(attrval); i++) + if (attrval[i] == State::S0 || attrval[i] == State::S1) + initbits[sigmap(SigBit(wire, i))] = (attrval[i] == State::S1); } - } - 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=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; ichunks().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) + if (!wire->port_id || !wire->port_input) 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; ichunks().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; ichunks().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; ichunks().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))); - } - } + + SigSpec sig = sigmap(wire); + int sid = get_bv_sid(GetSize(sig)); + int nid = next_nid++; + + btorf("%d input %d %s\n", nid, sid, log_id(wire)); + add_nid_sig(nid, sig); } - log("writing input\n"); - std::map inputs, outputs; - std::vector safety; + btorf_pop("inputs"); - 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) + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { - dump_memory(mem_it->second); + if (!cell->output(conn.first)) + continue; + + for (auto bit : sigmap(conn.second)) + bit_cell[bit] = cell; } - 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) + for (auto wire : module->wires()) { - dump_cell(cell_it->second); + if (!wire->port_id || !wire->port_output) + continue; + + btorf_push(stringf("output %s", log_id(wire))); + + int sid = get_bv_sid(GetSize(wire)); + int nid = get_sig_nid(wire); + btorf("%d output %d %d %s\n", next_nid++, sid, nid, log_id(wire)); + + btorf_pop(stringf("output %s", log_id(wire))); } - 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 cell : module->cells()) + { + if (cell->type == "$assume") + { + btorf_push(log_id(cell)); - for(auto it: safety) - dump_property(it); + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_en = get_sig_nid(cell->getPort("\\EN")); + int nid_not_en = next_nid++; + int nid_a_or_not_en = next_nid++; + int nid = next_nid++; - f << stringf("\n"); + btorf("%d not %d %d\n", nid_not_en, sid, nid_en); + btorf("%d or %d %d %d\n", nid_a_or_not_en, sid, nid_a, nid_not_en); + btorf("%d constraint %d\n", nid, nid_a_or_not_en); - 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)); + btorf_pop(log_id(cell)); + } + + if (cell->type == "$assert") + { + btorf_push(log_id(cell)); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_en = get_sig_nid(cell->getPort("\\EN")); + int nid_not_a = next_nid++; + int nid_en_and_not_a = next_nid++; + + btorf("%d not %d %d\n", nid_not_a, sid, nid_a); + btorf("%d and %d %d %d\n", nid_en_and_not_a, sid, nid_en, nid_not_a); + + if (single_bad) { + bad_properties.push_back(nid_en_and_not_a); + } else { + int nid = next_nid++; + btorf("%d bad %d\n", nid, nid_en_and_not_a); + } + + btorf_pop(log_id(cell)); + } } - 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(); + for (auto wire : module->wires()) + { + if (wire->port_id || wire->name[0] == '$') + continue; + + btorf_push(stringf("wire %s", log_id(wire))); + + int sid = get_bv_sid(GetSize(wire)); + int nid = get_sig_nid(sigmap(wire)); + + if (statewires.count(wire)) + continue; + + int this_nid = next_nid++; + btorf("%d uext %d %d %d %s\n", this_nid, sid, nid, 0, log_id(wire)); + + btorf_pop(stringf("wire %s", log_id(wire))); + continue; + } + + while (!ff_todo.empty()) + { + vector> todo; + todo.swap(ff_todo); + + for (auto &it : todo) + { + int nid = it.first; + Cell *cell = it.second; + + btorf_push(stringf("next %s", log_id(cell))); + + if (cell->type == "$mem") + { + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int wrports = cell->getParam("\\WR_PORTS").as_int(); + + SigSpec sig_wr_addr = sigmap(cell->getPort("\\WR_ADDR")); + SigSpec sig_wr_data = sigmap(cell->getPort("\\WR_DATA")); + SigSpec sig_wr_en = sigmap(cell->getPort("\\WR_EN")); + + int data_sid = get_bv_sid(width); + int bool_sid = get_bv_sid(1); + int sid = get_mem_sid(abits, width); + int nid_head = nid; + + for (int port = 0; port < wrports; port++) + { + SigSpec wa = sig_wr_addr.extract(port*abits, abits); + SigSpec wd = sig_wr_data.extract(port*width, width); + SigSpec we = sig_wr_en.extract(port*width, width); + + int wa_nid = get_sig_nid(wa); + int wd_nid = get_sig_nid(wd); + int we_nid = get_sig_nid(we); + + int nid2 = next_nid++; + btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); + + int nid3 = next_nid++; + btorf("%d not %d %d\n", nid3, data_sid, we_nid); + + int nid4 = next_nid++; + btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); + + int nid5 = next_nid++; + btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); + + int nid6 = next_nid++; + btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); + + int nid7 = next_nid++; + btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); + + int nid8 = next_nid++; + btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); + + int nid9 = next_nid++; + btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); + + nid_head = nid9; + } + + int nid2 = next_nid++; + btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head); + } + else + { + SigSpec sig = sigmap(cell->getPort("\\D")); + int nid_q = get_sig_nid(sig); + int sid = get_bv_sid(GetSize(sig)); + btorf("%d next %d %d %d\n", next_nid++, sid, nid, nid_q); + } + + btorf_pop(stringf("next %s", log_id(cell))); + } + } + + while (!bad_properties.empty()) + { + vector todo; + bad_properties.swap(todo); + + int sid = get_bv_sid(1); + int cursor = 0; + + while (cursor+1 < GetSize(todo)) + { + int nid_a = todo[cursor++]; + int nid_b = todo[cursor++]; + int nid = next_nid++; + + bad_properties.push_back(nid); + btorf("%d or %d %d %d\n", nid, sid, nid_a, nid_b); + } + + if (!bad_properties.empty()) { + if (cursor < GetSize(todo)) + bad_properties.push_back(todo[cursor++]); + log_assert(cursor == GetSize(todo)); + } else { + int nid = next_nid++; + log_assert(cursor == 0); + log_assert(GetSize(todo) == 1); + btorf("%d bad %d\n", nid, todo[cursor]); + } + } } }; struct BtorBackend : public Backend { BtorBackend() : Backend("btor", "write design to BTOR file") { } - - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" write_btor [filename]\n"); + log(" write_btor [options] [filename]\n"); + log("\n"); + log("Write a BTOR description of the current design.\n"); + log("\n"); + log(" -v\n"); + log(" Add comments and indentation to BTOR output file\n"); + log("\n"); + log(" -s\n"); + log(" Output only a single bad property for all asserts\n"); log("\n"); - log("Write the current design to an BTOR file.\n"); } - - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { - 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; + bool verbose = false, single_bad = false; 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 \n", yosys_version_str); - *f << stringf("; BTOR Backend developed by Ahmed Irfan - Fondazione Bruno Kessler, Trento, Italy\n"); - *f << stringf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"); - - std::vector mod_list; - - for (auto module_it : design->modules_) + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { - 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(); + if (args[argidx] == "-v") { + verbose = true; continue; } - - mod_list.push_back(module); + if (args[argidx] == "-s") { + single_bad = true; + continue; + } + break; } + extra_args(f, filename, args, argidx); - if (!top_module_name.empty()) - log_error("Can't find top module `%s'!\n", top_module_name.c_str()); + RTLIL::Module *topmod = design->top_module(); - for (auto module : mod_list) - BtorDumper::dump(*f, module, design, config); + if (topmod == nullptr) + log_cmd_error("No top module found.\n"); + + *f << stringf("; BTOR description generated by %s for module %s.\n", + yosys_version_str, log_id(topmod)); + + BtorWorker(*f, topmod, verbose, single_bad); + + *f << stringf("; end of yosys output\n"); } } BtorBackend; diff --git a/yosys/backends/btor/test_cells.sh b/yosys/backends/btor/test_cells.sh new file mode 100644 index 000000000..e0f1a0514 --- /dev/null +++ b/yosys/backends/btor/test_cells.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -ex + +rm -rf test_cells.tmp +mkdir -p test_cells.tmp +cd test_cells.tmp + +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod' + +for fn in test_*.il; do + ../../../yosys -p " + read_ilang $fn + rename gold gate + synth + + read_ilang $fn + miter -equiv -make_assert -flatten gold gate main + hierarchy -top main + write_btor ${fn%.il}.btor + " + boolectormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out + if grep " SATISFIABLE" ${fn%.il}.out; then + echo "Check failed for ${fn%.il}." + exit 1 + fi +done + +echo "OK." + diff --git a/yosys/backends/btor/verilog2btor.sh b/yosys/backends/btor/verilog2btor.sh deleted file mode 100755 index dfd7f1a85..000000000 --- a/yosys/backends/btor/verilog2btor.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -# -# Script to write BTOR from Verilog design -# - -if [ "$#" -ne 3 ]; then - echo "Usage: $0 input.v output.btor top-module-name" >&2 - exit 1 -fi -if ! [ -e "$1" ]; then - echo "$1 not found" >&2 - exit 1 -fi - -FULL_PATH=$(readlink -f $1) -DIR=$(dirname $FULL_PATH) - -./yosys -q -p " -read_verilog -sv $1; -hierarchy -top $3; -hierarchy -libdir $DIR; -hierarchy -check; -proc; -opt; opt_expr -mux_undef; opt; -rename -hide;;; -#techmap -map +/pmux2mux.v;; -splice; opt; -memory_dff -wr_only; -memory_collect;; -flatten;; -memory_unpack; -splitnets -driver; -setundef -zero -undriven; -opt;;; -write_btor $2;" - diff --git a/yosys/backends/edif/edif.cc b/yosys/backends/edif/edif.cc index d16f18316..d4e56a9eb 100644 --- a/yosys/backends/edif/edif.cc +++ b/yosys/backends/edif/edif.cc @@ -31,65 +31,66 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN #define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str() +#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br).c_str() #define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str() -namespace +struct EdifNames { - struct EdifNames + int counter; + char delim_left, delim_right; + std::set generated_names, used_names; + std::map name_map; + + EdifNames() : counter(1), delim_left('['), delim_right(']') { } + + std::string operator()(std::string id, bool define, bool port_rename = false, int range_left = 0, int range_right = 0) { - int counter; - std::set generated_names, used_names; - std::map 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; + if (define) { + std::string new_id = operator()(id, false); + if (port_rename) + return stringf("(rename %s \"%s%c%d:%d%c\")", new_id.c_str(), id.c_str(), delim_left, range_left, range_right, delim_right); + return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id; } - }; -} + + 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() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -105,17 +106,25 @@ struct EdifBackend : public Backend { log(" if the design contains constant nets. use \"hilomap\" to map to custom\n"); log(" constant drivers first)\n"); log("\n"); + log(" -attrprop\n"); + log(" create EDIF properties for cell attributes\n"); + log("\n"); + log(" -pvector {par|bra|ang}\n"); + log(" sets the delimiting character for module port rename clauses to\n"); + log(" parentheses, square brackets, or angle brackets.\n"); + log("\n"); log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n"); log("command generates EDIF files for the Xilinx place&route tools. It might be\n"); log("necessary to make small modifications to this command when a different tool\n"); log("is targeted.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing EDIF backend.\n"); - std::string top_module_name; + bool port_rename = false; + bool attr_properties = false; std::map> lib_cell_ports; bool nogndvcc = false; CellTypes ct(design); @@ -132,6 +141,23 @@ struct EdifBackend : public Backend { nogndvcc = true; continue; } + if (args[argidx] == "-attrprop") { + attr_properties = true; + continue; + } + if (args[argidx] == "-pvector" && argidx+1 < args.size()) { + std::string parray; + port_rename = true; + parray = args[++argidx]; + if (parray == "par") { + edif_names.delim_left = '(';edif_names.delim_right = ')'; + } else if (parray == "ang") { + edif_names.delim_left = '<';edif_names.delim_right = '>'; + } else { + edif_names.delim_left = '[';edif_names.delim_right = ']'; + } + continue; + } break; } extra_args(f, filename, args, argidx); @@ -214,8 +240,18 @@ struct EdifBackend : public Backend { } if (port_it.second == 1) *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir); - else - *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(port_it.first), port_it.second, dir); + else { + int b[2] = {port_it.second-1, 0}; + auto m = design->module(cell_it.first); + if (m) { + auto w = m->wire(port_it.first); + if (w) { + b[w->upto ? 0 : 1] = w->start_offset; + b[w->upto ? 1 : 0] = w->start_offset+GetSize(w)-1; + } + } + *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), port_it.second, dir); + } } *f << stringf(" )\n"); *f << stringf(" )\n"); @@ -283,10 +319,13 @@ struct EdifBackend : public Backend { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire)); net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name))); } else { - *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(wire->name), wire->width, dir); + int b[2]; + b[wire->upto ? 0 : 1] = wire->start_offset; + b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1; + *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir); for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); - net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), i)); + net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1)); } } } @@ -301,42 +340,79 @@ struct EdifBackend : public Backend { *f << stringf(" (instance %s\n", EDIF_DEF(cell->name)); *f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type), lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : ""); - for (auto &p : cell->parameters) - if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0) - *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str()); - else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def()) - *f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int()); + + auto add_prop = [&](IdString name, Const val) { + if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) + *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str()); + else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def()) + *f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int()); else { std::string hex_string = ""; - for (size_t i = 0; i < p.second.bits.size(); i += 4) { + for (size_t i = 0; i < val.bits.size(); i += 4) { int digit_value = 0; - if (i+0 < p.second.bits.size() && p.second.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1; - if (i+1 < p.second.bits.size() && p.second.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2; - if (i+2 < p.second.bits.size() && p.second.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4; - if (i+3 < p.second.bits.size() && p.second.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8; + if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1; + if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2; + if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4; + if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8; char digit_str[2] = { "0123456789abcdef"[digit_value], 0 }; hex_string = std::string(digit_str) + hex_string; } - *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str()); + *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str()); } + }; + + for (auto &p : cell->parameters) + add_prop(p.first, p.second); + if (attr_properties) + for (auto &p : cell->attributes) + add_prop(p.first, p.second); + *f << stringf(")\n"); for (auto &p : cell->connections()) { RTLIL::SigSpec sig = sigmap(p.second); for (int i = 0; i < GetSize(sig); i++) - if (sig.size() == 1) + if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1) + log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", + i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); + else if (sig.size() == 1) net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name))); - else - net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), i, EDIF_REF(cell->name))); + else { + int member_idx = GetSize(sig)-i-1; + auto m = design->module(cell->type); + if (m) { + auto w = m->wire(p.first); + if (w) + member_idx = GetSize(w)-i-1; + } + net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", + EDIF_REF(p.first), member_idx, EDIF_REF(cell->name))); + } } } for (auto &it : net_join_db) { RTLIL::SigBit sig = it.first; - if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) - continue; - std::string netname = log_signal(sig); - for (size_t i = 0; i < netname.size(); i++) - if (netname[i] == ' ' || netname[i] == '\\') - netname.erase(netname.begin() + i--); + if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) { + if (sig == RTLIL::State::Sx) { + for (auto &ref : it.second) + log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str()); + sig = RTLIL::State::S0; + } else { + for (auto &ref : it.second) + log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str()); + log_abort(); + } + } + std::string netname; + if (sig == RTLIL::State::S0) + netname = "GND_NET"; + else if (sig == RTLIL::State::S1) + netname = "VCC_NET"; + else { + netname = log_signal(sig); + for (size_t i = 0; i < netname.size(); i++) + if (netname[i] == ' ' || netname[i] == '\\') + netname.erase(netname.begin() + i--); + } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) *f << stringf(" %s\n", ref.c_str()); diff --git a/yosys/backends/edif/runtest.py b/yosys/backends/edif/runtest.py new file mode 100644 index 000000000..826876a86 --- /dev/null +++ b/yosys/backends/edif/runtest.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 + +import os +import numpy as np + +enable_upto = True +enable_offset = True +enable_hierarchy = True +enable_logic = True + +def make_module(f, modname, width, subs): + print("module %s (A, B, C, X, Y, Z);" % modname, file=f) + inbits = list() + outbits = list() + + for p in "ABC": + offset = np.random.randint(10) if enable_offset else 0 + if enable_upto and np.random.randint(2): + print(" input [%d:%d] %s;" % (offset, offset+width-1, p), file=f) + else: + print(" input [%d:%d] %s;" % (offset+width-1, offset, p), file=f) + for i in range(offset, offset+width): + inbits.append("%s[%d]" % (p, i)) + + for p in "XYZ": + offset = np.random.randint(10) if enable_offset else 0 + if enable_upto and np.random.randint(2): + print(" output [%d:%d] %s;" % (offset, offset+width-1, p), file=f) + else: + print(" output [%d:%d] %s;" % (offset+width-1, offset, p), file=f) + for i in range(offset, offset+width): + outbits.append("%s[%d]" % (p, i)) + + instidx = 0 + subcandidates = list(subs.keys()) + + while len(outbits) > 0: + submod = None + if len(subcandidates): + submod = np.random.choice(subcandidates) + subcandidates.remove(submod) + + if submod is None or 3*subs[submod] >= len(outbits): + for bit in outbits: + if enable_logic: + print(" assign %s = %s & ~%s;" % (bit, np.random.choice(inbits), np.random.choice(inbits)), file=f) + else: + print(" assign %s = %s;" % (bit, np.random.choice(inbits)), file=f) + break + + instidx += 1 + print(" %s inst%d (" % (submod, instidx), file=f) + + for p in "ABC": + print(" .%s({%s})," % (p, ",".join(np.random.choice(inbits, subs[submod]))), file=f) + + for p in "XYZ": + bits = list(np.random.choice(outbits, subs[submod], False)) + for bit in bits: + outbits.remove(bit) + print(" .%s({%s})%s" % (p, ",".join(bits), "," if p != "Z" else ""), file=f) + + print(" );", file=f); + + print("endmodule", file=f) + +with open("test_top.v", "w") as f: + if enable_hierarchy: + make_module(f, "sub1", 2, {}) + make_module(f, "sub2", 3, {}) + make_module(f, "sub3", 4, {}) + make_module(f, "sub4", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) + make_module(f, "sub5", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) + make_module(f, "sub6", 8, {"sub1": 2, "sub2": 3, "sub3": 4}) + make_module(f, "top", 32, {"sub4": 8, "sub5": 8, "sub6": 8}) + else: + make_module(f, "top", 32, {}) + +os.system("set -x; ../../yosys -p 'synth_xilinx -top top; write_edif -pvector par test_syn.edif' test_top.v") + +with open("test_syn.tcl", "w") as f: + print("read_edif test_syn.edif", file=f) + print("link_design", file=f) + print("write_verilog -force test_syn.v", file=f) + +os.system("set -x; vivado -nojournal -nolog -mode batch -source test_syn.tcl") + +with open("test_tb.v", "w") as f: + print("module tb;", file=f) + print(" reg [31:0] A, B, C;", file=f) + print(" wire [31:0] X, Y, Z;", file=f) + print("", file=f) + print(" top uut (", file=f) + print(" .A(A),", file=f) + print(" .B(B),", file=f) + print(" .C(C),", file=f) + print(" .X(X),", file=f) + print(" .Y(Y),", file=f) + print(" .Z(Z)", file=f) + print(" );", file=f) + print("", file=f) + print(" initial begin", file=f) + for i in range(100): + print(" A = 32'h%08x;" % np.random.randint(2**32), file=f) + print(" B = 32'h%08x;" % np.random.randint(2**32), file=f) + print(" C = 32'h%08x;" % np.random.randint(2**32), file=f) + print(" #10;", file=f) + print(" $display(\"%x %x %x\", X, Y, Z);", file=f) + print(" #10;", file=f) + print(" $finish;", file=f) + print(" end", file=f) + print("endmodule", file=f) + +os.system("set -x; iverilog -o test_gold test_tb.v test_top.v") +os.system("set -x; iverilog -o test_gate test_tb.v test_syn.v ../../techlibs/xilinx/cells_sim.v") + +os.system("set -x; ./test_gold > test_gold.out") +os.system("set -x; ./test_gate > test_gate.out") + +os.system("set -x; md5sum test_gold.out test_gate.out") + diff --git a/yosys/backends/firrtl/.gitignore b/yosys/backends/firrtl/.gitignore new file mode 100644 index 000000000..a2ac93abe --- /dev/null +++ b/yosys/backends/firrtl/.gitignore @@ -0,0 +1,2 @@ +test.fir +test_out.v diff --git a/yosys/backends/firrtl/Makefile.inc b/yosys/backends/firrtl/Makefile.inc new file mode 100644 index 000000000..fdf100d34 --- /dev/null +++ b/yosys/backends/firrtl/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/firrtl/firrtl.o + diff --git a/yosys/backends/firrtl/firrtl.cc b/yosys/backends/firrtl/firrtl.cc new file mode 100644 index 000000000..32410a651 --- /dev/null +++ b/yosys/backends/firrtl/firrtl.cc @@ -0,0 +1,677 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +pool used_names; +dict namecache; +int autoid_counter; + +typedef unsigned FDirection; +static const FDirection FD_NODIRECTION = 0x0; +static const FDirection FD_IN = 0x1; +static const FDirection FD_OUT = 0x2; +static const FDirection FD_INOUT = 0x3; + +// Get a port direction with respect to a specific module. +FDirection getPortFDirection(IdString id, Module *module) +{ + Wire *wire = module->wires_.at(id); + FDirection direction = FD_NODIRECTION; + if (wire && wire->port_id) + { + if (wire->port_input) + direction |= FD_IN; + if (wire->port_output) + direction |= FD_OUT; + } + return direction; +} + +string next_id() +{ + string new_id; + + while (1) { + new_id = stringf("_%d", autoid_counter++); + if (used_names.count(new_id) == 0) break; + } + + used_names.insert(new_id); + return new_id; +} + +const char *make_id(IdString id) +{ + if (namecache.count(id) != 0) + return namecache.at(id).c_str(); + + string new_id = log_id(id); + + for (int i = 0; i < GetSize(new_id); i++) + { + char &ch = new_id[i]; + if ('a' <= ch && ch <= 'z') continue; + if ('A' <= ch && ch <= 'Z') continue; + if ('0' <= ch && ch <= '9' && i != 0) continue; + if ('_' == ch) continue; + ch = '_'; + } + + while (used_names.count(new_id) != 0) + new_id += '_'; + + namecache[id] = new_id; + used_names.insert(new_id); + return namecache.at(id).c_str(); +} + +struct FirrtlWorker +{ + Module *module; + std::ostream &f; + + dict> reverse_wire_map; + string unconn_id; + RTLIL::Design *design; + std::string indent; + + void register_reverse_wire_map(string id, SigSpec sig) + { + for (int i = 0; i < GetSize(sig); i++) + reverse_wire_map[sig[i]] = make_pair(id, i); + } + + FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ") + { + } + + string make_expr(const SigSpec &sig) + { + string expr; + + for (auto chunk : sig.chunks()) + { + string new_expr; + + if (chunk.wire == nullptr) + { + std::vector bits = chunk.data; + new_expr = stringf("UInt<%d>(\"h", GetSize(bits)); + + while (GetSize(bits) % 4 != 0) + bits.push_back(State::S0); + + for (int i = GetSize(bits)-4; i >= 0; i -= 4) + { + int val = 0; + if (bits[i+0] == State::S1) val += 1; + if (bits[i+1] == State::S1) val += 2; + if (bits[i+2] == State::S1) val += 4; + if (bits[i+3] == State::S1) val += 8; + new_expr.push_back(val < 10 ? '0' + val : 'a' + val - 10); + } + + new_expr += "\")"; + } + else if (chunk.offset == 0 && chunk.width == chunk.wire->width) + { + new_expr = make_id(chunk.wire->name); + } + else + { + string wire_id = make_id(chunk.wire->name); + new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset); + } + + if (expr.empty()) + expr = new_expr; + else + expr = "cat(" + new_expr + ", " + expr + ")"; + } + + return expr; + } + + std::string fid(RTLIL::IdString internal_id) + { + const char *str = internal_id.c_str(); + return *str == '\\' ? str + 1 : str; + } + + + std::string cellname(RTLIL::Cell *cell) + { + return fid(cell->name).c_str(); + } + + void process_instance(RTLIL::Cell *cell, vector &wire_exprs) + { + std::string cell_type = fid(cell->type); + + std::string cell_name = cellname(cell); + std::string cell_name_comment; + if (cell_name != fid(cell->name)) + cell_name_comment = " /* " + fid(cell->name) + " */ "; + else + cell_name_comment = ""; + // Find the module corresponding to this instance. + auto instModule = design->module(cell->type); + wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.c_str())); + + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { + if (it->second.size() > 0) { + const SigSpec &secondSig = it->second; + const std::string firstName = cell_name + "." + make_id(it->first); + const std::string secondName = make_expr(secondSig); + // Find the direction for this port. + FDirection dir = getPortFDirection(it->first, instModule); + std::string source, sink; + switch (dir) { + case FD_INOUT: + log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second)); + case FD_OUT: + source = firstName; + sink = secondName; + break; + case FD_NODIRECTION: + log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second)); + /* FALL_THROUGH */ + case FD_IN: + source = secondName; + sink = firstName; + break; + default: + log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir); + break; + } + wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str())); + } + } + wire_exprs.push_back(stringf("\n")); + + } + + void run() + { + f << stringf(" module %s:\n", make_id(module->name)); + vector port_decls, wire_decls, cell_exprs, wire_exprs; + + for (auto wire : module->wires()) + { + const auto wireName = make_id(wire->name); + if (wire->port_id) + { + if (wire->port_input && wire->port_output) + log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); + port_decls.push_back(stringf(" %s %s: UInt<%d>\n", wire->port_input ? "input" : "output", + wireName, wire->width)); + } + else + { + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width)); + } + } + + for (auto cell : module->cells()) + { + // Is this cell is a module instance? + if (cell->type[0] != '$') + { + process_instance(cell, wire_exprs); + continue; + } + if (cell->type.in("$not", "$logic_not", "$neg", "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_bool", "$reduce_xnor")) + { + string y_id = make_id(cell->name); + bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + int y_width = cell->parameters.at("\\Y_WIDTH").as_int(); + string a_expr = make_expr(cell->getPort("\\A")); + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + + if (cell->parameters.at("\\A_SIGNED").as_bool()) { + a_expr = "asSInt(" + a_expr + ")"; + } + + // Don't use the results of logical operations (a single bit) to control padding + if (!(cell->type.in("$eq", "$eqx", "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$reduce_bool", "$logic_not") && y_width == 1) ) { + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + } + + string primop; + bool always_uint = false; + if (cell->type == "$not") primop = "not"; + if (cell->type == "$neg") primop = "neg"; + if (cell->type == "$logic_not") { + primop = "eq"; + a_expr = stringf("%s, UInt(0)", a_expr.c_str()); + } + if (cell->type == "$reduce_and") primop = "andr"; + if (cell->type == "$reduce_or") primop = "orr"; + if (cell->type == "$reduce_xor") primop = "xorr"; + if (cell->type == "$reduce_xnor") { + primop = "not"; + a_expr = stringf("xorr(%s)", a_expr.c_str()); + } + if (cell->type == "$reduce_bool") { + primop = "neq"; + // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. + bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + int a_width = cell->parameters.at("\\A_WIDTH").as_int(); + a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); + } + + string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); + + if ((is_signed && !always_uint)) + expr = stringf("asUInt(%s)", expr.c_str()); + + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Y")); + + continue; + } + if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx", + "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl", + "$logic_and", "$logic_or")) + { + string y_id = make_id(cell->name); + bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); + int y_width = cell->parameters.at("\\Y_WIDTH").as_int(); + string a_expr = make_expr(cell->getPort("\\A")); + string b_expr = make_expr(cell->getPort("\\B")); + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + + if (cell->parameters.at("\\A_SIGNED").as_bool()) { + a_expr = "asSInt(" + a_expr + ")"; + } + // Shift amount is always unsigned, and needn't be padded to result width. + if (!cell->type.in("$shr", "$sshr", "$shl", "$sshl")) { + if (cell->parameters.at("\\B_SIGNED").as_bool()) { + b_expr = "asSInt(" + b_expr + ")"; + } + b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + } + + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + + if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) { + a_expr = "asUInt(" + a_expr + ")"; + } + + string primop; + bool always_uint = false; + if (cell->type == "$add") primop = "add"; + if (cell->type == "$sub") primop = "sub"; + if (cell->type == "$mul") primop = "mul"; + if (cell->type == "$div") primop = "div"; + if (cell->type == "$mod") primop = "rem"; + if (cell->type == "$and") { + primop = "and"; + always_uint = true; + } + if (cell->type == "$or" ) { + primop = "or"; + always_uint = true; + } + if (cell->type == "$xor") { + primop = "xor"; + always_uint = true; + } + if ((cell->type == "$eq") | (cell->type == "$eqx")) { + primop = "eq"; + always_uint = true; + } + if ((cell->type == "$ne") | (cell->type == "$nex")) { + primop = "neq"; + always_uint = true; + } + if (cell->type == "$gt") { + primop = "gt"; + always_uint = true; + } + if (cell->type == "$ge") { + primop = "geq"; + always_uint = true; + } + if (cell->type == "$lt") { + primop = "lt"; + always_uint = true; + } + if (cell->type == "$le") { + primop = "leq"; + always_uint = true; + } + if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl"; + if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr"; + if ((cell->type == "$logic_and")) { + primop = "and"; + a_expr = "neq(" + a_expr + ", UInt(0))"; + b_expr = "neq(" + b_expr + ", UInt(0))"; + always_uint = true; + } + if ((cell->type == "$logic_or")) { + primop = "or"; + a_expr = "neq(" + a_expr + ", UInt(0))"; + b_expr = "neq(" + b_expr + ", UInt(0))"; + always_uint = true; + } + + if (!cell->parameters.at("\\B_SIGNED").as_bool()) { + b_expr = "asUInt(" + b_expr + ")"; + } + + string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); + + if ((is_signed && !always_uint) || cell->type.in("$sub")) + expr = stringf("asUInt(%s)", expr.c_str()); + + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Y")); + + continue; + } + + if (cell->type.in("$mux")) + { + string y_id = make_id(cell->name); + int width = cell->parameters.at("\\WIDTH").as_int(); + string a_expr = make_expr(cell->getPort("\\A")); + string b_expr = make_expr(cell->getPort("\\B")); + string s_expr = make_expr(cell->getPort("\\S")); + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), width)); + + string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); + + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Y")); + + continue; + } + + if (cell->type.in("$mem")) + { + string mem_id = make_id(cell->name); + int abits = cell->parameters.at("\\ABITS").as_int(); + int width = cell->parameters.at("\\WIDTH").as_int(); + int size = cell->parameters.at("\\SIZE").as_int(); + int rd_ports = cell->parameters.at("\\RD_PORTS").as_int(); + int wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); + + Const initdata = cell->parameters.at("\\INIT"); + for (State bit : initdata.bits) + if (bit != State::Sx) + log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(cell)); + + Const rd_clk_enable = cell->parameters.at("\\RD_CLK_ENABLE"); + Const wr_clk_enable = cell->parameters.at("\\WR_CLK_ENABLE"); + Const wr_clk_polarity = cell->parameters.at("\\WR_CLK_POLARITY"); + + int offset = cell->parameters.at("\\OFFSET").as_int(); + if (offset != 0) + log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell)); + + cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str())); + cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width)); + cell_exprs.push_back(stringf(" depth => %d\n", size)); + + for (int i = 0; i < rd_ports; i++) + cell_exprs.push_back(stringf(" reader => r%d\n", i)); + + for (int i = 0; i < wr_ports; i++) + cell_exprs.push_back(stringf(" writer => w%d\n", i)); + + cell_exprs.push_back(stringf(" read-latency => 0\n")); + cell_exprs.push_back(stringf(" write-latency => 1\n")); + cell_exprs.push_back(stringf(" read-under-write => undefined\n")); + + for (int i = 0; i < rd_ports; i++) + { + if (rd_clk_enable[i] != State::S0) + log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); + + SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width); + string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits)); + + cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str())); + cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i)); + cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i)); + + register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig); + } + + for (int i = 0; i < wr_ports; i++) + { + if (wr_clk_enable[i] != State::S1) + log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); + + if (wr_clk_polarity[i] != State::S1) + log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); + + string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits)); + string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width)); + string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i)); + + SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width); + string wen_expr = make_expr(wen_sig[0]); + + for (int i = 1; i < GetSize(wen_sig); i++) + if (wen_sig[0] != wen_sig[i]) + log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); + + cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str())); + cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str())); + cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str())); + cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i)); + cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str())); + } + + continue; + } + + if (cell->type.in("$dff")) + { + bool clkpol = cell->parameters.at("\\CLK_POLARITY").as_bool(); + if (clkpol == false) + log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell)); + + string q_id = make_id(cell->name); + int width = cell->parameters.at("\\WIDTH").as_int(); + string expr = make_expr(cell->getPort("\\D")); + string clk_expr = "asClock(" + make_expr(cell->getPort("\\CLK")) + ")"; + + wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", q_id.c_str(), width, clk_expr.c_str())); + + cell_exprs.push_back(stringf(" %s <= %s\n", q_id.c_str(), expr.c_str())); + register_reverse_wire_map(q_id, cell->getPort("\\Q")); + + continue; + } + + log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); + } + + for (auto conn : module->connections()) + { + string y_id = next_id(); + int y_width = GetSize(conn.first); + string expr = make_expr(conn.second); + + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, conn.first); + } + + for (auto wire : module->wires()) + { + string expr; + + if (wire->port_input) + continue; + + int cursor = 0; + bool is_valid = false; + bool make_unconn_id = false; + + while (cursor < wire->width) + { + int chunk_width = 1; + string new_expr; + + SigBit start_bit(wire, cursor); + + if (reverse_wire_map.count(start_bit)) + { + pair start_map = reverse_wire_map.at(start_bit); + + while (cursor+chunk_width < wire->width) + { + SigBit stop_bit(wire, cursor+chunk_width); + + if (reverse_wire_map.count(stop_bit) == 0) + break; + + pair stop_map = reverse_wire_map.at(stop_bit); + stop_map.second -= chunk_width; + + if (start_map != stop_map) + break; + + chunk_width++; + } + + new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(), + start_map.second + chunk_width - 1, start_map.second); + is_valid = true; + } + else + { + if (unconn_id.empty()) { + unconn_id = next_id(); + make_unconn_id = true; + } + new_expr = unconn_id; + } + + if (expr.empty()) + expr = new_expr; + else + expr = "cat(" + new_expr + ", " + expr + ")"; + + cursor += chunk_width; + } + + if (is_valid) { + if (make_unconn_id) { + wire_decls.push_back(stringf(" wire %s: UInt<1>\n", unconn_id.c_str())); + wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str())); + } + wire_exprs.push_back(stringf(" %s <= %s\n", make_id(wire->name), expr.c_str())); + } else { + if (make_unconn_id) { + unconn_id.clear(); + } + wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name))); + } + } + + for (auto str : port_decls) + f << str; + + f << stringf("\n"); + + for (auto str : wire_decls) + f << str; + + f << stringf("\n"); + + for (auto str : cell_exprs) + f << str; + + f << stringf("\n"); + + for (auto str : wire_exprs) + f << str; + } +}; + +struct FirrtlBackend : public Backend { + FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_firrtl [options] [filename]\n"); + log("\n"); + log("Write a FIRRTL netlist of the current design.\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-aig") { + // aig_mode = true; + // continue; + // } + break; + } + extra_args(f, filename, args, argidx); + + log_header(design, "Executing FIRRTL backend.\n"); + + Module *top = design->top_module(); + + if (top == nullptr) + log_error("No top module found!\n"); + + namecache.clear(); + autoid_counter = 0; + + for (auto module : design->modules()) { + make_id(module->name); + for (auto wire : module->wires()) + if (wire->port_id) + make_id(wire->name); + } + + *f << stringf("circuit %s:\n", make_id(top->name)); + + for (auto module : design->modules()) + { + FirrtlWorker worker(module, *f, design); + worker.run(); + } + + namecache.clear(); + autoid_counter = 0; + } +} FirrtlBackend; + +PRIVATE_NAMESPACE_END diff --git a/yosys/backends/firrtl/test.sh b/yosys/backends/firrtl/test.sh new file mode 100644 index 000000000..fe7e3a329 --- /dev/null +++ b/yosys/backends/firrtl/test.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -ex + +cd ../../ +make +cd backends/firrtl + +../../yosys -q -p 'prep -nordff; write_firrtl test.fir' $1 + +firrtl -i test.fir -o test_out.v -ll Info + +../../yosys -p " + read_verilog $1 + rename Top gold + + read_verilog test_out.v + rename Top gate + + prep + memory_map + miter -equiv -flatten gold gate miter + hierarchy -top miter + + sat -verify -prove trigger 0 -set-init-zero -seq 10 miter +" diff --git a/yosys/backends/firrtl/test.v b/yosys/backends/firrtl/test.v new file mode 100644 index 000000000..c6d62a847 --- /dev/null +++ b/yosys/backends/firrtl/test.v @@ -0,0 +1,63 @@ +module test( + input clk, wen, + input [7:0] uns, + input signed [7:0] a, b, + input signed [23:0] c, + input signed [2:0] sel, + output [15:0] s, d, y, z, u, q, p, mul, div, mod, mux, And, Or, Xor, eq, neq, gt, lt, geq, leq, eqx, shr, sshr, shl, sshl, Land, Lor, Lnot, Not, Neg, pos, Andr, Orr, Xorr, Xnorr, Reduce_bool, + output [7:0] PMux +); + //initial begin + //$display("shr = %b", shr); + //end + assign s = a+{b[6:2], 2'b1}; + assign d = a-b; + assign y = x; + assign z[7:0] = s+d; + assign z[15:8] = s-d; + assign p = a & b | x; + assign mul = a * b; + assign div = a / b; + assign mod = a % b; + assign mux = x[0] ? a : b; + assign And = a & b; + assign Or = a | b; + assign Xor = a ^ b; + assign Not = ~a; + assign Neg = -a; + assign eq = a == b; + assign neq = a != b; + assign gt = a > b; + assign lt = a < b; + assign geq = a >= b; + assign leq = a <= b; + assign eqx = a === b; + assign shr = a >> b; //0111111111000000 + assign sshr = a >>> b; + assign shl = a << b; + assign sshl = a <<< b; + assign Land = a && b; + assign Lor = a || b; + assign Lnot = !a; + assign pos = $signed(uns); + assign Andr = &a; + assign Orr = |a; + assign Xorr = ^a; + assign Xnorr = ~^a; + always @* + if(!a) begin + Reduce_bool = a; + end else begin + Reduce_bool = b; + end + //always @(sel or c or a) + // begin + // case (sel) + // 3'b000: PMux = a; + // 3'b001: PMux = c[7:0]; + // 3'b010: PMux = c[15:8]; + // 3'b100: PMux = c[23:16]; + // endcase + // end + +endmodule diff --git a/yosys/backends/ilang/ilang_backend.cc b/yosys/backends/ilang/ilang_backend.cc index 16d1a97f5..4c58ea087 100644 --- a/yosys/backends/ilang/ilang_backend.cc +++ b/yosys/backends/ilang/ilang_backend.cc @@ -219,7 +219,7 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT { f << stringf("%s" "sync ", indent.c_str()); switch (sy->type) { - if (0) case RTLIL::ST0: f << stringf("low "); + case RTLIL::ST0: f << stringf("low "); if (0) case RTLIL::ST1: f << stringf("high "); if (0) case RTLIL::STp: f << stringf("posedge "); if (0) case RTLIL::STn: f << stringf("negedge "); @@ -382,7 +382,7 @@ PRIVATE_NAMESPACE_BEGIN struct IlangBackend : public Backend { IlangBackend() : Backend("ilang", "write design to ilang file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -395,7 +395,7 @@ struct IlangBackend : public Backend { log(" only write selected parts of the design.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool selected = false; @@ -422,7 +422,7 @@ struct IlangBackend : public Backend { struct DumpPass : public Pass { DumpPass() : Pass("dump", "print parts of the design in ilang format") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -445,7 +445,7 @@ struct DumpPass : public Pass { log(" like -outfile but append instead of overwrite\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string filename; bool flag_m = false, flag_n = false, append = false; diff --git a/yosys/backends/intersynth/intersynth.cc b/yosys/backends/intersynth/intersynth.cc index 34cb52fb4..2eb08dbe9 100644 --- a/yosys/backends/intersynth/intersynth.cc +++ b/yosys/backends/intersynth/intersynth.cc @@ -46,7 +46,7 @@ static std::string netname(std::set &conntypes_code, std::set args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing INTERSYNTH backend.\n"); log_push(); diff --git a/yosys/backends/json/json.cc b/yosys/backends/json/json.cc index 4baffa33e..f5c687981 100644 --- a/yosys/backends/json/json.cc +++ b/yosys/backends/json/json.cc @@ -93,8 +93,10 @@ struct JsonWriter f << get_string(param.second.decode_string()); else if (GetSize(param.second.bits) > 32) f << get_string(param.second.as_string()); - else + else if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) f << stringf("%d", param.second.as_int()); + else + f << stringf("%u", param.second.as_int()); first = false; } } @@ -201,6 +203,8 @@ struct JsonWriter void write_design(Design *design_) { design = design_; + design->sort(); + f << stringf("{\n"); f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str()); f << stringf(" \"modules\": {\n"); @@ -248,7 +252,7 @@ struct JsonWriter struct JsonBackend : public Backend { JsonBackend() : Backend("json", "write design to a JSON file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -333,6 +337,10 @@ struct JsonBackend : public Backend { log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n"); log("a number.\n"); log("\n"); + log("Numeric parameter and attribute values up to 32 bits are written as decimal\n"); + log("values. Numbers larger than that are written as string holding the binary\n"); + log("representation of the value.\n"); + log("\n"); log("For example the following Verilog code:\n"); log("\n"); log(" module test(input x, y);\n"); @@ -452,7 +460,7 @@ struct JsonBackend : public Backend { log("format. A program processing this format must ignore all unknown fields.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool aig_mode = false; @@ -476,7 +484,7 @@ struct JsonBackend : public Backend { struct JsonPass : public Pass { JsonPass() : Pass("json", "write design in JSON format") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -493,7 +501,7 @@ struct JsonPass : public Pass { log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string filename; bool aig_mode = false; diff --git a/yosys/backends/protobuf/.gitignore b/yosys/backends/protobuf/.gitignore new file mode 100644 index 000000000..849b38d45 --- /dev/null +++ b/yosys/backends/protobuf/.gitignore @@ -0,0 +1,2 @@ +yosys.pb.cc +yosys.pb.h diff --git a/yosys/backends/protobuf/Makefile.inc b/yosys/backends/protobuf/Makefile.inc new file mode 100644 index 000000000..834cad42c --- /dev/null +++ b/yosys/backends/protobuf/Makefile.inc @@ -0,0 +1,8 @@ +ifeq ($(ENABLE_PROTOBUF),1) + +backends/protobuf/yosys.pb.cc backends/protobuf/yosys.pb.h: misc/yosys.proto + $(Q) cd misc && protoc --cpp_out "../backends/protobuf" yosys.proto + +OBJS += backends/protobuf/protobuf.o backends/protobuf/yosys.pb.o + +endif diff --git a/yosys/backends/protobuf/protobuf.cc b/yosys/backends/protobuf/protobuf.cc new file mode 100644 index 000000000..f56147cef --- /dev/null +++ b/yosys/backends/protobuf/protobuf.cc @@ -0,0 +1,370 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 Serge Bazanski + * + * 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 + +#include "kernel/rtlil.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cellaigs.h" +#include "kernel/log.h" +#include "yosys.pb.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ProtobufDesignSerializer +{ + bool aig_mode_; + bool use_selection_; + yosys::pb::Design *pb_; + + Design *design_; + Module *module_; + + SigMap sigmap_; + int sigidcounter_; + dict sigids_; + pool aig_models_; + + + ProtobufDesignSerializer(bool use_selection, bool aig_mode) : + aig_mode_(aig_mode), use_selection_(use_selection) { } + + string get_name(IdString name) + { + return RTLIL::unescape_id(name); + } + + + void serialize_parameters(google::protobuf::Map *out, + const dict ¶meters) + { + for (auto ¶m : parameters) { + std::string key = get_name(param.first); + + + yosys::pb::Parameter pb_param; + + if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) { + pb_param.set_str(param.second.decode_string()); + } else if (GetSize(param.second.bits) > 64) { + pb_param.set_str(param.second.as_string()); + } else { + pb_param.set_int_(param.second.as_int()); + } + + (*out)[key] = pb_param; + } + } + + void get_bits(yosys::pb::BitVector *out, SigSpec sig) + { + for (auto bit : sigmap_(sig)) { + auto sig = out->add_signal(); + + // Constant driver. + if (bit.wire == nullptr) { + if (bit == State::S0) sig->set_constant(sig->CONSTANT_DRIVER_LOW); + else if (bit == State::S1) sig->set_constant(sig->CONSTANT_DRIVER_HIGH); + else if (bit == State::Sz) sig->set_constant(sig->CONSTANT_DRIVER_Z); + else sig->set_constant(sig->CONSTANT_DRIVER_X); + continue; + } + + // Signal - give it a unique identifier. + if (sigids_.count(bit) == 0) { + sigids_[bit] = sigidcounter_++; + } + sig->set_id(sigids_[bit]); + } + } + + void serialize_module(yosys::pb::Module* out, Module *module) + { + module_ = module; + log_assert(module_->design == design_); + sigmap_.set(module_); + sigids_.clear(); + sigidcounter_ = 0; + + serialize_parameters(out->mutable_attribute(), module_->attributes); + + for (auto n : module_->ports) { + Wire *w = module->wire(n); + if (use_selection_ && !module_->selected(w)) + continue; + + yosys::pb::Module::Port pb_port; + pb_port.set_direction(w->port_input ? w->port_output ? + yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT : yosys::pb::DIRECTION_OUTPUT); + get_bits(pb_port.mutable_bits(), w); + (*out->mutable_port())[get_name(n)] = pb_port; + } + + for (auto c : module_->cells()) { + if (use_selection_ && !module_->selected(c)) + continue; + + yosys::pb::Module::Cell pb_cell; + pb_cell.set_hide_name(c->name[0] == '$'); + pb_cell.set_type(get_name(c->type)); + + if (aig_mode_) { + Aig aig(c); + if (aig.name.empty()) + continue; + pb_cell.set_model(aig.name); + aig_models_.insert(aig); + } + serialize_parameters(pb_cell.mutable_parameter(), c->parameters); + serialize_parameters(pb_cell.mutable_attribute(), c->attributes); + + if (c->known()) { + for (auto &conn : c->connections()) { + yosys::pb::Direction direction = yosys::pb::DIRECTION_OUTPUT; + if (c->input(conn.first)) + direction = c->output(conn.first) ? yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT; + (*pb_cell.mutable_port_direction())[get_name(conn.first)] = direction; + } + } + for (auto &conn : c->connections()) { + yosys::pb::BitVector vec; + get_bits(&vec, conn.second); + (*pb_cell.mutable_connection())[get_name(conn.first)] = vec; + } + + (*out->mutable_cell())[get_name(c->name)] = pb_cell; + } + + for (auto w : module_->wires()) { + if (use_selection_ && !module_->selected(w)) + continue; + + auto netname = out->add_netname(); + netname->set_hide_name(w->name[0] == '$'); + get_bits(netname->mutable_bits(), w); + serialize_parameters(netname->mutable_attributes(), w->attributes); + } + } + + + void serialize_models(google::protobuf::Map *models) + { + for (auto &aig : aig_models_) { + yosys::pb::Model pb_model; + for (auto &node : aig.nodes) { + auto pb_node = pb_model.add_node(); + if (node.portbit >= 0) { + if (node.inverter) { + pb_node->set_type(pb_node->TYPE_NPORT); + } else { + pb_node->set_type(pb_node->TYPE_PORT); + } + auto port = pb_node->mutable_port(); + port->set_portname(log_id(node.portname)); + port->set_bitindex(node.portbit); + } else if (node.left_parent < 0 && node.right_parent < 0) { + if (node.inverter) { + pb_node->set_type(pb_node->TYPE_TRUE); + } else { + pb_node->set_type(pb_node->TYPE_FALSE); + } + } else { + if (node.inverter) { + pb_node->set_type(pb_node->TYPE_NAND); + } else { + pb_node->set_type(pb_node->TYPE_AND); + } + auto gate = pb_node->mutable_gate(); + gate->set_left(node.left_parent); + gate->set_right(node.right_parent); + } + for (auto &op : node.outports) { + auto pb_op = pb_node->add_out_port(); + pb_op->set_name(log_id(op.first)); + pb_op->set_bit_index(op.second); + } + } + (*models)[aig.name] = pb_model; + } + } + + void serialize_design(yosys::pb::Design *pb, Design *design) + { + GOOGLE_PROTOBUF_VERIFY_VERSION; + pb_ = pb; + pb_->Clear(); + pb_->set_creator(yosys_version_str); + + design_ = design; + design_->sort(); + + auto modules = use_selection_ ? design_->selected_modules() : design_->modules(); + for (auto mod : modules) { + yosys::pb::Module pb_mod; + serialize_module(&pb_mod, mod); + (*pb->mutable_modules())[mod->name.str()] = pb_mod; + } + + serialize_models(pb_->mutable_models()); + } +}; + +struct ProtobufBackend : public Backend { + ProtobufBackend(): Backend("protobuf", "write design to a Protocol Buffer file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_protobuf [options] [filename]\n"); + log("\n"); + log("Write a JSON netlist of the current design.\n"); + log("\n"); + log(" -aig\n"); + log(" include AIG models for the different gate types\n"); + log("\n"); + log(" -text\n"); + log(" output protobuf in Text/ASCII representation\n"); + log("\n"); + log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n"); + log("Yosys source code distribution.\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + bool aig_mode = false; + bool text_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-aig") { + aig_mode = true; + continue; + } + if (args[argidx] == "-text") { + text_mode = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + log_header(design, "Executing Protobuf backend.\n"); + + yosys::pb::Design pb; + ProtobufDesignSerializer serializer(false, aig_mode); + serializer.serialize_design(&pb, design); + + if (text_mode) { + string out; + google::protobuf::TextFormat::PrintToString(pb, &out); + *f << out; + } else { + pb.SerializeToOstream(f); + } + } +} ProtobufBackend; + +struct ProtobufPass : public Pass { + ProtobufPass() : Pass("protobuf", "write design in Protobuf format") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" protobuf [options] [selection]\n"); + log("\n"); + log("Write a JSON netlist of all selected objects.\n"); + log("\n"); + log(" -o \n"); + log(" write to the specified file.\n"); + log("\n"); + log(" -aig\n"); + log(" include AIG models for the different gate types\n"); + log("\n"); + log(" -text\n"); + log(" output protobuf in Text/ASCII representation\n"); + log("\n"); + log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n"); + log("Yosys source code distribution.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + std::string filename; + bool aig_mode = false; + bool text_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-o" && argidx+1 < args.size()) { + filename = args[++argidx]; + continue; + } + if (args[argidx] == "-aig") { + aig_mode = true; + continue; + } + if (args[argidx] == "-text") { + text_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + std::ostream *f; + std::stringstream buf; + + if (!filename.empty()) { + std::ofstream *ff = new std::ofstream; + ff->open(filename.c_str(), std::ofstream::trunc); + if (ff->fail()) { + delete ff; + log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); + } + f = ff; + } else { + f = &buf; + } + + yosys::pb::Design pb; + ProtobufDesignSerializer serializer(true, aig_mode); + serializer.serialize_design(&pb, design); + + if (text_mode) { + string out; + google::protobuf::TextFormat::PrintToString(pb, &out); + *f << out; + } else { + pb.SerializeToOstream(f); + } + + if (!filename.empty()) { + delete f; + } else { + log("%s", buf.str().c_str()); + } + } +} ProtobufPass; + +PRIVATE_NAMESPACE_END; diff --git a/yosys/backends/simplec/.gitignore b/yosys/backends/simplec/.gitignore new file mode 100644 index 000000000..f08796168 --- /dev/null +++ b/yosys/backends/simplec/.gitignore @@ -0,0 +1,2 @@ +test00_tb +test00_uut.c diff --git a/yosys/backends/simplec/Makefile.inc b/yosys/backends/simplec/Makefile.inc new file mode 100644 index 000000000..fee1376c5 --- /dev/null +++ b/yosys/backends/simplec/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/simplec/simplec.o + diff --git a/yosys/backends/simplec/simplec.cc b/yosys/backends/simplec/simplec.cc new file mode 100644 index 000000000..6f2ccbe20 --- /dev/null +++ b/yosys/backends/simplec/simplec.cc @@ -0,0 +1,810 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct HierDirtyFlags; + +static pool reserved_cids; +static dict id2cid; + +static string cid(IdString id) +{ + if (id2cid.count(id) == 0) + { + string s = id.str(); + if (GetSize(s) < 2) log_abort(); + + if (s[0] == '\\') + s = s.substr(1); + + if ('0' <= s[0] && s[0] <= '9') { + s = "_" + s; + } + + for (int i = 0; i < GetSize(s); i++) { + if ('0' <= s[i] && s[i] <= '9') continue; + if ('A' <= s[i] && s[i] <= 'Z') continue; + if ('a' <= s[i] && s[i] <= 'z') continue; + s[i] = '_'; + } + + while (reserved_cids.count(s)) + s += "_"; + + reserved_cids.insert(s); + id2cid[id] = s; + } + + return id2cid.at(id); +} + +struct HierDirtyFlags +{ + int dirty; + Module *module; + IdString hiername; + HierDirtyFlags *parent; + pool dirty_bits; + pool dirty_cells; + pool sticky_dirty_bits; + dict children; + string prefix, log_prefix; + + HierDirtyFlags(Module *module, IdString hiername, HierDirtyFlags *parent, const string &prefix, const string &log_prefix) : + dirty(0), module(module), hiername(hiername), parent(parent), prefix(prefix), log_prefix(log_prefix) + { + for (Cell *cell : module->cells()) { + Module *mod = module->design->module(cell->type); + if (mod) children[cell->name] = new HierDirtyFlags(mod, cell->name, this, + prefix + cid(cell->name) + ".", log_prefix + "." + prefix + log_id(cell->name)); + } + } + + ~HierDirtyFlags() + { + for (auto &child : children) + delete child.second; + } + + void set_dirty(SigBit bit) + { + if (dirty_bits.count(bit)) + return; + + dirty_bits.insert(bit); + sticky_dirty_bits.insert(bit); + + HierDirtyFlags *p = this; + while (p != nullptr) { + p->dirty++; + p = p->parent; + } + } + + void unset_dirty(SigBit bit) + { + if (dirty_bits.count(bit) == 0) + return; + + dirty_bits.erase(bit); + + HierDirtyFlags *p = this; + while (p != nullptr) { + p->dirty--; + log_assert(p->dirty >= 0); + p = p->parent; + } + } + + void set_dirty(Cell *cell) + { + if (dirty_cells.count(cell)) + return; + + dirty_cells.insert(cell); + + HierDirtyFlags *p = this; + while (p != nullptr) { + p->dirty++; + p = p->parent; + } + } + + void unset_dirty(Cell *cell) + { + if (dirty_cells.count(cell) == 0) + return; + + dirty_cells.erase(cell); + + HierDirtyFlags *p = this; + while (p != nullptr) { + p->dirty--; + log_assert(p->dirty >= 0); + p = p->parent; + } + } +}; + +struct SimplecWorker +{ + bool verbose = false; + int max_uintsize = 32; + + Design *design; + dict sigmaps; + + vector signal_declarations; + pool generated_sigtypes; + + vector util_declarations; + pool generated_utils; + + vector struct_declarations; + pool generated_structs; + + vector funct_declarations; + + dict>>> bit2cell; + dict>> bit2output; + dict> driven_bits; + + dict topoidx; + + pool activated_cells; + pool reactivated_cells; + + SimplecWorker(Design *design) : design(design) + { + } + + string sigtype(int n) + { + string struct_name = stringf("signal%d_t", n); + + if (generated_sigtypes.count(n) == 0) + { + signal_declarations.push_back(""); + signal_declarations.push_back(stringf("#ifndef YOSYS_SIMPLEC_SIGNAL%d_T", n)); + signal_declarations.push_back(stringf("#define YOSYS_SIMPLEC_SIGNAL%d_T", n)); + signal_declarations.push_back(stringf("typedef struct {")); + + for (int k = 8; k <= max_uintsize; k = 2*k) + if (n <= k && k <= max_uintsize) { + signal_declarations.push_back(stringf(" uint%d_t value_%d_0 : %d;", k, n-1, n)); + goto end_struct; + } + + for (int k = 0; k < n; k += max_uintsize) { + int bits = std::min(max_uintsize, n-k); + signal_declarations.push_back(stringf(" uint%d_t value_%d_%d : %d;", max_uintsize, k+bits-1, k, bits)); + } + + end_struct: + signal_declarations.push_back(stringf("} signal%d_t;", n)); + signal_declarations.push_back(stringf("#endif")); + generated_sigtypes.insert(n); + } + + return struct_name; + } + + void util_ifdef_guard(string s) + { + for (int i = 0; i < GetSize(s); i++) + if ('a' <= s[i] && s[i] <= 'z') + s[i] -= 'a' - 'A'; + + util_declarations.push_back(""); + util_declarations.push_back(stringf("#ifndef %s", s.c_str())); + util_declarations.push_back(stringf("#define %s", s.c_str())); + } + + string util_get_bit(const string &signame, int n, int idx) + { + if (n == 1 && idx == 0) + return signame + ".value_0_0"; + + string util_name = stringf("yosys_simplec_get_bit_%d_of_%d", idx, n); + + if (generated_utils.count(util_name) == 0) + { + util_ifdef_guard(util_name); + util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str())); + util_declarations.push_back(stringf("{")); + + int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; + string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); + + util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset)); + + util_declarations.push_back(stringf("}")); + util_declarations.push_back(stringf("#endif")); + generated_utils.insert(util_name); + } + + return stringf("%s(&%s)", util_name.c_str(), signame.c_str()); + } + + string util_set_bit(const string &signame, int n, int idx, const string &expr) + { + if (n == 1 && idx == 0) + return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str()); + + string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n); + + if (generated_utils.count(util_name) == 0) + { + util_ifdef_guard(util_name); + util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str())); + util_declarations.push_back(stringf("{")); + + int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize; + string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize); + + #if 0 + util_declarations.push_back(stringf(" if (value)")); + util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset)); + util_declarations.push_back(stringf(" else")); + util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset)); + #else + util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);", + value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset)); + #endif + + util_declarations.push_back(stringf("}")); + util_declarations.push_back(stringf("#endif")); + generated_utils.insert(util_name); + } + + return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str()); + } + + void create_module_struct(Module *mod) + { + if (generated_structs.count(mod->name)) + return; + + generated_structs.insert(mod->name); + sigmaps[mod].set(mod); + + for (Wire *w : mod->wires()) + { + if (w->port_output) + for (auto bit : SigSpec(w)) + bit2output[mod][sigmaps.at(mod)(bit)].insert(bit); + } + + for (Cell *c : mod->cells()) + { + for (auto &conn : c->connections()) + { + if (!c->input(conn.first)) { + for (auto bit : sigmaps.at(mod)(conn.second)) + driven_bits[mod].insert(bit); + continue; + } + + int idx = 0; + for (auto bit : sigmaps.at(mod)(conn.second)) + bit2cell[mod][bit].insert(tuple(c, conn.first, idx++)); + } + + if (design->module(c->type)) + create_module_struct(design->module(c->type)); + } + + TopoSort topo; + + for (Cell *c : mod->cells()) + { + topo.node(c->name); + + for (auto &conn : c->connections()) + { + if (!c->input(conn.first)) + continue; + + for (auto bit : sigmaps.at(mod)(conn.second)) + for (auto &it : bit2cell[mod][bit]) + topo.edge(c->name, std::get<0>(it)->name); + } + } + + topo.analyze_loops = false; + topo.sort(); + + for (int i = 0; i < GetSize(topo.sorted); i++) + topoidx[mod->cell(topo.sorted[i])] = i; + + string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str()); + + for (int i = 0; i < GetSize(ifdef_name); i++) + if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z') + ifdef_name[i] -= 'a' - 'A'; + + struct_declarations.push_back(""); + struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str())); + struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str())); + struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str())); + struct_declarations.push_back("{"); + + struct_declarations.push_back(" // Input Ports"); + for (Wire *w : mod->wires()) + if (w->port_input) + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + + struct_declarations.push_back(""); + struct_declarations.push_back(" // Output Ports"); + for (Wire *w : mod->wires()) + if (!w->port_input && w->port_output) + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + + struct_declarations.push_back(""); + struct_declarations.push_back(" // Internal Wires"); + for (Wire *w : mod->wires()) + if (!w->port_input && !w->port_output) + struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w))); + + for (Cell *c : mod->cells()) + if (design->module(c->type)) + struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c))); + + struct_declarations.push_back(stringf("};")); + struct_declarations.push_back("#endif"); + } + + void eval_cell(HierDirtyFlags *work, Cell *cell) + { + if (cell->type.in("$_BUF_", "$_NOT_")) + { + SigBit a = sigmaps.at(work->module)(cell->getPort("\\A")); + SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y")); + + string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; + string expr; + + if (cell->type == "$_BUF_") expr = a_expr; + if (cell->type == "$_NOT_") expr = "!" + a_expr; + + log_assert(y.wire); + funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); + + work->set_dirty(y); + return; + } + + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) + { + SigBit a = sigmaps.at(work->module)(cell->getPort("\\A")); + SigBit b = sigmaps.at(work->module)(cell->getPort("\\B")); + SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y")); + + string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; + string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; + string expr; + + if (cell->type == "$_AND_") expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_NAND_") expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_OR_") expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_NOR_") expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_XOR_") expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_XNOR_") expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_ANDNOT_") expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str()); + if (cell->type == "$_ORNOT_") expr = stringf("%s | (!%s)", a_expr.c_str(), b_expr.c_str()); + + log_assert(y.wire); + funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); + + work->set_dirty(y); + return; + } + + if (cell->type.in("$_AOI3_", "$_OAI3_")) + { + SigBit a = sigmaps.at(work->module)(cell->getPort("\\A")); + SigBit b = sigmaps.at(work->module)(cell->getPort("\\B")); + SigBit c = sigmaps.at(work->module)(cell->getPort("\\C")); + SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y")); + + string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; + string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; + string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; + string expr; + + if (cell->type == "$_AOI3_") expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); + if (cell->type == "$_OAI3_") expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str()); + + log_assert(y.wire); + funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); + + work->set_dirty(y); + return; + } + + if (cell->type.in("$_AOI4_", "$_OAI4_")) + { + SigBit a = sigmaps.at(work->module)(cell->getPort("\\A")); + SigBit b = sigmaps.at(work->module)(cell->getPort("\\B")); + SigBit c = sigmaps.at(work->module)(cell->getPort("\\C")); + SigBit d = sigmaps.at(work->module)(cell->getPort("\\D")); + SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y")); + + string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; + string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; + string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0"; + string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0"; + string expr; + + if (cell->type == "$_AOI4_") expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); + if (cell->type == "$_OAI4_") expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str()); + + log_assert(y.wire); + funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); + + work->set_dirty(y); + return; + } + + if (cell->type == "$_MUX_") + { + SigBit a = sigmaps.at(work->module)(cell->getPort("\\A")); + SigBit b = sigmaps.at(work->module)(cell->getPort("\\B")); + SigBit s = sigmaps.at(work->module)(cell->getPort("\\S")); + SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y")); + + string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0"; + string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0"; + string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0"; + + // casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933) + string expr = stringf("%s ? (bool)%s : (bool)%s", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); + + log_assert(y.wire); + funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + + stringf(" // %s (%s)", log_id(cell), log_id(cell->type))); + + work->set_dirty(y); + return; + } + + log_error("No C model for %s available at the moment (FIXME).\n", log_id(cell->type)); + } + + void eval_dirty(HierDirtyFlags *work) + { + while (work->dirty) + { + if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty())) + log(" In %s:\n", work->log_prefix.c_str()); + + while (!work->dirty_bits.empty() || !work->dirty_cells.empty()) + { + if (!work->dirty_bits.empty()) + { + SigSpec dirtysig(work->dirty_bits); + dirtysig.sort_and_unify(); + + for (SigChunk chunk : dirtysig.chunks()) { + if (chunk.wire == nullptr) + continue; + if (verbose) + log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset); + funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk))); + } + + for (SigBit bit : dirtysig) + { + if (bit2output[work->module].count(bit) && work->parent) + for (auto outbit : bit2output[work->module][bit]) + { + Module *parent_mod = work->parent->module; + Cell *parent_cell = parent_mod->cell(work->hiername); + + IdString port_name = outbit.wire->name; + int port_offset = outbit.offset; + SigBit parent_bit = sigmaps.at(parent_mod)(parent_cell->getPort(port_name)[port_offset]); + + log_assert(bit.wire && parent_bit.wire); + funct_declarations.push_back(util_set_bit(work->parent->prefix + cid(parent_bit.wire->name), parent_bit.wire->width, parent_bit.offset, + util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset))); + work->parent->set_dirty(parent_bit); + + if (verbose) + log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset, + work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset); + } + + for (auto &port : bit2cell[work->module][bit]) + { + if (work->children.count(std::get<0>(port)->name)) + { + HierDirtyFlags *child = work->children.at(std::get<0>(port)->name); + SigBit child_bit = sigmaps.at(child->module)(SigBit(child->module->wire(std::get<1>(port)), std::get<2>(port))); + log_assert(bit.wire && child_bit.wire); + + funct_declarations.push_back(util_set_bit(work->prefix + cid(child->hiername) + "." + cid(child_bit.wire->name), + child_bit.wire->width, child_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset))); + child->set_dirty(child_bit); + + if (verbose) + log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset, + work->log_prefix.c_str(), log_id(std::get<0>(port)), log_id(child_bit.wire), child_bit.offset); + } else { + if (verbose) + log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix.c_str(), log_id(std::get<0>(port)), + work->log_prefix.c_str(), log_id(bit.wire), bit.offset); + work->set_dirty(std::get<0>(port)); + } + } + work->unset_dirty(bit); + } + } + + if (!work->dirty_cells.empty()) + { + Cell *cell = nullptr; + for (auto c : work->dirty_cells) + if (cell == nullptr || topoidx.at(cell) < topoidx.at(c)) + cell = c; + + string hiername = work->log_prefix + "." + log_id(cell); + + if (verbose) + log(" Evaluating %s (%s, best of %d).\n", hiername.c_str(), log_id(cell->type), GetSize(work->dirty_cells)); + + if (activated_cells.count(hiername)) + reactivated_cells.insert(hiername); + activated_cells.insert(hiername); + + eval_cell(work, cell); + work->unset_dirty(cell); + } + } + + for (auto &child : work->children) + eval_dirty(child.second); + } + } + + void eval_sticky_dirty(HierDirtyFlags *work) + { + Module *mod = work->module; + + for (Wire *w : mod->wires()) + for (SigBit bit : SigSpec(w)) + { + SigBit canonical_bit = sigmaps.at(mod)(bit); + + if (canonical_bit == bit) + continue; + + if (work->sticky_dirty_bits.count(canonical_bit) == 0) + continue; + + if (bit.wire == nullptr || canonical_bit.wire == nullptr) + continue; + + funct_declarations.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, + util_get_bit(work->prefix + cid(canonical_bit.wire->name), canonical_bit.wire->width, canonical_bit.offset).c_str())); + + if (verbose) + log(" Propagating alias %s.%s[%d] -> %s.%s[%d].\n", + work->log_prefix.c_str(), log_id(canonical_bit.wire), canonical_bit.offset, + work->log_prefix.c_str(), log_id(bit.wire), bit.offset); + } + + work->sticky_dirty_bits.clear(); + + for (auto &child : work->children) + eval_sticky_dirty(child.second); + } + + void make_func(HierDirtyFlags *work, const string &func_name, const vector &preamble) + { + log("Generating function %s():\n", func_name.c_str()); + + activated_cells.clear(); + reactivated_cells.clear(); + + funct_declarations.push_back(""); + funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str())); + funct_declarations.push_back("{"); + for (auto &line : preamble) + funct_declarations.push_back(line); + eval_dirty(work); + eval_sticky_dirty(work); + funct_declarations.push_back("}"); + + log(" Activated %d cells (%d activated more than once).\n", GetSize(activated_cells), GetSize(reactivated_cells)); + } + + void eval_init(HierDirtyFlags *work, vector &preamble) + { + Module *module = work->module; + + for (Wire *w : module->wires()) + { + if (w->attributes.count("\\init")) + { + SigSpec sig = sigmaps.at(module)(w); + Const val = w->attributes.at("\\init"); + val.bits.resize(GetSize(sig), State::Sx); + + for (int i = 0; i < GetSize(sig); i++) + if (val[i] == State::S0 || val[i] == State::S1) { + SigBit bit = sig[i]; + preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false")); + work->set_dirty(bit); + } + } + + for (SigBit bit : SigSpec(w)) + { + SigBit val = sigmaps.at(module)(bit); + + if (val == State::S0 || val == State::S1) + preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false")); + + if (driven_bits.at(module).count(val) == 0) + work->set_dirty(val); + } + } + + work->set_dirty(State::S0); + work->set_dirty(State::S1); + + for (auto &child : work->children) + eval_init(child.second, preamble); + } + + void make_init_func(HierDirtyFlags *work) + { + vector preamble; + eval_init(work, preamble); + make_func(work, cid(work->module->name) + "_init", preamble); + } + + void make_eval_func(HierDirtyFlags *work) + { + Module *mod = work->module; + vector preamble; + + for (Wire *w : mod->wires()) { + if (w->port_input) + for (SigBit bit : sigmaps.at(mod)(w)) + work->set_dirty(bit); + } + + make_func(work, cid(work->module->name) + "_eval", preamble); + } + + void make_tick_func(HierDirtyFlags* /* work */) + { + // FIXME + } + + void run(Module *mod) + { + create_module_struct(mod); + + HierDirtyFlags work(mod, IdString(), nullptr, "state->", log_id(mod->name)); + + make_init_func(&work); + make_eval_func(&work); + make_tick_func(&work); + } + + void write(std::ostream &f) + { + f << "#include " << std::endl; + f << "#include " << std::endl; + + for (auto &line : signal_declarations) + f << line << std::endl; + + for (auto &line : util_declarations) + f << line << std::endl; + + for (auto &line : struct_declarations) + f << line << std::endl; + + for (auto &line : funct_declarations) + f << line << std::endl; + } +}; + +struct SimplecBackend : public Backend { + SimplecBackend() : Backend("simplec", "convert design to simple C code") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_simplec [options] [filename]\n"); + log("\n"); + log("Write simple C code for simulating the design. The C code written can be used to\n"); + log("simulate the design in a C environment, but the purpose of this command is to\n"); + log("generate code that works well with C-based formal verification.\n"); + log("\n"); + log(" -verbose\n"); + log(" this will print the recursive walk used to export the modules.\n"); + log("\n"); + log(" -i8, -i16, -i32, -i64\n"); + log(" set the maximum integer bit width to use in the generated code.\n"); + log("\n"); + log("THIS COMMAND IS UNDER CONSTRUCTION\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + reserved_cids.clear(); + id2cid.clear(); + + SimplecWorker worker(design); + + log_header(design, "Executing SIMPLEC backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-verbose") { + worker.verbose = true; + continue; + } + if (args[argidx] == "-i8") { + worker.max_uintsize = 8; + continue; + } + if (args[argidx] == "-i16") { + worker.max_uintsize = 16; + continue; + } + if (args[argidx] == "-i32") { + worker.max_uintsize = 32; + continue; + } + if (args[argidx] == "-i64") { + worker.max_uintsize = 64; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + Module *topmod = design->top_module(); + + if (topmod == nullptr) + log_error("Current design has no top module.\n"); + + worker.run(topmod); + worker.write(*f); + } +} SimplecBackend; + +PRIVATE_NAMESPACE_END diff --git a/yosys/backends/simplec/test00.sh b/yosys/backends/simplec/test00.sh new file mode 100644 index 000000000..ede757273 --- /dev/null +++ b/yosys/backends/simplec/test00.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ex +../../yosys -p 'synth -top test; write_simplec -verbose -i8 test00_uut.c' test00_uut.v +clang -o test00_tb test00_tb.c +./test00_tb diff --git a/yosys/backends/simplec/test00_tb.c b/yosys/backends/simplec/test00_tb.c new file mode 100644 index 000000000..7fac48265 --- /dev/null +++ b/yosys/backends/simplec/test00_tb.c @@ -0,0 +1,93 @@ +#include +#include +#include "test00_uut.c" + +uint32_t xorshift32() +{ + static uint32_t x32 = 314159265; + x32 ^= x32 << 13; + x32 ^= x32 >> 17; + x32 ^= x32 << 5; + return x32; +} + +int main() +{ + struct test_state_t state; + uint32_t a, b, c, x, y, z, w; + bool first_eval = true; + + for (int i = 0; i < 10; i++) + { + a = xorshift32(); + b = xorshift32(); + c = xorshift32(); + + x = (a & b) | c; + y = a & (b | c); + z = a ^ b ^ c; + w = z; + + state.a.value_7_0 = a; + state.a.value_15_8 = a >> 8; + state.a.value_23_16 = a >> 16; + state.a.value_31_24 = a >> 24; + + state.b.value_7_0 = b; + state.b.value_15_8 = b >> 8; + state.b.value_23_16 = b >> 16; + state.b.value_31_24 = b >> 24; + + state.c.value_7_0 = c; + state.c.value_15_8 = c >> 8; + state.c.value_23_16 = c >> 16; + state.c.value_31_24 = c >> 24; + + if (first_eval) { + first_eval = false; + test_init(&state); + } else { + test_eval(&state); + } + + uint32_t uut_x = 0; + uut_x |= (uint32_t)state.x.value_7_0; + uut_x |= (uint32_t)state.x.value_15_8 << 8; + uut_x |= (uint32_t)state.x.value_23_16 << 16; + uut_x |= (uint32_t)state.x.value_31_24 << 24; + + uint32_t uut_y = 0; + uut_y |= (uint32_t)state.y.value_7_0; + uut_y |= (uint32_t)state.y.value_15_8 << 8; + uut_y |= (uint32_t)state.y.value_23_16 << 16; + uut_y |= (uint32_t)state.y.value_31_24 << 24; + + uint32_t uut_z = 0; + uut_z |= (uint32_t)state.z.value_7_0; + uut_z |= (uint32_t)state.z.value_15_8 << 8; + uut_z |= (uint32_t)state.z.value_23_16 << 16; + uut_z |= (uint32_t)state.z.value_31_24 << 24; + + uint32_t uut_w = 0; + uut_w |= (uint32_t)state.w.value_7_0; + uut_w |= (uint32_t)state.w.value_15_8 << 8; + uut_w |= (uint32_t)state.w.value_23_16 << 16; + uut_w |= (uint32_t)state.w.value_31_24 << 24; + + printf("---\n"); + printf("A: 0x%08x\n", a); + printf("B: 0x%08x\n", b); + printf("C: 0x%08x\n", c); + printf("X: 0x%08x 0x%08x\n", x, uut_x); + printf("Y: 0x%08x 0x%08x\n", y, uut_y); + printf("Z: 0x%08x 0x%08x\n", z, uut_z); + printf("W: 0x%08x 0x%08x\n", w, uut_w); + + assert(x == uut_x); + assert(y == uut_y); + assert(z == uut_z); + assert(w == uut_w); + } + + return 0; +} diff --git a/yosys/backends/simplec/test00_uut.v b/yosys/backends/simplec/test00_uut.v new file mode 100644 index 000000000..92329a6f9 --- /dev/null +++ b/yosys/backends/simplec/test00_uut.v @@ -0,0 +1,14 @@ +module test(input [31:0] a, b, c, output [31:0] x, y, z, w); + unit_x unit_x_inst (.a(a), .b(b), .c(c), .x(x)); + unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y)); + assign z = a ^ b ^ c, w = z; +endmodule + +module unit_x(input [31:0] a, b, c, output [31:0] x); + assign x = (a & b) | c; +endmodule + +module unit_y(input [31:0] a, b, c, output [31:0] y); + assign y = a & (b | c); +endmodule + diff --git a/yosys/backends/smt2/Makefile.inc b/yosys/backends/smt2/Makefile.inc index eacda2734..dce82f01a 100644 --- a/yosys/backends/smt2/Makefile.inc +++ b/yosys/backends/smt2/Makefile.inc @@ -6,7 +6,7 @@ ifneq ($(CONFIG),emcc) TARGETS += yosys-smtbmc yosys-smtbmc: backends/smt2/smtbmc.py - $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new + $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new $(Q) chmod +x $@.new $(Q) mv $@.new $@ diff --git a/yosys/backends/smt2/smt2.cc b/yosys/backends/smt2/smt2.cc index ddac69009..418f8d766 100644 --- a/yosys/backends/smt2/smt2.cc +++ b/yosys/backends/smt2/smt2.cc @@ -32,14 +32,18 @@ struct Smt2Worker CellTypes ct; SigMap sigmap; RTLIL::Module *module; - bool bvmode, memmode, wiresmode, verbose; - int idcounter; + bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode; + dict &mod_stbv_width; + int idcounter = 0, statebv_width = 0; - std::vector decls, trans, hier; + std::vector decls, trans, hier, dtmembers; std::map bit_driver; std::set exported_cells, hiercells, hiercells_queue; pool recursive_cells, registers; + pool clock_posedge, clock_negedge; + vector ex_state_eq, ex_input_eq; + std::map> fcache; std::map memarrays; std::map bvsizes; @@ -63,17 +67,64 @@ struct Smt2Worker return get_id(obj->name); } - Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose) : - ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), - wiresmode(wiresmode), verbose(verbose), idcounter(0) + void makebits(std::string name, int width = 0, std::string comment = std::string()) { - decls.push_back(stringf("(declare-sort |%s_s| 0)\n", get_id(module))); - decls.push_back(stringf("(declare-fun |%s_is| (|%s_s|) Bool)\n", get_id(module), get_id(module))); + std::string decl_str; + + if (statebv) + { + if (width == 0) { + decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name.c_str(), get_id(module), statebv_width, statebv_width); + statebv_width += 1; + } else { + decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name.c_str(), get_id(module), width, statebv_width+width-1, statebv_width); + statebv_width += width; + } + } + else if (statedt) + { + if (width == 0) { + decl_str = stringf(" (|%s| Bool)", name.c_str()); + } else { + decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width); + } + } + else + { + if (width == 0) { + decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module)); + } else { + decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width); + } + } + + if (!comment.empty()) + decl_str += " ; " + comment; + + if (statedt) + dtmembers.push_back(decl_str + "\n"); + else + decls.push_back(decl_str + "\n"); + } + + Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode, + dict &mod_stbv_width, dict>> &mod_clk_cache) : + ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode), + verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width) + { + pool noclock; + + makebits(stringf("%s_is", get_id(module))); for (auto cell : module->cells()) - for (auto &conn : cell->connections()) { + for (auto &conn : cell->connections()) + { + if (GetSize(conn.second) == 0) + continue; + bool is_input = ct.cell_input(cell->type, conn.first); bool is_output = ct.cell_output(cell->type, conn.first); + if (is_output && !is_input) for (auto bit : sigmap(conn.second)) { if (bit_driver.count(bit)) @@ -83,6 +134,66 @@ struct Smt2Worker else if (is_output || !is_input) log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n", log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type)); + + if (cell->type.in("$mem") && conn.first.in("\\RD_CLK", "\\WR_CLK")) + { + SigSpec clk = sigmap(conn.second); + for (int i = 0; i < GetSize(clk); i++) + { + if (clk[i].wire == nullptr) + continue; + + if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_ENABLE" : "\\WR_CLK_ENABLE")[i] != State::S1) + continue; + + if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_POLARITY" : "\\WR_CLK_POLARITY")[i] == State::S1) + clock_posedge.insert(clk[i]); + else + clock_negedge.insert(clk[i]); + } + } + else + if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_") && conn.first.in("\\CLK", "\\C")) + { + bool posedge = (cell->type == "$_DFF_N_") || (cell->type == "$dff" && cell->getParam("\\CLK_POLARITY").as_bool()); + for (auto bit : sigmap(conn.second)) { + if (posedge) + clock_posedge.insert(bit); + else + clock_negedge.insert(bit); + } + } + else + if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first)) + { + for (auto bit : sigmap(conn.second)) { + if (mod_clk_cache.at(cell->type).at(conn.first).first) + clock_posedge.insert(bit); + if (mod_clk_cache.at(cell->type).at(conn.first).second) + clock_negedge.insert(bit); + } + } + else + { + for (auto bit : sigmap(conn.second)) + noclock.insert(bit); + } + } + + for (auto bit : noclock) { + clock_posedge.erase(bit); + clock_negedge.erase(bit); + } + + for (auto wire : module->wires()) + { + if (!wire->port_input || GetSize(wire) != 1) + continue; + SigBit bit = sigmap(wire); + if (clock_posedge.count(bit)) + mod_clk_cache[module->name][wire->name].first = true; + if (clock_negedge.count(bit)) + mod_clk_cache[module->name][wire->name].second = true; } } @@ -162,8 +273,7 @@ struct Smt2Worker if (fcache.count(bit) == 0) { if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "", log_signal(bit)); - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n", - get_id(module), idcounter, get_id(module), log_signal(bit))); + makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(bit)); register_bool(bit, idcounter++); } @@ -237,8 +347,7 @@ struct Smt2Worker log_signal(sig.extract(i, j))); for (auto bit : sig.extract(i, j)) log_assert(bit_driver.count(bit) == 0); - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n", - get_id(module), idcounter, get_id(module), j, log_signal(sig.extract(i, j)))); + makebits(stringf("%s#%d", get_id(module), idcounter), j, log_signal(sig.extract(i, j))); subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name)); register_bv(sig.extract(i, j), idcounter++); } @@ -288,7 +397,8 @@ struct Smt2Worker if (type == 's' || type == 'd' || type == 'b') { width = max(width, GetSize(cell->getPort("\\A"))); - width = max(width, GetSize(cell->getPort("\\B"))); + if (cell->hasPort("\\B")) + width = max(width, GetSize(cell->getPort("\\B"))); } if (cell->hasPort("\\A")) { @@ -382,8 +492,7 @@ struct Smt2Worker if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_")) { registers.insert(cell); - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n", - get_id(module), idcounter, get_id(module), log_signal(cell->getPort("\\Q")))); + makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(cell->getPort("\\Q"))); register_bool(cell->getPort("\\Q"), idcounter++); recursive_cells.erase(cell); return; @@ -397,6 +506,8 @@ struct Smt2Worker if (cell->type == "$_NOR_") return export_gate(cell, "(not (or A B))"); if (cell->type == "$_XOR_") return export_gate(cell, "(xor A B)"); if (cell->type == "$_XNOR_") return export_gate(cell, "(not (xor A B))"); + if (cell->type == "$_ANDNOT_") return export_gate(cell, "(and A (not B))"); + if (cell->type == "$_ORNOT_") return export_gate(cell, "(or A (not B))"); if (cell->type == "$_MUX_") return export_gate(cell, "(ite S B A)"); if (cell->type == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))"); if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))"); @@ -410,20 +521,22 @@ struct Smt2Worker if (cell->type.in("$ff", "$dff")) { registers.insert(cell); - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n", - get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")))); + makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))); register_bv(cell->getPort("\\Q"), idcounter++); recursive_cells.erase(cell); return; } - if (cell->type.in("$anyconst", "$anyseq")) + if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq")) { registers.insert(cell); - decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, - cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell))); - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n", - get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y")))); + string infostr = cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell); + if (cell->attributes.count("\\reg")) + infostr += " " + cell->attributes.at("\\reg").decode_string(); + decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort("\\Y")), infostr.c_str())); + makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y"))); + if (cell->type == "$anyseq") + ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter)); register_bv(cell->getPort("\\Y"), idcounter++); recursive_cells.erase(cell); return; @@ -441,7 +554,9 @@ struct Smt2Worker if (cell->type.in("$shift", "$shiftx")) { if (cell->getParam("\\B_SIGNED").as_bool()) { - /* FIXME */ + return export_bvop(cell, stringf("(ite (bvsge B #b%0*d) " + "(bvlshr A B) (bvlshr A (bvneg B)))", + GetSize(cell->getPort("\\B")), 0), 's'); } else { return export_bvop(cell, "(bvlshr A B)", 's'); } @@ -467,6 +582,13 @@ struct Smt2Worker if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd'); if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd'); + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool") && + 2*GetSize(cell->getPort("\\A").chunks()) < GetSize(cell->getPort("\\A"))) { + bool is_and = cell->type == "$reduce_and"; + string bits(GetSize(cell->getPort("\\A")), is_and ? '1' : '0'); + return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b'); + } + if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true); if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false); if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false); @@ -513,30 +635,97 @@ struct Smt2Worker int abits = cell->getParam("\\ABITS").as_int(); int width = cell->getParam("\\WIDTH").as_int(); int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); - decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", - get_id(module), arrayid, get_id(module), abits, width, get_id(cell))); + bool async_read = false; + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) { + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_zero()) + log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module)); + async_read = true; + } - decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d\n", get_id(cell), abits, width, rd_ports)); - decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n", - get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid)); + decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync")); - for (int i = 0; i < rd_ports; i++) + string memstate; + if (async_read) { + memstate = stringf("%s#%d#final", get_id(module), arrayid); + } else { + memstate = stringf("%s#%d#0", get_id(module), arrayid); + } + + if (statebv) { - 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); + int mem_size = cell->getParam("\\SIZE").as_int(); + int mem_offset = cell->getParam("\\OFFSET").as_int(); - 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)); + makebits(memstate, width*mem_size, get_id(cell)); + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n", + get_id(module), get_id(cell), get_id(module), width*mem_size, memstate.c_str())); - 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))); + 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); - 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++); + if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool()) + log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " + "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module)); + + decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); + + std::string read_expr = "#b"; + for (int k = 0; k < width; k++) + read_expr += "0"; + + for (int k = 0; k < mem_size; k++) + read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)", + get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(), + width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str()); + + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n", + get_id(module), idcounter, get_id(module), width, read_expr.c_str(), log_signal(data_sig))); + + decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", + get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter)); + + register_bv(data_sig, idcounter++); + } + } + else + { + if (statedt) + dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + memstate.c_str(), abits, width, get_id(cell))); + else + decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + memstate.c_str(), get_id(module), abits, width, get_id(cell))); + + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n", + get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str())); + + for (int i = 0; i < rd_ports; i++) + { + SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(abits*i, abits); + SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width); + std::string addr = get_bv(addr_sig); + + if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool()) + log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! " + "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module)); + + decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); + + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n", + get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig))); + + decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", + get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter)); + + register_bv(data_sig, idcounter++); + } } registers.insert(cell); @@ -553,32 +742,38 @@ struct Smt2Worker for (auto &conn : cell->connections()) { + if (GetSize(conn.second) == 0) + continue; + Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); if (w->port_output && !w->port_input) { if (GetSize(w) > 1) { if (bvmode) { - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n", - get_id(module), idcounter, get_id(module), GetSize(w), log_signal(sig))); + makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(w), log_signal(sig)); register_bv(sig, idcounter++); } else { for (int i = 0; i < GetSize(w); i++) { - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n", - get_id(module), idcounter, get_id(module), log_signal(sig[i]))); + makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig[i])); register_bool(sig[i], idcounter++); } } } else { - decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n", - get_id(module), idcounter, get_id(module), log_signal(sig))); + makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig)); register_bool(sig, idcounter++); } } } - 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))); + if (statebv) + makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type)); + else if (statedt) + dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n", + get_id(module), get_id(cell->name), get_id(cell->type))); + else + decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n", + get_id(module), get_id(cell->name), get_id(module), get_id(cell->type))); hiercells.insert(cell); hiercells_queue.insert(cell); @@ -617,17 +812,30 @@ struct Smt2Worker decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width)); if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width)); + if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) + decls.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire), + clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : "")); if (bvmode && GetSize(sig) > 1) { decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", get_id(module), get_id(wire), get_id(module), GetSize(sig), get_bv(sig).c_str())); + if (wire->port_input) + ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", + get_id(module), get_id(wire), get_id(module), get_id(wire))); } else { for (int i = 0; i < GetSize(sig); i++) - if (GetSize(sig) > 1) + if (GetSize(sig) > 1) { decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), i, get_id(module), get_bool(sig[i]).c_str())); - else + if (wire->port_input) + ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))", + get_id(module), get_id(wire), i, get_id(module), get_id(wire), i)); + } else { decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), get_id(module), get_bool(sig[i]).c_str())); + if (wire->port_input) + ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", + get_id(module), get_id(wire), get_id(module), get_id(wire))); + } } } } @@ -639,31 +847,104 @@ struct Smt2Worker if (wire->attributes.count("\\init")) { RTLIL::SigSpec sig = sigmap(wire); Const val = wire->attributes.at("\\init"); - val.bits.resize(GetSize(sig)); + val.bits.resize(GetSize(sig), State::Sx); if (bvmode && GetSize(sig) > 1) { - init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire))); + Const mask(State::S1, GetSize(sig)); + bool use_mask = false; + for (int i = 0; i < GetSize(sig); i++) + if (val[i] != State::S0 && val[i] != State::S1) { + val[i] = State::S0; + mask[i] = State::S0; + use_mask = true; + } + if (use_mask) + init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig).c_str(), mask.as_string().c_str(), val.as_string().c_str(), get_id(wire))); + else + init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire))); } else { for (int i = 0; i < GetSize(sig); i++) - init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val.bits[i] == State::S1 ? "true" : "false", get_id(wire))); + if (val[i] == State::S0 || val[i] == State::S1) + init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val[i] == State::S1 ? "true" : "false", get_id(wire))); } } if (verbose) log("=> export logic driving asserts\n"); - vector assert_list, assume_list; + int assert_id = 0, assume_id = 0, cover_id = 0; + vector assert_list, assume_list, cover_list; + for (auto cell : module->cells()) - if (cell->type.in("$assert", "$assume")) { + { + if (cell->type.in("$assert", "$assume", "$cover")) + { + int &id = cell->type == "$assert" ? assert_id : + cell->type == "$assume" ? assume_id : + cell->type == "$cover" ? cover_id : *(int*)nullptr; + + char postfix = cell->type == "$assert" ? 'a' : + cell->type == "$assume" ? 'u' : + cell->type == "$cover" ? 'c' : 0; + string name_a = get_bool(cell->getPort("\\A")); string name_en = get_bool(cell->getPort("\\EN")); - decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, + decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell))); - decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n", - get_id(module), idcounter, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); - if (cell->type == "$assert") - assert_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++)); + + if (cell->type == "$cover") + decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n", + get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); else - assume_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++)); + decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n", + get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell))); + + if (cell->type == "$assert") + assert_list.push_back(stringf("(|%s_a %d| state)", get_id(module), id)); + else if (cell->type == "$assume") + assume_list.push_back(stringf("(|%s_u %d| state)", get_id(module), id)); + + id++; } + } + + if (verbose) log("=> export logic driving hierarchical cells\n"); + + for (auto cell : module->cells()) + if (module->design->module(cell->type) != nullptr) + export_cell(cell); + + while (!hiercells_queue.empty()) + { + std::set queue; + queue.swap(hiercells_queue); + + for (auto cell : queue) + { + string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); + Module *m = module->design->module(cell->type); + log_assert(m != nullptr); + + hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n", + get_id(module), get_id(cell->type), cell_state.c_str())); + + for (auto &conn : cell->connections()) + { + if (GetSize(conn.second) == 0) + continue; + + Wire *w = m->wire(conn.first); + SigSpec sig = sigmap(conn.second); + + if (bvmode || GetSize(w) == 1) { + hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(), + get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w))); + } else { + for (int i = 0; i < GetSize(w); i++) + hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(), + get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i)); + } + } + } + } for (int iter = 1; !registers.empty(); iter++) { @@ -679,6 +960,7 @@ struct Smt2Worker std::string expr_d = get_bool(cell->getPort("\\D")); std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q")))); + ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort("\\Q")).c_str(), get_bool(cell->getPort("\\Q"), "other_state").c_str())); } if (cell->type.in("$ff", "$dff")) @@ -686,13 +968,16 @@ struct Smt2Worker std::string expr_d = get_bv(cell->getPort("\\D")); std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q")))); + ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Q")).c_str(), get_bv(cell->getPort("\\Q"), "other_state").c_str())); } - if (cell->type == "$anyconst") + if (cell->type.in("$anyconst", "$allconst")) { std::string expr_d = get_bv(cell->getPort("\\Y")); std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Y")))); + if (cell->type == "$anyconst") + ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Y")).c_str(), get_bv(cell->getPort("\\Y"), "other_state").c_str())); } if (cell->type == "$mem") @@ -703,24 +988,111 @@ struct Smt2Worker int width = cell->getParam("\\WIDTH").as_int(); int wr_ports = cell->getParam("\\WR_PORTS").as_int(); - for (int i = 0; i < wr_ports; i++) + bool async_read = false; + string initial_memstate, final_memstate; + + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) { + log_assert(cell->getParam("\\WR_CLK_ENABLE").is_fully_zero()); + async_read = true; + initial_memstate = stringf("%s#%d#0", get_id(module), arrayid); + final_memstate = stringf("%s#%d#final", get_id(module), arrayid); + } + + if (statebv) { - 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)); + int mem_size = cell->getParam("\\SIZE").as_int(); + int mem_offset = cell->getParam("\\OFFSET").as_int(); - 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()); + if (async_read) { + makebits(final_memstate, width*mem_size, get_id(cell)); + } - 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))); + for (int i = 0; i < wr_ports; i++) + { + SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits); + SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width); + SigSpec mask_sig = cell->getPort("\\WR_EN").extract(width*i, width); + + std::string addr = get_bv(addr_sig); + std::string data = get_bv(data_sig); + std::string mask = get_bv(mask_sig); + + decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); + addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell)); + + decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig))); + data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell)); + + decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig))); + mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell)); + + std::string data_expr; + + for (int k = mem_size-1; k >= 0; k--) { + std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))", + data.c_str(), mask.c_str(), width*(k+1)-1, width*k, get_id(module), arrayid, i, mask.c_str()); + data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))", + addr.c_str(), Const(k+mem_offset, abits).as_string().c_str(), new_data.c_str(), + width*(k+1)-1, width*k, get_id(module), arrayid, i); + } + + decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n", + get_id(module), arrayid, i+1, get_id(module), width*mem_size, data_expr.c_str(), get_id(cell))); + } + } + else + { + if (async_read) { + if (statedt) + dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + initial_memstate.c_str(), abits, width, get_id(cell))); + else + decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + initial_memstate.c_str(), get_id(module), abits, width, get_id(cell))); + } + + for (int i = 0; i < wr_ports; i++) + { + SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits); + SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width); + SigSpec mask_sig = cell->getPort("\\WR_EN").extract(width*i, width); + + std::string addr = get_bv(addr_sig); + std::string data = get_bv(data_sig); + std::string mask = get_bv(mask_sig); + + decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); + addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell)); + + decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig))); + data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell)); + + decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", + get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig))); + mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell)); + + data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))", + data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str()); + + 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))); + ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid)); + + if (async_read) + hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(cell))); Const init_data = cell->getParam("\\INIT"); int memsize = cell->getParam("\\SIZE").as_int(); @@ -737,46 +1109,20 @@ struct Smt2Worker 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 (gen_init_constr) + { + if (statebv) + /* FIXME */; + else + init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]", + get_id(module), arrayid, Const(i, abits).as_string().c_str(), + initword.as_string().c_str(), get_id(cell), i)); } } } } } - if (verbose) log("=> export logic driving hierarchical cells\n"); - - while (!hiercells_queue.empty()) - { - std::set queue; - queue.swap(hiercells_queue); - - for (auto cell : queue) - { - string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name)); - Module *m = module->design->module(cell->type); - log_assert(m != nullptr); - - for (auto &conn : cell->connections()) - { - Wire *w = m->wire(conn.first); - SigSpec sig = sigmap(conn.second); - - if (bvmode || GetSize(w) == 1) { - hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(), - get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w))); - } else { - for (int i = 0; i < GetSize(w); i++) - hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(), - get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i)); - } - } - } - } - if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module)); for (auto c : hiercells) { @@ -786,6 +1132,37 @@ struct Smt2Worker hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name))); trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n", get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); + ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n", + get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); + } + + if (forallmode) + { + string expr = ex_state_eq.empty() ? "true" : "(and"; + if (!ex_state_eq.empty()) { + if (GetSize(ex_state_eq) == 1) { + expr = "\n " + ex_state_eq.front() + "\n"; + } else { + for (auto &str : ex_state_eq) + expr += stringf("\n %s", str.c_str()); + expr += "\n)"; + } + } + decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", + get_id(module), get_id(module), get_id(module), expr.c_str())); + + expr = ex_input_eq.empty() ? "true" : "(and"; + if (!ex_input_eq.empty()) { + if (GetSize(ex_input_eq) == 1) { + expr = "\n " + ex_input_eq.front() + "\n"; + } else { + for (auto &str : ex_input_eq) + expr += stringf("\n %s", str.c_str()); + expr += "\n)"; + } + } + decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", + get_id(module), get_id(module), get_id(module), expr.c_str())); } string assert_expr = assert_list.empty() ? "true" : "(and"; @@ -832,6 +1209,18 @@ struct Smt2Worker { f << stringf("; yosys-smt2-module %s\n", get_id(module)); + if (statebv) { + f << stringf("(define-sort |%s_s| () (_ BitVec %d))\n", get_id(module), statebv_width); + mod_stbv_width[module->name] = statebv_width; + } else + if (statedt) { + f << stringf("(declare-datatype |%s_s| ((|%s_mk|\n", get_id(module), get_id(module)); + for (auto it : dtmembers) + f << it; + f << stringf(")))\n"); + } else + f << stringf("(declare-sort |%s_s| 0)\n", get_id(module)); + for (auto it : decls) f << it; @@ -864,44 +1253,92 @@ struct Smt2Worker struct Smt2Backend : public Backend { Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_smt2 [options] [filename]\n"); log("\n"); log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\n"); - log("'' this will declare the sort '_s' (state of the module) and the\n"); - log("functions operating on that state.\n"); + log("'' this will declare the sort '_s' (state of the module) and will\n"); + log("define and declare functions operating on that state.\n"); log("\n"); - log("The '_s' sort represents a module state. Additional '_n' functions\n"); - log("are provided that can be used to access the values of the signals in the module.\n"); - log("By default only ports, registers, and wires with the 'keep' attribute set are\n"); - log("made available via such functions. With the -nobv option, multi-bit wires are\n"); - log("exported as separate functions of type Bool for the individual bits. Without\n"); - log("-nobv multi-bit wires are exported as single functions of type BitVec.\n"); + log("The following SMT2 functions are generated for a module with name ''.\n"); + log("Some declarations/definitions are printed with a special comment. A prover\n"); + log("using the SMT2 files can use those comments to collect all relevant metadata\n"); + log("about the design.\n"); log("\n"); - log("The '_t' function evaluates to 'true' when the given pair of states\n"); - log("describes a valid state transition.\n"); + log(" ; yosys-smt2-module \n"); + log(" (declare-sort |_s| 0)\n"); + log(" The sort representing a state of module .\n"); log("\n"); - log("The '_a' function evaluates to 'true' when the given state satisfies\n"); - log("the asserts in the module.\n"); + log(" (define-fun |_h| ((state |_s|)) Bool (...))\n"); + log(" This function must be asserted for each state to establish the\n"); + log(" design hierarchy.\n"); log("\n"); - log("The '_u' function evaluates to 'true' when the given state satisfies\n"); - log("the assumptions in the module.\n"); + log(" ; yosys-smt2-input \n"); + log(" ; yosys-smt2-output \n"); + log(" ; yosys-smt2-register \n"); + log(" ; yosys-smt2-wire \n"); + log(" (define-fun |_n | (|_s|) (_ BitVec ))\n"); + log(" (define-fun |_n | (|_s|) Bool)\n"); + log(" For each port, register, and wire with the 'keep' attribute set an\n"); + log(" accessor function is generated. Single-bit wires are returned as Bool,\n"); + log(" multi-bit wires as BitVec.\n"); log("\n"); - log("The '_i' function evaluates to 'true' when the given state conforms\n"); - log("to the initial state. Furthermore the '_is' function should be asserted\n"); - log("to be true for initial states in addition to '_i', and should be\n"); - log("asserted to be false for non-initial states.\n"); + log(" ; yosys-smt2-cell \n"); + log(" (declare-fun |_h | (|_s|) |_s|)\n"); + log(" There is a function like that for each hierarchical instance. It\n"); + log(" returns the sort that represents the state of the sub-module that\n"); + log(" implements the instance.\n"); log("\n"); - log("For hierarchical designs, the '_h' function must be asserted for each\n"); - log("state to establish the design hierarchy. The '_h ' function\n"); - log("evaluates to the state corresponding to the given cell within .\n"); + log(" (declare-fun |_is| (|_s|) Bool)\n"); + log(" This function must be asserted 'true' for initial states, and 'false'\n"); + log(" otherwise.\n"); + log("\n"); + log(" (define-fun |_i| ((state |_s|)) Bool (...))\n"); + log(" This function must be asserted 'true' for initial states. For\n"); + log(" non-initial states it must be left unconstrained.\n"); + log("\n"); + log(" (define-fun |_t| ((state |_s|) (next_state |_s|)) Bool (...))\n"); + log(" This function evaluates to 'true' if the states 'state' and\n"); + log(" 'next_state' form a valid state transition.\n"); + log("\n"); + log(" (define-fun |_a| ((state |_s|)) Bool (...))\n"); + log(" This function evaluates to 'true' if all assertions hold in the state.\n"); + log("\n"); + log(" (define-fun |_u| ((state |_s|)) Bool (...))\n"); + log(" This function evaluates to 'true' if all assumptions hold in the state.\n"); + log("\n"); + log(" ; yosys-smt2-assert \n"); + log(" (define-fun |_a | ((state |_s|)) Bool (...))\n"); + log(" Each $assert cell is converted into one of this functions. The function\n"); + log(" evaluates to 'true' if the assert statement holds in the state.\n"); + log("\n"); + log(" ; yosys-smt2-assume \n"); + log(" (define-fun |_u | ((state |_s|)) Bool (...))\n"); + log(" Each $assume cell is converted into one of this functions. The function\n"); + log(" evaluates to 'true' if the assume statement holds in the state.\n"); + log("\n"); + log(" ; yosys-smt2-cover \n"); + log(" (define-fun |_c | ((state |_s|)) Bool (...))\n"); + log(" Each $cover cell is converted into one of this functions. The function\n"); + log(" evaluates to 'true' if the cover statement is activated in the state.\n"); + log("\n"); + log("Options:\n"); log("\n"); log(" -verbose\n"); log(" this will print the recursive walk used to export the modules.\n"); log("\n"); + log(" -stbv\n"); + log(" Use a BitVec sort to represent a state instead of an uninterpreted\n"); + log(" sort. As a side-effect this will prevent use of arrays to model\n"); + log(" memories.\n"); + log("\n"); + log(" -stdt\n"); + log(" Use SMT-LIB 2.6 style datatypes to represent a state instead of an\n"); + log(" uninterpreted sort.\n"); + log("\n"); log(" -nobv\n"); log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n"); log(" option multi-bit wires are represented using the BitVec sort and\n"); @@ -972,10 +1409,11 @@ struct Smt2Backend : public Backend { log("from non-zero to zero in the test design.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::ifstream template_f; - bool bvmode = true, memmode = true, wiresmode = false, verbose = false; + bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false; + bool forallmode = false; log_header(design, "Executing SMT2 backend.\n"); @@ -992,6 +1430,16 @@ struct Smt2Backend : public Backend { log_warning("Options -bv and -mem are now the default. Support for -bv and -mem will be removed in the future.\n"); continue; } + if (args[argidx] == "-stbv") { + statebv = true; + statedt = false; + continue; + } + if (args[argidx] == "-stdt") { + statebv = false; + statedt = true; + continue; + } if (args[argidx] == "-nobv") { bvmode = false; memmode = false; @@ -1033,6 +1481,12 @@ struct Smt2Backend : public Backend { if (!memmode) *f << stringf("; yosys-smt2-nomem\n"); + if (statebv) + *f << stringf("; yosys-smt2-stbv\n"); + + if (statedt) + *f << stringf("; yosys-smt2-stdt\n"); + std::vector sorted_modules; // extract module dependencies @@ -1062,9 +1516,23 @@ struct Smt2Backend : public Backend { module_deps.erase(sorted_modules.at(sorted_modules_idx++)); } + dict mod_stbv_width; + dict>> mod_clk_cache; Module *topmod = design->top_module(); std::string topmod_id; + for (auto module : sorted_modules) + for (auto cell : module->cells()) + if (cell->type.in("$allconst", "$allseq")) + goto found_forall; + if (0) { + found_forall: + forallmode = true; + *f << stringf("; yosys-smt2-forall\n"); + if (!statebv && !statedt) + log_error("Forall-exists problems are only supported in -stbv or -stdt mode.\n"); + } + for (auto module : sorted_modules) { if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn()) @@ -1072,7 +1540,7 @@ struct Smt2Backend : public Backend { log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); - Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose); + Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode, mod_stbv_width, mod_clk_cache); worker.run(); worker.write(*f); diff --git a/yosys/backends/smt2/smtbmc.py b/yosys/backends/smt2/smtbmc.py index 04c25f914..94a5e2da0 100644 --- a/yosys/backends/smt2/smtbmc.py +++ b/yosys/backends/smt2/smtbmc.py @@ -22,21 +22,33 @@ import os, sys, getopt, re from smtio import SmtIo, SmtOpts, MkVcd from collections import defaultdict +got_topt = False skip_steps = 0 step_size = 1 num_steps = 20 +append_steps = 0 vcdfile = None cexfile = None +aimfile = None +aiwfile = None +aigheader = True +btorwitfile = None vlogtbfile = None +vlogtbtop = None inconstr = list() outconstr = None gentrace = False +covermode = False tempind = False dumpall = False assume_skipped = None final_only = False topmod = None noinfo = False +presat = False +smtcinit = False +smtctop = None +noinit = False so = SmtOpts() @@ -56,6 +68,9 @@ yosys-smtbmc [options] -i instead of BMC run temporal induction + -c + instead of regular BMC run cover analysis + -m name of the top module @@ -65,10 +80,33 @@ yosys-smtbmc [options] --cex read cex file as written by ABC's "write_cex -n" + --aig + read AIGER map file (as written by Yosys' "write_aiger -map") + and AIGER witness file. The file names are .aim for + the map file and .aiw for the witness file. + + --aig : + like above, but for map files and witness files that do not + share a filename prefix (or use different file extensions). + + --aig-noheader + the AIGER witness file does not include the status and + properties lines. + + --btorwit + read a BTOR witness. + --noinfo only run the core proof, do not collect and print any additional information (e.g. which assert failed) + --presat + check if the design with assumptions but without assertions + is SAT before checking if assertions are UNSAT. This will + detect if there are contradicting assumptions. In some cases + this will also help to "warm up" the solver, potentially + yielding a speedup. + --final-only only check final constraints, assume base case @@ -85,25 +123,48 @@ yosys-smtbmc [options] --dump-vlogtb write trace as Verilog test bench + --vlogtb-top + use the given entity as top module for the generated + Verilog test bench. The is relative + to the design top module without the top module name. + --dump-smtc write trace as constraints file + --smtc-init + write just the last state as initial constraint to smtc file + + --smtc-top [:] + replace with in constraints dumped to smtc + file and only dump object below in design hierarchy. + + --noinit + do not assume initial conditions in state 0 + --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. + + --append + add time steps at the end of the trace + when creating a counter example (this additional time + steps will still be constrained by assumptions) """ + 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"]) + opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts + + ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat", + "dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=", + "smtc-init", "smtc-top=", "noinit"]) except: usage() for o, a in opts: if o == "-t": + got_topt = True a = a.split(":") if len(a) == 1: num_steps = int(a[0]) @@ -115,7 +176,7 @@ for o, a in opts: step_size = int(a[1]) num_steps = int(a[2]) else: - assert 0 + assert False elif o == "--assume-skipped": assume_skipped = int(a) elif o == "--final-only": @@ -124,20 +185,48 @@ for o, a in opts: inconstr.append(a) elif o == "--cex": cexfile = a + elif o == "--aig": + if ":" in a: + aimfile, aiwfile = a.split(":") + else: + aimfile = a + ".aim" + aiwfile = a + ".aiw" + elif o == "--aig-noheader": + aigheader = False + elif o == "--btorwit": + btorwitfile = a elif o == "--dump-vcd": vcdfile = a elif o == "--dump-vlogtb": vlogtbfile = a + elif o == "--vlogtb-top": + vlogtbtop = a elif o == "--dump-smtc": outconstr = a + elif o == "--smtc-init": + smtcinit = True + elif o == "--smtc-top": + smtctop = a.split(":") + if len(smtctop) == 1: + smtctop.append("") + assert len(smtctop) == 2 + smtctop = tuple(smtctop) elif o == "--dump-all": dumpall = True + elif o == "--presat": + presat = True elif o == "--noinfo": noinfo = True + elif o == "--noinit": + noinit = True + elif o == "--append": + append_steps = int(a) elif o == "-i": tempind = True elif o == "-g": gentrace = True + elif o == "-c": + covermode = True elif o == "-m": topmod = a elif so.handle(o, a): @@ -148,6 +237,8 @@ for o, a in opts: if len(args) != 1: usage() +if sum([tempind, gentrace, covermode]) > 1: + usage() constr_final_start = None constr_asserts = defaultdict(list) @@ -182,12 +273,11 @@ for fn in inconstr: 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) + arg = abs(int(tokens[1])) + current_states = set(["final-%d" % i for i in range(arg, num_steps+1)]) + constr_final_start = arg if constr_final_start is None else min(constr_final_start, arg) else: - assert 0 + assert False continue if tokens[0] == "state": @@ -206,18 +296,17 @@ for fn in inconstr: for i in range(lower, upper+1): current_states.add(i) else: - assert 0 + assert False 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)) + arg = abs(int(tokens[1])) + current_states = set(range(arg, num_steps+1)) else: - assert 0 + assert False continue if tokens[0] == "assert": @@ -244,7 +333,7 @@ for fn in inconstr: so.logic = " ".join(tokens[1:]) continue - assert 0 + assert False def get_constr_expr(db, state, final=False, getvalues=False): @@ -255,7 +344,7 @@ def get_constr_expr(db, state, final=False, getvalues=False): if state not in db: return ([], [], []) if getvalues else "true" - netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*)\](?=[ )]|$)') + netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*|\S*)\](?=[ )]|$)') def replace_netref(match): state_sel = match.group(2) @@ -318,6 +407,11 @@ assert topmod is not None assert topmod in smt.modinfo if cexfile is not None: + if not got_topt: + assume_skipped = 0 + skip_steps = 0 + num_steps = 0 + with open(cexfile, "r") as f: cex_regex = re.compile(r'([^\[@=]+)(\[\d+\])?([^@=]*)(@\d+)=([01])') for entry in f.read().split(): @@ -349,6 +443,241 @@ if cexfile is not None: # print("cex@%d: %s" % (step, smtexpr)) constr_assumes[step].append((cexfile, smtexpr)) + if not got_topt: + skip_steps = max(skip_steps, step) + num_steps = max(num_steps, step+1) + +if aimfile is not None: + input_map = dict() + init_map = dict() + latch_map = dict() + + if not got_topt: + assume_skipped = 0 + skip_steps = 0 + num_steps = 0 + + with open(aimfile, "r") as f: + for entry in f.read().splitlines(): + entry = entry.split() + + if entry[0] == "input": + input_map[int(entry[1])] = (entry[3], int(entry[2])) + continue + + if entry[0] == "init": + init_map[int(entry[1])] = (entry[3], int(entry[2])) + continue + + if entry[0] in ["latch", "invlatch"]: + latch_map[int(entry[1])] = (entry[3], int(entry[2]), entry[0] == "invlatch") + continue + + if entry[0] in ["output", "wire"]: + continue + + assert False + + with open(aiwfile, "r") as f: + got_state = False + got_ffinit = False + step = 0 + + if not aigheader: + got_state = True + + for entry in f.read().splitlines(): + if len(entry) == 0 or entry[0] in "bcjfu.": + continue + + if not got_state: + got_state = True + assert entry == "1" + continue + + if not got_ffinit: + got_ffinit = True + if len(init_map) == 0: + for i in range(len(entry)): + if entry[i] == "x": + continue + + if i in latch_map: + value = int(entry[i]) + name = latch_map[i][0] + bitidx = latch_map[i][1] + invert = latch_map[i][2] + + if invert: + value = 1 - value + + path = smt.get_path(topmod, name) + width = smt.net_width(topmod, path) + + if width == 1: + assert bitidx == 0 + smtexpr = "(= [%s] %s)" % (name, "true" if value else "false") + else: + smtexpr = "(= ((_ extract %d %d) [%s]) #b%d)" % (bitidx, bitidx, name, value) + + constr_assumes[0].append((cexfile, smtexpr)) + continue + + for i in range(len(entry)): + if entry[i] == "x": + continue + + if (step == 0) and (i in init_map): + value = int(entry[i]) + name = init_map[i][0] + bitidx = init_map[i][1] + + path = smt.get_path(topmod, name) + + if not smt.net_exists(topmod, path): + match = re.match(r"(.*)\[(\d+)\]$", path[-1]) + if match: + path[-1] = match.group(1) + addr = int(match.group(2)) + + if not match or not smt.mem_exists(topmod, path): + print_msg("Ignoring init value for unknown net: %s" % (name)) + continue + + meminfo = smt.mem_info(topmod, path) + smtexpr = "(select [%s] #b%s)" % (".".join(path), bin(addr)[2:].zfill(meminfo[0])) + width = meminfo[1] + + else: + smtexpr = "[%s]" % name + width = smt.net_width(topmod, path) + + if width == 1: + assert bitidx == 0 + smtexpr = "(= %s %s)" % (smtexpr, "true" if value else "false") + else: + smtexpr = "(= ((_ extract %d %d) %s) #b%d)" % (bitidx, bitidx, smtexpr, value) + + constr_assumes[0].append((cexfile, smtexpr)) + + if i in input_map: + value = int(entry[i]) + name = input_map[i][0] + bitidx = input_map[i][1] + + path = smt.get_path(topmod, name) + width = smt.net_width(topmod, path) + + if width == 1: + assert bitidx == 0 + smtexpr = "(= [%s] %s)" % (name, "true" if value else "false") + else: + smtexpr = "(= ((_ extract %d %d) [%s]) #b%d)" % (bitidx, bitidx, name, value) + + constr_assumes[step].append((cexfile, smtexpr)) + + if not got_topt: + skip_steps = max(skip_steps, step) + num_steps = max(num_steps, step+1) + step += 1 + +if btorwitfile is not None: + with open(btorwitfile, "r") as f: + step = None + suffix = None + altsuffix = None + header_okay = False + + for line in f: + line = line.strip() + + if line == "sat": + header_okay = True + continue + + if not header_okay: + continue + + if line == "" or line[0] == "b" or line[0] == "j": + continue + + if line == ".": + break + + if line[0] == '#' or line[0] == '@': + step = int(line[1:]) + suffix = line + altsuffix = suffix + if suffix[0] == "@": + altsuffix = "#" + suffix[1:] + else: + altsuffix = "@" + suffix[1:] + continue + + line = line.split() + + if len(line) == 0: + continue + + if line[-1].endswith(suffix): + line[-1] = line[-1][0:len(line[-1]) - len(suffix)] + + if line[-1].endswith(altsuffix): + line[-1] = line[-1][0:len(line[-1]) - len(altsuffix)] + + if line[-1][0] == "$": + continue + + # BV assignments + if len(line) == 3 and line[1][0] != "[": + value = line[1] + name = line[2] + + path = smt.get_path(topmod, name) + + if not smt.net_exists(topmod, path): + continue + + width = smt.net_width(topmod, path) + + if width == 1: + assert value in ["0", "1"] + value = "true" if value == "1" else "false" + else: + value = "#b" + value + + smtexpr = "(= [%s] %s)" % (name, value) + constr_assumes[step].append((btorwitfile, smtexpr)) + + # Array assignments + if len(line) == 4 and line[1][0] == "[": + index = line[1] + value = line[2] + name = line[3] + + path = smt.get_path(topmod, name) + + if not smt.mem_exists(topmod, path): + continue + + meminfo = smt.mem_info(topmod, path) + + if meminfo[1] == 1: + assert value in ["0", "1"] + value = "true" if value == "1" else "false" + else: + value = "#b" + value + + assert index[0] == "[" + assert index[-1] == "]" + index = "#b" + index[1:-1] + + smtexpr = "(= (select [%s] %s) %s)" % (name, index, value) + constr_assumes[step].append((btorwitfile, smtexpr)) + + skip_steps = step + num_steps = step+1 + def write_vcd_trace(steps_start, steps_stop, index): filename = vcdfile.replace("%", index) print_msg("Writing trace to VCD file: %s" % (filename)) @@ -363,14 +692,123 @@ def write_vcd_trace(steps_start, steps_stop, index): if n.startswith("$"): hidden_net = True if not hidden_net: - vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath)) + edge = smt.net_clock(topmod, netpath) + if edge is None: + vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath)) + else: + vcd.add_clock([topmod] + netpath, edge) path_list.append(netpath) + mem_trace_data = dict() + for mempath in sorted(smt.hiermems(topmod)): + abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath) + + expr_id = list() + expr_list = list() + for i in range(steps_start, steps_stop): + for j in range(rports): + expr_id.append(('R', i-steps_start, j, 'A')) + expr_id.append(('R', i-steps_start, j, 'D')) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j)) + for j in range(wports): + expr_id.append(('W', i-steps_start, j, 'A')) + expr_id.append(('W', i-steps_start, j, 'D')) + expr_id.append(('W', i-steps_start, j, 'M')) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j)) + expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j)) + + rdata = list() + wdata = list() + addrs = set() + + for eid, edat in zip(expr_id, smt.get_list(expr_list)): + t, i, j, f = eid + + if t == 'R': + c = rdata + elif t == 'W': + c = wdata + else: + assert False + + while len(c) <= i: + c.append(list()) + c = c[i] + + while len(c) <= j: + c.append(dict()) + c = c[j] + + c[f] = smt.bv2bin(edat) + + if f == 'A': + addrs.add(c[f]) + + for addr in addrs: + tdata = list() + data = ["x"] * width + gotread = False + + if len(wdata) == 0 and len(rdata) != 0: + wdata = [[]] * len(rdata) + + assert len(rdata) == len(wdata) + + for i in range(len(wdata)): + if not gotread: + for j_data in rdata[i]: + if j_data["A"] == addr: + data = list(j_data["D"]) + gotread = True + break + + if gotread: + buf = data[:] + for i in reversed(range(len(tdata))): + for k in range(width): + if tdata[i][k] == "x": + tdata[i][k] = buf[k] + else: + buf[k] = tdata[i][k] + + if not asyncwr: + tdata.append(data[:]) + + for j_data in wdata[i]: + if j_data["A"] != addr: + continue + + D = j_data["D"] + M = j_data["M"] + + for k in range(width): + if M[k] == "1": + data[k] = D[k] + + if asyncwr: + tdata.append(data[:]) + + assert len(tdata) == len(rdata) + + netpath = mempath[:] + netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int(addr, 2)) + vcd.add_net([topmod] + netpath, width) + + for i in range(steps_start, steps_stop): + if i not in mem_trace_data: + mem_trace_data[i] = list() + mem_trace_data[i].append((netpath, "".join(tdata[i-steps_start]))) + 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) + if i in mem_trace_data: + for path, value in mem_trace_data[i]: + vcd.set_net([topmod] + path, value) vcd.set_time(steps_stop) @@ -379,18 +817,38 @@ def write_vlogtb_trace(steps_start, steps_stop, index): filename = vlogtbfile.replace("%", index) print_msg("Writing trace to Verilog testbench: %s" % (filename)) + vlogtb_topmod = topmod + vlogtb_state = "s@@step_idx@@" + + if vlogtbtop is not None: + for item in vlogtbtop.split("."): + if item in smt.modinfo[vlogtb_topmod].cells: + vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state) + vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item] + else: + print_msg("Vlog top module '%s' not found: no cell '%s' in module '%s'" % (vlogtbtop, item, vlogtb_topmod)) + break + with open(filename, "w") as f: + print("`ifndef VERILATOR", file=f) print("module testbench;", file=f) print(" reg [4095:0] vcdfile;", file=f) - print(" reg clock = 0, genclock = 1;", file=f) + print(" reg clock;", file=f) + print("`else", file=f) + print("module testbench(input clock, output reg genclock);", file=f) + print(" initial genclock = 1;", file=f) + print("`endif", file=f) + + print(" reg genclock = 1;", file=f) + print(" reg [31:0] cycle = 0;", file=f) primary_inputs = list() clock_inputs = set() - for name in smt.modinfo[topmod].inputs: + for name in smt.modinfo[vlogtb_topmod].inputs: if name in ["clk", "clock", "CLK", "CLOCK"]: clock_inputs.add(name) - width = smt.modinfo[topmod].wsize[name] + width = smt.modinfo[vlogtb_topmod].wsize[name] primary_inputs.append((name, width)) for name, width in primary_inputs: @@ -399,27 +857,32 @@ def write_vlogtb_trace(steps_start, steps_stop, index): else: print(" reg [%d:0] PI_%s;" % (width-1, name), file=f) - print(" %s UUT (" % topmod, file=f) + print(" %s UUT (" % vlogtb_topmod, file=f) print(",\n".join(" .{name}(PI_{name})".format(name=name) for name, _ in primary_inputs), file=f) print(" );", file=f) + print("`ifndef VERILATOR", 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(" #5 clock = 0;", file=f) print(" while (genclock) begin", file=f) - print(" #5; clock = 0;", file=f) - print(" #5; clock = 1;", file=f) + print(" #5 clock = 0;", file=f) + print(" #5 clock = 1;", file=f) print(" end", file=f) print(" end", file=f) + print("`endif", 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) + regs = sorted(smt.hiernets(vlogtb_topmod, regs_only=True)) + regvals = smt.get_net_bin_list(vlogtb_topmod, regs, vlogtb_state.replace("@@step_idx@@", str(steps_start))) + print("`ifndef VERILATOR", file=f) print(" #1;", file=f) + print("`endif", file=f) for reg, val in zip(regs, regvals): hidden_net = False for n in reg: @@ -427,40 +890,74 @@ def write_vlogtb_trace(steps_start, steps_stop, index): 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)) + anyconsts = sorted(smt.hieranyconsts(vlogtb_topmod)) + for info in anyconsts: + if info[3] is not None: + modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps_start)), info[0]) + value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate))) + print(" UUT.%s = %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f); + + mems = sorted(smt.hiermems(vlogtb_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) + abits, width, rports, wports, asyncwr = smt.mem_info(vlogtb_topmod, mempath) addr_expr_list = list() + data_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)) + for j in range(rports): + addr_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j)) + data_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j)) - addr_list = set() - for val in smt.get_list(addr_expr_list): - addr_list.add(smt.bv2int(val)) + addr_list = smt.get_list(addr_expr_list) + data_list = smt.get_list(data_expr_list) - expr_list = list() - for i in addr_list: - expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits))) + addr_data = dict() + for addr, data in zip(addr_list, data_list): + addr = smt.bv2bin(addr) + data = smt.bv2bin(data) + if addr not in addr_data: + addr_data[addr] = data - 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 addr, data in addr_data.items(): + print(" UUT.%s[%d'b%s] = %d'b%s;" % (".".join(mempath), len(addr), addr, len(data), data), file=f) + + print("", file=f) + anyseqs = sorted(smt.hieranyseqs(vlogtb_topmod)) 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) + pi_values = smt.get_net_bin_list(vlogtb_topmod, pi_names, vlogtb_state.replace("@@step_idx@@", str(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) + if i > 0: + print(" if (cycle == %d) begin" % (i-1), file=f) + + for name, val in zip(pi_names, pi_values): + if i > 0: + print(" PI_%s <= %d'b%s;" % (".".join(name), len(val), val), file=f) + else: + print(" PI_%s = %d'b%s;" % (".".join(name), len(val), val), file=f) + + for info in anyseqs: + if info[3] is not None: + modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), info[0]) + value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate))) + if i > 0: + print(" UUT.%s <= %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f); + else: + print(" UUT.%s = %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f); + + if i > 0: + print(" end", file=f) + print("", file=f) + + if i == 0: + print(" end", file=f) + print(" always @(posedge clock) begin", file=f) + + print(" genclock <= cycle < %d;" % (steps_stop-1), file=f) + print(" cycle <= cycle + 1;", file=f) print(" end", file=f) print("endmodule", file=f) @@ -470,52 +967,71 @@ def write_constr_trace(steps_start, steps_stop, index): filename = outconstr.replace("%", index) print_msg("Writing trace to constraints file: %s" % (filename)) + constr_topmod = topmod + constr_state = "s@@step_idx@@" + constr_prefix = "" + + if smtctop is not None: + for item in smtctop[0].split("."): + assert item in smt.modinfo[constr_topmod].cells + constr_state = "(|%s_h %s| %s)" % (constr_topmod, item, constr_state) + constr_topmod = smt.modinfo[constr_topmod].cells[item] + if smtctop[1] != "": + constr_prefix = smtctop[1] + "." + + if smtcinit: + steps_start = steps_stop - 1 + with open(filename, "w") as f: primary_inputs = list() - for name in smt.modinfo[topmod].inputs: - width = smt.modinfo[topmod].wsize[name] + for name in smt.modinfo[constr_topmod].inputs: + width = smt.modinfo[constr_topmod].wsize[name] primary_inputs.append((name, width)) - if steps_start == 0: + if steps_start == 0 or smtcinit: 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) + regnames = sorted(smt.hiernets(constr_topmod, regs_only=True)) + regvals = smt.get_net_list(constr_topmod, regnames, constr_state.replace("@@step_idx@@", str(steps_start))) for name, val in zip(regnames, regvals): - print("assume (= [%s] %s)" % (".".join(name), val), file=f) + print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f) - mems = sorted(smt.hiermems(topmod)) + mems = sorted(smt.hiermems(constr_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) + abits, width, rports, wports, asyncwr = smt.mem_info(constr_topmod, mempath) addr_expr_list = list() + data_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)) + for j in range(rports): + addr_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j)) + data_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j)) - addr_list = set((smt.bv2int(val) for val in smt.get_list(addr_expr_list))) + addr_list = smt.get_list(addr_expr_list) + data_list = smt.get_list(data_expr_list) - expr_list = list() - for i in addr_list: - expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits))) + addr_data = dict() + for addr, data in zip(addr_list, data_list): + if addr not in addr_data: + addr_data[addr] = data - 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 addr, data in addr_data.items(): + print("assume (= (select [%s%s] %s) %s)" % (constr_prefix, ".".join(mempath), addr, data), file=f) for k in range(steps_start, steps_stop): - print("", file=f) - print("state %d" % k, file=f) + if not smtcinit: + 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) + pi_values = smt.get_net_list(constr_topmod, pi_names, constr_state.replace("@@step_idx@@", str(k))) for name, val in zip(pi_names, pi_values): - print("assume (= [%s] %s)" % (".".join(name), val), file=f) + print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f) def write_trace(steps_start, steps_stop, index): @@ -529,30 +1045,40 @@ def write_trace(steps_start, steps_stop, index): write_constr_trace(steps_start, steps_stop, index) -def print_failed_asserts_worker(mod, state, path): +def print_failed_asserts_worker(mod, state, path, extrainfo): assert mod in smt.modinfo + found_failed_assert = False 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) + if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo): + found_failed_assert = True 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)) + print_msg("Assert failed in %s: %s%s" % (path, assertinfo, extrainfo)) + found_failed_assert = True + + return found_failed_assert -def print_failed_asserts(state, final=False): +def print_failed_asserts(state, final=False, extrainfo=""): if noinfo: return loc_list, expr_list, value_list = get_constr_expr(constr_asserts, state, final=final, getvalues=True) + found_failed_assert = False 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)) + print_msg("Assert %s failed: %s%s" % (loc, expr, extrainfo)) + found_failed_assert = True if not final: - print_failed_asserts_worker(topmod, "s%d" % state, topmod) + if print_failed_asserts_worker(topmod, "s%d" % state, topmod, extrainfo): + found_failed_assert = True + + return found_failed_assert def print_anyconsts_worker(mod, state, path): @@ -562,7 +1088,10 @@ def print_anyconsts_worker(mod, state, path): 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))))) + if info[1] is None: + print_msg("Value for anyconst in %s (%s): %d" % (path, info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state))))) + else: + print_msg("Value for anyconst %s.%s (%s): %d" % (path, info[1], info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state))))) def print_anyconsts(state): @@ -570,23 +1099,183 @@ def print_anyconsts(state): print_anyconsts_worker(topmod, "s%d" % state, topmod) +def get_cover_list(mod, base): + assert mod in smt.modinfo + + cover_expr = list() + cover_desc = list() + + for expr, desc in smt.modinfo[mod].covers.items(): + cover_expr.append("(ite (|%s| %s) #b1 #b0)" % (expr, base)) + cover_desc.append(desc) + + for cell, submod in smt.modinfo[mod].cells.items(): + e, d = get_cover_list(submod, "(|%s_h %s| %s)" % (mod, cell, base)) + cover_expr += e + cover_desc += d + + return cover_expr, cover_desc + +states = list() +asserts_antecedent_cache = [list()] +asserts_consequent_cache = [list()] +asserts_cache_dirty = False + +def smt_state(step): + smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod)) + states.append("s%d" % step) + +def smt_assert(expr): + if expr == "true": + return + + smt.write("(assert %s)" % expr) + +def smt_assert_antecedent(expr): + if expr == "true": + return + + smt.write("(assert %s)" % expr) + + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_antecedent_cache[-1].append(expr) + +def smt_assert_consequent(expr): + if expr == "true": + return + + smt.write("(assert %s)" % expr) + + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_consequent_cache[-1].append(expr) + +def smt_forall_assert(): + if not smt.forall: + return + + global asserts_cache_dirty + asserts_cache_dirty = False + + def make_assert_expr(asserts_cache): + expr = list() + for lst in asserts_cache: + expr += lst + + assert len(expr) != 0 + + if len(expr) == 1: + expr = expr[0] + else: + expr = "(and %s)" % (" ".join(expr)) + return expr + + antecedent_expr = make_assert_expr(asserts_antecedent_cache) + consequent_expr = make_assert_expr(asserts_consequent_cache) + + states_db = set(states) + used_states_db = set() + new_antecedent_expr = list() + new_consequent_expr = list() + assert_expr = list() + + def make_new_expr(new_expr, expr): + cursor = 0 + while cursor < len(expr): + l = 1 + if expr[cursor] in '|"': + while cursor+l+1 < len(expr) and expr[cursor] != expr[cursor+l]: + l += 1 + l += 1 + elif expr[cursor] not in '() ': + while cursor+l < len(expr) and expr[cursor+l] not in '|"() ': + l += 1 + + word = expr[cursor:cursor+l] + if word in states_db: + used_states_db.add(word) + word += "_" + + new_expr.append(word) + cursor += l + + make_new_expr(new_antecedent_expr, antecedent_expr) + make_new_expr(new_consequent_expr, consequent_expr) + + new_antecedent_expr = ["".join(new_antecedent_expr)] + new_consequent_expr = ["".join(new_consequent_expr)] + + if states[0] in used_states_db: + new_antecedent_expr.append("(|%s_ex_state_eq| %s %s_)" % (topmod, states[0], states[0])) + for s in states: + if s in used_states_db: + new_antecedent_expr.append("(|%s_ex_input_eq| %s %s_)" % (topmod, s, s)) + + if len(new_antecedent_expr) == 0: + new_antecedent_expr = "true" + elif len(new_antecedent_expr) == 1: + new_antecedent_expr = new_antecedent_expr[0] + else: + new_antecedent_expr = "(and %s)" % (" ".join(new_antecedent_expr)) + + if len(new_consequent_expr) == 0: + new_consequent_expr = "true" + elif len(new_consequent_expr) == 1: + new_consequent_expr = new_consequent_expr[0] + else: + new_consequent_expr = "(and %s)" % (" ".join(new_consequent_expr)) + + assert_expr.append("(assert (forall (") + first_state = True + for s in states: + if s in used_states_db: + assert_expr.append("%s(%s_ |%s_s|)" % ("" if first_state else " ", s, topmod)) + first_state = False + assert_expr.append(") (=> %s %s)))" % (new_antecedent_expr, new_consequent_expr)) + + smt.write("".join(assert_expr)) + +def smt_push(): + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_antecedent_cache.append(list()) + asserts_consequent_cache.append(list()) + smt.write("(push 1)") + +def smt_pop(): + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_antecedent_cache.pop() + asserts_consequent_cache.pop() + smt.write("(pop 1)") + +def smt_check_sat(): + if asserts_cache_dirty: + smt_forall_assert() + return smt.check_sat() + 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 smt.forall: + print_msg("Temporal induction not supported for exists-forall problems.") + break + + smt_state(step) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + smt_assert_consequent(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))) + smt_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)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step, step+1)) + smt_assert("(|%s_a| s%d)" % (topmod, step)) + smt_assert(get_constr_expr(constr_asserts, step)) if step > num_steps-skip_steps: print_msg("Skipping induction in step %d.." % (step)) @@ -600,9 +1289,9 @@ if tempind: skip_counter = 0 print_msg("Trying induction in step %d.." % (step)) - if smt.check_sat() == "sat": + if smt_check_sat() == "sat": if step == 0: - print("%s Temporal induction failed!" % smt.timestamp()) + print_msg("Temporal induction failed!") print_anyconsts(num_steps) print_failed_asserts(num_steps) write_trace(step, num_steps+1, '%') @@ -613,33 +1302,139 @@ if tempind: write_trace(step, num_steps+1, "%d" % step) else: - print("%s Temporal induction successful." % smt.timestamp()) + print_msg("Temporal induction successful.") retstatus = True break +elif covermode: + cover_expr, cover_desc = get_cover_list(topmod, "state") + cover_mask = "1" * len(cover_desc) -else: # not tempind + if len(cover_expr) > 1: + cover_expr = "(concat %s)" % " ".join(cover_expr) + elif len(cover_expr) == 1: + cover_expr = cover_expr[0] + else: + cover_expr = "#b0" + + coveridx = 0 + smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr)) + + step = 0 + retstatus = False + found_failed_assert = False + + assert step_size == 1 + + while step < num_steps: + smt_state(step) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) + smt_assert_consequent(get_constr_expr(constr_assumes, step)) + + if step == 0: + if noinit: + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + else: + smt_assert_antecedent("(|%s_i| s0)" % (topmod)) + smt_assert_antecedent("(|%s_is| s0)" % (topmod)) + + else: + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + + while "1" in cover_mask: + print_msg("Checking cover reachability in step %d.." % (step)) + smt_push() + smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc))) + + if smt_check_sat() == "unsat": + smt_pop() + break + + if append_steps > 0: + for i in range(step+1, step+1+append_steps): + print_msg("Appending additional step %d." % i) + smt_state(i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) + smt_assert_consequent(get_constr_expr(constr_assumes, i)) + print_msg("Re-solving with appended steps..") + if smt_check_sat() == "unsat": + print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) + found_failed_assert = True + retstatus = False + break + + reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step))) + assert len(reached_covers) == len(cover_desc) + + new_cover_mask = [] + + for i in range(len(reached_covers)): + if reached_covers[i] == "0": + new_cover_mask.append(cover_mask[i]) + continue + + print_msg("Reached cover statement at %s in step %d." % (cover_desc[i], step)) + new_cover_mask.append("0") + + cover_mask = "".join(new_cover_mask) + + for i in range(step+1+append_steps): + if print_failed_asserts(i, extrainfo=" (step %d)" % i): + found_failed_assert = True + + write_trace(0, step+1+append_steps, "%d" % coveridx) + + if found_failed_assert: + break + + coveridx += 1 + smt_pop() + smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask)) + + if found_failed_assert: + break + + if "1" not in cover_mask: + retstatus = True + break + + step += 1 + + if "1" in cover_mask: + for i in range(len(cover_mask)): + if cover_mask[i] == "1": + print_msg("Unreached cover statement at %s." % cover_desc[i]) + +else: # not tempind, covermode 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)) + smt_state(step) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) + smt_assert_consequent(get_constr_expr(constr_assumes, step)) if step == 0: - smt.write("(assert (|%s_i| s0))" % (topmod)) - smt.write("(assert (|%s_is| s0))" % (topmod)) + if noinit: + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + else: + smt_assert_antecedent("(|%s_i| s0)" % (topmod)) + smt_assert_antecedent("(|%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)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step)) + smt_assert_antecedent("(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)) + smt_assert("(|%s_a| s%d)" % (topmod, step)) + smt_assert(get_constr_expr(constr_asserts, step)) else: print_msg("Skipping step %d.." % (step)) step += 1 @@ -648,39 +1443,65 @@ else: # not tempind 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)) + smt_state(step+i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step+i)) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step+i)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step+i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step+i-1, step+i)) + smt_assert_consequent(get_constr_expr(constr_assumes, step+i)) last_check_step = step+i if not gentrace: - if not final_only: + if presat: if last_check_step == step: - print_msg("Checking asserts in step %d.." % (step)) + print_msg("Checking assumptions in step %d.." % (step)) else: - print_msg("Checking asserts in steps %d to %d.." % (step, last_check_step)) - smt.write("(push 1)") + print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step)) - 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, '%') + if smt_check_sat() == "unsat": + print("%s Warmup failed!" % smt.timestamp()) retstatus = False break - smt.write("(pop 1)") + if not final_only: + if last_check_step == step: + print_msg("Checking assertions in step %d.." % (step)) + else: + print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step)) + smt_push() + + smt_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()) + if append_steps > 0: + for i in range(last_check_step+1, last_check_step+1+append_steps): + print_msg("Appending additional step %d." % i) + smt_state(i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) + smt_assert_consequent(get_constr_expr(constr_assumes, i)) + print_msg("Re-solving with appended steps..") + if smt_check_sat() == "unsat": + print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) + retstatus = False + break + print_anyconsts(step) + for i in range(step, last_check_step+1): + print_failed_asserts(i) + write_trace(0, last_check_step+1+append_steps, '%') + retstatus = False + break + + smt_pop() 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)) + smt_assert("(|%s_a| s%d)" % (topmod, i)) + smt_assert(get_constr_expr(constr_asserts, i)) if constr_final_start is not None: for i in range(step, last_check_step+1): @@ -688,12 +1509,12 @@ else: # not tempind continue print_msg("Checking final constraints in step %d.." % (i)) - smt.write("(push 1)") + smt_push() - 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)) + smt_assert_consequent(get_constr_expr(constr_assumes, i, final=True)) + smt_assert("(not %s)" % get_constr_expr(constr_asserts, i, final=True)) - if smt.check_sat() == "sat": + if smt_check_sat() == "sat": print("%s BMC failed!" % smt.timestamp()) print_anyconsts(i) print_failed_asserts(i, final=True) @@ -701,17 +1522,17 @@ else: # not tempind retstatus = False break - smt.write("(pop 1)") + smt_pop() 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)) + smt_assert("(|%s_a| s%d)" % (topmod, i)) + smt_assert(get_constr_expr(constr_asserts, i)) print_msg("Solving for step %d.." % (last_check_step)) - if smt.check_sat() != "sat": + if smt_check_sat() != "sat": print("%s No solution found!" % smt.timestamp()) retstatus = False break @@ -722,7 +1543,7 @@ else: # not tempind step += step_size - if gentrace: + if gentrace and retstatus: print_anyconsts(0) write_trace(0, num_steps, '%') diff --git a/yosys/backends/smt2/smtio.py b/yosys/backends/smt2/smtio.py index 865eed1f1..68783e744 100644 --- a/yosys/backends/smt2/smtio.py +++ b/yosys/backends/smt2/smtio.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # # yosys -- Yosys Open SYnthesis Suite # @@ -17,10 +16,63 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -import sys, subprocess, re +import sys, re, os, signal +import subprocess +if os.name == "posix": + import resource from copy import deepcopy from select import select from time import time +from queue import Queue, Empty +from threading import Thread + + +# This is needed so that the recursive SMT2 S-expression parser +# does not run out of stack frames when parsing large expressions +if os.name == "posix": + smtio_reclimit = 64 * 1024 + if sys.getrecursionlimit() < smtio_reclimit: + sys.setrecursionlimit(smtio_reclimit) + + current_rlimit_stack = resource.getrlimit(resource.RLIMIT_STACK) + if current_rlimit_stack[0] != resource.RLIM_INFINITY: + smtio_stacksize = 128 * 1024 * 1024 + if os.uname().sysname == "Darwin": + # MacOS has rather conservative stack limits + smtio_stacksize = 16 * 1024 * 1024 + if current_rlimit_stack[1] != resource.RLIM_INFINITY: + smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1]) + if current_rlimit_stack[0] < smtio_stacksize: + resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1])) + + +# currently running solvers (so we can kill them) +running_solvers = dict() +forced_shutdown = False +solvers_index = 0 + +def force_shutdown(signum, frame): + global forced_shutdown + if not forced_shutdown: + forced_shutdown = True + if signum is not None: + print("<%s>" % signal.Signals(signum).name) + for p in running_solvers.values(): + # os.killpg(os.getpgid(p.pid), signal.SIGTERM) + os.kill(p.pid, signal.SIGTERM) + sys.exit(1) + +if os.name == "posix": + signal.signal(signal.SIGHUP, force_shutdown) +signal.signal(signal.SIGINT, force_shutdown) +signal.signal(signal.SIGTERM, force_shutdown) + +def except_hook(exctype, value, traceback): + if not forced_shutdown: + sys.__excepthook__(exctype, value, traceback) + force_shutdown(None, None) + +sys.excepthook = except_hook hex_dict = { @@ -41,25 +93,38 @@ class SmtModInfo: self.memories = dict() self.wires = set() self.wsize = dict() + self.clocks = dict() self.cells = dict() self.asserts = dict() + self.covers = dict() self.anyconsts = dict() + self.anyseqs = dict() + self.allconsts = dict() + self.allseqs = dict() + self.asize = dict() class SmtIo: def __init__(self, opts=None): + global solvers_index + self.logic = None self.logic_qf = True self.logic_ax = True self.logic_uf = True self.logic_bv = True + self.logic_dt = False + self.forall = False self.produce_models = True self.smt2cache = [list()] self.p = None + self.p_index = solvers_index + solvers_index += 1 if opts is not None: self.logic = opts.logic self.solver = opts.solver + self.solver_opts = opts.solver_opts self.debug_print = opts.debug_print self.debug_file = opts.debug_file self.dummy_file = opts.dummy_file @@ -70,34 +135,66 @@ class SmtIo: self.nocomments = opts.nocomments else: - self.solver = "z3" + self.solver = "yices" + self.solver_opts = list() self.debug_print = False self.debug_file = None self.dummy_file = None - self.timeinfo = True + self.timeinfo = os.name != "nt" self.unroll = False self.noincr = False self.info_stmts = list() self.nocomments = False + self.start_time = time() + + self.modinfo = dict() + self.curmod = None + self.topmod = None + self.setup_done = False + + def __del__(self): + if self.p is not None and not forced_shutdown: + os.killpg(os.getpgid(self.p.pid), signal.SIGTERM) + if running_solvers is not None: + del running_solvers[self.p_index] + + def setup(self): + assert not self.setup_done + + if self.forall: + self.unroll = False + if self.solver == "yices": - self.popen_vargs = ['yices-smt2', '--incremental'] + if self.noincr: + self.popen_vargs = ['yices-smt2'] + self.solver_opts + else: + self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts if self.solver == "z3": - self.popen_vargs = ['z3', '-smt2', '-in'] + self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts if self.solver == "cvc4": - self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2'] + if self.noincr: + self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts + else: + self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts if self.solver == "mathsat": - self.popen_vargs = ['mathsat'] + self.popen_vargs = ['mathsat'] + self.solver_opts if self.solver == "boolector": - self.popen_vargs = ['boolector', '--smt2', '-i'] + if self.noincr: + self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts + else: + self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts self.unroll = True if self.solver == "abc": - self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000'] + if len(self.solver_opts) > 0: + self.popen_vargs = ['yosys-abc', '-S', '; '.join(self.solver_opts)] + else: + self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000'] self.logic_ax = False self.unroll = True self.noincr = True @@ -109,9 +206,10 @@ class SmtIo: if self.dummy_file is not None: self.dummy_fd = open(self.dummy_file, "w") if not self.noincr: - self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.p_open() if self.unroll: + assert not self.forall self.logic_uf = False self.unroll_idcnt = 0 self.unroll_buffer = "" @@ -121,36 +219,27 @@ class SmtIo: self.unroll_cache = dict() self.unroll_stack = list() - self.start_time = time() - - self.modinfo = dict() - self.curmod = None - self.topmod = None - self.setup_done = False - - def setup(self): - assert not self.setup_done - if self.logic is None: self.logic = "" if self.logic_qf: self.logic += "QF_" if self.logic_ax: self.logic += "A" if self.logic_uf: self.logic += "UF" if self.logic_bv: self.logic += "BV" + if self.logic_dt: self.logic = "ALL" self.setup_done = True + for stmt in self.info_stmts: + self.write(stmt) + if self.produce_models: self.write("(set-option :produce-models true)") self.write("(set-logic %s)" % self.logic) - for stmt in self.info_stmts: - self.write(stmt) - def timestamp(self): secs = int(time() - self.start_time) - return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) + return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60) def replace_in_stmt(self, stmt, pat, repl): if stmt == pat: @@ -201,18 +290,75 @@ class SmtIo: return stmt + def p_thread_main(self): + while True: + data = self.p.stdout.readline().decode("ascii") + if data == "": break + self.p_queue.put(data) + self.p_queue.put("") + self.p_running = False + + def p_open(self): + assert self.p is None + self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + running_solvers[self.p_index] = self.p + self.p_running = True + self.p_next = None + self.p_queue = Queue() + self.p_thread = Thread(target=self.p_thread_main) + self.p_thread.start() + + def p_write(self, data, flush): + assert self.p is not None + self.p.stdin.write(bytes(data, "ascii")) + if flush: self.p.stdin.flush() + + def p_read(self): + assert self.p is not None + if self.p_next is not None: + data = self.p_next + self.p_next = None + return data + if not self.p_running: + return "" + return self.p_queue.get() + + def p_poll(self, timeout=0.1): + assert self.p is not None + assert self.p_running + if self.p_next is not None: + return False + try: + self.p_next = self.p_queue.get(True, timeout) + return False + except Empty: + return True + + def p_close(self): + assert self.p is not None + self.p.stdin.close() + self.p_thread.join() + assert not self.p_running + del running_solvers[self.p_index] + self.p = None + self.p_next = None + self.p_queue = None + self.p_thread = None + def write(self, stmt, unroll=True): if stmt.startswith(";"): self.info(stmt) + if not self.setup_done: + self.info_stmts.append(stmt) + return elif not self.setup_done: self.setup() stmt = stmt.strip() if self.nocomments or self.unroll: - if stmt.startswith(";"): - return - stmt = re.sub(r" ;.*", "", stmt) + stmt = re.sub(r" *;.*", "", stmt) + if stmt == "": return if unroll and self.unroll: stmt = self.unroll_buffer + stmt @@ -271,20 +417,17 @@ class SmtIo: if self.solver != "dummy": if self.noincr: if self.p is not None and not stmt.startswith("(get-"): - self.p.stdin.close() - self.p = None + self.p_close() if stmt == "(push 1)": self.smt2cache.append(list()) elif stmt == "(pop 1)": self.smt2cache.pop() else: if self.p is not None: - self.p.stdin.write(bytes(stmt + "\n", "ascii")) - self.p.stdin.flush() + self.p_write(stmt + "\n", True) self.smt2cache[-1].append(stmt) else: - self.p.stdin.write(bytes(stmt + "\n", "ascii")) - self.p.stdin.flush() + self.p_write(stmt + "\n", True) def info(self, stmt): if not stmt.startswith("; yosys-smt2-"): @@ -300,6 +443,15 @@ class SmtIo: if self.logic is None: self.logic_bv = False + if fields[1] == "yosys-smt2-stdt": + if self.logic is None: + self.logic_dt = True + + if fields[1] == "yosys-smt2-forall": + if self.logic is None: + self.logic_qf = False + self.forall = True + if fields[1] == "yosys-smt2-module": self.curmod = fields[2] self.modinfo[self.curmod] = SmtModInfo() @@ -323,17 +475,40 @@ class SmtIo: self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3]) if fields[1] == "yosys-smt2-memory": - self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5])) + self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]), fields[7] == "async") if fields[1] == "yosys-smt2-wire": self.modinfo[self.curmod].wires.add(fields[2]) self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3]) + if fields[1] == "yosys-smt2-clock": + for edge in fields[3:]: + if fields[2] not in self.modinfo[self.curmod].clocks: + self.modinfo[self.curmod].clocks[fields[2]] = edge + elif self.modinfo[self.curmod].clocks[fields[2]] != edge: + self.modinfo[self.curmod].clocks[fields[2]] = "event" + if fields[1] == "yosys-smt2-assert": - self.modinfo[self.curmod].asserts[fields[2]] = fields[3] + self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3] + + if fields[1] == "yosys-smt2-cover": + self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3] if fields[1] == "yosys-smt2-anyconst": - self.modinfo[self.curmod].anyconsts[fields[2]] = fields[3] + self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + + if fields[1] == "yosys-smt2-anyseq": + self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + + if fields[1] == "yosys-smt2-allconst": + self.modinfo[self.curmod].allconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + + if fields[1] == "yosys-smt2-allseq": + self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) def hiernets(self, top, regs_only=False): def hiernets_worker(nets, mod, cursor): @@ -347,6 +522,54 @@ class SmtIo: hiernets_worker(nets, top, []) return nets + def hieranyconsts(self, top): + def worker(results, mod, cursor): + for name, value in sorted(self.modinfo[mod].anyconsts.items()): + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(results, celltype, cursor + [cellname]) + + results = list() + worker(results, top, []) + return results + + def hieranyseqs(self, top): + def worker(results, mod, cursor): + for name, value in sorted(self.modinfo[mod].anyseqs.items()): + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(results, celltype, cursor + [cellname]) + + results = list() + worker(results, top, []) + return results + + def hierallconsts(self, top): + def worker(results, mod, cursor): + for name, value in sorted(self.modinfo[mod].allconsts.items()): + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(results, celltype, cursor + [cellname]) + + results = list() + worker(results, top, []) + return results + + def hierallseqs(self, top): + def worker(results, mod, cursor): + for name, value in sorted(self.modinfo[mod].allseqs.items()): + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(results, celltype, cursor + [cellname]) + + results = list() + worker(results, top, []) + return results + def hiermems(self, top): def hiermems_worker(mems, mod, cursor): for memname in sorted(self.modinfo[mod].memories.keys()): @@ -366,7 +589,7 @@ class SmtIo: if self.solver == "dummy": line = self.dummy_fd.readline().strip() else: - line = self.p.stdout.readline().decode("ascii").strip() + line = self.p_read().strip() if self.dummy_file is not None: self.dummy_fd.write(line + "\n") @@ -379,12 +602,14 @@ class SmtIo: if count_brackets == 0: break if self.solver != "dummy" and self.p.poll(): - print("SMT Solver terminated unexpectedly: %s" % "".join(stmt)) + print("%s Solver terminated unexpectedly: %s" % (self.timestamp(), "".join(stmt)), flush=True) sys.exit(1) stmt = "".join(stmt) if stmt.startswith("(error"): - print("SMT Solver Error: %s" % stmt, file=sys.stderr) + print("%s Solver Error: %s" % (self.timestamp(), stmt), flush=True) + if self.solver != "dummy": + self.p_close() sys.exit(1) return stmt @@ -399,15 +624,13 @@ class SmtIo: if self.solver != "dummy": if self.noincr: if self.p is not None: - self.p.stdin.close() - self.p = None - self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.p_close() + self.p_open() for cache_ctx in self.smt2cache: for cache_stmt in cache_ctx: - self.p.stdin.write(bytes(cache_stmt + "\n", "ascii")) + self.p_write(cache_stmt + "\n", False) - self.p.stdin.write(bytes("(check-sat)\n", "ascii")) - self.p.stdin.flush() + self.p_write("(check-sat)\n", True) if self.timeinfo: i = 0 @@ -415,7 +638,7 @@ class SmtIo: count = 0 num_bs = 0 - while select([self.p.stdout], [], [], 0.1) == ([], [], []): + while self.p_poll(): count += 1 if count < 25: @@ -444,11 +667,43 @@ class SmtIo: print("\b \b" * num_bs, end="", file=sys.stderr) sys.stderr.flush() + else: + count = 0 + while self.p_poll(60): + count += 1 + msg = None + + if count == 1: + msg = "1 minute" + + elif count in [5, 10, 15, 30]: + msg = "%d minutes" % count + + elif count == 60: + msg = "1 hour" + + elif count % 60 == 0: + msg = "%d hours" % (count // 60) + + if msg is not None: + print("%s waiting for solver (%s)" % (self.timestamp(), msg), flush=True) + result = self.read() + if self.debug_file: print("(set-info :status %s)" % result, file=self.debug_file) print("(check-sat)", file=self.debug_file) self.debug_file.flush() + + if result not in ["sat", "unsat"]: + if result == "": + print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True) + else: + print("%s Unexpected response from solver: %s" % (self.timestamp(), result), flush=True) + if self.solver != "dummy": + self.p_close() + sys.exit(1) + return result def parse(self, stmt): @@ -503,6 +758,9 @@ class SmtIo: return h def bv2bin(self, v): + if type(v) is list and len(v) == 3 and v[0] == "_" and v[1].startswith("bv"): + x, n = int(v[1][2:]), int(v[2]) + return "".join("1" if (x & (1 << i)) else "0" for i in range(n-1, -1, -1)) if v == "true": return "1" if v == "false": return "0" if v.startswith("#b"): @@ -539,6 +797,9 @@ class SmtIo: return [".".join(path)] def net_expr(self, mod, base, path): + if len(path) == 0: + return base + if len(path) == 1: assert mod in self.modinfo if path[0] == "": @@ -568,23 +829,54 @@ class SmtIo: assert net_path[-1] in self.modinfo[mod].wsize return self.modinfo[mod].wsize[net_path[-1]] - def mem_expr(self, mod, base, path, portidx=None, infomode=False): + def net_clock(self, mod, net_path): + for i in range(len(net_path)-1): + assert mod in self.modinfo + assert net_path[i] in self.modinfo[mod].cells + mod = self.modinfo[mod].cells[net_path[i]] + + assert mod in self.modinfo + if net_path[-1] not in self.modinfo[mod].clocks: + return None + return self.modinfo[mod].clocks[net_path[-1]] + + def net_exists(self, mod, net_path): + for i in range(len(net_path)-1): + if mod not in self.modinfo: return False + if net_path[i] not in self.modinfo[mod].cells: return False + mod = self.modinfo[mod].cells[net_path[i]] + + if mod not in self.modinfo: return False + if net_path[-1] not in self.modinfo[mod].wsize: return False + return True + + def mem_exists(self, mod, mem_path): + for i in range(len(mem_path)-1): + if mod not in self.modinfo: return False + if mem_path[i] not in self.modinfo[mod].cells: return False + mod = self.modinfo[mod].cells[mem_path[i]] + + if mod not in self.modinfo: return False + if mem_path[-1] not in self.modinfo[mod].memories: return False + return True + + def mem_expr(self, mod, base, path, port=None, infomode=False): if len(path) == 1: assert mod in self.modinfo assert path[0] in self.modinfo[mod].memories if infomode: return self.modinfo[mod].memories[path[0]] - return "(|%s_m%s %s| %s)" % (mod, "" if portidx is None else ":%d" % portidx, path[0], base) + return "(|%s_m%s %s| %s)" % (mod, "" if port is None else ":%s" % port, path[0], base) assert mod in self.modinfo assert path[0] in self.modinfo[mod].cells nextmod = self.modinfo[mod].cells[path[0]] nextbase = "(|%s_h %s| %s)" % (mod, path[0], base) - return self.mem_expr(nextmod, nextbase, path[1:], portidx=portidx, infomode=infomode) + return self.mem_expr(nextmod, nextbase, path[1:], port=port, infomode=infomode) - def mem_info(self, mod, base, path): - return self.mem_expr(mod, base, path, infomode=True) + def mem_info(self, mod, path): + return self.mem_expr(mod, "", path, infomode=True) def get_net(self, mod_name, net_path, state_name): return self.get(self.net_expr(mod_name, state_name, net_path)) @@ -607,19 +899,21 @@ class SmtIo: def wait(self): if self.p is not None: self.p.wait() + self.p_close() class SmtOpts: def __init__(self): - self.shortopts = "s:v" + self.shortopts = "s:S:v" self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"] - self.solver = "z3" + self.solver = "yices" + self.solver_opts = list() self.debug_print = False self.debug_file = None self.dummy_file = None self.unroll = False self.noincr = False - self.timeinfo = True + self.timeinfo = os.name != "nt" self.logic = None self.info_stmts = list() self.nocomments = False @@ -627,6 +921,8 @@ class SmtOpts: def handle(self, o, a): if o == "-s": self.solver = a + elif o == "-S": + self.solver_opts.append(a) elif o == "-v": self.debug_print = True elif o == "--unroll": @@ -634,7 +930,7 @@ class SmtOpts: elif o == "--noincr": self.noincr = True elif o == "--noprogress": - self.timeinfo = True + self.timeinfo = False elif o == "--dump-smt2": self.debug_file = open(a, "w") elif o == "--logic": @@ -652,8 +948,11 @@ class SmtOpts: def helpmsg(self): return """ -s - set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy - default: z3 + set SMT solver: z3, yices, boolector, cvc4, mathsat, dummy + default: yices + + -S + pass as command line argument to the solver --logic use the specified SMT2 logic (e.g. QF_AUFBV) @@ -674,6 +973,7 @@ class SmtOpts: --noprogress disable timer display during solving + (this option is set implicitly on Windows) --dump-smt2 write smt2 statements to file @@ -691,6 +991,7 @@ class MkVcd: self.f = f self.t = -1 self.nets = dict() + self.clocks = dict() def add_net(self, path, width): path = tuple(path) @@ -698,11 +999,25 @@ class MkVcd: key = "n%d" % len(self.nets) self.nets[path] = (key, width) + def add_clock(self, path, edge): + path = tuple(path) + assert self.t == -1 + key = "n%d" % len(self.nets) + self.nets[path] = (key, 1) + self.clocks[path] = (key, edge) + def set_net(self, path, bits): path = tuple(path) assert self.t >= 0 assert path in self.nets - print("b%s %s" % (bits, self.nets[path][0]), file=self.f) + if path not in self.clocks: + print("b%s %s" % (bits, self.nets[path][0]), file=self.f) + + def escape_name(self, name): + name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name) + if re.match("[\[\]]", name) and name[0] != "\\": + name = "\\" + name + return name def set_time(self, t): assert t >= self.t @@ -710,22 +1025,52 @@ class MkVcd: if self.t == -1: print("$var integer 32 t smt_step $end", file=self.f) print("$var event 1 ! smt_clock $end", file=self.f) + scope = [] for path in sorted(self.nets): - while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]): + key, width = self.nets[path] + + uipath = list(path) + if "." in uipath[-1]: + uipath = uipath[0:-1] + uipath[-1].split(".") + for i in range(len(uipath)): + uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i]) + + while uipath[:len(scope)] != scope: print("$upscope $end", file=self.f) scope = scope[:-1] - while len(scope)+1 < len(path): - print("$scope module %s $end" % path[len(scope)], file=self.f) - scope.append(path[len(scope)-1]) - key, width = self.nets[path] - print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f) + + while uipath[:-1] != scope: + print("$scope module %s $end" % uipath[len(scope)], file=self.f) + scope.append(uipath[len(scope)]) + + if path in self.clocks and self.clocks[path][1] == "event": + print("$var event 1 %s %s $end" % (key, uipath[-1]), file=self.f) + else: + print("$var wire %d %s %s $end" % (width, key, uipath[-1]), file=self.f) + for i in range(len(scope)): print("$upscope $end", file=self.f) + print("$enddefinitions $end", file=self.f) + self.t = t assert self.t >= 0 + + if self.t > 0: + print("#%d" % (10 * self.t - 5), file=self.f) + for path in sorted(self.clocks.keys()): + if self.clocks[path][1] == "posedge": + print("b0 %s" % self.nets[path][0], file=self.f) + elif self.clocks[path][1] == "negedge": + print("b1 %s" % self.nets[path][0], file=self.f) + print("#%d" % (10 * self.t), file=self.f) print("1!", file=self.f) print("b%s t" % format(self.t, "032b"), file=self.f) + for path in sorted(self.clocks.keys()): + if self.clocks[path][1] == "negedge": + print("b0 %s" % self.nets[path][0], file=self.f) + else: + print("b1 %s" % self.nets[path][0], file=self.f) diff --git a/yosys/backends/smv/smv.cc b/yosys/backends/smv/smv.cc index 162ce4906..f379c9c48 100644 --- a/yosys/backends/smv/smv.cc +++ b/yosys/backends/smv/smv.cc @@ -42,7 +42,7 @@ struct SmvWorker pool partial_assignment_wires; dict> partial_assignment_bits; - vector assignments, invarspecs; + vector inputvars, vars, definitions, assignments, invarspecs; const char *cid() { @@ -195,7 +195,7 @@ struct SmvWorker return rvalue(sig); const char *temp_id = cid(); - f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig)); +// f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig)); int offset = 0; for (auto bit : sig) { @@ -210,14 +210,14 @@ struct SmvWorker void run() { f << stringf("MODULE %s\n", cid(module->name)); - f << stringf(" VAR\n"); for (auto wire : module->wires()) { if (SigSpec(wire) != sigmap(wire)) partial_assignment_wires.insert(wire); - f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire)); + if (wire->port_input) + inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, log_id(wire))); if (wire->attributes.count("\\init")) assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init")))); @@ -275,8 +275,8 @@ struct SmvWorker const char *b_shr = rvalue_u(sig_b); const char *b_shl = cid(); - f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b)); - assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b))); +// f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b)); + definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b))); string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y); string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y); @@ -303,7 +303,7 @@ struct SmvWorker GetSize(sig_b)-shift_b_width, width_y, expr.c_str()); } - assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); + definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); continue; } @@ -319,12 +319,12 @@ struct SmvWorker if (cell->getParam("\\A_SIGNED").as_bool()) { - assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue_s(cell->getPort("\\A"), width))); } else { - assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue_u(cell->getPort("\\A"), width))); } @@ -346,12 +346,12 @@ struct SmvWorker if (cell->getParam("\\A_SIGNED").as_bool()) { - assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")), rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width))); } else { - assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width))); } @@ -370,12 +370,12 @@ struct SmvWorker if (cell->getParam("\\A_SIGNED").as_bool()) { - assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y)); } else { - assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")), rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y)); } @@ -407,7 +407,7 @@ struct SmvWorker expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width); } - assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y")))); continue; @@ -425,7 +425,7 @@ struct SmvWorker if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a); if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a); - assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); continue; } @@ -444,7 +444,7 @@ struct SmvWorker if (cell->type == "$reduce_xnor") expr = "!(" + expr + ")"; - assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y)); continue; } @@ -462,7 +462,7 @@ struct SmvWorker if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b; if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b; - assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); continue; } @@ -474,7 +474,7 @@ struct SmvWorker string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a); const char *expr_y = lvalue(cell->getPort("\\Y")); - assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y)); continue; } @@ -490,12 +490,13 @@ struct SmvWorker expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width))); expr += rvalue(sig_a); - assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); + definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); continue; } if (cell->type == "$dff") { + vars.push_back(stringf("%s : unsigned word[%d]; -- %s", lvalue(cell->getPort("\\Q")), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")))); assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D")))); continue; } @@ -503,59 +504,63 @@ struct SmvWorker if (cell->type.in("$_BUF_", "$_NOT_")) { string op = cell->type == "$_NOT_" ? "!" : ""; - assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A")))); + definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A")))); continue; } - if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) { string op; - if (cell->type.in("$_AND_", "$_NAND_")) op = "&"; - if (cell->type.in("$_OR_", "$_NOR_")) op = "|"; + if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_")) op = "&"; + if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_")) op = "|"; if (cell->type.in("$_XOR_")) op = "xor"; if (cell->type.in("$_XNOR_")) op = "xnor"; - if (cell->type.in("$_NAND_", "$_NOR_")) - assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")), + if (cell->type.in("$_ANDNOT_", "$_ORNOT_")) + definitions.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); else - assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), + if (cell->type.in("$_NAND_", "$_NOR_")) + definitions.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); + else + definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); continue; } if (cell->type == "$_MUX_") { - assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A")))); continue; } if (cell->type == "$_AOI3_") { - assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")))); continue; } if (cell->type == "$_OAI3_") { - assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")))); continue; } if (cell->type == "$_AOI4_") { - assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D")))); continue; } if (cell->type == "$_OAI4_") { - assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D")))); continue; } @@ -563,13 +568,13 @@ struct SmvWorker if (cell->type[0] == '$') log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell)); - f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); +// f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); for (auto &conn : cell->connections()) if (cell->output(conn.first)) - assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first))); + definitions.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first))); else - assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second))); + definitions.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second))); } for (Wire *wire : partial_assignment_wires) @@ -653,7 +658,25 @@ struct SmvWorker } } - assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str())); + definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str())); + } + + if (!inputvars.empty()) { + f << stringf(" IVAR\n"); + for (const string &line : inputvars) + f << stringf(" %s\n", line.c_str()); + } + + if (!vars.empty()) { + f << stringf(" VAR\n"); + for (const string &line : vars) + f << stringf(" %s\n", line.c_str()); + } + + if (!definitions.empty()) { + f << stringf(" DEFINE\n"); + for (const string &line : definitions) + f << stringf(" %s\n", line.c_str()); } if (!assignments.empty()) { @@ -671,7 +694,7 @@ struct SmvWorker struct SmvBackend : public Backend { SmvBackend() : Backend("smv", "write design to SMV file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -689,7 +712,7 @@ struct SmvBackend : public Backend { log("THIS COMMAND IS UNDER CONSTRUCTION\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::ifstream template_f; bool verbose = false; diff --git a/yosys/backends/spice/spice.cc b/yosys/backends/spice/spice.cc index 4101cbf98..b6a3f1e77 100644 --- a/yosys/backends/spice/spice.cc +++ b/yosys/backends/spice/spice.cc @@ -132,7 +132,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De struct SpiceBackend : public Backend { SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -161,7 +161,7 @@ struct SpiceBackend : public Backend { log(" set the specified module as design top module\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string top_module_name; RTLIL::Module *top_module = NULL; diff --git a/yosys/backends/table/Makefile.inc b/yosys/backends/table/Makefile.inc new file mode 100644 index 000000000..8cd1dc619 --- /dev/null +++ b/yosys/backends/table/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += backends/table/table.o + diff --git a/yosys/backends/table/table.cc b/yosys/backends/table/table.cc new file mode 100644 index 000000000..b75169ea4 --- /dev/null +++ b/yosys/backends/table/table.cc @@ -0,0 +1,120 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct TableBackend : public Backend { + TableBackend() : Backend("table", "write design as connectivity table") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_table [options] [filename]\n"); + log("\n"); + log("Write the current design as connectivity table. The output is a tab-separated\n"); + log("ASCII table with the following columns:\n"); + log("\n"); + log(" module name\n"); + log(" cell name\n"); + log(" cell type\n"); + log(" cell port\n"); + log(" direction\n"); + log(" signal\n"); + log("\n"); + log("module inputs and outputs are output using cell type and port '-' and with\n"); + log("'pi' (primary input) or 'po' (primary output) or 'pio' as direction.\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing TABLE backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-top" && argidx+1 < args.size()) { + // top_module_name = args[++argidx]; + // continue; + // } + break; + } + extra_args(f, filename, args, argidx); + + design->sort(); + + for (auto module : design->modules()) + { + if (module->get_bool_attribute("\\blackbox")) + continue; + + SigMap sigmap(module); + + for (auto wire : module->wires()) + { + if (wire->port_id == 0) + continue; + + *f << log_id(module) << "\t"; + *f << log_id(wire) << "\t"; + *f << "-" << "\t"; + *f << "-" << "\t"; + + if (wire->port_input && wire->port_output) + *f << "pio" << "\t"; + else if (wire->port_input) + *f << "pi" << "\t"; + else if (wire->port_output) + *f << "po" << "\t"; + else + log_abort(); + + *f << log_signal(sigmap(wire)) << "\n"; + } + + for (auto cell : module->cells()) + for (auto conn : cell->connections()) + { + *f << log_id(module) << "\t"; + *f << log_id(cell) << "\t"; + *f << log_id(cell->type) << "\t"; + *f << log_id(conn.first) << "\t"; + + if (cell->input(conn.first) && cell->output(conn.first)) + *f << "inout" << "\t"; + else if (cell->input(conn.first)) + *f << "in" << "\t"; + else if (cell->output(conn.first)) + *f << "out" << "\t"; + else + *f << "unknown" << "\t"; + + *f << log_signal(sigmap(conn.second)) << "\n"; + } + } + } +} TableBackend; + +PRIVATE_NAMESPACE_END diff --git a/yosys/backends/verilog/verilog_backend.cc b/yosys/backends/verilog/verilog_backend.cc index a617215f0..2537e18e5 100644 --- a/yosys/backends/verilog/verilog_backend.cc +++ b/yosys/backends/verilog/verilog_backend.cc @@ -33,13 +33,15 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nostr, defparam; +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal; int auto_name_counter, auto_name_offset, auto_name_digits; std::map auto_name_map; std::set reg_wires, reg_ct; std::string auto_prefix; RTLIL::Module *active_module; +dict active_initdata; +SigMap active_sigmap; void reset_auto_counter_id(RTLIL::IdString id, bool may_rename) { @@ -159,23 +161,73 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o if (width < 0) width = data.bits.size() - offset; if (nostr) - goto dump_bits; + goto dump_hex; if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) { if (width == 32 && !no_decimal && !nodec) { int32_t val = 0; for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1) - goto dump_bits; + goto dump_hex; if (data.bits[i] == RTLIL::S1) val |= 1 << (i - offset); } - if (set_signed && val < 0) + if (decimal) + f << stringf("%d", val); + else if (set_signed && val < 0) f << stringf("-32'sd%u", -val); else f << stringf("32'%sd%u", set_signed ? "s" : "", val); } else { - dump_bits: + dump_hex: + if (nohex) + goto dump_bin; + vector bin_digits, hex_digits; + for (int i = offset; i < offset+width; i++) { + log_assert(i < (int)data.bits.size()); + switch (data.bits[i]) { + case RTLIL::S0: bin_digits.push_back('0'); break; + case RTLIL::S1: bin_digits.push_back('1'); break; + case RTLIL::Sx: bin_digits.push_back('x'); break; + case RTLIL::Sz: bin_digits.push_back('z'); break; + case RTLIL::Sa: bin_digits.push_back('z'); break; + case RTLIL::Sm: log_error("Found marker state in final netlist."); + } + } + if (GetSize(bin_digits) == 0) + goto dump_bin; + while (GetSize(bin_digits) % 4 != 0) + if (bin_digits.back() == '1') + bin_digits.push_back('0'); + else + bin_digits.push_back(bin_digits.back()); + for (int i = 0; i < GetSize(bin_digits); i += 4) + { + char bit_3 = bin_digits[i+3]; + char bit_2 = bin_digits[i+2]; + char bit_1 = bin_digits[i+1]; + char bit_0 = bin_digits[i+0]; + if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') { + if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x') + goto dump_bin; + hex_digits.push_back('x'); + continue; + } + if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') { + if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z') + goto dump_bin; + hex_digits.push_back('z'); + continue; + } + int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); + hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); + } + f << stringf("%d'%sh", width, set_signed ? "s" : ""); + for (int i = GetSize(hex_digits)-1; i >= 0; i--) + f << hex_digits[i]; + } + if (0) { + dump_bin: f << stringf("%d'%sb", width, set_signed ? "s" : ""); if (width == 0) f << stringf("0"); @@ -214,6 +266,26 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o } } +void dump_reg_init(std::ostream &f, SigSpec sig) +{ + Const initval; + bool gotinit = false; + + for (auto bit : active_sigmap(sig)) { + if (active_initdata.count(bit)) { + initval.bits.push_back(active_initdata.at(bit)); + gotinit = true; + } else { + initval.bits.push_back(State::Sx); + } + } + + if (gotinit) { + f << " = "; + dump_const(f, initval); + } +} + void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false) { if (chunk.wire == NULL) { @@ -302,12 +374,12 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) if (wire->port_input && wire->port_output) f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (reg_wires.count(wire->name)) { - f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); + f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str()); if (wire->attributes.count("\\init")) { - f << stringf("%s" "initial %s = ", indent.c_str(), id(wire->name).c_str()); + f << stringf(" = "); dump_const(f, wire->attributes.at("\\init")); - f << stringf(";\n"); } + f << stringf(";\n"); } else if (!wire->port_input && !wire->port_output) f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str()); #endif @@ -316,7 +388,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) { dump_attributes(f, indent, memory->attributes); - f << stringf("%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1); + f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset); } void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) @@ -400,7 +472,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) { + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort("\\Y")); f << stringf(" = "); @@ -408,16 +480,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("~("); dump_cell_expr_port(f, cell, "A", false); f << stringf(" "); - if (cell->type.in("$_AND_", "$_NAND_")) + if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_")) f << stringf("&"); - if (cell->type.in("$_OR_", "$_NOR_")) + if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_")) f << stringf("|"); if (cell->type.in("$_XOR_", "$_XNOR_")) f << stringf("^"); dump_attributes(f, "", cell->attributes, ' '); f << stringf(" "); + if (cell->type.in("$_ANDNOT_", "$_ORNOT_")) + f << stringf("~("); dump_cell_expr_port(f, cell, "B", false); - if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_")) + if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) f << stringf(")"); f << stringf(";\n"); return true; @@ -474,8 +548,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); - if (!out_is_reg_wire) - f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str()); + if (!out_is_reg_wire) { + f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str()); + dump_reg_init(f, cell->getPort("\\Q")); + f << ";\n"; + } dump_attributes(f, indent, cell->attributes); f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg"); @@ -514,8 +591,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); - if (!out_is_reg_wire) - f << stringf("%s" "reg %s;\n", indent.c_str(), reg_name.c_str()); + if (!out_is_reg_wire) { + f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str()); + dump_reg_init(f, cell->getPort("\\Q")); + f << ";\n"; + } dump_attributes(f, indent, cell->attributes); f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg"); @@ -598,6 +678,52 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) #undef HANDLE_UNIOP #undef HANDLE_BINOP + if (cell->type == "$shift") + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = "); + if (cell->getParam("\\B_SIGNED").as_bool()) + { + f << stringf("$signed("); + dump_sigspec(f, cell->getPort("\\B")); + f << stringf(")"); + f << stringf(" < 0 ? "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" << - "); + dump_sigspec(f, cell->getPort("\\B")); + f << stringf(" : "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" >> "); + dump_sigspec(f, cell->getPort("\\B")); + } + else + { + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" >> "); + dump_sigspec(f, cell->getPort("\\B")); + } + f << stringf(";\n"); + return true; + } + + if (cell->type == "$shiftx") + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf("["); + if (cell->getParam("\\B_SIGNED").as_bool()) + f << stringf("$signed("); + dump_sigspec(f, cell->getPort("\\B")); + if (cell->getParam("\\B_SIGNED").as_bool()) + f << stringf(")"); + f << stringf(" +: %d", cell->getParam("\\Y_WIDTH").as_int()); + f << stringf("];\n"); + return true; + } + if (cell->type == "$mux") { f << stringf("%s" "assign ", indent.c_str()); @@ -682,6 +808,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$lut") + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = "); + dump_const(f, cell->parameters.at("\\LUT")); + f << stringf(" >> "); + dump_attributes(f, "", cell->attributes, ' '); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(";\n"); + return true; + } + if (cell->type == "$dffsr") { SigSpec sig_clk = cell->getPort("\\CLK"); @@ -698,8 +837,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(sig_q, reg_name); - if (!out_is_reg_wire) - f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str()); + if (!out_is_reg_wire) { + f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str()); + dump_reg_init(f, sig_q); + f << ";\n"; + } for (int i = 0; i < width; i++) { f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); @@ -754,8 +896,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); - if (!out_is_reg_wire) - f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str()); + if (!out_is_reg_wire) { + f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str()); + dump_reg_init(f, cell->getPort("\\Q")); + f << ";\n"; + } f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg"); dump_sigspec(f, sig_clk); @@ -794,27 +939,64 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$dlatch") + { + RTLIL::SigSpec sig_en; + bool pol_en = false; + + sig_en = cell->getPort("\\EN"); + pol_en = cell->parameters["\\EN_POLARITY"].as_bool(); + + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); + + if (!out_is_reg_wire) { + f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str()); + dump_reg_init(f, cell->getPort("\\Q")); + f << ";\n"; + } + + f << stringf("%s" "always @*\n", indent.c_str()); + + f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!"); + dump_sigspec(f, sig_en); + f << stringf(")\n"); + + f << stringf("%s" " %s = ", indent.c_str(), reg_name.c_str()); + dump_cell_expr_port(f, cell, "D", false); + f << stringf(";\n"); + + if (!out_is_reg_wire) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Q")); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + return true; + } + if (cell->type == "$mem") { RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string(); std::string mem_id = id(cell->parameters["\\MEMID"].decode_string()); int abits = cell->parameters["\\ABITS"].as_int(); int size = cell->parameters["\\SIZE"].as_int(); + int offset = cell->parameters["\\OFFSET"].as_int(); int width = cell->parameters["\\WIDTH"].as_int(); bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef()); // for memory block make something like: // reg [7:0] memid [3:0]; // initial begin - // memid[0] <= ... + // memid[0] = ... // end - f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0); + f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset); if (use_init) { f << stringf("%s" "initial begin\n", indent.c_str()); for (int i=0; iparameters["\\WR_PORTS"].as_int(); RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en; bool wr_clk_posedge; - SigMap sigmap(active_module); + // write ports for (int i=0; i < nwrite_ports; i++) { @@ -937,7 +1119,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) int start_i = i, width = 1; SigBit wen_bit = sig_wr_en[i]; - while (i+1 < GetSize(sig_wr_en) && sigmap(sig_wr_en[i+1]) == sigmap(wen_bit)) + while (i+1 < GetSize(sig_wr_en) && active_sigmap(sig_wr_en[i+1]) == active_sigmap(wen_bit)) i++, width++; if (wen_bit == State::S0) @@ -1199,6 +1381,8 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo if (sync->type == RTLIL::STa) { f << stringf("%s" "always @* begin\n", indent.c_str()); + } else if (sync->type == RTLIL::STi) { + f << stringf("%s" "initial begin\n", indent.c_str()); } else { f << stringf("%s" "always @(", indent.c_str()); if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1) @@ -1251,12 +1435,22 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) reg_wires.clear(); reset_auto_counter(module); active_module = module; + active_sigmap.set(module); + active_initdata.clear(); + + for (auto wire : module->wires()) + if (wire->attributes.count("\\init")) { + SigSpec sig = active_sigmap(wire); + Const val = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++) + active_initdata[sig[i]] = val.bits.at(i); + } if (!module->processes.empty()) - log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n" + log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" "can't always be mapped directly to Verilog always blocks. Unintended\n" "changes in simulation behavior are possible! Use \"proc\" to convert\n" - "processes to logic networks and registers.", log_id(module)); + "processes to logic networks and registers.\n", log_id(module)); f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) @@ -1327,11 +1521,13 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) f << stringf("%s" "endmodule\n", indent.c_str()); active_module = NULL; + active_sigmap.clear(); + active_initdata.clear(); } struct VerilogBackend : public Backend { VerilogBackend() : Backend("verilog", "write design to Verilog file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1359,13 +1555,21 @@ struct VerilogBackend : public Backend { log("\n"); log(" -nodec\n"); log(" 32-bit constant values are by default dumped as decimal numbers,\n"); - log(" not bit pattern. This option decativates this feature and instead\n"); + log(" not bit pattern. This option deactivates this feature and instead\n"); log(" will write out all constants in binary.\n"); log("\n"); + log(" -decimal\n"); + log(" dump 32-bit constants in decimal and without size and radix\n"); + log("\n"); + log(" -nohex\n"); + log(" constant values that are compatible with hex output are usually\n"); + log(" dumped as hex values. This option deactivates this feature and\n"); + log(" instead will write out all constants in binary.\n"); + log("\n"); log(" -nostr\n"); log(" Parameters and attributes that are specified as strings in the\n"); log(" original input will be output as strings by this back-end. This\n"); - log(" decativates this feature and instead will write string constants\n"); + log(" deactivates this feature and instead will write string constants\n"); log(" as binary numbers.\n"); log("\n"); log(" -defparam\n"); @@ -1391,7 +1595,7 @@ struct VerilogBackend : public Backend { log("this command is called on a design with RTLIL processes.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing Verilog backend.\n"); @@ -1401,8 +1605,10 @@ struct VerilogBackend : public Backend { attr2comment = false; noexpr = false; nodec = false; + nohex = false; nostr = false; defparam = false; + decimal = false; auto_prefix = ""; bool blackboxes = false; @@ -1412,6 +1618,8 @@ struct VerilogBackend : public Backend { reg_ct.insert("$dff"); reg_ct.insert("$adff"); + reg_ct.insert("$dffe"); + reg_ct.insert("$dlatch"); reg_ct.insert("$_DFF_N_"); reg_ct.insert("$_DFF_P_"); @@ -1461,6 +1669,10 @@ struct VerilogBackend : public Backend { nodec = true; continue; } + if (arg == "-nohex") { + nohex = true; + continue; + } if (arg == "-nostr") { nostr = true; continue; @@ -1469,6 +1681,10 @@ struct VerilogBackend : public Backend { defparam = true; continue; } + if (arg == "-decimal") { + decimal = true; + continue; + } if (arg == "-blackboxes") { blackboxes = true; continue; diff --git a/yosys/examples/aiger/.gitignore b/yosys/examples/aiger/.gitignore new file mode 100644 index 000000000..3524e9362 --- /dev/null +++ b/yosys/examples/aiger/.gitignore @@ -0,0 +1,5 @@ +demo.aig +demo.aim +demo.aiw +demo.smt2 +demo.vcd diff --git a/yosys/examples/aiger/README b/yosys/examples/aiger/README new file mode 100644 index 000000000..4e7694e95 --- /dev/null +++ b/yosys/examples/aiger/README @@ -0,0 +1,22 @@ +AIGER is a format for And-Inverter Graphs (AIGs). +See http://fmv.jku.at/aiger/ for details. + +AIGER is used in the Hardware Model Checking Competition (HWMCC), +therefore all solvers competing in the competition have to support +the format. + +The example in this directory is using super_prove as solver. Check +http://downloads.bvsrc.org/super_prove/ for the lates release. (See +https://bitbucket.org/sterin/super_prove_build for sources.) + +The "demo.sh" script in this directory expects a "super_prove" executable +in the PATH. E.g. extract the release to /usr/local/libexec/super_prove +and then create a /usr/local/bin/super_prove file with the following +contents (and "chmod +x" that file): + + #!/bin/bash + exec /usr/local/libexec/super_prove/bin/super_prove.sh "$@" + +The "demo.sh" script also expects the "z3" SMT2 solver in the PATH for +converting the witness file generated by super_prove to VCD using +yosys-smtbmc. See https://github.com/Z3Prover/z3 for install notes. diff --git a/yosys/examples/aiger/demo.sh b/yosys/examples/aiger/demo.sh new file mode 100644 index 000000000..8728b6722 --- /dev/null +++ b/yosys/examples/aiger/demo.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -ex +yosys -p ' + read_verilog -formal demo.v + prep -flatten -nordff -top demo + write_smt2 -wires demo.smt2 + flatten demo; delete -output + memory_map; opt -full + techmap; opt -fast + abc -fast -g AND; opt_clean + write_aiger -map demo.aim demo.aig +' +super_prove demo.aig > demo.aiw +yosys-smtbmc --dump-vcd demo.vcd --aig demo demo.smt2 diff --git a/yosys/examples/aiger/demo.v b/yosys/examples/aiger/demo.v new file mode 100644 index 000000000..b98287424 --- /dev/null +++ b/yosys/examples/aiger/demo.v @@ -0,0 +1,12 @@ +module demo(input clk, reset, ctrl); + localparam NBITS = 10; + reg [NBITS-1:0] counter; + initial counter[NBITS-2] = 0; + initial counter[0] = 1; + always @(posedge clk) begin + counter <= reset ? 1 : ctrl ? counter + 1 : counter - 1; + assume(counter != 0); + assume(counter != 1 << (NBITS-1)); + assert(counter != (1 << NBITS)-1); + end +endmodule diff --git a/yosys/examples/basys3/example.xdc b/yosys/examples/basys3/example.xdc index c1fd0e925..8cdaa1996 100644 --- a/yosys/examples/basys3/example.xdc +++ b/yosys/examples/basys3/example.xdc @@ -19,3 +19,6 @@ set_property -dict { IOSTANDARD LVCMOS33 PACKAGE_PIN L1 } [get_ports {LD[15]}] create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports CLK] +set_property CONFIG_VOLTAGE 3.3 [current_design] +set_property CFGBVS VCCO [current_design] + diff --git a/yosys/examples/basys3/run_prog.tcl b/yosys/examples/basys3/run_prog.tcl index d711af840..b078ad511 100644 --- a/yosys/examples/basys3/run_prog.tcl +++ b/yosys/examples/basys3/run_prog.tcl @@ -1,3 +1,4 @@ +open_hw connect_hw_server open_hw_target [lindex [get_hw_targets] 0] set_property PROGRAM.FILE example.bit [lindex [get_hw_devices] 0] diff --git a/yosys/examples/cmos/counter.ys b/yosys/examples/cmos/counter.ys index a784f3465..d0b093667 100644 --- a/yosys/examples/cmos/counter.ys +++ b/yosys/examples/cmos/counter.ys @@ -1,11 +1,12 @@ - read_verilog counter.v read_verilog -lib cmos_cells.v -proc;; memory;; techmap;; - +synth dfflibmap -liberty cmos_cells.lib -abc -liberty cmos_cells.lib;; +abc -liberty cmos_cells.lib +opt_clean + +stat -liberty cmos_cells.lib # http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/tsmc025/signalstorm/osu025_stdcells.lib # dfflibmap -liberty osu025_stdcells.lib @@ -13,4 +14,3 @@ abc -liberty cmos_cells.lib;; write_verilog synth.v write_spice synth.sp - diff --git a/yosys/examples/cmos/counter_tb.v b/yosys/examples/cmos/counter_tb.v index bcd7d992c..11e82507e 100644 --- a/yosys/examples/cmos/counter_tb.v +++ b/yosys/examples/cmos/counter_tb.v @@ -12,7 +12,7 @@ module counter_tb; # 4 reset = 0; # 6 $finish; end - + /* Make enable with period of 8 and 6,7 low */ reg en = 1; always begin @@ -25,7 +25,7 @@ module counter_tb; /* Make a regular pulsing clock. */ reg clk = 0; always #1 clk = !clk; - + /* UUT */ wire [2:0] count; counter c1 (clk, reset, en, count); diff --git a/yosys/examples/cmos/testbench_digital.sh b/yosys/examples/cmos/testbench_digital.sh index afaaf4d43..d7ab0fe1f 100644 --- a/yosys/examples/cmos/testbench_digital.sh +++ b/yosys/examples/cmos/testbench_digital.sh @@ -4,7 +4,7 @@ set -ex # iverlog simulation echo "Doing Verilog simulation with iverilog" -iverilog -o counter_tb counter.v counter_tb.v +iverilog -o counter_tb counter.v counter_tb.v ./counter_tb; gtkwave counter_tb.gtkw & # yosys synthesis diff --git a/yosys/examples/cxx-api/evaldemo.cc b/yosys/examples/cxx-api/evaldemo.cc index e5cc8d8e7..34373487d 100644 --- a/yosys/examples/cxx-api/evaldemo.cc +++ b/yosys/examples/cxx-api/evaldemo.cc @@ -22,7 +22,7 @@ struct EvalDemoPass : public Pass { EvalDemoPass() : Pass("evaldemo") { } - virtual void execute(vector, Design *design) + void execute(vector, Design *design) YS_OVERRIDE { Module *module = design->top_module(); diff --git a/yosys/examples/gowin/.gitignore b/yosys/examples/gowin/.gitignore new file mode 100644 index 000000000..71030bdb8 --- /dev/null +++ b/yosys/examples/gowin/.gitignore @@ -0,0 +1,8 @@ +demo.bit +demo.out +demo.rpt +demo_syn.v +demo_out.v +demo_tr.html +testbench +testbench.vcd diff --git a/yosys/examples/gowin/README b/yosys/examples/gowin/README new file mode 100644 index 000000000..0194e9f09 --- /dev/null +++ b/yosys/examples/gowin/README @@ -0,0 +1,17 @@ +Simple test project for Gowinsemi GW2A-55K Eval Board Mini. + +Follow the install instructions for the Gowinsemi tools below, +then run "bash run.sh" in this directory. + + +Install instructions for gowinTool_linux +---------------------------------------- + +1.) extract gowinTool_linux.zip + +2.) set GOWIN_HOME env variable to the full path to the +gowinTool_linux directory + +3.) edit gowinTool_linux/bin/gwlicense.ini. Set lic="..." to +the full path to the license file. + diff --git a/yosys/examples/gowin/demo.cst b/yosys/examples/gowin/demo.cst new file mode 100644 index 000000000..22d7eb668 --- /dev/null +++ b/yosys/examples/gowin/demo.cst @@ -0,0 +1,41 @@ +// 50 MHz Clock +IO_LOC "clk" D11; + +// LEDs +IO_LOC "leds[0]" D22; +IO_LOC "leds[1]" E22; +IO_LOC "leds[2]" G22; +IO_LOC "leds[3]" J22; +IO_LOC "leds[4]" L22; +IO_LOC "leds[5]" L19; +IO_LOC "leds[6]" L20; +IO_LOC "leds[7]" M21; +IO_LOC "leds[8]" N19; +IO_LOC "leds[9]" R19; +IO_LOC "leds[10]" T18; +IO_LOC "leds[11]" AA22; +IO_LOC "leds[12]" U18; +IO_LOC "leds[13]" V20; +IO_LOC "leds[14]" AA21; +IO_LOC "leds[15]" AB21; + + +// 7-Segment Display +IO_LOC "seg7dig[0]" E20; +IO_LOC "seg7dig[1]" G18; +IO_LOC "seg7dig[2]" G20; +IO_LOC "seg7dig[3]" F21; +IO_LOC "seg7dig[4]" J20; +IO_LOC "seg7dig[5]" H21; +IO_LOC "seg7dig[6]" H18; +IO_LOC "seg7dig[7]" D20; +IO_LOC "seg7sel[0]" C19; +IO_LOC "seg7sel[1]" B22; +IO_LOC "seg7sel[2]" C20; +IO_LOC "seg7sel[3]" C21; + +// Switches +IO_LOC "sw[0]" AB20; +IO_LOC "sw[1]" AB19; +IO_LOC "sw[2]" AB18; +IO_LOC "sw[3]" AB17; diff --git a/yosys/examples/gowin/demo.sdc b/yosys/examples/gowin/demo.sdc new file mode 100644 index 000000000..6c90325fa --- /dev/null +++ b/yosys/examples/gowin/demo.sdc @@ -0,0 +1 @@ +create_clock -name clk -period 20 -waveform {0 10} [get_ports {clk}] diff --git a/yosys/examples/gowin/demo.v b/yosys/examples/gowin/demo.v new file mode 100644 index 000000000..6ea108384 --- /dev/null +++ b/yosys/examples/gowin/demo.v @@ -0,0 +1,12 @@ +module demo ( + input clk, + input [3:0] sw, + output [15:0] leds, + output [7:0] seg7dig, + output [3:0] seg7sel +); + localparam PRESCALE = 20; + reg [PRESCALE+3:0] counter = 0; + always @(posedge clk) counter <= counter + 1; + assign leds = 1 << counter[PRESCALE +: 4]; +endmodule diff --git a/yosys/examples/gowin/run.sh b/yosys/examples/gowin/run.sh new file mode 100644 index 000000000..33a7b5c37 --- /dev/null +++ b/yosys/examples/gowin/run.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -ex +yosys -p "synth_gowin -top demo -vout demo_syn.v" demo.v +$GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW2A55-PBGA484-6 \ + -warning_all -out demo_out.v -rpt demo.rpt -tr demo_tr.html -bit demo.bit + +# post place&route simulation (icarus verilog) +if false; then + iverilog -D POST_IMPL -o testbench -s testbench testbench.v \ + demo_out.v $(yosys-config --datdir/gowin/cells_sim.v) + vvp -N testbench +fi diff --git a/yosys/examples/gowin/testbench.v b/yosys/examples/gowin/testbench.v new file mode 100644 index 000000000..6d206381e --- /dev/null +++ b/yosys/examples/gowin/testbench.v @@ -0,0 +1,40 @@ +module testbench; + reg clk; + + initial begin + #5 clk = 0; + forever #5 clk = ~clk; + end + + wire [15:0] leds; + + initial begin + // $dumpfile("testbench.vcd"); + // $dumpvars(0, testbench); + $monitor("%b", leds); + end + + demo uut ( + .clk (clk ), +`ifdef POST_IMPL + .\leds[0] (leds[0]), + .\leds[1] (leds[1]), + .\leds[2] (leds[2]), + .\leds[3] (leds[3]), + .\leds[4] (leds[4]), + .\leds[5] (leds[5]), + .\leds[6] (leds[6]), + .\leds[7] (leds[7]), + .\leds[8] (leds[8]), + .\leds[9] (leds[9]), + .\leds[10] (leds[10]), + .\leds[11] (leds[11]), + .\leds[12] (leds[12]), + .\leds[13] (leds[13]), + .\leds[14] (leds[14]), + .\leds[15] (leds[15]) +`else + .leds(leds) +`endif + ); +endmodule diff --git a/yosys/examples/igloo2/.gitignore b/yosys/examples/igloo2/.gitignore new file mode 100644 index 000000000..fa3c3d7ed --- /dev/null +++ b/yosys/examples/igloo2/.gitignore @@ -0,0 +1,3 @@ +/netlist.edn +/netlist.v +/work diff --git a/yosys/examples/igloo2/example.v b/yosys/examples/igloo2/example.v new file mode 100644 index 000000000..3eb7007c5 --- /dev/null +++ b/yosys/examples/igloo2/example.v @@ -0,0 +1,22 @@ +module top ( + input clk, + output LED1, + output LED2, + output LED3, + output LED4, + output LED5 +); + + localparam BITS = 5; + localparam LOG2DELAY = 22; + + reg [BITS+LOG2DELAY-1:0] counter = 0; + reg [BITS-1:0] outcnt; + + always @(posedge clk) begin + counter <= counter + 1; + outcnt <= counter >> LOG2DELAY; + end + + assign {LED1, LED2, LED3, LED4, LED5} = outcnt ^ (outcnt >> 1); +endmodule diff --git a/yosys/examples/igloo2/example.ys b/yosys/examples/igloo2/example.ys new file mode 100644 index 000000000..872f97b99 --- /dev/null +++ b/yosys/examples/igloo2/example.ys @@ -0,0 +1,3 @@ +read_verilog example.v +synth_sf2 -top top -edif netlist.edn +write_verilog netlist.v diff --git a/yosys/examples/igloo2/libero.tcl b/yosys/examples/igloo2/libero.tcl new file mode 100644 index 000000000..9f6d3b792 --- /dev/null +++ b/yosys/examples/igloo2/libero.tcl @@ -0,0 +1,35 @@ +# Run with "libero SCRIPT:libero.tcl" + +new_project \ + -name top \ + -location work \ + -family IGLOO2 \ + -die PA4MGL500 \ + -package tq144 \ + -speed -1 \ + -hdl VERILOG + +# import_files -edif "[pwd]/netlist.edn" + +import_files -hdl_source "[pwd]/netlist.v" +set_root top + +save_project + +puts "**> SYNTHESIZE" +run_tool -name {SYNTHESIZE} +puts "<** SYNTHESIZE" + +puts "**> COMPILE" +run_tool -name {COMPILE} +puts "<** COMPILE" + +puts "**> PLACEROUTE" +run_tool -name {PLACEROUTE} +puts "<** PLACEROUTE" + +# puts "**> export_bitstream" +# export_bitstream_file -trusted_facility_file 1 -trusted_facility_file_components {FABRIC} +# puts "<** export_bitstream" + +exit 0 diff --git a/yosys/examples/igloo2/runme.sh b/yosys/examples/igloo2/runme.sh new file mode 100644 index 000000000..4edfb5409 --- /dev/null +++ b/yosys/examples/igloo2/runme.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ex +rm -rf work +yosys example.ys +LM_LICENSE_FILE=1702@`hostname` /opt/microsemi/Libero_SoC_v11.9/Libero/bin/libero SCRIPT:libero.tcl diff --git a/yosys/examples/intel/DE2i-150/quartus_compile/de2i.qpf b/yosys/examples/intel/DE2i-150/quartus_compile/de2i.qpf new file mode 100644 index 000000000..9fc734eb3 --- /dev/null +++ b/yosys/examples/intel/DE2i-150/quartus_compile/de2i.qpf @@ -0,0 +1,4 @@ +QUARTUS_VERSION = "16.1" +# Revisions + +PROJECT_REVISION = "de2i" diff --git a/yosys/examples/intel/DE2i-150/quartus_compile/de2i.qsf b/yosys/examples/intel/DE2i-150/quartus_compile/de2i.qsf new file mode 100644 index 000000000..5a230155f --- /dev/null +++ b/yosys/examples/intel/DE2i-150/quartus_compile/de2i.qsf @@ -0,0 +1,1099 @@ +set_global_assignment -name FAMILY "Cyclone IV GX" +set_global_assignment -name DEVICE EP4CGX150DF31C7 +set_global_assignment -name TOP_LEVEL_ENTITY "top" +set_global_assignment -name DEVICE_FILTER_PACKAGE FBGA + + +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CLOCK2_50 +set_instance_assignment -name IO_STANDARD "2.5 V" -to CLOCK3_50 +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CLOCK_50 + +#============================================================ +# DRAM +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_ADDR[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_BA[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_BA[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CAS_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CKE +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_CS_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[19] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[20] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[21] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[22] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[23] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[24] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[25] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[26] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[27] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[28] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[29] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[30] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQ[31] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_DQM[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_RAS_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to DRAM_WE_N + +#============================================================ +# EEP +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to EEP_I2C_SCLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to EEP_I2C_SDAT + +#============================================================ +# ENET +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_GTX_CLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_INT_N +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_LINK100 +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_MDC +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_MDIO +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RST_N +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_CLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_COL +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_CRS +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DATA[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_DV +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_RX_ER +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_CLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_DATA[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_EN +set_instance_assignment -name IO_STANDARD "2.5 V" -to ENET_TX_ER + +#============================================================ +# FAN +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to FAN_CTRL + +#============================================================ +# FLASH +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_RESET_N + +#============================================================ +# FS +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[19] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[20] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[21] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[22] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[23] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[24] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[25] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_ADDR[26] + +#============================================================ +# FL +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_CE_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_OE_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_RY +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_WE_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FL_WP_N + +#============================================================ +# FS +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[19] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[20] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[21] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[22] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[23] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[24] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[25] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[26] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[27] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[28] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[29] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[30] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to FS_DQ[31] + +#============================================================ +# GPIO +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[8] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[9] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[10] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[11] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[12] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[13] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[14] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[15] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[16] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[17] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[18] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[19] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[20] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[21] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[22] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[23] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[24] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[25] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[26] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[27] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[28] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[29] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[30] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[31] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[32] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[33] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[34] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO[35] + +#============================================================ +# G +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to G_SENSOR_INT1 +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to G_SENSOR_SCLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to G_SENSOR_SDAT + +#============================================================ +# HEX0 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX0[6] + +#============================================================ +# HEX1 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX1[6] + +#============================================================ +# HEX2 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX2[6] + +#============================================================ +# HEX3 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX3[6] + +#============================================================ +# HEX4 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX4[6] + +#============================================================ +# HEX5 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX5[6] + +#============================================================ +# HEX6 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX6[6] + +#============================================================ +# HEX7 +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HEX7[6] + +#============================================================ +# HSMC +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN0 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_N1 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_N2 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_P1 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKIN_P2 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT0 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_N1 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_N2 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_P1 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_CLKOUT_P2 +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_D[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_I2C_SCLK +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_I2C_SDAT +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_N[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_RX_D_P[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_N[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to HSMC_TX_D_P[16] + +#============================================================ +# I2C +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to I2C_SCLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to I2C_SDAT + +#============================================================ +# IRDA +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to IRDA_RXD + +#============================================================ +# KEY +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to KEY[3] + +#============================================================ +# LCD +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_DATA[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_EN +set_instance_assignment -name IO_STANDARD "2.5 V" -to LCD_ON +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_RS +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LCD_RW + +#============================================================ +# LEDG +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDG[8] + +#============================================================ +# LEDR +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to LEDR[17] + +#============================================================ +# PCIE +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_PERST_N +set_instance_assignment -name IO_STANDARD HCSL -to PCIE_REFCLK_P +set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_RX_P[0] +set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_RX_P[1] +set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_TX_P[0] +set_instance_assignment -name IO_STANDARD "1.5-V PCML" -to PCIE_TX_P[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to PCIE_WAKE_N + +#============================================================ +# SD +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_CLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_CMD +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_DAT[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SD_WP_N + +#============================================================ +# SMA +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SMA_CLKIN +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SMA_CLKOUT + +#============================================================ +# SSRAM0 +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM0_CE_N + +#============================================================ +# SSRAM1 +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM1_CE_N + +#============================================================ +# SSRAM +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_ADSC_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_ADSP_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_ADV_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_BE[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_CLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_GW_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_OE_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SSRAM_WE_N + +#============================================================ +# SW +#============================================================ +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[0] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[1] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[2] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[3] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[4] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[5] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[6] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[7] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[8] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[9] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[10] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[11] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[12] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[13] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[14] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[15] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[16] +set_instance_assignment -name IO_STANDARD "2.5 V" -to SW[17] + +#============================================================ +# TD +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_CLK27 +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_DATA[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_HS +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_RESET_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to TD_VS + +#============================================================ +# UART +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_CTS +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_RTS +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_RXD +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_TXD + +#============================================================ +# VGA +#============================================================ +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_B[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_BLANK_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_CLK +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_G[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_HS +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[0] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[1] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[2] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[3] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[4] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[5] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[6] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_R[7] +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_SYNC_N +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to VGA_VS + +#============================================================ +# End of pin assignments by Terasic System Builder +#============================================================ + + + +set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_location_assignment PIN_A15 -to CLOCK2_50 +set_location_assignment PIN_V11 -to CLOCK3_50 +set_location_assignment PIN_AJ16 -to CLOCK_50 +set_location_assignment PIN_AG7 -to DRAM_ADDR[0] +set_location_assignment PIN_AJ7 -to DRAM_ADDR[1] +set_location_assignment PIN_AG8 -to DRAM_ADDR[2] +set_location_assignment PIN_AH8 -to DRAM_ADDR[3] +set_location_assignment PIN_AE16 -to DRAM_ADDR[4] +set_location_assignment PIN_AF16 -to DRAM_ADDR[5] +set_location_assignment PIN_AE14 -to DRAM_ADDR[6] +set_location_assignment PIN_AE15 -to DRAM_ADDR[7] +set_location_assignment PIN_AE13 -to DRAM_ADDR[8] +set_location_assignment PIN_AE12 -to DRAM_ADDR[9] +set_location_assignment PIN_AH6 -to DRAM_ADDR[10] +set_location_assignment PIN_AE11 -to DRAM_ADDR[11] +set_location_assignment PIN_AE10 -to DRAM_ADDR[12] +set_location_assignment PIN_AH5 -to DRAM_BA[0] +set_location_assignment PIN_AG6 -to DRAM_BA[1] +set_location_assignment PIN_AJ4 -to DRAM_CAS_N +set_location_assignment PIN_AD6 -to DRAM_CKE +set_location_assignment PIN_AE6 -to DRAM_CLK +set_location_assignment PIN_AG5 -to DRAM_CS_N +set_location_assignment PIN_AD10 -to DRAM_DQ[0] +set_location_assignment PIN_AD9 -to DRAM_DQ[1] +set_location_assignment PIN_AE9 -to DRAM_DQ[2] +set_location_assignment PIN_AE8 -to DRAM_DQ[3] +set_location_assignment PIN_AE7 -to DRAM_DQ[4] +set_location_assignment PIN_AF7 -to DRAM_DQ[5] +set_location_assignment PIN_AF6 -to DRAM_DQ[6] +set_location_assignment PIN_AF9 -to DRAM_DQ[7] +set_location_assignment PIN_AB13 -to DRAM_DQ[8] +set_location_assignment PIN_AF13 -to DRAM_DQ[9] +set_location_assignment PIN_AF12 -to DRAM_DQ[10] +set_location_assignment PIN_AG9 -to DRAM_DQ[11] +set_location_assignment PIN_AA13 -to DRAM_DQ[12] +set_location_assignment PIN_AB11 -to DRAM_DQ[13] +set_location_assignment PIN_AA12 -to DRAM_DQ[14] +set_location_assignment PIN_AA15 -to DRAM_DQ[15] +set_location_assignment PIN_AH11 -to DRAM_DQ[16] +set_location_assignment PIN_AG11 -to DRAM_DQ[17] +set_location_assignment PIN_AH12 -to DRAM_DQ[18] +set_location_assignment PIN_AG12 -to DRAM_DQ[19] +set_location_assignment PIN_AH13 -to DRAM_DQ[20] +set_location_assignment PIN_AG13 -to DRAM_DQ[21] +set_location_assignment PIN_AG14 -to DRAM_DQ[22] +set_location_assignment PIN_AH14 -to DRAM_DQ[23] +set_location_assignment PIN_AH9 -to DRAM_DQ[24] +set_location_assignment PIN_AK8 -to DRAM_DQ[25] +set_location_assignment PIN_AG10 -to DRAM_DQ[26] +set_location_assignment PIN_AK7 -to DRAM_DQ[27] +set_location_assignment PIN_AH7 -to DRAM_DQ[28] +set_location_assignment PIN_AK6 -to DRAM_DQ[29] +set_location_assignment PIN_AJ6 -to DRAM_DQ[30] +set_location_assignment PIN_AK5 -to DRAM_DQ[31] +set_location_assignment PIN_AF10 -to DRAM_DQM[0] +set_location_assignment PIN_AB14 -to DRAM_DQM[1] +set_location_assignment PIN_AH15 -to DRAM_DQM[2] +set_location_assignment PIN_AH10 -to DRAM_DQM[3] +set_location_assignment PIN_AK4 -to DRAM_RAS_N +set_location_assignment PIN_AK3 -to DRAM_WE_N +set_location_assignment PIN_AG27 -to EEP_I2C_SCLK +set_location_assignment PIN_AG25 -to EEP_I2C_SDAT +set_location_assignment PIN_A12 -to ENET_GTX_CLK +set_location_assignment PIN_E16 -to ENET_INT_N +set_location_assignment PIN_F5 -to ENET_LINK100 +set_location_assignment PIN_C16 -to ENET_MDC +set_location_assignment PIN_C15 -to ENET_MDIO +set_location_assignment PIN_C14 -to ENET_RST_N +set_location_assignment PIN_L15 -to ENET_RX_CLK +set_location_assignment PIN_G15 -to ENET_RX_COL +set_location_assignment PIN_D6 -to ENET_RX_CRS +set_location_assignment PIN_F15 -to ENET_RX_DATA[0] +set_location_assignment PIN_E13 -to ENET_RX_DATA[1] +set_location_assignment PIN_A5 -to ENET_RX_DATA[2] +set_location_assignment PIN_B7 -to ENET_RX_DATA[3] +set_location_assignment PIN_A8 -to ENET_RX_DV +set_location_assignment PIN_D11 -to ENET_RX_ER +set_location_assignment PIN_F13 -to ENET_TX_CLK +set_location_assignment PIN_B12 -to ENET_TX_DATA[0] +set_location_assignment PIN_E7 -to ENET_TX_DATA[1] +set_location_assignment PIN_C13 -to ENET_TX_DATA[2] +set_location_assignment PIN_D15 -to ENET_TX_DATA[3] +set_location_assignment PIN_D14 -to ENET_TX_EN +set_location_assignment PIN_D13 -to ENET_TX_ER +set_location_assignment PIN_AF28 -to FAN_CTRL +set_location_assignment PIN_AG18 -to FL_RESET_N +set_location_assignment PIN_AB22 -to FS_ADDR[1] +set_location_assignment PIN_AH19 -to FS_ADDR[2] +set_location_assignment PIN_AK19 -to FS_ADDR[3] +set_location_assignment PIN_AJ18 -to FS_ADDR[4] +set_location_assignment PIN_AA18 -to FS_ADDR[5] +set_location_assignment PIN_AH18 -to FS_ADDR[6] +set_location_assignment PIN_AK17 -to FS_ADDR[7] +set_location_assignment PIN_Y20 -to FS_ADDR[8] +set_location_assignment PIN_AK21 -to FS_ADDR[9] +set_location_assignment PIN_AH21 -to FS_ADDR[10] +set_location_assignment PIN_AG21 -to FS_ADDR[11] +set_location_assignment PIN_AG22 -to FS_ADDR[12] +set_location_assignment PIN_AD22 -to FS_ADDR[13] +set_location_assignment PIN_AE24 -to FS_ADDR[14] +set_location_assignment PIN_AD23 -to FS_ADDR[15] +set_location_assignment PIN_AB21 -to FS_ADDR[16] +set_location_assignment PIN_AH17 -to FS_ADDR[17] +set_location_assignment PIN_AE17 -to FS_ADDR[18] +set_location_assignment PIN_AG20 -to FS_ADDR[19] +set_location_assignment PIN_AK20 -to FS_ADDR[20] +set_location_assignment PIN_AE19 -to FS_ADDR[21] +set_location_assignment PIN_AA16 -to FS_ADDR[22] +set_location_assignment PIN_AF15 -to FS_ADDR[23] +set_location_assignment PIN_AG15 -to FS_ADDR[24] +set_location_assignment PIN_Y17 -to FS_ADDR[25] +set_location_assignment PIN_AB16 -to FS_ADDR[26] +set_location_assignment PIN_AG19 -to FL_CE_N +set_location_assignment PIN_AJ19 -to FL_OE_N +set_location_assignment PIN_AF19 -to FL_RY +set_location_assignment PIN_AG17 -to FL_WE_N +set_location_assignment PIN_AK18 -to FL_WP_N +set_location_assignment PIN_AK29 -to FS_DQ[0] +set_location_assignment PIN_AE23 -to FS_DQ[1] +set_location_assignment PIN_AH24 -to FS_DQ[2] +set_location_assignment PIN_AH23 -to FS_DQ[3] +set_location_assignment PIN_AA21 -to FS_DQ[4] +set_location_assignment PIN_AE20 -to FS_DQ[5] +set_location_assignment PIN_Y19 -to FS_DQ[6] +set_location_assignment PIN_AA17 -to FS_DQ[7] +set_location_assignment PIN_AB17 -to FS_DQ[8] +set_location_assignment PIN_Y18 -to FS_DQ[9] +set_location_assignment PIN_AA20 -to FS_DQ[10] +set_location_assignment PIN_AE21 -to FS_DQ[11] +set_location_assignment PIN_AH22 -to FS_DQ[12] +set_location_assignment PIN_AJ24 -to FS_DQ[13] +set_location_assignment PIN_AE22 -to FS_DQ[14] +set_location_assignment PIN_AK28 -to FS_DQ[15] +set_location_assignment PIN_AK9 -to FS_DQ[16] +set_location_assignment PIN_AJ10 -to FS_DQ[17] +set_location_assignment PIN_AK11 -to FS_DQ[18] +set_location_assignment PIN_AK12 -to FS_DQ[19] +set_location_assignment PIN_AJ13 -to FS_DQ[20] +set_location_assignment PIN_AK15 -to FS_DQ[21] +set_location_assignment PIN_AC16 -to FS_DQ[22] +set_location_assignment PIN_AH16 -to FS_DQ[23] +set_location_assignment PIN_AG16 -to FS_DQ[24] +set_location_assignment PIN_AD16 -to FS_DQ[25] +set_location_assignment PIN_AJ15 -to FS_DQ[26] +set_location_assignment PIN_AK14 -to FS_DQ[27] +set_location_assignment PIN_AK13 -to FS_DQ[28] +set_location_assignment PIN_AJ12 -to FS_DQ[29] +set_location_assignment PIN_AK10 -to FS_DQ[30] +set_location_assignment PIN_AJ9 -to FS_DQ[31] +set_location_assignment PIN_G16 -to GPIO[0] +set_location_assignment PIN_F17 -to GPIO[1] +set_location_assignment PIN_D18 -to GPIO[2] +set_location_assignment PIN_F18 -to GPIO[3] +set_location_assignment PIN_D19 -to GPIO[4] +set_location_assignment PIN_K21 -to GPIO[5] +set_location_assignment PIN_F19 -to GPIO[6] +set_location_assignment PIN_K22 -to GPIO[7] +set_location_assignment PIN_B21 -to GPIO[8] +set_location_assignment PIN_C21 -to GPIO[9] +set_location_assignment PIN_D22 -to GPIO[10] +set_location_assignment PIN_D21 -to GPIO[11] +set_location_assignment PIN_D23 -to GPIO[12] +set_location_assignment PIN_D24 -to GPIO[13] +set_location_assignment PIN_B28 -to GPIO[14] +set_location_assignment PIN_C25 -to GPIO[15] +set_location_assignment PIN_C26 -to GPIO[16] +set_location_assignment PIN_D28 -to GPIO[17] +set_location_assignment PIN_D25 -to GPIO[18] +set_location_assignment PIN_F20 -to GPIO[19] +set_location_assignment PIN_E21 -to GPIO[20] +set_location_assignment PIN_F23 -to GPIO[21] +set_location_assignment PIN_G20 -to GPIO[22] +set_location_assignment PIN_F22 -to GPIO[23] +set_location_assignment PIN_G22 -to GPIO[24] +set_location_assignment PIN_G24 -to GPIO[25] +set_location_assignment PIN_G23 -to GPIO[26] +set_location_assignment PIN_A25 -to GPIO[27] +set_location_assignment PIN_A26 -to GPIO[28] +set_location_assignment PIN_A19 -to GPIO[29] +set_location_assignment PIN_A28 -to GPIO[30] +set_location_assignment PIN_A27 -to GPIO[31] +set_location_assignment PIN_B30 -to GPIO[32] +set_location_assignment PIN_AG28 -to GPIO[33] +set_location_assignment PIN_AG26 -to GPIO[34] +set_location_assignment PIN_Y21 -to GPIO[35] +set_location_assignment PIN_AC30 -to G_SENSOR_INT1 +set_location_assignment PIN_AK27 -to G_SENSOR_SCLK +set_location_assignment PIN_AK26 -to G_SENSOR_SDAT +set_location_assignment PIN_E15 -to HEX0[0] +set_location_assignment PIN_E12 -to HEX0[1] +set_location_assignment PIN_G11 -to HEX0[2] +set_location_assignment PIN_F11 -to HEX0[3] +set_location_assignment PIN_F16 -to HEX0[4] +set_location_assignment PIN_D16 -to HEX0[5] +set_location_assignment PIN_F14 -to HEX0[6] +set_location_assignment PIN_G14 -to HEX1[0] +set_location_assignment PIN_B13 -to HEX1[1] +set_location_assignment PIN_G13 -to HEX1[2] +set_location_assignment PIN_F12 -to HEX1[3] +set_location_assignment PIN_G12 -to HEX1[4] +set_location_assignment PIN_J9 -to HEX1[5] +set_location_assignment PIN_G10 -to HEX1[6] +set_location_assignment PIN_G8 -to HEX2[0] +set_location_assignment PIN_G7 -to HEX2[1] +set_location_assignment PIN_F7 -to HEX2[2] +set_location_assignment PIN_AG30 -to HEX2[3] +set_location_assignment PIN_F6 -to HEX2[4] +set_location_assignment PIN_F4 -to HEX2[5] +set_location_assignment PIN_F10 -to HEX2[6] +set_location_assignment PIN_D10 -to HEX3[0] +set_location_assignment PIN_D7 -to HEX3[1] +set_location_assignment PIN_E6 -to HEX3[2] +set_location_assignment PIN_E4 -to HEX3[3] +set_location_assignment PIN_E3 -to HEX3[4] +set_location_assignment PIN_D5 -to HEX3[5] +set_location_assignment PIN_D4 -to HEX3[6] +set_location_assignment PIN_A14 -to HEX4[0] +set_location_assignment PIN_A13 -to HEX4[1] +set_location_assignment PIN_C7 -to HEX4[2] +set_location_assignment PIN_C6 -to HEX4[3] +set_location_assignment PIN_C5 -to HEX4[4] +set_location_assignment PIN_C4 -to HEX4[5] +set_location_assignment PIN_C3 -to HEX4[6] +set_location_assignment PIN_D3 -to HEX5[0] +set_location_assignment PIN_A10 -to HEX5[1] +set_location_assignment PIN_A9 -to HEX5[2] +set_location_assignment PIN_A7 -to HEX5[3] +set_location_assignment PIN_A6 -to HEX5[4] +set_location_assignment PIN_A11 -to HEX5[5] +set_location_assignment PIN_B6 -to HEX5[6] +set_location_assignment PIN_B9 -to HEX6[0] +set_location_assignment PIN_B10 -to HEX6[1] +set_location_assignment PIN_C8 -to HEX6[2] +set_location_assignment PIN_C9 -to HEX6[3] +set_location_assignment PIN_D8 -to HEX6[4] +set_location_assignment PIN_D9 -to HEX6[5] +set_location_assignment PIN_E9 -to HEX6[6] +set_location_assignment PIN_E10 -to HEX7[0] +set_location_assignment PIN_F8 -to HEX7[1] +set_location_assignment PIN_F9 -to HEX7[2] +set_location_assignment PIN_C10 -to HEX7[3] +set_location_assignment PIN_C11 -to HEX7[4] +set_location_assignment PIN_C12 -to HEX7[5] +set_location_assignment PIN_D12 -to HEX7[6] +set_location_assignment PIN_K15 -to HSMC_CLKIN0 +set_location_assignment PIN_V30 -to HSMC_CLKIN_N1 +set_location_assignment PIN_T30 -to HSMC_CLKIN_N2 +set_location_assignment PIN_V29 -to HSMC_CLKIN_P1 +set_location_assignment PIN_T29 -to HSMC_CLKIN_P2 +set_location_assignment PIN_G6 -to HSMC_CLKOUT0 +set_location_assignment PIN_AB28 -to HSMC_CLKOUT_N1 +set_location_assignment PIN_Y28 -to HSMC_CLKOUT_N2 +set_location_assignment PIN_AB27 -to HSMC_CLKOUT_P1 +set_location_assignment PIN_AA28 -to HSMC_CLKOUT_P2 +set_location_assignment PIN_AC25 -to HSMC_D[0] +set_location_assignment PIN_E27 -to HSMC_D[1] +set_location_assignment PIN_AB26 -to HSMC_D[2] +set_location_assignment PIN_E28 -to HSMC_D[3] +set_location_assignment PIN_AD26 -to HSMC_I2C_SCLK +set_location_assignment PIN_AD25 -to HSMC_I2C_SDAT +set_location_assignment PIN_G27 -to HSMC_RX_D_N[0] +set_location_assignment PIN_G29 -to HSMC_RX_D_N[1] +set_location_assignment PIN_H27 -to HSMC_RX_D_N[2] +set_location_assignment PIN_K29 -to HSMC_RX_D_N[3] +set_location_assignment PIN_L28 -to HSMC_RX_D_N[4] +set_location_assignment PIN_M28 -to HSMC_RX_D_N[5] +set_location_assignment PIN_N30 -to HSMC_RX_D_N[6] +set_location_assignment PIN_P28 -to HSMC_RX_D_N[7] +set_location_assignment PIN_R28 -to HSMC_RX_D_N[8] +set_location_assignment PIN_U28 -to HSMC_RX_D_N[9] +set_location_assignment PIN_W28 -to HSMC_RX_D_N[10] +set_location_assignment PIN_W30 -to HSMC_RX_D_N[11] +set_location_assignment PIN_M30 -to HSMC_RX_D_N[12] +set_location_assignment PIN_Y27 -to HSMC_RX_D_N[13] +set_location_assignment PIN_AA29 -to HSMC_RX_D_N[14] +set_location_assignment PIN_AD28 -to HSMC_RX_D_N[15] +set_location_assignment PIN_AE28 -to HSMC_RX_D_N[16] +set_location_assignment PIN_G26 -to HSMC_RX_D_P[0] +set_location_assignment PIN_G28 -to HSMC_RX_D_P[1] +set_location_assignment PIN_J27 -to HSMC_RX_D_P[2] +set_location_assignment PIN_K28 -to HSMC_RX_D_P[3] +set_location_assignment PIN_L27 -to HSMC_RX_D_P[4] +set_location_assignment PIN_M27 -to HSMC_RX_D_P[5] +set_location_assignment PIN_N29 -to HSMC_RX_D_P[6] +set_location_assignment PIN_P27 -to HSMC_RX_D_P[7] +set_location_assignment PIN_R27 -to HSMC_RX_D_P[8] +set_location_assignment PIN_U27 -to HSMC_RX_D_P[9] +set_location_assignment PIN_W27 -to HSMC_RX_D_P[10] +set_location_assignment PIN_W29 -to HSMC_RX_D_P[11] +set_location_assignment PIN_M29 -to HSMC_RX_D_P[12] +set_location_assignment PIN_AA27 -to HSMC_RX_D_P[13] +set_location_assignment PIN_AB29 -to HSMC_RX_D_P[14] +set_location_assignment PIN_AD27 -to HSMC_RX_D_P[15] +set_location_assignment PIN_AE27 -to HSMC_RX_D_P[16] +set_location_assignment PIN_H28 -to HSMC_TX_D_N[0] +set_location_assignment PIN_F29 -to HSMC_TX_D_N[1] +set_location_assignment PIN_D30 -to HSMC_TX_D_N[2] +set_location_assignment PIN_E30 -to HSMC_TX_D_N[3] +set_location_assignment PIN_G30 -to HSMC_TX_D_N[4] +set_location_assignment PIN_J30 -to HSMC_TX_D_N[5] +set_location_assignment PIN_K27 -to HSMC_TX_D_N[6] +set_location_assignment PIN_K30 -to HSMC_TX_D_N[7] +set_location_assignment PIN_T25 -to HSMC_TX_D_N[8] +set_location_assignment PIN_N28 -to HSMC_TX_D_N[9] +set_location_assignment PIN_V26 -to HSMC_TX_D_N[10] +set_location_assignment PIN_Y30 -to HSMC_TX_D_N[11] +set_location_assignment PIN_AC28 -to HSMC_TX_D_N[12] +set_location_assignment PIN_AD30 -to HSMC_TX_D_N[13] +set_location_assignment PIN_AE30 -to HSMC_TX_D_N[14] +set_location_assignment PIN_AH30 -to HSMC_TX_D_N[15] +set_location_assignment PIN_AG29 -to HSMC_TX_D_N[16] +set_location_assignment PIN_J28 -to HSMC_TX_D_P[0] +set_location_assignment PIN_F28 -to HSMC_TX_D_P[1] +set_location_assignment PIN_D29 -to HSMC_TX_D_P[2] +set_location_assignment PIN_F30 -to HSMC_TX_D_P[3] +set_location_assignment PIN_H30 -to HSMC_TX_D_P[4] +set_location_assignment PIN_J29 -to HSMC_TX_D_P[5] +set_location_assignment PIN_K26 -to HSMC_TX_D_P[6] +set_location_assignment PIN_L30 -to HSMC_TX_D_P[7] +set_location_assignment PIN_U25 -to HSMC_TX_D_P[8] +set_location_assignment PIN_N27 -to HSMC_TX_D_P[9] +set_location_assignment PIN_V25 -to HSMC_TX_D_P[10] +set_location_assignment PIN_AA30 -to HSMC_TX_D_P[11] +set_location_assignment PIN_AC27 -to HSMC_TX_D_P[12] +set_location_assignment PIN_AD29 -to HSMC_TX_D_P[13] +set_location_assignment PIN_AE29 -to HSMC_TX_D_P[14] +set_location_assignment PIN_AJ30 -to HSMC_TX_D_P[15] +set_location_assignment PIN_AH29 -to HSMC_TX_D_P[16] +set_location_assignment PIN_C27 -to I2C_SCLK +set_location_assignment PIN_G21 -to I2C_SDAT +set_location_assignment PIN_AH28 -to IRDA_RXD +set_location_assignment PIN_AA26 -to KEY[0] +set_location_assignment PIN_AE25 -to KEY[1] +set_location_assignment PIN_AF30 -to KEY[2] +set_location_assignment PIN_AE26 -to KEY[3] +set_location_assignment PIN_AG4 -to LCD_DATA[0] +set_location_assignment PIN_AF3 -to LCD_DATA[1] +set_location_assignment PIN_AH3 -to LCD_DATA[2] +set_location_assignment PIN_AE5 -to LCD_DATA[3] +set_location_assignment PIN_AH2 -to LCD_DATA[4] +set_location_assignment PIN_AE3 -to LCD_DATA[5] +set_location_assignment PIN_AH4 -to LCD_DATA[6] +set_location_assignment PIN_AE4 -to LCD_DATA[7] +set_location_assignment PIN_AF4 -to LCD_EN +set_location_assignment PIN_AF27 -to LCD_ON +set_location_assignment PIN_AG3 -to LCD_RS +set_location_assignment PIN_AJ3 -to LCD_RW +set_location_assignment PIN_AA25 -to LEDG[0] +set_location_assignment PIN_AB25 -to LEDG[1] +set_location_assignment PIN_F27 -to LEDG[2] +set_location_assignment PIN_F26 -to LEDG[3] +set_location_assignment PIN_W26 -to LEDG[4] +set_location_assignment PIN_Y22 -to LEDG[5] +set_location_assignment PIN_Y25 -to LEDG[6] +set_location_assignment PIN_AA22 -to LEDG[7] +set_location_assignment PIN_J25 -to LEDG[8] +set_location_assignment PIN_T23 -to LEDR[0] +set_location_assignment PIN_T24 -to LEDR[1] +set_location_assignment PIN_V27 -to LEDR[2] +set_location_assignment PIN_W25 -to LEDR[3] +set_location_assignment PIN_T21 -to LEDR[4] +set_location_assignment PIN_T26 -to LEDR[5] +set_location_assignment PIN_R25 -to LEDR[6] +set_location_assignment PIN_T27 -to LEDR[7] +set_location_assignment PIN_P25 -to LEDR[8] +set_location_assignment PIN_R24 -to LEDR[9] +set_location_assignment PIN_P21 -to LEDR[10] +set_location_assignment PIN_N24 -to LEDR[11] +set_location_assignment PIN_N21 -to LEDR[12] +set_location_assignment PIN_M25 -to LEDR[13] +set_location_assignment PIN_K24 -to LEDR[14] +set_location_assignment PIN_L25 -to LEDR[15] +set_location_assignment PIN_M21 -to LEDR[16] +set_location_assignment PIN_M22 -to LEDR[17] +set_location_assignment PIN_A4 -to PCIE_PERST_N +set_location_assignment PIN_V15 -to PCIE_REFCLK_P +set_location_assignment PIN_AC2 -to PCIE_RX_P[0] +set_location_assignment PIN_AA2 -to PCIE_RX_P[1] +set_location_assignment PIN_AB4 -to PCIE_TX_P[0] +set_location_assignment PIN_Y4 -to PCIE_TX_P[1] +set_location_assignment PIN_C29 -to PCIE_WAKE_N +set_location_assignment PIN_AH25 -to SD_CLK +set_location_assignment PIN_AF18 -to SD_CMD +set_location_assignment PIN_AH27 -to SD_DAT[0] +set_location_assignment PIN_AJ28 -to SD_DAT[1] +set_location_assignment PIN_AD24 -to SD_DAT[2] +set_location_assignment PIN_AE18 -to SD_DAT[3] +set_location_assignment PIN_AJ27 -to SD_WP_N +set_location_assignment PIN_AK16 -to SMA_CLKIN +set_location_assignment PIN_AF25 -to SMA_CLKOUT +set_location_assignment PIN_AJ21 -to SSRAM0_CE_N +set_location_assignment PIN_AG23 -to SSRAM1_CE_N +set_location_assignment PIN_AK25 -to SSRAM_ADSC_N +set_location_assignment PIN_AJ25 -to SSRAM_ADSP_N +set_location_assignment PIN_AH26 -to SSRAM_ADV_N +set_location_assignment PIN_AF22 -to SSRAM_BE[0] +set_location_assignment PIN_AK22 -to SSRAM_BE[1] +set_location_assignment PIN_AJ22 -to SSRAM_BE[2] +set_location_assignment PIN_AF21 -to SSRAM_BE[3] +set_location_assignment PIN_AF24 -to SSRAM_CLK +set_location_assignment PIN_AK23 -to SSRAM_GW_N +set_location_assignment PIN_AG24 -to SSRAM_OE_N +set_location_assignment PIN_AK24 -to SSRAM_WE_N +set_location_assignment PIN_V28 -to SW[0] +set_location_assignment PIN_U30 -to SW[1] +set_location_assignment PIN_V21 -to SW[2] +set_location_assignment PIN_C2 -to SW[3] +set_location_assignment PIN_AB30 -to SW[4] +set_location_assignment PIN_U21 -to SW[5] +set_location_assignment PIN_T28 -to SW[6] +set_location_assignment PIN_R30 -to SW[7] +set_location_assignment PIN_P30 -to SW[8] +set_location_assignment PIN_R29 -to SW[9] +set_location_assignment PIN_R26 -to SW[10] +set_location_assignment PIN_N26 -to SW[11] +set_location_assignment PIN_M26 -to SW[12] +set_location_assignment PIN_N25 -to SW[13] +set_location_assignment PIN_J26 -to SW[14] +set_location_assignment PIN_K25 -to SW[15] +set_location_assignment PIN_C30 -to SW[16] +set_location_assignment PIN_H25 -to SW[17] +set_location_assignment PIN_B15 -to TD_CLK27 +set_location_assignment PIN_C17 -to TD_DATA[0] +set_location_assignment PIN_D17 -to TD_DATA[1] +set_location_assignment PIN_A16 -to TD_DATA[2] +set_location_assignment PIN_B16 -to TD_DATA[3] +set_location_assignment PIN_G18 -to TD_DATA[4] +set_location_assignment PIN_G17 -to TD_DATA[5] +set_location_assignment PIN_K18 -to TD_DATA[6] +set_location_assignment PIN_K17 -to TD_DATA[7] +set_location_assignment PIN_C28 -to TD_HS +set_location_assignment PIN_E25 -to TD_RESET_N +set_location_assignment PIN_E22 -to TD_VS +set_location_assignment PIN_D26 -to UART_CTS +set_location_assignment PIN_A29 -to UART_RTS +set_location_assignment PIN_B27 -to UART_RXD +set_location_assignment PIN_H24 -to UART_TXD +set_location_assignment PIN_E24 -to VGA_B[0] +set_location_assignment PIN_C24 -to VGA_B[1] +set_location_assignment PIN_B25 -to VGA_B[2] +set_location_assignment PIN_C23 -to VGA_B[3] +set_location_assignment PIN_F24 -to VGA_B[4] +set_location_assignment PIN_A23 -to VGA_B[5] +set_location_assignment PIN_G25 -to VGA_B[6] +set_location_assignment PIN_C22 -to VGA_B[7] +set_location_assignment PIN_F25 -to VGA_BLANK_N +set_location_assignment PIN_D27 -to VGA_CLK +set_location_assignment PIN_D20 -to VGA_G[0] +set_location_assignment PIN_C20 -to VGA_G[1] +set_location_assignment PIN_A20 -to VGA_G[2] +set_location_assignment PIN_K19 -to VGA_G[3] +set_location_assignment PIN_A21 -to VGA_G[4] +set_location_assignment PIN_F21 -to VGA_G[5] +set_location_assignment PIN_A22 -to VGA_G[6] +set_location_assignment PIN_B22 -to VGA_G[7] +set_location_assignment PIN_B24 -to VGA_HS +set_location_assignment PIN_A17 -to VGA_R[0] +set_location_assignment PIN_C18 -to VGA_R[1] +set_location_assignment PIN_B18 -to VGA_R[2] +set_location_assignment PIN_A18 -to VGA_R[3] +set_location_assignment PIN_E18 -to VGA_R[4] +set_location_assignment PIN_E19 -to VGA_R[5] +set_location_assignment PIN_B19 -to VGA_R[6] +set_location_assignment PIN_C19 -to VGA_R[7] +set_location_assignment PIN_AH20 -to VGA_SYNC_N +set_location_assignment PIN_A24 -to VGA_VS +set_instance_assignment -name VIRTUAL_PIN ON -to FS_ADDR[0] +#============================================================ +set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top +set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top +set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top +set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V" +set_global_assignment -name VQM_FILE ../top.vqm +set_global_assignment -name SDC_FILE de2i_150_golden_top.sdc +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 +set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW" +set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)" +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/yosys/examples/intel/DE2i-150/quartus_compile/runme_quartus b/yosys/examples/intel/DE2i-150/quartus_compile/runme_quartus new file mode 100644 index 000000000..83aa3b609 --- /dev/null +++ b/yosys/examples/intel/DE2i-150/quartus_compile/runme_quartus @@ -0,0 +1,7 @@ +#!/bin/bash + +export REV="de2i" + +quartus_map -c $REV top && \ + quartus_fit -c $REV top && \ + quartus_asm -c $REV top diff --git a/yosys/examples/intel/DE2i-150/run_cycloneiv b/yosys/examples/intel/DE2i-150/run_cycloneiv new file mode 100644 index 000000000..518807b57 --- /dev/null +++ b/yosys/examples/intel/DE2i-150/run_cycloneiv @@ -0,0 +1,2 @@ +#/bin/env bash +yosys -p "synth_intel -family cycloneiv -top top -vqm top.vqm" top.v sevenseg.v diff --git a/yosys/examples/intel/DE2i-150/sevenseg.v b/yosys/examples/intel/DE2i-150/sevenseg.v new file mode 100644 index 000000000..06cf7c146 --- /dev/null +++ b/yosys/examples/intel/DE2i-150/sevenseg.v @@ -0,0 +1,25 @@ +module sevenseg ( output reg [6:0] HEX0, + input [3:0] SW ); + + always @(*) begin + case(SW) + 4'h1: HEX0 = 7'b1111001; + 4'h2: HEX0 = 7'b0100100; + 4'h3: HEX0 = 7'b0110000; + 4'h4: HEX0 = 7'b0011001; + 4'h5: HEX0 = 7'b0010010; + 4'h6: HEX0 = 7'b0000010; + 4'h7: HEX0 = 7'b1111000; + 4'h8: HEX0 = 7'b0000000; + 4'h9: HEX0 = 7'b0011000; + 4'ha: HEX0 = 7'b0001000; + 4'hb: HEX0 = 7'b0000011; + 4'hc: HEX0 = 7'b1000110; + 4'hd: HEX0 = 7'b0100001; + 4'he: HEX0 = 7'b0000110; + 4'hf: HEX0 = 7'b0001110; + 4'h0: HEX0 = 7'b1000000; + endcase // case (SW) + end + +endmodule diff --git a/yosys/examples/intel/DE2i-150/top.v b/yosys/examples/intel/DE2i-150/top.v new file mode 100644 index 000000000..2bada0e21 --- /dev/null +++ b/yosys/examples/intel/DE2i-150/top.v @@ -0,0 +1,15 @@ +`default_nettype none +module top ( output wire [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7, + input wire [15:0] SW ); + + + sevenseg UUD0 (.HEX0(HEX0), .SW(4'h7)); + sevenseg UUD1 (.HEX0(HEX1), .SW(4'h1)); + sevenseg UUD2 (.HEX0(HEX2), .SW(4'h0)); + sevenseg UUD3 (.HEX0(HEX3), .SW(4'h2)); + sevenseg UUD4 (.HEX0(HEX4), .SW(SW[3:0])); + sevenseg UUD5 (.HEX0(HEX5), .SW(SW[7:4])); + sevenseg UUD6 (.HEX0(HEX6), .SW(SW[11:8])); + sevenseg UUD7 (.HEX0(HEX7), .SW(SW[15:12])); + +endmodule diff --git a/yosys/examples/intel/MAX10/run_max10 b/yosys/examples/intel/MAX10/run_max10 new file mode 100644 index 000000000..0378e4fa7 --- /dev/null +++ b/yosys/examples/intel/MAX10/run_max10 @@ -0,0 +1 @@ +yosys -p "synth_intel -family max10 -top top -vqm top.vqm" top.v sevenseg.v diff --git a/yosys/examples/intel/MAX10/runme_postsynth b/yosys/examples/intel/MAX10/runme_postsynth new file mode 100644 index 000000000..f16210540 --- /dev/null +++ b/yosys/examples/intel/MAX10/runme_postsynth @@ -0,0 +1,5 @@ +#!/bin/bash + +iverilog -D POST_IMPL -o verif_post -s tb_top tb_top.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v) +vvp -N verif_post + diff --git a/yosys/examples/intel/MAX10/sevenseg.v b/yosys/examples/intel/MAX10/sevenseg.v new file mode 100644 index 000000000..06cf7c146 --- /dev/null +++ b/yosys/examples/intel/MAX10/sevenseg.v @@ -0,0 +1,25 @@ +module sevenseg ( output reg [6:0] HEX0, + input [3:0] SW ); + + always @(*) begin + case(SW) + 4'h1: HEX0 = 7'b1111001; + 4'h2: HEX0 = 7'b0100100; + 4'h3: HEX0 = 7'b0110000; + 4'h4: HEX0 = 7'b0011001; + 4'h5: HEX0 = 7'b0010010; + 4'h6: HEX0 = 7'b0000010; + 4'h7: HEX0 = 7'b1111000; + 4'h8: HEX0 = 7'b0000000; + 4'h9: HEX0 = 7'b0011000; + 4'ha: HEX0 = 7'b0001000; + 4'hb: HEX0 = 7'b0000011; + 4'hc: HEX0 = 7'b1000110; + 4'hd: HEX0 = 7'b0100001; + 4'he: HEX0 = 7'b0000110; + 4'hf: HEX0 = 7'b0001110; + 4'h0: HEX0 = 7'b1000000; + endcase // case (SW) + end + +endmodule diff --git a/yosys/examples/intel/MAX10/top.v b/yosys/examples/intel/MAX10/top.v new file mode 100644 index 000000000..2bada0e21 --- /dev/null +++ b/yosys/examples/intel/MAX10/top.v @@ -0,0 +1,15 @@ +`default_nettype none +module top ( output wire [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7, + input wire [15:0] SW ); + + + sevenseg UUD0 (.HEX0(HEX0), .SW(4'h7)); + sevenseg UUD1 (.HEX0(HEX1), .SW(4'h1)); + sevenseg UUD2 (.HEX0(HEX2), .SW(4'h0)); + sevenseg UUD3 (.HEX0(HEX3), .SW(4'h2)); + sevenseg UUD4 (.HEX0(HEX4), .SW(SW[3:0])); + sevenseg UUD5 (.HEX0(HEX5), .SW(SW[7:4])); + sevenseg UUD6 (.HEX0(HEX6), .SW(SW[11:8])); + sevenseg UUD7 (.HEX0(HEX7), .SW(SW[15:12])); + +endmodule diff --git a/yosys/examples/intel/asicworld_lfsr/README b/yosys/examples/intel/asicworld_lfsr/README new file mode 100644 index 000000000..ba365fabf --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/README @@ -0,0 +1,6 @@ +Source of the files: +http://www.asic-world.com/examples/verilog/lfsr.html + +Run first: runme_presynth +Generate output netlist with run_max10 or run_cycloneiv +Then, check with: runme_postsynth diff --git a/yosys/examples/intel/asicworld_lfsr/lfsr_updown.v b/yosys/examples/intel/asicworld_lfsr/lfsr_updown.v new file mode 100644 index 000000000..43db1606a --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/lfsr_updown.v @@ -0,0 +1,35 @@ +`default_nettype none +module lfsr_updown ( +clk , // Clock input +reset , // Reset input +enable , // Enable input +up_down , // Up Down input +count , // Count output +overflow // Overflow output +); + + input clk; + input reset; + input enable; + input up_down; + + output [7 : 0] count; + output overflow; + + reg [7 : 0] count; + + assign overflow = (up_down) ? (count == {{7{1'b0}}, 1'b1}) : + (count == {1'b1, {7{1'b0}}}) ; + + always @(posedge clk) + if (reset) + count <= {7{1'b0}}; + else if (enable) begin + if (up_down) begin + count <= {~(^(count & 8'b01100011)),count[7:1]}; + end else begin + count <= {count[5:0],~(^(count & 8'b10110001))}; + end + end + +endmodule diff --git a/yosys/examples/intel/asicworld_lfsr/lfsr_updown_tb.v b/yosys/examples/intel/asicworld_lfsr/lfsr_updown_tb.v new file mode 100644 index 000000000..db29e60f1 --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/lfsr_updown_tb.v @@ -0,0 +1,34 @@ +module tb(); + reg clk; + reg reset; + reg enable; + reg up_down; + + wire [7 : 0] count; + wire overflow; + +initial begin + $monitor("rst %b en %b updown %b cnt %b overflow %b", + reset,enable,up_down,count, overflow); + clk = 0; + reset = 1; + enable = 0; + up_down = 0; + #10 reset = 0; + #1 enable = 1; + #20 up_down = 1; + #30 $finish; +end + +always #1 clk = ~clk; + +lfsr_updown U( +.clk ( clk ), +.reset ( reset ), +.enable ( enable ), +.up_down ( up_down ), +.count ( count ), +.overflow ( overflow ) +); + +endmodule diff --git a/yosys/examples/intel/asicworld_lfsr/run_cycloneiv b/yosys/examples/intel/asicworld_lfsr/run_cycloneiv new file mode 100755 index 000000000..c7498bded --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/run_cycloneiv @@ -0,0 +1,2 @@ +#!/bin/env bash +yosys -p "synth_intel -family cycloneiv -top lfsr_updown -vqm top.vqm" lfsr_updown.v diff --git a/yosys/examples/intel/asicworld_lfsr/run_max10 b/yosys/examples/intel/asicworld_lfsr/run_max10 new file mode 100755 index 000000000..b75d552bb --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/run_max10 @@ -0,0 +1,2 @@ +#!/bin/env bash +yosys -p "synth_intel -family max10 -top lfsr_updown -vqm top.vqm" lfsr_updown.v diff --git a/yosys/examples/intel/asicworld_lfsr/runme_postsynth b/yosys/examples/intel/asicworld_lfsr/runme_postsynth new file mode 100755 index 000000000..c3b26b034 --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/runme_postsynth @@ -0,0 +1,5 @@ +#!/bin/bash + +iverilog -D POST_IMPL -o verif_post -s tb lfsr_updown_tb.v top.vqm $(yosys-config --datdir/altera_intel/max10/cells_comb_max10.v) +vvp -N verif_post + diff --git a/yosys/examples/intel/asicworld_lfsr/runme_presynth b/yosys/examples/intel/asicworld_lfsr/runme_presynth new file mode 100755 index 000000000..51118bb4b --- /dev/null +++ b/yosys/examples/intel/asicworld_lfsr/runme_presynth @@ -0,0 +1,5 @@ +#!/bin/bash + +iverilog -o presynth lfsr_updown_tb.v lfsr_updown.v &&\ + +vvp -N presynth \ No newline at end of file diff --git a/yosys/examples/osu035/.gitignore b/yosys/examples/osu035/.gitignore new file mode 100644 index 000000000..3abf340bb --- /dev/null +++ b/yosys/examples/osu035/.gitignore @@ -0,0 +1,3 @@ +osu035_stdcells.lib +example.yslog +example.edif diff --git a/yosys/examples/osu035/Makefile b/yosys/examples/osu035/Makefile new file mode 100644 index 000000000..2bb8162b3 --- /dev/null +++ b/yosys/examples/osu035/Makefile @@ -0,0 +1,13 @@ + +example.edif: example.ys example.v example.constr osu035_stdcells.lib + yosys -l example.yslog -q example.ys + +osu035_stdcells.lib: + rm -f osu035_stdcells.lib.part osu035_stdcells.lib + wget -O osu035_stdcells.lib.part https://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/ami035/signalstorm/osu035_stdcells.lib + mv osu035_stdcells.lib.part osu035_stdcells.lib + +clean: + rm -f osu035_stdcells.lib + rm -f example.yslog example.edif + diff --git a/yosys/examples/osu035/example.constr b/yosys/examples/osu035/example.constr new file mode 100644 index 000000000..eb2c6e8d5 --- /dev/null +++ b/yosys/examples/osu035/example.constr @@ -0,0 +1,2 @@ +set_driving_cell INVX1 +set_load 0.015 diff --git a/yosys/examples/osu035/example.v b/yosys/examples/osu035/example.v new file mode 100644 index 000000000..0f043e5fc --- /dev/null +++ b/yosys/examples/osu035/example.v @@ -0,0 +1,3 @@ +module top (input clk, input [7:0] a, b, output reg [15:0] c); + always @(posedge clk) c <= a * b; +endmodule diff --git a/yosys/examples/osu035/example.ys b/yosys/examples/osu035/example.ys new file mode 100644 index 000000000..6821ef426 --- /dev/null +++ b/yosys/examples/osu035/example.ys @@ -0,0 +1,11 @@ +read_verilog example.v +read_liberty -lib osu035_stdcells.lib + +synth -top top + +dfflibmap -liberty osu035_stdcells.lib +abc -D 10000 -constr example.constr -liberty osu035_stdcells.lib +opt_clean + +stat -liberty osu035_stdcells.lib +write_edif example.edif diff --git a/yosys/examples/smtbmc/.gitignore b/yosys/examples/smtbmc/.gitignore index a3f4f0f24..278f5ebf7 100644 --- a/yosys/examples/smtbmc/.gitignore +++ b/yosys/examples/smtbmc/.gitignore @@ -20,3 +20,5 @@ demo6.smt2 demo6.yslog demo7.smt2 demo7.yslog +demo8.smt2 +demo8.yslog diff --git a/yosys/examples/smtbmc/Makefile b/yosys/examples/smtbmc/Makefile index 2f7060bda..96fa058d6 100644 --- a/yosys/examples/smtbmc/Makefile +++ b/yosys/examples/smtbmc/Makefile @@ -1,5 +1,5 @@ -all: demo1 demo2 demo3 demo4 demo5 demo6 demo7 +all: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 demo1: demo1.smt2 yosys-smtbmc --dump-vcd demo1.vcd demo1.smt2 @@ -25,6 +25,9 @@ demo6: demo6.smt2 demo7: demo7.smt2 yosys-smtbmc -t 10 demo7.smt2 +demo8: demo8.smt2 + yosys-smtbmc -s z3 -t 1 -g demo8.smt2 + demo1.smt2: demo1.v yosys -ql demo1.yslog -p 'read_verilog -formal demo1.v; prep -top demo1 -nordff; write_smt2 -wires demo1.smt2' @@ -46,6 +49,9 @@ demo6.smt2: demo6.v demo7.smt2: demo7.v yosys -ql demo7.yslog -p 'read_verilog -formal demo7.v; prep -top demo7 -nordff; write_smt2 -wires demo7.smt2' +demo8.smt2: demo8.v + yosys -ql demo8.yslog -p 'read_verilog -formal demo8.v; prep -top demo8 -nordff; write_smt2 -stbv -wires demo8.smt2' + clean: rm -f demo1.yslog demo1.smt2 demo1.vcd rm -f demo2.yslog demo2.smt2 demo2.vcd demo2.smtc demo2_tb.v demo2_tb demo2_tb.vcd @@ -54,6 +60,7 @@ clean: rm -f demo5.yslog demo5.smt2 demo5.vcd rm -f demo6.yslog demo6.smt2 rm -f demo7.yslog demo7.smt2 + rm -f demo8.yslog demo8.smt2 -.PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 clean +.PHONY: demo1 demo2 demo3 demo4 demo5 demo6 demo7 demo8 clean diff --git a/yosys/examples/smtbmc/demo2.v b/yosys/examples/smtbmc/demo2.v index 34745e898..0cf529a42 100644 --- a/yosys/examples/smtbmc/demo2.v +++ b/yosys/examples/smtbmc/demo2.v @@ -9,7 +9,7 @@ module demo2(input clk, input [4:0] addr, output reg [31:0] data); reg [31:0] mem [0:31]; - always @(posedge clk) + always @(negedge clk) data <= mem[addr]; reg [31:0] used_addr = 0; diff --git a/yosys/examples/smtbmc/demo8.v b/yosys/examples/smtbmc/demo8.v new file mode 100644 index 000000000..c4c396cde --- /dev/null +++ b/yosys/examples/smtbmc/demo8.v @@ -0,0 +1,12 @@ +// Simple exists-forall demo + +module demo8; + wire [7:0] prime = $anyconst; + wire [3:0] factor = $allconst; + + always @* begin + if (1 < factor && factor < prime) + assume((prime % factor) != 0); + assume(prime > 1); + end +endmodule diff --git a/yosys/frontends/ast/ast.cc b/yosys/frontends/ast/ast.cc index 92513a244..5a1bae7a7 100644 --- a/yosys/frontends/ast/ast.cc +++ b/yosys/frontends/ast/ast.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 Ruben Undheim * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,16 +36,16 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; -// instanciate global variables (public API) +// instantiate global variables (public API) namespace AST { std::string current_filename; void (*set_line_num)(int) = NULL; int (*get_line_num)() = NULL; } -// instanciate global variables (private API) +// instantiate global variables (private API) namespace AST_INTERNAL { - bool flag_dump_ast1, flag_dump_ast2, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit; bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; AstNode *current_ast, *current_ast_mod; std::map current_scope; @@ -84,6 +85,9 @@ std::string AST::type2str(AstNodeType type) X(AST_PREFIX) X(AST_ASSERT) X(AST_ASSUME) + X(AST_LIVE) + X(AST_FAIR) + X(AST_COVER) X(AST_FCALL) X(AST_TO_BITS) X(AST_TO_SIGNED) @@ -168,8 +172,7 @@ bool AstNode::get_bool_attribute(RTLIL::IdString id) AstNode *attr = attributes.at(id); if (attr->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - id.c_str(), attr->filename.c_str(), attr->linenum); + log_file_error(attr->filename, attr->linenum, "Attribute `%s' with non-constant value!\n", id.c_str()); return attr->integer != 0; } @@ -188,8 +191,10 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch is_input = false; is_output = false; is_reg = false; + is_logic = false; is_signed = false; is_string = false; + was_checked = false; range_valid = false; range_swapped = false; port_id = 0; @@ -209,7 +214,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch } // create a (deep recursive) copy of a node -AstNode *AstNode::clone() +AstNode *AstNode::clone() const { AstNode *that = new AstNode; *that = *this; @@ -221,7 +226,7 @@ AstNode *AstNode::clone() } // create a (deep recursive) copy of a node use 'other' as target root node -void AstNode::cloneInto(AstNode *other) +void AstNode::cloneInto(AstNode *other) const { AstNode *tmp = clone(); other->delete_children(); @@ -251,7 +256,7 @@ AstNode::~AstNode() // create a nice text representation of the node // (traverse tree by recursion, use 'other' pointer for diffing two AST trees) -void AstNode::dumpAst(FILE *f, std::string indent) +void AstNode::dumpAst(FILE *f, std::string indent) const { if (f == NULL) { for (auto f : log_files) @@ -262,10 +267,12 @@ void AstNode::dumpAst(FILE *f, std::string indent) std::string type_name = type2str(type); fprintf(f, "%s%s <%s:%d>", indent.c_str(), type_name.c_str(), filename.c_str(), linenum); - if (id2ast) - fprintf(f, " [%p -> %p]", this, id2ast); - else - fprintf(f, " [%p]", this); + if (!flag_no_dump_ptr) { + if (id2ast) + fprintf(f, " [%p -> %p]", this, id2ast); + else + fprintf(f, " [%p]", this); + } if (!str.empty()) fprintf(f, " str='%s'", str.c_str()); @@ -282,7 +289,9 @@ void AstNode::dumpAst(FILE *f, std::string indent) fprintf(f, " input"); if (is_output) fprintf(f, " output"); - if (is_reg) + if (is_logic) + fprintf(f, " logic"); + if (is_reg) // this is an AST dump, not Verilog - if we see "logic reg" that's fine. fprintf(f, " reg"); if (is_signed) fprintf(f, " signed"); @@ -330,7 +339,7 @@ static std::string id2vl(std::string txt) } // dump AST node as Verilog pseudo-code -void AstNode::dumpVlog(FILE *f, std::string indent) +void AstNode::dumpVlog(FILE *f, std::string indent) const { bool first = true; std::string txt; @@ -649,6 +658,8 @@ bool AstNode::operator==(const AstNode &other) const return false; if (is_output != other.is_output) return false; + if (is_logic != other.is_logic) + return false; if (is_reg != other.is_reg) return false; if (is_signed != other.is_signed) @@ -752,7 +763,7 @@ AstNode *AstNode::mkconst_str(const std::string &str) return node; } -bool AstNode::bits_only_01() +bool AstNode::bits_only_01() const { for (auto bit : bits) if (bit != RTLIL::S0 && bit != RTLIL::S1) @@ -803,7 +814,7 @@ RTLIL::Const AstNode::asParaConst() return val; } -bool AstNode::asBool() +bool AstNode::asBool() const { log_assert(type == AST_CONSTANT); for (auto &bit : bits) @@ -812,7 +823,7 @@ bool AstNode::asBool() return false; } -int AstNode::isConst() +int AstNode::isConst() const { if (type == AST_CONSTANT) return 1; @@ -892,9 +903,9 @@ RTLIL::Const AstNode::realAsConst(int width) } // create a new AstModule from an AST_MODULE AST node -static AstModule* process_module(AstNode *ast, bool defer) +static AstModule* process_module(AstNode *ast, bool defer, AstNode *original_ast = NULL) { - log_assert(ast->type == AST_MODULE); + log_assert(ast->type == AST_MODULE || ast->type == AST_INTERFACE); if (defer) log("Storing AST representation for module `%s'.\n", ast->str.c_str()); @@ -905,9 +916,14 @@ static AstModule* process_module(AstNode *ast, bool defer) current_module->ast = NULL; current_module->name = ast->str; current_module->attributes["\\src"] = stringf("%s:%d", ast->filename.c_str(), ast->linenum); + current_module->set_bool_attribute("\\cells_not_processed"); current_ast_mod = ast; - AstNode *ast_before_simplify = ast->clone(); + AstNode *ast_before_simplify; + if (original_ast != NULL) + ast_before_simplify = original_ast; + else + ast_before_simplify = ast->clone(); if (flag_dump_ast1) { log("Dumping Verilog AST before simplification:\n"); @@ -952,8 +968,7 @@ static AstModule* process_module(AstNode *ast, bool defer) for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), ast->filename.c_str(), ast->linenum); + log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); current_module->attributes[attr.first] = attr.second->asAttrConst(); } for (size_t i = 0; i < ast->children.size(); i++) { @@ -978,6 +993,8 @@ static AstModule* process_module(AstNode *ast, bool defer) ignoreThisSignalsInInitial = RTLIL::SigSpec(); } + if (ast->type == AST_INTERFACE) + current_module->set_bool_attribute("\\is_interface"); current_module->ast = ast_before_simplify; current_module->nolatches = flag_nolatches; current_module->nomeminit = flag_nomeminit; @@ -999,12 +1016,13 @@ static AstModule* process_module(AstNode *ast, bool defer) } // create AstModule instances for all modules in the AST tree and add them to 'design' -void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil, - bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire) +void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil, + bool nolatches, bool nomeminit, bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire) { current_ast = ast; flag_dump_ast1 = dump_ast1; flag_dump_ast2 = dump_ast2; + flag_no_dump_ptr = no_dump_ptr; flag_dump_vlog = dump_vlog; flag_dump_rtlil = dump_rtlil; flag_nolatches = nolatches; @@ -1016,14 +1034,12 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump flag_icells = icells; flag_autowire = autowire; - std::vector global_decls; - log_assert(current_ast->type == AST_DESIGN); for (auto it = current_ast->children.begin(); it != current_ast->children.end(); it++) { - if ((*it)->type == AST_MODULE) + if ((*it)->type == AST_MODULE || (*it)->type == AST_INTERFACE) { - for (auto n : global_decls) + for (auto n : design->verilog_globals) (*it)->children.push_back(n->clone()); for (auto n : design->verilog_packages){ @@ -1041,12 +1057,19 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump (*it)->str = "$abstract" + (*it)->str; if (design->has((*it)->str)) { - if (!ignore_redef) - log_error("Re-definition of module `%s' at %s:%d!\n", + RTLIL::Module *existing_mod = design->module((*it)->str); + if (!nooverwrite && !overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { + log_file_error((*it)->filename, (*it)->linenum, "Re-definition of module `%s'!\n", (*it)->str.c_str()); + } else if (nooverwrite) { + log("Ignoring re-definition of module `%s' at %s:%d.\n", (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); - log("Ignoring re-definition of module `%s' at %s:%d!\n", - (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); - continue; + continue; + } else { + log("Replacing existing%s module `%s' at %s:%d.\n", + existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", + (*it)->str.c_str(), (*it)->filename.c_str(), (*it)->linenum); + design->remove(existing_mod); + } } design->add(process_module(*it, defer)); @@ -1054,7 +1077,7 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump else if ((*it)->type == AST_PACKAGE) design->verilog_packages.push_back((*it)->clone()); else - global_decls.push_back(*it); + design->verilog_globals.push_back((*it)->clone()); } } @@ -1065,8 +1088,264 @@ AstModule::~AstModule() delete ast; } + +// An interface port with modport is specified like this: +// . +// This function splits the interface_name from the modport_name, and fails if it is not a valid combination +std::pair AST::split_modport_from_type(std::string name_type) +{ + std::string interface_type = ""; + std::string interface_modport = ""; + size_t ndots = std::count(name_type.begin(), name_type.end(), '.'); + // Separate the interface instance name from any modports: + if (ndots == 0) { // Does not have modport + interface_type = name_type; + } + else { + std::stringstream name_type_stream(name_type); + std::string segment; + std::vector seglist; + while(std::getline(name_type_stream, segment, '.')) { + seglist.push_back(segment); + } + if (ndots == 1) { // Has modport + interface_type = seglist[0]; + interface_modport = seglist[1]; + } + else { // Erroneous port type + log_error("More than two '.' in signal port type (%s)\n", name_type.c_str()); + } + } + return std::pair(interface_type, interface_modport); + +} + +AstNode * AST::find_modport(AstNode *intf, std::string name) +{ + for (auto &ch : intf->children) + if (ch->type == AST_MODPORT) + if (ch->str == name) // Modport found + return ch; + return NULL; +} + +// Iterate over all wires in an interface and add them as wires in the AST module: +void AST::explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport) +{ + for (auto &wire_it : intfmodule->wires_){ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); + std::string origname = log_id(wire_it.first); + std::string newname = intfname + "." + origname; + wire->str = newname; + if (modport != NULL) { + bool found_in_modport = false; + // Search for the current wire in the wire list for the current modport + for (auto &ch : modport->children) { + if (ch->type == AST_MODPORTMEMBER) { + std::string compare_name = "\\" + origname; + if (ch->str == compare_name) { // Found signal. The modport decides whether it is input or output + found_in_modport = true; + wire->is_input = ch->is_input; + wire->is_output = ch->is_output; + break; + } + } + } + if (found_in_modport) { + module_ast->children.push_back(wire); + } + else { // If not found in modport, do not create port + delete wire; + } + } + else { // If no modport, set inout + wire->is_input = true; + wire->is_output = true; + module_ast->children.push_back(wire); + } + } +} + +// When an interface instance is found in a module, the whole RTLIL for the module will be rederived again +// from AST. The interface members are copied into the AST module with the prefix of the interface. +void AstModule::reprocess_module(RTLIL::Design *design, dict local_interfaces) +{ + bool is_top = false; + AstNode *new_ast = ast->clone(); + for (auto &intf : local_interfaces) { + std::string intfname = intf.first.str(); + RTLIL::Module *intfmodule = intf.second; + for (auto &wire_it : intfmodule->wires_){ + AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, AstNode::mkconst_int(wire_it.second->width -1, true), AstNode::mkconst_int(0, true))); + std::string newname = log_id(wire_it.first); + newname = intfname + "." + newname; + wire->str = newname; + new_ast->children.push_back(wire); + } + } + + AstNode *ast_before_replacing_interface_ports = new_ast->clone(); + + // Explode all interface ports. Note this will only have an effect on 'top + // level' modules. Other sub-modules will have their interface ports + // exploded via the derive(..) function + for (size_t i =0; ichildren.size(); i++) + { + AstNode *ch2 = new_ast->children[i]; + if (ch2->type == AST_INTERFACEPORT) { // Is an interface port + std::string name_port = ch2->str; // Name of the interface port + if (ch2->children.size() > 0) { + for(size_t j=0; jchildren.size();j++) { + AstNode *ch = ch2->children[j]; + if(ch->type == AST_INTERFACEPORTTYPE) { // Found the AST node containing the type of the interface + std::pair res = split_modport_from_type(ch->str); + std::string interface_type = res.first; + std::string interface_modport = res.second; // Is "", if no modport + if (design->modules_.count(interface_type) > 0) { + // Add a cell to the module corresponding to the interface port such that + // it can further propagated down if needed: + AstNode *celltype_for_intf = new AstNode(AST_CELLTYPE); + celltype_for_intf->str = interface_type; + AstNode *cell_for_intf = new AstNode(AST_CELL, celltype_for_intf); + cell_for_intf->str = name_port + "_inst_from_top_dummy"; + new_ast->children.push_back(cell_for_intf); + + // Get all members of this non-overridden dummy interface instance: + RTLIL::Module *intfmodule = design->modules_[interface_type]; // All interfaces should at this point in time (assuming + // reprocess_module is called from the hierarchy pass) be + // present in design->modules_ + AstModule *ast_module_of_interface = (AstModule*)intfmodule; + std::string interface_modport_compare_str = "\\" + interface_modport; + AstNode *modport = find_modport(ast_module_of_interface->ast, interface_modport_compare_str); // modport == NULL if no modport + // Iterate over all wires in the interface and add them to the module: + explode_interface_port(new_ast, intfmodule, name_port, modport); + } + break; + } + } + } + } + } + + // The old module will be deleted. Rename and mark for deletion: + std::string original_name = this->name.str(); + std::string changed_name = original_name + "_before_replacing_local_interfaces"; + design->rename(this, changed_name); + this->set_bool_attribute("\\to_delete"); + + // Check if the module was the top module. If it was, we need to remove the top attribute and put it on the + // new module. + if (this->get_bool_attribute("\\initial_top")) { + this->attributes.erase("\\initial_top"); + is_top = true; + } + + // Generate RTLIL from AST for the new module and add to the design: + AstModule *newmod = process_module(new_ast, false, ast_before_replacing_interface_ports); + delete(new_ast); + design->add(newmod); + RTLIL::Module* mod = design->module(original_name); + if (is_top) + mod->set_bool_attribute("\\top"); + + // Set the attribute "interfaces_replaced_in_module" so that it does not happen again. + mod->set_bool_attribute("\\interfaces_replaced_in_module"); +} + +// create a new parametric module (when needed) and return the name of the generated module - WITH support for interfaces +// This method is used to explode the interface when the interface is a port of the module (not instantiated inside) +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict parameters, dict interfaces, dict modports, bool mayfail) +{ + AstNode *new_ast = NULL; + std::string modname = derive_common(design, parameters, &new_ast, mayfail); + + // Since interfaces themselves may be instantiated with different parameters, + // "modname" must also take those into account, so that unique modules + // are derived for any variant of interface connections: + std::string interf_info = ""; + + bool has_interfaces = false; + for(auto &intf : interfaces) { + interf_info += log_id(intf.second->name); + has_interfaces = true; + } + + if (has_interfaces) + modname += "$interfaces$" + interf_info; + + + if (!design->has(modname)) { + new_ast->str = modname; + + // Iterate over all interfaces which are ports in this module: + for(auto &intf : interfaces) { + RTLIL::Module * intfmodule = intf.second; + std::string intfname = intf.first.str(); + // Check if a modport applies for the interface port: + AstNode *modport = NULL; + if (modports.count(intfname) > 0) { + std::string interface_modport = modports.at(intfname).str(); + AstModule *ast_module_of_interface = (AstModule*)intfmodule; + AstNode *ast_node_of_interface = ast_module_of_interface->ast; + modport = find_modport(ast_node_of_interface, interface_modport); + } + // Iterate over all wires in the interface and add them to the module: + explode_interface_port(new_ast, intfmodule, intfname, modport); + } + + design->add(process_module(new_ast, false)); + design->module(modname)->check(); + + RTLIL::Module* mod = design->module(modname); + + // Now that the interfaces have been exploded, we can delete the dummy port related to every interface. + for(auto &intf : interfaces) { + if(mod->wires_.count(intf.first)) { + mod->wires_.erase(intf.first); + mod->fixup_ports(); + // We copy the cell of the interface to the sub-module such that it can further be found if it is propagated + // down to sub-sub-modules etc. + RTLIL::Cell * new_subcell = mod->addCell(intf.first, intf.second->name); + new_subcell->set_bool_attribute("\\is_interface"); + } + else { + log_error("No port with matching name found (%s) in %s. Stopping\n", log_id(intf.first), modname.c_str()); + } + } + + // If any interfaces were replaced, set the attribute 'interfaces_replaced_in_module': + if (interfaces.size() > 0) { + mod->set_bool_attribute("\\interfaces_replaced_in_module"); + } + + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + +// create a new parametric module (when needed) and return the name of the generated module - without support for interfaces +RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict parameters, bool mayfail) +{ + AstNode *new_ast = NULL; + std::string modname = derive_common(design, parameters, &new_ast, mayfail); + + if (!design->has(modname)) { + new_ast->str = modname; + design->add(process_module(new_ast, false)); + design->module(modname)->check(); + } else { + log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); + } + + delete new_ast; + return modname; +} + // create a new parametric module (when needed) and return the name of the generated module -RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict parameters) +std::string AstModule::derive_common(RTLIL::Design *design, dict parameters, AstNode **new_ast_out, bool) { std::string stripped_name = name.str(); @@ -1105,7 +1384,10 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dictstr.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); + if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0) + child->children[0] = AstNode::mkconst_str(parameters[para_id].decode_string()); + else + child->children[0] = AstNode::mkconst_bits(parameters[para_id].bits, (parameters[para_id].flags & RTLIL::CONST_FLAG_SIGNED) != 0); parameters.erase(para_id); continue; } @@ -1115,8 +1397,16 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dict 0) - log_error("Requested parameter `%s' does not exist in module `%s'!\n", parameters.begin()->first.c_str(), stripped_name.c_str()); + + for (auto param : parameters) { + AstNode *defparam = new AstNode(AST_DEFPARAM, new AstNode(AST_IDENTIFIER)); + defparam->children[0]->str = param.first.str(); + if ((param.second.flags & RTLIL::CONST_FLAG_STRING) != 0) + defparam->children.push_back(AstNode::mkconst_str(param.second.decode_string())); + else + defparam->children.push_back(AstNode::mkconst_bits(param.second.bits, (param.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0)); + new_ast->children.push_back(defparam); + } std::string modname; @@ -1127,15 +1417,8 @@ RTLIL::IdString AstModule::derive(RTLIL::Design *design, dicthas(modname)) { - new_ast->str = modname; - design->add(process_module(new_ast, false)); - design->module(modname)->check(); - } else { - log("Found cached RTLIL representation for module `%s'.\n", modname.c_str()); - } - delete new_ast; + (*new_ast_out) = new_ast; return modname; } @@ -1177,4 +1460,3 @@ void AST::use_internal_line_num() } YOSYS_NAMESPACE_END - diff --git a/yosys/frontends/ast/ast.h b/yosys/frontends/ast/ast.h index cd6e264e6..08f91c9c3 100644 --- a/yosys/frontends/ast/ast.h +++ b/yosys/frontends/ast/ast.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -65,6 +65,9 @@ namespace AST AST_PREFIX, AST_ASSERT, AST_ASSUME, + AST_LIVE, + AST_FAIR, + AST_COVER, AST_FCALL, AST_TO_BITS, @@ -139,6 +142,11 @@ namespace AST AST_NEGEDGE, AST_EDGE, + AST_INTERFACE, + AST_INTERFACEPORT, + AST_INTERFACEPORTTYPE, + AST_MODPORT, + AST_MODPORTMEMBER, AST_PACKAGE }; @@ -165,7 +173,7 @@ namespace AST // node content - most of it is unused in most node types std::string str; std::vector bits; - bool is_input, is_output, is_reg, is_signed, is_string, range_valid, range_swapped; + bool is_input, is_output, is_reg, is_logic, is_signed, is_string, range_valid, range_swapped, was_checked; int port_id, range_left, range_right; uint32_t integer; double realvalue; @@ -187,8 +195,8 @@ namespace AST // creating and deleting nodes AstNode(AstNodeType type = AST_NONE, AstNode *child1 = NULL, AstNode *child2 = NULL, AstNode *child3 = NULL); - AstNode *clone(); - void cloneInto(AstNode *other); + AstNode *clone() const; + void cloneInto(AstNode *other) const; void delete_children(); ~AstNode(); @@ -231,8 +239,8 @@ namespace AST AstNode *eval_const_function(AstNode *fcall); // create a human-readable text representation of the AST (for debugging) - void dumpAst(FILE *f, std::string indent); - void dumpVlog(FILE *f, std::string indent); + void dumpAst(FILE *f, std::string indent) const; + void dumpVlog(FILE *f, std::string indent) const; // used by genRTLIL() for detecting expression width and sign void detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *found_real = NULL); @@ -261,27 +269,30 @@ namespace AST RTLIL::Const asAttrConst(); RTLIL::Const asParaConst(); uint64_t asInt(bool is_signed); - bool bits_only_01(); - bool asBool(); + bool bits_only_01() const; + bool asBool() const; // helper functions for real valued const eval - int isConst(); // return '1' for AST_CONSTANT and '2' for AST_REALVALUE + int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE double asReal(bool is_signed); RTLIL::Const realAsConst(int width); }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code - void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit, - bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool ignore_redef, bool defer, bool autowire); + void process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump_ast2, bool no_dump_ptr, bool dump_vlog, bool dump_rtlil, bool nolatches, bool nomeminit, + bool nomem2reg, bool mem2reg, bool lib, bool noopt, bool icells, bool nooverwrite, bool overwrite, bool defer, bool autowire); // parametric modules are supported directly by the AST library // therefore we need our own derivate of RTLIL::Module with overloaded virtual functions struct AstModule : RTLIL::Module { AstNode *ast; bool nolatches, nomeminit, nomem2reg, mem2reg, lib, noopt, icells, autowire; - virtual ~AstModule(); - virtual RTLIL::IdString derive(RTLIL::Design *design, dict parameters); - virtual RTLIL::Module *clone() const; + ~AstModule() YS_OVERRIDE; + RTLIL::IdString derive(RTLIL::Design *design, dict parameters, bool mayfail) YS_OVERRIDE; + RTLIL::IdString derive(RTLIL::Design *design, dict parameters, dict interfaces, dict modports, bool mayfail) YS_OVERRIDE; + std::string derive_common(RTLIL::Design *design, dict parameters, AstNode **new_ast_out, bool mayfail); + void reprocess_module(RTLIL::Design *design, dict local_interfaces) YS_OVERRIDE; + RTLIL::Module *clone() const YS_OVERRIDE; }; // this must be set by the language frontend before parsing the sources @@ -297,12 +308,17 @@ namespace AST // call a DPI function AstNode *dpi_call(const std::string &rtype, const std::string &fname, const std::vector &argtypes, const std::vector &args); + + // Helper functions related to handling SystemVerilog interfaces + std::pair split_modport_from_type(std::string name_type); + AstNode * find_modport(AstNode *intf, std::string name); + void explode_interface_port(AstNode *module_ast, RTLIL::Module * intfmodule, std::string intfname, AstNode *modport); } namespace AST_INTERNAL { // internal state variables - extern bool flag_dump_ast1, flag_dump_ast2, flag_dump_rtlil, flag_nolatches, flag_nomeminit; + extern bool flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_rtlil, flag_nolatches, flag_nomeminit; extern bool flag_nomem2reg, flag_mem2reg, flag_lib, flag_noopt, flag_icells, flag_autowire; extern AST::AstNode *current_ast, *current_ast_mod; extern std::map current_scope; diff --git a/yosys/frontends/ast/genrtlil.cc b/yosys/frontends/ast/genrtlil.cc index db8d7409f..9531dd356 100644 --- a/yosys/frontends/ast/genrtlil.cc +++ b/yosys/frontends/ast/genrtlil.cc @@ -55,8 +55,7 @@ static RTLIL::SigSpec uniop2rtlil(AstNode *that, std::string type, int result_wi if (gen_attributes) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), that->filename.c_str(), that->linenum); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -89,8 +88,7 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s if (that != NULL) for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), that->filename.c_str(), that->linenum); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -117,8 +115,7 @@ static RTLIL::SigSpec binop2rtlil(AstNode *that, std::string type, int result_wi for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), that->filename.c_str(), that->linenum); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -152,8 +149,7 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), that->filename.c_str(), that->linenum); + log_file_error(that->filename, that->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -207,8 +203,8 @@ struct AST_INTERNAL::ProcessGenerator proc->name = stringf("$proc$%s:%d$%d", always->filename.c_str(), always->linenum, autoidx++); for (auto &attr : always->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), always->filename.c_str(), always->linenum); + log_file_error(always->filename, always->linenum, "Attribute `%s' with non-constant value!\n", + attr.first.c_str()); proc->attributes[attr.first] = attr.second->asAttrConst(); } current_module->processes[proc->name] = proc; @@ -223,16 +219,22 @@ struct AST_INTERNAL::ProcessGenerator bool found_global_syncs = false; bool found_anyedge_syncs = false; for (auto child : always->children) + { + if ((child->type == AST_POSEDGE || child->type == AST_NEGEDGE) && GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && + child->children.at(0)->id2ast && child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute("\\gclk")) { + found_global_syncs = true; + } if (child->type == AST_EDGE) { if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->str == "\\$global_clock") found_global_syncs = true; else found_anyedge_syncs = true; } + } if (found_anyedge_syncs) { if (found_global_syncs) - log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum); + log_file_error(always->filename, always->linenum, "Found non-synthesizable event list!\n"); log("Note: Assuming pure combinatorial block at %s:%d in\n", always->filename.c_str(), always->linenum); log("compliance with IEC 62142(E):2005 / IEEE Std. 1364.1(E):2002. Recommending\n"); log("use of @* instead of @(...) for better match of synthesis and simulation.\n"); @@ -242,14 +244,17 @@ struct AST_INTERNAL::ProcessGenerator bool found_clocked_sync = false; for (auto child : always->children) if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) { + if (GetSize(child->children) == 1 && child->children.at(0)->type == AST_IDENTIFIER && child->children.at(0)->id2ast && + child->children.at(0)->id2ast->type == AST_WIRE && child->children.at(0)->id2ast->get_bool_attribute("\\gclk")) + continue; found_clocked_sync = true; if (found_global_syncs || found_anyedge_syncs) - log_error("Found non-synthesizable event list at %s:%d!\n", always->filename.c_str(), always->linenum); + log_file_error(always->filename, always->linenum, "Found non-synthesizable event list!\n"); RTLIL::SyncRule *syncrule = new RTLIL::SyncRule; syncrule->type = child->type == AST_POSEDGE ? RTLIL::STp : RTLIL::STn; syncrule->signal = child->children[0]->genRTLIL(); if (GetSize(syncrule->signal) != 1) - log_error("Found posedge/negedge event on a signal that is not 1 bit wide at %s:%d!\n", always->filename.c_str(), always->linenum); + log_file_error(always->filename, always->linenum, "Found posedge/negedge event on a signal that is not 1 bit wide!\n"); addChunkActions(syncrule->actions, subst_lvalue_from, subst_lvalue_to, true); proc->syncs.push_back(syncrule); } @@ -471,8 +476,7 @@ struct AST_INTERNAL::ProcessGenerator for (auto &attr : ast->attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), ast->filename.c_str(), ast->linenum); + log_file_error(ast->filename, ast->linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); sw->attributes[attr.first] = attr.second->asAttrConst(); } @@ -540,12 +544,12 @@ struct AST_INTERNAL::ProcessGenerator break; case AST_WIRE: - log_error("Found wire declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); + log_file_error(ast->filename, ast->linenum, "Found wire declaration in block without label!\n"); break; case AST_PARAMETER: case AST_LOCALPARAM: - log_error("Found parameter declaration in block without label at at %s:%d!\n", ast->filename.c_str(), ast->linenum); + log_file_error(ast->filename, ast->linenum, "Found parameter declaration in block without label!\n"); break; case AST_NONE: @@ -554,6 +558,8 @@ struct AST_INTERNAL::ProcessGenerator break; default: + // ast->dumpAst(NULL, "ast> "); + // current_ast_mod->dumpAst(NULL, "mod> "); log_abort(); } } @@ -591,7 +597,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (id_ast == NULL && current_scope.count(str)) id_ast = current_scope.at(str); if (!id_ast) - log_error("Failed to resolve identifier %s for width detection at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str()); if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) { if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; @@ -601,7 +607,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (id_ast->children[0]->type == AST_CONSTANT) this_width = id_ast->children[0]->bits.size(); else - log_error("Failed to detect width for parameter %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to detect width for parameter %s!\n", str.c_str()); if (children.size() != 0) range = children[0]; } else if (id_ast->type == AST_WIRE || id_ast->type == AST_AUTOWIRE) { @@ -613,7 +619,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun // log("---\n"); // id_ast->dumpAst(NULL, "decl> "); // dumpAst(NULL, "ref> "); - log_error("Failed to detect width of signal access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to detect width of signal access `%s'!\n", str.c_str()); } } else { this_width = id_ast->range_left - id_ast->range_right + 1; @@ -624,10 +630,10 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun this_width = 32; } else if (id_ast->type == AST_MEMORY) { if (!id_ast->children[0]->range_valid) - log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str()); this_width = id_ast->children[0]->range_left - id_ast->children[0]->range_right + 1; } else - log_error("Failed to detect width for identifier %s at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to detect width for identifier %s!\n", str.c_str()); if (range) { if (range->children.size() == 1) this_width = 1; @@ -637,8 +643,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); this_width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; delete left_at_zero_ast; delete right_at_zero_ast; @@ -654,7 +659,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_TO_BITS: while (children[0]->simplify(true, false, false, 1, -1, false, false) == true) { } if (children[0]->type != AST_CONSTANT) - log_error("Left operand of tobits expression is not constant at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Left operand of tobits expression is not constant!\n"); children[1]->detectSignWidthWorker(sub_width_hint, sign_hint); width_hint = max(width_hint, children[0]->bitsAsConst().as_int()); break; @@ -682,7 +687,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_REPLICATE: while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { } if (children[0]->type != AST_CONSTANT) - log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Left operand of replicate expression is not constant!\n"); children[1]->detectSignWidthWorker(sub_width_hint, sub_sign_hint); width_hint = max(width_hint, children[0]->bitsAsConst().as_int() * sub_width_hint); sign_hint = false; @@ -756,18 +761,18 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun if (!id2ast->is_signed) sign_hint = false; if (!id2ast->children[0]->range_valid) - log_error("Failed to detect width of memory access `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", str.c_str()); this_width = id2ast->children[0]->range_left - id2ast->children[0]->range_right + 1; width_hint = max(width_hint, this_width); break; case AST_FCALL: - if (str == "\\$anyconst" || str == "\\$anyseq") { + if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { if (GetSize(children) == 1) { while (children[0]->simplify(true, false, false, 1, -1, false, true) == true) { } if (children[0]->type != AST_CONSTANT) - log_error("System function %s called with non-const argument at %s:%d!\n", - RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "System function %s called with non-const argument!\n", + RTLIL::unescape_id(str).c_str()); width_hint = max(width_hint, int(children[0]->asInt(true))); } break; @@ -788,8 +793,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun default: for (auto f : log_files) current_ast->dumpAst(f, "verilog-ast> "); - log_error("Don't know how to detect sign and width for %s node at %s:%d!\n", - type2str(type).c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Don't know how to detect sign and width for %s node!\n", type2str(type).c_str()); } if (*found_real) @@ -842,6 +846,35 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_GENIF: case AST_GENCASE: case AST_PACKAGE: + case AST_MODPORT: + case AST_MODPORTMEMBER: + break; + case AST_INTERFACEPORT: { + // If a port in a module with unknown type is found, mark it with the attribute 'is_interface' + // This is used by the hierarchy pass to know when it can replace interface connection with the individual + // signals. + RTLIL::Wire *wire = current_module->addWire(str, 1); + wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + wire->start_offset = 0; + wire->port_id = port_id; + wire->port_input = true; + wire->port_output = true; + wire->set_bool_attribute("\\is_interface"); + if (children.size() > 0) { + for(size_t i=0; itype == AST_INTERFACEPORTTYPE) { + std::pair res = AST::split_modport_from_type(children[i]->str); + wire->attributes["\\interface_type"] = res.first; + if (res.second != "") + wire->attributes["\\interface_modport"] = res.second; + break; + } + } + } + wire->upto = 0; + } + break; + case AST_INTERFACEPORTTYPE: break; // remember the parameter, needed for example in techmap @@ -852,11 +885,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Wire for an AST_WIRE node case AST_WIRE: { if (current_module->wires_.count(str) != 0) - log_error("Re-definition of signal `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Re-definition of signal `%s'!\n", str.c_str()); if (!range_valid) - log_error("Signal `%s' with non-constant width at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Signal `%s' with non-constant width!\n", str.c_str()); log_assert(range_left >= range_right || (range_left == -1 && range_right == 0)); @@ -870,8 +901,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); wire->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -880,16 +910,14 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // create an RTLIL::Memory for an AST_MEMORY node case AST_MEMORY: { if (current_module->memories.count(str) != 0) - log_error("Re-definition of memory `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Re-definition of memory `%s'!\n", str.c_str()); log_assert(children.size() >= 2); log_assert(children[0]->type == AST_RANGE); log_assert(children[1]->type == AST_RANGE); if (!children[0]->range_valid || !children[1]->range_valid) - log_error("Memory `%s' with non-constant width or size at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Memory `%s' with non-constant width or size!\n", str.c_str()); RTLIL::Memory *memory = new RTLIL::Memory; memory->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); @@ -906,8 +934,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); memory->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -926,8 +953,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_REALVALUE: { RTLIL::SigSpec sig = realAsConst(width_hint); - log_warning("converting real value %e to binary %s at %s:%d.\n", - realvalue, log_signal(sig), filename.c_str(), linenum); + log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", realvalue, log_signal(sig)); return sig; } @@ -938,6 +964,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) { RTLIL::Wire *wire = NULL; RTLIL::SigChunk chunk; + bool is_interface = false; int add_undef_bits_msb = 0; int add_undef_bits_lsb = 0; @@ -947,25 +974,48 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); wire->name = str; if (flag_autowire) - log_warning("Identifier `%s' is implicitly declared at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + log_file_warning(filename, linenum, "Identifier `%s' is implicitly declared.\n", str.c_str()); else - log_error("Identifier `%s' is implicitly declared at %s:%d and `default_nettype is set to none.\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); } else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) { if (id2ast->children[0]->type != AST_CONSTANT) - log_error("Parameter %s does not evaluate to constant value at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str()); chunk = RTLIL::Const(id2ast->children[0]->bits); goto use_const_chunk; } - else if (!id2ast || (id2ast->type != AST_WIRE && id2ast->type != AST_AUTOWIRE && - id2ast->type != AST_MEMORY) || current_module->wires_.count(str) == 0) - log_error("Identifier `%s' doesn't map to any signal at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + else if (id2ast && (id2ast->type == AST_WIRE || id2ast->type == AST_AUTOWIRE || id2ast->type == AST_MEMORY) && current_module->wires_.count(str) != 0) { + RTLIL::Wire *current_wire = current_module->wire(str); + if (current_wire->get_bool_attribute("\\is_interface")) + is_interface = true; + // Ignore + } + // If an identifier is found that is not already known, assume that it is an interface: + else if (1) { // FIXME: Check if sv_mode first? + is_interface = true; + } + else { + log_file_error(filename, linenum, "Identifier `%s' doesn't map to any signal!\n", str.c_str()); + } if (id2ast->type == AST_MEMORY) - log_error("Identifier `%s' does map to an unexpanded memory at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Identifier `%s' does map to an unexpanded memory!\n", str.c_str()); + + // If identifier is an interface, create a RTLIL::SigSpec with a dummy wire with a attribute called 'is_interface' + // This makes it possible for the hierarchy pass to see what are interface connections and then replace them + // with the individual signals: + if (is_interface) { + RTLIL::Wire *dummy_wire; + std::string dummy_wire_name = "$dummywireforinterface" + str; + if (current_module->wires_.count(dummy_wire_name)) + dummy_wire = current_module->wires_[dummy_wire_name]; + else { + dummy_wire = current_module->addWire(dummy_wire_name); + dummy_wire->set_bool_attribute("\\is_interface"); + } + RTLIL::SigSpec tmp = RTLIL::SigSpec(dummy_wire); + return tmp; + } wire = current_module->wires_[str]; chunk.wire = wire; @@ -974,7 +1024,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) use_const_chunk: if (children.size() != 0) { - log_assert(children[0]->type == AST_RANGE); + if (children[0]->type != AST_RANGE) + log_file_error(filename, linenum, "Single range expected.\n"); int source_width = id2ast->range_left - id2ast->range_right + 1; int source_offset = id2ast->range_right; if (!children[0]->range_valid) { @@ -983,8 +1034,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : children[0]->children[0]->clone()); @@ -1012,11 +1062,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset = (id2ast->range_left - id2ast->range_right + 1) - (chunk.offset + chunk.width); if (chunk.offset >= source_width || chunk.offset + chunk.width < 0) { if (chunk.width == 1) - log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting result bit to undef.\n", - str.c_str(), filename.c_str(), linenum); + log_file_warning(filename, linenum, "Range select out of bounds on signal `%s': Setting result bit to undef.\n", + str.c_str()); else - log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting all %d result bits to undef.\n", - str.c_str(), filename.c_str(), linenum, chunk.width); + log_file_warning(filename, linenum, "Range select [%d:%d] out of bounds on signal `%s': Setting all %d result bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), chunk.width); chunk = RTLIL::SigChunk(RTLIL::State::Sx, chunk.width); } else { if (chunk.width + chunk.offset > source_width) { @@ -1029,11 +1079,11 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) chunk.offset += add_undef_bits_lsb; } if (add_undef_bits_lsb) - log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d LSB bits to undef.\n", - str.c_str(), filename.c_str(), linenum, add_undef_bits_lsb); + log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d LSB bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_lsb); if (add_undef_bits_msb) - log_warning("Range select out of bounds on signal `%s' at %s:%d: Setting %d MSB bits to undef.\n", - str.c_str(), filename.c_str(), linenum, add_undef_bits_msb); + log_file_warning(filename, linenum, "Range [%d:%d] select out of bounds on signal `%s': Setting %d MSB bits to undef.\n", + children[0]->range_left, children[0]->range_right, str.c_str(), add_undef_bits_msb); } } } @@ -1072,7 +1122,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) RTLIL::SigSpec left = children[0]->genRTLIL(); RTLIL::SigSpec right = children[1]->genRTLIL(); if (!left.is_fully_const()) - log_error("Left operand of replicate expression is not constant at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Left operand of replicate expression is not constant!\n"); int count = left.as_int(); RTLIL::SigSpec sig; for (int i = 0; i < count; i++) @@ -1289,6 +1339,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(0); cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0); + if (!sign_hint) + is_signed = false; + return RTLIL::SigSpec(wire); } @@ -1308,7 +1361,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) int num_words = 1; if (type == AST_MEMINIT) { if (children[2]->type != AST_CONSTANT) - log_error("Memory init with non-constant word count at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Memory init with non-constant word count!\n"); num_words = int(children[2]->asInt(false)); cell->parameters["\\WORDS"] = RTLIL::Const(num_words); } @@ -1336,9 +1389,16 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // generate $assert cells case AST_ASSERT: case AST_ASSUME: + case AST_LIVE: + case AST_FAIR: + case AST_COVER: { - const char *celltype = "$assert"; + const char *celltype = nullptr; + if (type == AST_ASSERT) celltype = "$assert"; if (type == AST_ASSUME) celltype = "$assume"; + if (type == AST_LIVE) celltype = "$live"; + if (type == AST_FAIR) celltype = "$fair"; + if (type == AST_COVER) celltype = "$cover"; log_assert(children.size() == 2); @@ -1358,8 +1418,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } @@ -1380,9 +1439,9 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) new_left.append(left[i]); new_right.append(right[i]); } - log_warning("Ignoring assignment to constant bits at %s:%d:\n" + log_file_warning(filename, linenum, "Ignoring assignment to constant bits:\n" " old assignment: %s = %s\n new assignment: %s = %s.\n", - filename.c_str(), linenum, log_signal(left), log_signal(right), + log_signal(left), log_signal(right), log_signal(new_left), log_signal(new_right)); left = new_left; right = new_right; @@ -1397,11 +1456,12 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) int port_counter = 0, para_counter = 0; if (current_module->count_id(str) != 0) - log_error("Re-definition of cell `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Re-definition of cell `%s'!\n", str.c_str()); RTLIL::Cell *cell = current_module->addCell(str, ""); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); + // Set attribute 'module_not_derived' which will be cleared again after the hierarchy pass + cell->set_bool_attribute("\\module_not_derived"); for (auto it = children.begin(); it != children.end(); it++) { AstNode *child = *it; @@ -1414,16 +1474,15 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (child->type == AST_PARASET) { IdString paraname = child->str.empty() ? stringf("$%d", ++para_counter) : child->str; if (child->children[0]->type == AST_REALVALUE) { - log_warning("Replacing floating point parameter %s.%s = %f with string at %s:%d.\n", - log_id(cell), log_id(paraname), child->children[0]->realvalue, - filename.c_str(), linenum); + log_file_warning(filename, linenum, "Replacing floating point parameter %s.%s = %f with string.\n", + log_id(cell), log_id(paraname), child->children[0]->realvalue); auto strnode = AstNode::mkconst_str(stringf("%f", child->children[0]->realvalue)); strnode->cloneInto(child->children[0]); delete strnode; } if (child->children[0]->type != AST_CONSTANT) - log_error("Parameter %s.%s with non-constant value at %s:%d!\n", - log_id(cell), log_id(paraname), filename.c_str(), linenum); + log_file_error(filename, linenum, "Parameter %s.%s with non-constant value!\n", + log_id(cell), log_id(paraname)); cell->parameters[paraname] = child->children[0]->asParaConst(); continue; } @@ -1444,8 +1503,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) - log_error("Attribute `%s' with non-constant value at %s:%d!\n", - attr.first.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Attribute `%s' with non-constant value!\n", attr.first.c_str()); cell->attributes[attr.first] = attr.second->asAttrConst(); } } @@ -1466,30 +1524,36 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) } break; case AST_FCALL: { - if (str == "\\$anyconst" || str == "\\$anyseq") + if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { string myid = stringf("%s$%d", str.c_str() + 1, autoidx++); int width = width_hint; if (GetSize(children) > 1) - log_error("System function %s got %d arguments, expected 1 or 0 at %s:%d.\n", - RTLIL::unescape_id(str).c_str(), GetSize(children), filename.c_str(), linenum); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 0.\n", + RTLIL::unescape_id(str).c_str(), GetSize(children)); if (GetSize(children) == 1) { if (children[0]->type != AST_CONSTANT) - log_error("System function %s called with non-const argument at %s:%d!\n", - RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "System function %s called with non-const argument!\n", + RTLIL::unescape_id(str).c_str()); width = children[0]->asInt(true); } if (width <= 0) - log_error("Failed to detect width of %s at %s:%d!\n", - RTLIL::unescape_id(str).c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Failed to detect width of %s!\n", RTLIL::unescape_id(str).c_str()); Cell *cell = current_module->addCell(myid, str.substr(1)); cell->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); cell->parameters["\\WIDTH"] = width; + if (attributes.count("\\reg")) { + auto &attr = attributes.at("\\reg"); + if (attr->type != AST_CONSTANT) + log_file_error(filename, linenum, "Attribute `reg' with non-constant value!\n"); + cell->attributes["\\reg"] = attr->asAttrConst(); + } + Wire *wire = current_module->addWire(myid + "_wire", width); wire->attributes["\\src"] = stringf("%s:%d", filename.c_str(), linenum); cell->setPort("\\Y", wire); @@ -1504,8 +1568,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) for (auto f : log_files) current_ast->dumpAst(f, "verilog-ast> "); type_name = type2str(type); - log_error("Don't know how to generate RTLIL code for %s node at %s:%d!\n", - type_name.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Don't know how to generate RTLIL code for %s node!\n", type_name.c_str()); } return RTLIL::SigSpec(); @@ -1535,4 +1598,3 @@ RTLIL::SigSpec AstNode::genWidthRTLIL(int width, const dict(); deep_recursion_warning = true; @@ -177,13 +177,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // 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); + log_file_warning(filename, linenum, "Ignoring call to system %s %s.\n", type == AST_FCALL ? "function" : "task", str.c_str()); 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); + log_file_warning(filename, linenum, "System task `%s' outside initial block is unsupported.\n", str.c_str()); delete_children(); str = std::string(); } @@ -195,14 +195,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { 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); + log_file_error(filename, linenum, "System task `%s' got %d arguments, expected >= 1.\n", + str.c_str(), int(children.size())); // 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); + log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str()); std::string sformat = node_string->bitsAsConst().decode_string(); // Other arguments are placeholders. Process the string as we go through it @@ -215,7 +215,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { // 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); + log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str()); char cformat = sformat[++i]; @@ -239,13 +239,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n", + cformat, str.c_str()); 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); + log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); break; case 'm': @@ -253,7 +253,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, break; default: - log_error("System task `%s' called with invalid/unsupported format specifier at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); break; } @@ -327,6 +327,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (node->type == AST_WIRE) { if (this_wire_scope.count(node->str) > 0) { AstNode *first_node = this_wire_scope[node->str]; + if (first_node->is_input && node->is_reg) + goto wires_are_incompatible; 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) { @@ -361,6 +363,8 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, first_node->is_output = true; if (node->is_reg) first_node->is_reg = true; + if (node->is_logic) + first_node->is_logic = true; if (node->is_signed) first_node->is_signed = true; for (auto &it : node->attributes) { @@ -374,7 +378,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Incompatible re-declaration of wire %s.\n", node->str.c_str()); continue; } this_wire_scope[node->str] = node; @@ -387,7 +391,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; - if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE) + if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_MEMORY) while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) did_something = true; } @@ -401,13 +405,20 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_ALWAYS || type == AST_INITIAL) { + if (current_always != nullptr) + log_file_error(filename, linenum, "Invalid nesting of always blocks and/or initializations.\n"); + current_always = this; current_always_clocked = false; if (type == AST_ALWAYS) - for (auto child : children) + for (auto child : children) { if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) current_always_clocked = true; + if (child->type == AST_EDGE && GetSize(child->children) == 1 && + child->children[0]->type == AST_IDENTIFIER && child->children[0]->str == "\\$global_clock") + current_always_clocked = true; + } } int backup_width_hint = width_hint; @@ -433,6 +444,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, children[1]->detectSignWidth(width_hint, sign_hint); width_hint = max(width_hint, backup_width_hint); child_0_is_self_determined = true; + // test only once, before optimizations and memory mappings but after assignment LHS was mapped to an identifier + if (children[0]->id2ast && !children[0]->was_checked) { + if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->id2ast->is_logic) + children[0]->id2ast->is_reg = true; // if logic type is used in a block asignment + if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && !children[0]->id2ast->is_reg) + log_warning("wire '%s' is assigned in a block at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum); + if (type == AST_ASSIGN && children[0]->id2ast->is_reg) { + bool is_rand_reg = false; + if (children[1]->type == AST_FCALL) { + if (children[1]->str == "\\$anyconst") + is_rand_reg = true; + if (children[1]->str == "\\$anyseq") + is_rand_reg = true; + if (children[1]->str == "\\$allconst") + is_rand_reg = true; + if (children[1]->str == "\\$allseq") + is_rand_reg = true; + } + if (!is_rand_reg) + log_warning("reg '%s' is assigned in a continuous assignment at %s:%d.\n", children[0]->str.c_str(), filename.c_str(), linenum); + } + children[0]->was_checked = true; + } break; case AST_PARAMETER: @@ -444,7 +478,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n"); width_hint = max(width_hint, children[1]->range_left - children[1]->range_right + 1); } break; @@ -685,26 +719,47 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, current_scope.clear(); // convert defparam nodes to cell parameters - if (type == AST_DEFPARAM && !str.empty()) { - size_t pos = str.rfind('.'); + if (type == AST_DEFPARAM && !children.empty()) + { + if (children[0]->type != AST_IDENTIFIER) + log_file_error(filename, linenum, "Module name in defparam contains non-constant expressions!\n"); + + string modname, paramname = children[0]->str; + + size_t pos = paramname.rfind('.'); + + while (pos != 0 && pos != std::string::npos) + { + modname = paramname.substr(0, pos); + + if (current_scope.count(modname)) + break; + + pos = paramname.rfind('.', pos - 1); + } + 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(); + log_file_error(filename, linenum, "Can't find object for defparam `%s`!\n", RTLIL::unescape_id(paramname).c_str()); + + paramname = "\\" + paramname.substr(pos+1); + + if (current_scope.at(modname)->type != AST_CELL) + log_file_error(filename, linenum, "Defparam argument `%s . %s` does not match a cell!\n", + RTLIL::unescape_id(modname).c_str(), RTLIL::unescape_id(paramname).c_str()); + + AstNode *paraset = new AstNode(AST_PARASET, children[1]->clone(), GetSize(children) > 2 ? children[2]->clone() : NULL); + paraset->str = paramname; + + AstNode *cell = current_scope.at(modname); cell->children.insert(cell->children.begin() + 1, paraset); - paraset->type = AST_PARASET; - paraset->str = paraname; - str.clear(); + delete_children(); } // 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); + log_file_error(filename, linenum, "Index in generate block prefix syntax is not constant!\n"); } if (children[1]->type == AST_PREFIX) children[1]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param); @@ -720,9 +775,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // 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); + log_file_error(filename, linenum, "Left operand of to_bits expression is not constant!\n"); 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); + log_file_error(filename, linenum, "Right operand of to_bits expression is not constant!\n"); 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; @@ -786,7 +841,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Non-constant range on memory decl.\n"); 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(); @@ -804,7 +859,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Insufficient number of array indices for %s.\n", log_id(str)); AstNode *new_index_expr = children[0]->children[i]->children.at(0)->clone(); @@ -833,12 +888,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n"); 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); + log_file_warning(filename, linenum, "converting real value %e to binary %s.\n", + children[0]->realvalue, log_signal(constvalue)); delete children[0]; children[0] = mkconst_bits(constvalue.bits, sign_hint); did_something = true; @@ -896,7 +951,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Invalid bit-select on memory access!\n"); int mem_width, mem_size, addr_bits; id2ast->meminfo(mem_width, mem_size, addr_bits); @@ -921,6 +976,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *assign = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), data); assign->children[0]->str = wire_id; + assign->children[0]->was_checked = true; if (current_block) { @@ -945,10 +1001,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (type == AST_WHILE) - log_error("While loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "While loops are only allowed in constant functions!\n"); if (type == AST_REPEAT) - log_error("Repeat loops are only allowed in constant functions at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Repeat loops are only allowed in constant functions!\n"); // unroll for loops and generate-for blocks if ((type == AST_GENFOR || type == AST_FOR) && children.size() != 0) @@ -963,31 +1019,31 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Unsupported 1st expression of generate for-loop!\n"); if (next_ast->type != AST_ASSIGN_EQ) - log_error("Unsupported 3rd expression of generate for-loop at %s:%d!\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Unsupported 3rd expression of generate for-loop!\n"); 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); + log_file_error(filename, linenum, "Left hand side of 1st expression of generate for-loop is not a gen var!\n"); 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); + log_file_error(filename, linenum, "Left hand side of 3rd expression of generate for-loop is not a gen var!\n"); } 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); + log_file_error(filename, linenum, "Left hand side of 1st expression of generate for-loop is not a register!\n"); 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); + log_file_error(filename, linenum, "Left hand side of 3rd expression of generate for-loop is not a register!\n"); } 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); + log_file_error(filename, linenum, "Incompatible left-hand sides in 1st and 3rd expression of generate for-loop!\n"); // 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); + log_file_error(filename, linenum, "Right hand side of 1st expression of generate for-loop is not constant!\n"); varbuf = new AstNode(AST_LOCALPARAM, varbuf); varbuf->str = init_ast->children[0]->str; @@ -1009,7 +1065,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "2nd expression of generate for-loop is not constant!\n"); if (buf->integer == 0) { delete buf; @@ -1050,7 +1106,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Right hand side of 3rd expression of generate for-loop is not constant!\n"); delete varbuf->children[0]; varbuf->children[0] = buf; @@ -1062,6 +1118,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, did_something = true; } + // check for local objects in unnamed block + if (type == AST_BLOCK && str.empty()) + { + for (size_t i = 0; i < children.size(); i++) + if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM) + log_file_error(children[i]->filename, children[i]->linenum, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); + } + // transform block with name if (type == AST_BLOCK && !str.empty()) { @@ -1070,7 +1134,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, std::vector 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) { + if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || 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]; @@ -1107,7 +1171,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Condition for generate if is not constant!\n"); } if (buf->asBool() != 0) { delete buf; @@ -1148,7 +1212,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Condition for generate case is not constant!\n"); } bool ref_signed = buf->is_signed; @@ -1182,7 +1246,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Expression in generate case is not constant!\n"); } bool is_selected = RTLIL::const_eq(ref_value, buf->bitsAsConst(), ref_signed && buf->is_signed, ref_signed && buf->is_signed, 1).as_bool(); @@ -1223,7 +1287,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Non-constant array range on cell array.\n"); 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; @@ -1234,7 +1298,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Cell arrays of primitives are currently not supported.\n"); } 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()); @@ -1248,8 +1312,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, 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); + log_file_error(filename, linenum, "Insufficient number of arguments for primitive `%s'!\n", str.c_str()); std::vector children_list; for (auto child : children) { @@ -1264,8 +1327,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (str == "bufif0" || str == "bufif1" || str == "notif0" || str == "notif1") { if (children_list.size() != 3) - log_error("Invalid number of arguments for primitive `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Invalid number of arguments for primitive `%s'!\n", str.c_str()); std::vector z_const(1, RTLIL::State::Sz); @@ -1285,6 +1347,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, str.clear(); type = AST_ASSIGN; children.push_back(children_list.at(0)); + children.back()->was_checked = true; children.push_back(node); did_something = true; } @@ -1321,6 +1384,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, str.clear(); type = AST_ASSIGN; children.push_back(children_list[0]); + children.back()->was_checked = true; children.push_back(node); did_something = true; } @@ -1350,8 +1414,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, while (left_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, stage, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", - str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; } did_something = true; @@ -1370,7 +1433,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } skip_dynamic_range_lvalue_expansion:; - if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && current_block != NULL) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL) { std::stringstream sstr; sstr << "$formal$" << filename << ":" << linenum << "$" << (autoidx++); @@ -1378,16 +1441,19 @@ skip_dynamic_range_lvalue_expansion:; AstNode *wire_check = new AstNode(AST_WIRE); wire_check->str = id_check; + wire_check->was_checked = true; 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; + wire_en->was_checked = true; 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_ast_mod->children.back()->children[0]->children[0]->children[0]->was_checked = true; } current_scope[wire_en->str] = wire_en; while (wire_en->simplify(true, false, false, 1, -1, false, false)) { } @@ -1397,9 +1463,11 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign_check = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_bits(x_bit, false)); assign_check->children[0]->str = id_check; + assign_check->children[0]->was_checked = true; AstNode *assign_en = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), mkconst_int(0, false, 1)); assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; AstNode *default_signals = new AstNode(AST_BLOCK); default_signals->children.push_back(assign_check); @@ -1408,6 +1476,7 @@ skip_dynamic_range_lvalue_expansion:; 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; + assign_check->children[0]->was_checked = true; 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)); @@ -1416,6 +1485,7 @@ skip_dynamic_range_lvalue_expansion:; assign_en->children[1]->str = "\\$initstate"; } assign_en->children[0]->str = id_en; + assign_en->children[0]->was_checked = true; newNode = new AstNode(AST_BLOCK); newNode->children.push_back(assign_check); @@ -1432,7 +1502,7 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME) && children.size() == 1) + if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && children.size() == 1) { children.push_back(mkconst_int(1, false, 1)); did_something = true; @@ -1472,6 +1542,7 @@ skip_dynamic_range_lvalue_expansion:; wire_tmp_id->str = wire_tmp->str; newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, wire_tmp_id, children[1]->clone())); + newNode->children.back()->was_checked = true; int cursor = 0; for (auto child : children[0]->children) @@ -1524,12 +1595,14 @@ skip_dynamic_range_lvalue_expansion:; 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->was_checked = true; 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->was_checked = true; wire_data->is_signed = mem_signed; current_ast_mod->children.push_back(wire_data); current_scope[wire_data->str] = wire_data; @@ -1539,6 +1612,7 @@ skip_dynamic_range_lvalue_expansion:; 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; + wire_en->was_checked = true; 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)) { } @@ -1554,14 +1628,17 @@ skip_dynamic_range_lvalue_expansion:; 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; + assign_addr->children[0]->was_checked = true; 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; + assign_data->children[0]->was_checked = true; 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; + assign_en->children[0]->was_checked = true; } AstNode *default_signals = new AstNode(AST_BLOCK); @@ -1573,6 +1650,7 @@ skip_dynamic_range_lvalue_expansion:; 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; + assign_addr->children[0]->was_checked = true; if (children[0]->children.size() == 2) { @@ -1587,12 +1665,14 @@ skip_dynamic_range_lvalue_expansion:; 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; + assign_data->children[0]->was_checked = true; 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; + assign_en->children[0]->was_checked = true; } } else @@ -1608,12 +1688,13 @@ skip_dynamic_range_lvalue_expansion:; while (left_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } while (right_at_zero_ast->simplify(true, true, false, 1, -1, false, false)) { } if (left_at_zero_ast->type != AST_CONSTANT || right_at_zero_ast->type != AST_CONSTANT) - log_error("Unsupported expression on dynamic range select on signal `%s' at %s:%d!\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "Unsupported expression on dynamic range select on signal `%s'!\n", str.c_str()); int width = left_at_zero_ast->integer - right_at_zero_ast->integer + 1; 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; + assign_data->children[0]->was_checked = true; if (current_always->type != AST_INITIAL) { for (int i = 0; i < mem_width; i++) @@ -1621,6 +1702,7 @@ skip_dynamic_range_lvalue_expansion:; 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; + assign_en->children[0]->was_checked = true; } delete left_at_zero_ast; @@ -1632,10 +1714,12 @@ skip_dynamic_range_lvalue_expansion:; { assign_data = new AstNode(AST_ASSIGN_LE, new AstNode(AST_IDENTIFIER), children[1]->clone()); assign_data->children[0]->str = id_data; + assign_data->children[0]->was_checked = true; 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; + assign_en->children[0]->was_checked = true; } } @@ -1700,19 +1784,19 @@ skip_dynamic_range_lvalue_expansion:; 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); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); 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); + log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n", + RTLIL::unescape_id(str).c_str()); if (GetSize(children) == 2) { AstNode *buf = children[1]->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, false, false, stage, -1, false, 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); num_steps = buf->asInt(true); delete buf; @@ -1726,6 +1810,11 @@ skip_dynamic_range_lvalue_expansion:; log_assert(block != nullptr); + if (num_steps == 0) { + newNode = children[0]->clone(); + goto apply_newNode; + } + int myidx = autoidx++; AstNode *outreg = nullptr; @@ -1744,6 +1833,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *regid = new AstNode(AST_IDENTIFIER); regid->str = reg->str; regid->id2ast = reg; + regid->was_checked = true; AstNode *rhs = nullptr; @@ -1765,15 +1855,15 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } - if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell") + if (str == "\\$stable" || str == "\\$rose" || str == "\\$fell" || str == "\\$changed") { if (GetSize(children) != 1) - log_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); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); 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); + log_file_error(filename, linenum, "System function %s is only allowed in clocked blocks.\n", + RTLIL::unescape_id(str).c_str()); AstNode *present = children.at(0)->clone(); AstNode *past = clone(); @@ -1782,11 +1872,18 @@ skip_dynamic_range_lvalue_expansion:; if (str == "\\$stable") newNode = new AstNode(AST_EQ, past, present); + else if (str == "\\$changed") + newNode = new AstNode(AST_NE, past, present); + else if (str == "\\$rose") - newNode = new AstNode(AST_LOGIC_AND, new AstNode(AST_LOGIC_NOT, past), present); + newNode = new AstNode(AST_LOGIC_AND, + new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, past, mkconst_int(1,false))), + new AstNode(AST_BIT_AND, present, mkconst_int(1,false))); else if (str == "\\$fell") - newNode = new AstNode(AST_LOGIC_AND, past, new AstNode(AST_LOGIC_NOT, present)); + newNode = new AstNode(AST_LOGIC_AND, + new AstNode(AST_BIT_AND, past, mkconst_int(1,false)), + new AstNode(AST_LOGIC_NOT, new AstNode(AST_BIT_AND, present, mkconst_int(1,false)))); else log_abort(); @@ -1794,23 +1891,8 @@ skip_dynamic_range_lvalue_expansion:; 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") { + if (str == "\\$anyconst" || str == "\\$anyseq" || str == "\\$allconst" || str == "\\$allseq") { recursion_counter--; return false; } @@ -1818,13 +1900,13 @@ skip_dynamic_range_lvalue_expansion:; 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); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); AstNode *buf = children[0]->clone(); while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant value.\n", str.c_str()); RTLIL::Const arg_value = buf->bitsAsConst(); if (arg_value.as_bool()) @@ -1836,33 +1918,104 @@ skip_dynamic_range_lvalue_expansion:; if (arg_value.bits.at(i) == RTLIL::State::S1) result = i + 1; - newNode = mkconst_int(result, false); + newNode = mkconst_int(result, true); + goto apply_newNode; + } + + if (str == "\\$size" || str == "\\$bits") + { + if (str == "\\$bits" && children.size() != 1) + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + if (str == "\\$size" && children.size() != 1 && children.size() != 2) + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1 or 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); + + int dim = 1; + if (str == "\\$size" && children.size() == 2) { + AstNode *buf = children[1]->clone(); + // Evaluate constant expression + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + dim = buf->asInt(false); + delete buf; + } + AstNode *buf = children[0]->clone(); + int mem_depth = 1; + AstNode *id_ast = NULL; + + // Is this needed? + //while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + buf->detectSignWidth(width_hint, sign_hint); + + if (buf->type == AST_IDENTIFIER) { + id_ast = buf->id2ast; + if (id_ast == NULL && current_scope.count(buf->str)) + id_ast = current_scope.at(buf->str); + if (!id_ast) + log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", buf->str.c_str()); + if (id_ast->type == AST_MEMORY) { + // We got here only if the argument is a memory + // Otherwise $size() and $bits() return the expression width + AstNode *mem_range = id_ast->children[1]; + if (str == "\\$bits") { + if (mem_range->type == AST_RANGE) { + if (!mem_range->range_valid) + log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); + mem_depth = mem_range->range_left - mem_range->range_right + 1; + } else + log_file_error(filename, linenum, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); + } else { + // $size() + if (mem_range->type == AST_RANGE) { + if (!mem_range->range_valid) + log_file_error(filename, linenum, "Failed to detect width of memory access `%s'!\n", buf->str.c_str()); + int dims; + if (id_ast->multirange_dimensions.empty()) + dims = 1; + else + dims = GetSize(id_ast->multirange_dimensions)/2; + if (dim == 1) + width_hint = (dims > 1) ? id_ast->multirange_dimensions[1] : (mem_range->range_left - mem_range->range_right + 1); + else if (dim <= dims) { + width_hint = id_ast->multirange_dimensions[2*dim-1]; + } else if ((dim > dims+1) || (dim < 0)) + log_file_error(filename, linenum, "Dimension %d out of range in `%s', as it only has dimensions 1..%d!\n", dim, buf->str.c_str(), dims+1); + } else + log_file_error(filename, linenum, "Unknown memory depth AST type in `%s'!\n", buf->str.c_str()); + } + } + } + delete buf; + + newNode = mkconst_int(width_hint * mem_depth, 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") + str == "\\$sinh" || str == "\\$cosh" || str == "\\$tanh" || str == "\\$asinh" || str == "\\$acosh" || str == "\\$atanh" || + str == "\\$rtoi" || str == "\\$itor") { 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); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 2.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); } 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); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 1.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); } if (children.size() >= 1) { while (children[0]->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } if (!children[0]->isConst()) - log_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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n", + RTLIL::unescape_id(str).c_str()); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; children[0]->detectSignWidth(child_width_hint, child_sign_hint); @@ -1872,37 +2025,42 @@ skip_dynamic_range_lvalue_expansion:; 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant argument.\n", + RTLIL::unescape_id(str).c_str()); int child_width_hint = width_hint; bool child_sign_hint = sign_hint; children[1]->detectSignWidth(child_width_hint, child_sign_hint); 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(); + if (str == "\\$rtoi") { + newNode = AstNode::mkconst_int(x, true); + } else { + 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 if (str == "\\$itor") newNode->realvalue = x; + else log_abort(); + } goto apply_newNode; } @@ -1920,14 +2078,14 @@ skip_dynamic_range_lvalue_expansion:; 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); + log_file_error(filename, linenum, "Insufficient number of arguments in DPI function call.\n"); 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); + log_file_error(filename, linenum, "Failed to evaluate DPI function with non-constant argument.\n"); } newNode = dpi_call(rtype, fname, argtypes, args); @@ -1939,7 +2097,7 @@ skip_dynamic_range_lvalue_expansion:; } 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); + log_file_error(filename, linenum, "Can't resolve function name `%s'.\n", str.c_str()); } if (type == AST_TCALL) @@ -1947,26 +2105,26 @@ skip_dynamic_range_lvalue_expansion:; 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_file_error(filename, linenum, "System task `%s' outside initial block is unsupported.\n", str.c_str()); - log_error("System task `%s' executed at %s:%d.\n", str.c_str(), filename.c_str(), linenum); + log_file_error(filename, linenum, "System task `%s' executed.\n", str.c_str()); } 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); + log_file_error(filename, linenum, "System function %s got %d arguments, expected 2-4.\n", + RTLIL::unescape_id(str).c_str(), int(children.size())); AstNode *node_filename = children[0]->clone(); while (node_filename->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-memory 2nd argument.\n", str.c_str()); int start_addr = -1, finish_addr = -1; @@ -1974,7 +2132,7 @@ skip_dynamic_range_lvalue_expansion:; 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 3rd argument.\n", str.c_str()); start_addr = int(node_addr->asInt(false)); } @@ -1982,7 +2140,7 @@ skip_dynamic_range_lvalue_expansion:; 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); + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 4th argument.\n", str.c_str()); finish_addr = int(node_addr->asInt(false)); } @@ -2008,7 +2166,7 @@ skip_dynamic_range_lvalue_expansion:; } 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); + log_file_error(filename, linenum, "Can't resolve task name `%s'.\n", str.c_str()); } AstNode *decl = current_scope[str]; @@ -2036,9 +2194,9 @@ skip_dynamic_range_lvalue_expansion:; } if (in_param) - log_error("Non-constant function call in constant expression at %s:%d.\n", filename.c_str(), linenum); + log_file_error(filename, linenum, "Non-constant function call in constant expression.\n"); 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); + log_file_error(filename, linenum, "Function %s can only be called with constant arguments.\n", str.c_str()); } size_t arg_count = 0; @@ -2069,6 +2227,8 @@ skip_dynamic_range_lvalue_expansion:; AstNode *always = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK, new AstNode(AST_ASSIGN_EQ, lvalue, clone()))); + always->children[0]->children[0]->was_checked = true; + current_ast_mod->children.push_back(always); goto replace_fcall_with_id; @@ -2118,6 +2278,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign = child->is_input ? new AstNode(AST_ASSIGN_EQ, wire_id->clone(), arg) : new AstNode(AST_ASSIGN_EQ, arg, wire_id->clone()); + assign->children[0]->was_checked = true; for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { if (*it != current_block_child) @@ -2137,7 +2298,7 @@ skip_dynamic_range_lvalue_expansion:; } for (auto child : decl->children) - if (child->type == AST_WIRE || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) + if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) { AstNode *wire = nullptr; @@ -2147,9 +2308,16 @@ skip_dynamic_range_lvalue_expansion:; 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 if (!child->children.empty()) { + while (child->simplify(true, false, false, stage, -1, false, false)) { } + if (GetSize(child->children) == GetSize(wire->children)) { + for (int i = 0; i < GetSize(child->children); i++) + if (*child->children.at(i) != *wire->children.at(i)) + goto tcall_incompatible_wires; + } else { + tcall_incompatible_wires: + log_file_error(filename, linenum, "Incompatible re-declaration of wire %s.\n", child->str.c_str()); + } } } else @@ -2181,6 +2349,7 @@ skip_dynamic_range_lvalue_expansion:; AstNode *assign = child->is_input ? new AstNode(AST_ASSIGN_EQ, wire_id, arg) : new AstNode(AST_ASSIGN_EQ, arg, wire_id); + assign->children[0]->was_checked = true; for (auto it = current_block->children.begin(); it != current_block->children.end(); it++) { if (*it != current_block_child) @@ -2197,7 +2366,7 @@ skip_dynamic_range_lvalue_expansion:; } for (auto child : decl->children) - if (child->type != AST_WIRE && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) + if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) { AstNode *stmt = child->clone(); stmt->replace_ids(prefix, replace_rules); @@ -2533,9 +2702,10 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m std::ifstream f; f.open(mem_filename.c_str()); + yosys_input_files.insert(mem_filename); 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_file_error(filename, linenum, "Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str()); 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; @@ -2581,7 +2751,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m 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); + log_file_error(filename, linenum, "Can not parse address `%s` for %s.\n", nptr, str.c_str()); continue; } @@ -2619,6 +2789,7 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); block->children.back()->children[0]->str = memory->str; block->children.back()->children[0]->id2ast = memory; + block->children.back()->children[0]->was_checked = true; } cursor += increment; @@ -2660,7 +2831,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma 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; + pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; else pos = pos + 1; new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); @@ -2837,7 +3008,7 @@ bool AstNode::mem2reg_check(pool &mem2reg_set) 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); + log_file_error(filename, linenum, "Invalid array access.\n"); return true; } @@ -2879,6 +3050,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *newNode = clone(); newNode->type = AST_ASSIGN_EQ; + newNode->children[0]->was_checked = true; async_block->children[0]->children.push_back(newNode); newNode = new AstNode(AST_NONE); @@ -2902,6 +3074,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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->was_checked = 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)) { } @@ -2909,6 +3082,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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->was_checked = true; wire_data->is_signed = mem_signed; wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_data); @@ -2922,6 +3096,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); assign_addr->children[0]->str = id_addr; + assign_addr->children[0]->was_checked = true; block->children.insert(block->children.begin()+assign_idx+1, assign_addr); AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); @@ -2945,6 +3120,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, children[0]->id2ast = NULL; children[0]->str = id_data; type = AST_ASSIGN_EQ; + children[0]->was_checked = true; did_something = true; } @@ -2977,6 +3153,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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->was_checked = true; if (block) wire_addr->attributes["\\nosync"] = AstNode::mkconst_int(1, false); mod->children.push_back(wire_addr); @@ -2985,6 +3162,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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->was_checked = true; wire_data->is_signed = mem_signed; if (block) wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); @@ -2993,6 +3171,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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; + assign_addr->children[0]->was_checked = true; AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); case_node->children[0]->str = id_addr; @@ -3003,6 +3182,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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[0]->was_checked = true; 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); @@ -3015,6 +3195,7 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, 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; + assign_reg->children[0]->was_checked = true; cond_node->children[1]->children.push_back(assign_reg); case_node->children.push_back(cond_node); @@ -3043,6 +3224,8 @@ bool AstNode::mem2reg_as_needed_pass2(pool &mem2reg_set, AstNode *mod, if (bit_part_sel) children.push_back(bit_part_sel); + + did_something = true; } log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); @@ -3094,13 +3277,13 @@ void AstNode::replace_variables(std::map &varia 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); + log_file_error(filename, linenum, "Memory access in constant function is not supported\n%s:%d: ...called from here.\n", + fcall->filename.c_str(), fcall->linenum); 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); + log_file_error(filename, linenum, "Non-constant range\n%s:%d: ... called from here.\n", + 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); } @@ -3139,8 +3322,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) { 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); + log_file_error(child->filename, child->linenum, "Can't determine size of variable %s\n%s:%d: ... called from here.\n", + child->str.c_str(), fcall->filename.c_str(), fcall->linenum); 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; @@ -3183,24 +3366,24 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) 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); + log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here. X\n", + fcall->filename.c_str(), fcall->linenum); 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); + log_file_error(stmt->filename, stmt->linenum, "Unsupported composite left hand side in constant function\n%s:%d: ... called from here.\n", + fcall->filename.c_str(), fcall->linenum); 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); + log_file_error(stmt->filename, stmt->linenum, "Assignment to non-local variable in constant function\n%s:%d: ... called from here.\n", + fcall->filename.c_str(), fcall->linenum); 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); + log_file_error(range->filename, range->linenum, "Non-constant range\n%s:%d: ... called from here.\n", + 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]; @@ -3231,8 +3414,8 @@ AstNode *AstNode::eval_const_function(AstNode *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); + log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n", + fcall->filename.c_str(), fcall->linenum); if (cond->asBool()) { block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); @@ -3252,8 +3435,8 @@ AstNode *AstNode::eval_const_function(AstNode *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); + log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n", + fcall->filename.c_str(), fcall->linenum); block->children.erase(block->children.begin()); for (int i = 0; i < num->bitsAsConst().as_int(); i++) @@ -3290,8 +3473,8 @@ AstNode *AstNode::eval_const_function(AstNode *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); + log_file_error(stmt->filename, stmt->linenum, "Non-constant expression in constant function\n%s:%d: ... called from here.\n", + fcall->filename.c_str(), fcall->linenum); found_match = cond->asBool(); delete cond; @@ -3320,8 +3503,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) 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_file_error(stmt->filename, stmt->linenum, "Unsupported language construct in constant function\n%s:%d: ... called from here.\n", + fcall->filename.c_str(), fcall->linenum); log_abort(); } @@ -3338,4 +3521,3 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) } YOSYS_NAMESPACE_END - diff --git a/yosys/frontends/blif/blifparse.cc b/yosys/frontends/blif/blifparse.cc index 6d4d60870..9116b257f 100644 --- a/yosys/frontends/blif/blifparse.cc +++ b/yosys/frontends/blif/blifparse.cc @@ -55,12 +55,37 @@ static bool read_next_line(char *&buffer, size_t &buffer_size, int &line_count, } } -void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode) +static std::pair wideports_split(std::string name) +{ + int pos = -1; + + if (name.empty() || name.back() != ']') + goto failed; + + for (int i = 0; i+1 < GetSize(name); i++) { + if (name[i] == '[') + pos = i; + else if (name[i] < '0' || name[i] > '9') + pos = -1; + else if (i == pos+1 && name[i] == '0' && name[i+1] != ']') + pos = -1; + } + + if (pos >= 0) + return std::pair("\\" + name.substr(0, pos), atoi(name.c_str() + pos+1)+1); + +failed: + return std::pair("\\" + name, 0); +} + +void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean, bool sop_mode, bool wideports) { RTLIL::Module *module = nullptr; RTLIL::Const *lutptr = NULL; RTLIL::Cell *sopcell = NULL; + RTLIL::Cell *lastcell = nullptr; RTLIL::State lut_default_state = RTLIL::State::Sx; + std::string err_reason; int blif_maxnum = 0, sopmode = -1; auto blif_wire = [&](const std::string &wire_name) -> Wire* @@ -96,6 +121,8 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo dict *obj_attributes = nullptr; dict *obj_parameters = nullptr; + dict> wideports_cache; + size_t buffer_size = 4096; char *buffer = (char*)malloc(buffer_size); int line_count = 0; @@ -134,6 +161,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo if (module != nullptr) goto error; module = new RTLIL::Module; + lastcell = nullptr; module->name = RTLIL::escape_id(strtok(NULL, " \t\r\n")); obj_attributes = &module->attributes; obj_parameters = nullptr; @@ -148,7 +176,32 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo if (!strcmp(cmd, ".end")) { + for (auto &wp : wideports_cache) + { + auto name = wp.first; + int width = wp.second.first; + bool isinput = wp.second.second; + + RTLIL::Wire *wire = module->addWire(name, width); + wire->port_input = isinput; + wire->port_output = !isinput; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + if (isinput) + module->connect(other_wire, SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), other_wire); + } + } + } + module->fixup_ports(); + wideports_cache.clear(); if (run_clean) { @@ -182,14 +235,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo } module = nullptr; + lastcell = nullptr; obj_attributes = nullptr; obj_parameters = nullptr; continue; } - if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) { + if (!strcmp(cmd, ".inputs") || !strcmp(cmd, ".outputs")) + { char *p; - while ((p = strtok(NULL, " \t\r\n")) != NULL) { + while ((p = strtok(NULL, " \t\r\n")) != NULL) + { RTLIL::IdString wire_name(stringf("\\%s", p)); RTLIL::Wire *wire = module->wire(wire_name); if (wire == nullptr) @@ -198,12 +254,36 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo wire->port_input = true; else wire->port_output = true; + + if (wideports) { + std::pair wp = wideports_split(p); + if (wp.second > 0) { + wideports_cache[wp.first].first = std::max(wideports_cache[wp.first].first, wp.second); + wideports_cache[wp.first].second = !strcmp(cmd, ".inputs"); + } + } } obj_attributes = nullptr; obj_parameters = nullptr; continue; } + if (!strcmp(cmd, ".cname")) + { + char *p = strtok(NULL, " \t\r\n"); + if (p == NULL) + goto error; + + if(lastcell == nullptr || module == nullptr) + { + err_reason = stringf("No primitive object to attach .cname %s.", p); + goto error_with_reason; + } + + module->rename(lastcell, p); + continue; + } + if (!strcmp(cmd, ".attr") || !strcmp(cmd, ".param")) { char *n = strtok(NULL, " \t\r\n"); char *v = strtok(NULL, "\r\n"); @@ -221,12 +301,16 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo const_v.bits[i] = v[n-i-1] != '0' ? State::S1 : State::S0; } if (!strcmp(cmd, ".attr")) { - if (obj_attributes == nullptr) - goto error; + if (obj_attributes == nullptr) { + err_reason = stringf("No object to attach .attr too."); + goto error_with_reason; + } (*obj_attributes)[id_n] = const_v; } else { - if (obj_parameters == nullptr) - goto error; + if (obj_parameters == nullptr) { + err_reason = stringf("No object to attach .param too."); + goto error_with_reason; + } (*obj_parameters)[id_n] = const_v; } continue; @@ -271,6 +355,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo } } + lastcell = cell; obj_attributes = &cell->attributes; obj_parameters = &cell->parameters; continue; @@ -285,14 +370,45 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo IdString celltype = RTLIL::escape_id(p); RTLIL::Cell *cell = module->addCell(NEW_ID, celltype); - while ((p = strtok(NULL, " \t\r\n")) != NULL) { + dict> cell_wideports_cache; + + 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()); + + if (wideports) { + std::pair wp = wideports_split(p); + if (wp.second > 0) + cell_wideports_cache[wp.first][wp.second-1] = blif_wire(q); + else + cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); + } else { + cell->setPort(RTLIL::escape_id(p), *q ? blif_wire(q) : SigSpec()); + } } + for (auto &it : cell_wideports_cache) + { + int width = 0; + for (auto &b : it.second) + width = std::max(width, b.first + 1); + + SigSpec sig; + + for (int i = 0; i < width; i++) { + if (it.second.count(i)) + sig.append(it.second.at(i)); + else + sig.append(module->addWire(NEW_ID)); + } + + cell->setPort(it.first, sig); + } + + lastcell = cell; obj_attributes = &cell->attributes; obj_parameters = &cell->parameters; continue; @@ -301,7 +417,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo obj_attributes = nullptr; obj_parameters = nullptr; - if (!strcmp(cmd, ".barbuf")) + if (!strcmp(cmd, ".barbuf") || !strcmp(cmd, ".conn")) { char *p = strtok(NULL, " \t\r\n"); if (p == NULL) @@ -369,6 +485,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo sopcell->setPort("\\A", input_sig); sopcell->setPort("\\Y", output_sig); sopmode = -1; + lastcell = sopcell; } else { @@ -379,6 +496,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo cell->setPort("\\Y", output_sig); lutptr = &cell->parameters.at("\\LUT"); lut_default_state = RTLIL::State::Sx; + lastcell = cell; } continue; } @@ -432,7 +550,7 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo if (lutptr) { - if (input_len > 8) + if (input_len > 12) goto error; for (int i = 0; i < (1 << input_len); i++) { @@ -452,13 +570,17 @@ void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bo } } + return; + error: log_error("Syntax error in line %d!\n", line_count); +error_with_reason: + log_error("Syntax error in line %d: %s\n", line_count, err_reason.c_str()); } struct BlifFrontend : public Frontend { BlifFrontend() : Frontend("blif", "read BLIF file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -469,10 +591,15 @@ struct BlifFrontend : public Frontend { log(" -sop\n"); log(" Create $sop cells instead of $lut cells\n"); log("\n"); + log(" -wideports\n"); + log(" Merge ports that match the pattern 'name[int]' into a single\n"); + log(" multi-bit port 'name'.\n"); + log("\n"); } - virtual void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool sop_mode = false; + bool wideports = false; log_header(design, "Executing BLIF frontend.\n"); @@ -483,11 +610,15 @@ struct BlifFrontend : public Frontend { sop_mode = true; continue; } + if (arg == "-wideports") { + wideports = true; + continue; + } break; } extra_args(f, filename, args, argidx); - parse_blif(design, *f, "", true, sop_mode); + parse_blif(design, *f, "", true, sop_mode, wideports); } } BlifFrontend; diff --git a/yosys/frontends/blif/blifparse.h b/yosys/frontends/blif/blifparse.h index 058087d81..955b6aacf 100644 --- a/yosys/frontends/blif/blifparse.h +++ b/yosys/frontends/blif/blifparse.h @@ -24,7 +24,8 @@ YOSYS_NAMESPACE_BEGIN -extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, bool run_clean = false, bool sop_mode = false); +extern void parse_blif(RTLIL::Design *design, std::istream &f, std::string dff_name, + bool run_clean = false, bool sop_mode = false, bool wideports = false); YOSYS_NAMESPACE_END diff --git a/yosys/frontends/ilang/.gitignore b/yosys/frontends/ilang/.gitignore index 43106a814..f586b33c7 100644 --- a/yosys/frontends/ilang/.gitignore +++ b/yosys/frontends/ilang/.gitignore @@ -1,4 +1,4 @@ ilang_lexer.cc ilang_parser.output ilang_parser.tab.cc -ilang_parser.tab.h +ilang_parser.tab.hh diff --git a/yosys/frontends/ilang/Makefile.inc b/yosys/frontends/ilang/Makefile.inc index e2a476c93..6f1f0e8fc 100644 --- a/yosys/frontends/ilang/Makefile.inc +++ b/yosys/frontends/ilang/Makefile.inc @@ -1,15 +1,14 @@ GENFILES += frontends/ilang/ilang_parser.tab.cc -GENFILES += frontends/ilang/ilang_parser.tab.h +GENFILES += frontends/ilang/ilang_parser.tab.hh GENFILES += frontends/ilang/ilang_parser.output GENFILES += frontends/ilang/ilang_lexer.cc frontends/ilang/ilang_parser.tab.cc: frontends/ilang/ilang_parser.y $(Q) mkdir -p $(dir $@) - $(P) $(BISON) -d -r all -b frontends/ilang/ilang_parser $< - $(Q) mv frontends/ilang/ilang_parser.tab.c frontends/ilang/ilang_parser.tab.cc + $(P) $(BISON) -o $@ -d -r all -b frontends/ilang/ilang_parser $< -frontends/ilang/ilang_parser.tab.h: frontends/ilang/ilang_parser.tab.cc +frontends/ilang/ilang_parser.tab.hh: frontends/ilang/ilang_parser.tab.cc frontends/ilang/ilang_lexer.cc: frontends/ilang/ilang_lexer.l $(Q) mkdir -p $(dir $@) diff --git a/yosys/frontends/ilang/ilang_frontend.cc b/yosys/frontends/ilang/ilang_frontend.cc index ed6789987..6b302a796 100644 --- a/yosys/frontends/ilang/ilang_frontend.cc +++ b/yosys/frontends/ilang/ilang_frontend.cc @@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN struct IlangFrontend : public Frontend { IlangFrontend() : Frontend("ilang", "read modules from ilang file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -44,11 +44,39 @@ struct IlangFrontend : public Frontend { log("Load modules from an ilang file to the current design. (ilang is a text\n"); log("representation of a design in yosys's internal format.)\n"); log("\n"); + log(" -nooverwrite\n"); + log(" ignore re-definitions of modules. (the default behavior is to\n"); + log(" create an error message if the existing module is not a blackbox\n"); + log(" module, and overwrite the existing module if it is a blackbox module.)\n"); + log("\n"); + log(" -overwrite\n"); + log(" overwrite existing modules with the same name\n"); + log("\n"); } - virtual void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { + ILANG_FRONTEND::flag_nooverwrite = false; + ILANG_FRONTEND::flag_overwrite = false; + log_header(design, "Executing ILANG frontend.\n"); - extra_args(f, filename, args, 1); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-nooverwrite") { + ILANG_FRONTEND::flag_nooverwrite = true; + ILANG_FRONTEND::flag_overwrite = false; + continue; + } + if (arg == "-overwrite") { + ILANG_FRONTEND::flag_nooverwrite = false; + ILANG_FRONTEND::flag_overwrite = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + log("Input filename: %s\n", filename.c_str()); ILANG_FRONTEND::lexin = f; diff --git a/yosys/frontends/ilang/ilang_frontend.h b/yosys/frontends/ilang/ilang_frontend.h index ad3ffec90..052dd4cb2 100644 --- a/yosys/frontends/ilang/ilang_frontend.h +++ b/yosys/frontends/ilang/ilang_frontend.h @@ -32,6 +32,8 @@ YOSYS_NAMESPACE_BEGIN namespace ILANG_FRONTEND { extern std::istream *lexin; extern RTLIL::Design *current_design; + extern bool flag_nooverwrite; + extern bool flag_overwrite; } YOSYS_NAMESPACE_END diff --git a/yosys/frontends/ilang/ilang_lexer.l b/yosys/frontends/ilang/ilang_lexer.l index 842388548..d8e01ae4d 100644 --- a/yosys/frontends/ilang/ilang_lexer.l +++ b/yosys/frontends/ilang/ilang_lexer.l @@ -30,7 +30,7 @@ #endif #include "frontends/ilang/ilang_frontend.h" -#include "ilang_parser.tab.h" +#include "ilang_parser.tab.hh" USING_YOSYS_NAMESPACE diff --git a/yosys/frontends/ilang/ilang_parser.y b/yosys/frontends/ilang/ilang_parser.y index bfc062fec..5bcc01f42 100644 --- a/yosys/frontends/ilang/ilang_parser.y +++ b/yosys/frontends/ilang/ilang_parser.y @@ -37,6 +37,8 @@ namespace ILANG_FRONTEND { std::vector*> switch_stack; std::vector case_stack; dict attrbuf; + bool flag_nooverwrite, flag_overwrite; + bool delete_current_module; } using namespace ILANG_FRONTEND; YOSYS_NAMESPACE_END @@ -93,18 +95,36 @@ design: module: TOK_MODULE TOK_ID EOL { - if (current_design->has($2)) - rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + delete_current_module = false; + if (current_design->has($2)) { + RTLIL::Module *existing_mod = current_design->module($2); + if (!flag_overwrite && attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()) { + log("Ignoring blackbox re-definition of module %s.\n", $2); + delete_current_module = true; + } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { + rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); + } else if (flag_nooverwrite) { + log("Ignoring re-definition of module %s.\n", $2); + delete_current_module = true; + } else { + log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", $2); + current_design->remove(existing_mod); + } + } current_module = new RTLIL::Module; current_module->name = $2; current_module->attributes = attrbuf; - current_design->add(current_module); + if (!delete_current_module) + current_design->add(current_module); attrbuf.clear(); free($2); } module_body TOK_END { if (attrbuf.size() != 0) rtlil_frontend_ilang_yyerror("dangling attribute"); current_module->fixup_ports(); + if (delete_current_module) + delete current_module; + current_module = nullptr; } EOL; module_body: @@ -387,17 +407,13 @@ sigspec: $$ = new RTLIL::SigSpec(current_module->wires_[$1]); free($1); } | - TOK_ID '[' TOK_INT ']' { - if (current_module->wires_.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); - $$ = new RTLIL::SigSpec(current_module->wires_[$1], $3); - free($1); + sigspec '[' TOK_INT ']' { + $$ = new RTLIL::SigSpec($1->extract($3)); + delete $1; } | - TOK_ID '[' TOK_INT ':' TOK_INT ']' { - if (current_module->wires_.count($1) == 0) - rtlil_frontend_ilang_yyerror(stringf("ilang error: wire %s not found", $1).c_str()); - $$ = new RTLIL::SigSpec(current_module->wires_[$1], $5, $3 - $5 + 1); - free($1); + sigspec '[' TOK_INT ':' TOK_INT ']' { + $$ = new RTLIL::SigSpec($1->extract($5, $3 - $5 + 1)); + delete $1; } | '{' sigspec_list '}' { $$ = $2; diff --git a/yosys/frontends/json/Makefile.inc b/yosys/frontends/json/Makefile.inc new file mode 100644 index 000000000..0fe1b3722 --- /dev/null +++ b/yosys/frontends/json/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += frontends/json/jsonparse.o + diff --git a/yosys/frontends/json/jsonparse.cc b/yosys/frontends/json/jsonparse.cc new file mode 100644 index 000000000..82361ea9b --- /dev/null +++ b/yosys/frontends/json/jsonparse.cc @@ -0,0 +1,541 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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" + +YOSYS_NAMESPACE_BEGIN + +struct JsonNode +{ + char type; // S=String, N=Number, A=Array, D=Dict + string data_string; + int data_number; + vector data_array; + dict data_dict; + vector data_dict_keys; + + JsonNode(std::istream &f) + { + type = 0; + data_number = 0; + + while (1) + { + int ch = f.get(); + + if (ch == EOF) + log_error("Unexpected EOF in JSON file.\n"); + + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') + continue; + + if (ch == '"') + { + type = 'S'; + + while (1) + { + ch = f.get(); + + if (ch == EOF) + log_error("Unexpected EOF in JSON string.\n"); + + if (ch == '"') + break; + + if (ch == '\\') { + int ch = f.get(); + + if (ch == EOF) + log_error("Unexpected EOF in JSON string.\n"); + } + + data_string += ch; + } + + break; + } + + if ('0' <= ch && ch <= '9') + { + type = 'N'; + data_number = ch - '0'; + data_string += ch; + + while (1) + { + ch = f.get(); + + if (ch == EOF) + break; + + if (ch == '.') + goto parse_real; + + if (ch < '0' || '9' < ch) { + f.unget(); + break; + } + + data_number = data_number*10 + (ch - '0'); + data_string += ch; + } + + data_string = ""; + break; + + parse_real: + type = 'S'; + data_number = 0; + data_string += ch; + + while (1) + { + ch = f.get(); + + if (ch == EOF) + break; + + if (ch < '0' || '9' < ch) { + f.unget(); + break; + } + + data_string += ch; + } + + break; + } + + if (ch == '[') + { + type = 'A'; + + while (1) + { + ch = f.get(); + + if (ch == EOF) + log_error("Unexpected EOF in JSON file.\n"); + + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') + continue; + + if (ch == ']') + break; + + f.unget(); + data_array.push_back(new JsonNode(f)); + } + + break; + } + + if (ch == '{') + { + type = 'D'; + + while (1) + { + ch = f.get(); + + if (ch == EOF) + log_error("Unexpected EOF in JSON file.\n"); + + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ',') + continue; + + if (ch == '}') + break; + + f.unget(); + JsonNode key(f); + + while (1) + { + ch = f.get(); + + if (ch == EOF) + log_error("Unexpected EOF in JSON file.\n"); + + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == ':') + continue; + + f.unget(); + break; + } + + JsonNode *value = new JsonNode(f); + + if (key.type != 'S') + log_error("Unexpected non-string key in JSON dict.\n"); + + data_dict[key.data_string] = value; + data_dict_keys.push_back(key.data_string); + } + + break; + } + + log_error("Unexpected character in JSON file: '%c'\n", ch); + } + } + + ~JsonNode() + { + for (auto it : data_array) + delete it; + for (auto &it : data_dict) + delete it.second; + } +}; + +void json_parse_attr_param(dict &results, JsonNode *node) +{ + if (node->type != 'D') + log_error("JSON attributes or parameters node is not a dictionary.\n"); + + for (auto it : node->data_dict) + { + IdString key = RTLIL::escape_id(it.first.c_str()); + JsonNode *value_node = it.second; + Const value; + + if (value_node->type == 'S') { + string &s = value_node->data_string; + if (s.find_first_not_of("01xz") == string::npos) + value = Const::from_string(s); + else + value = Const(s); + } else + if (value_node->type == 'N') { + value = Const(value_node->data_number, 32); + } else + if (value_node->type == 'A') { + log_error("JSON attribute or parameter value is an array.\n"); + } else + if (value_node->type == 'D') { + log_error("JSON attribute or parameter value is a dict.\n"); + } else { + log_abort(); + } + + results[key] = value; + } +} + +void json_import(Design *design, string &modname, JsonNode *node) +{ + log("Importing module %s from JSON tree.\n", modname.c_str()); + + Module *module = new RTLIL::Module; + module->name = RTLIL::escape_id(modname.c_str()); + + if (design->module(module->name)) + log_error("Re-definition of module %s.\n", log_id(module->name)); + + design->add(module); + + if (node->data_dict.count("attributes")) + json_parse_attr_param(module->attributes, node->data_dict.at("attributes")); + + dict signal_bits; + + if (node->data_dict.count("ports")) + { + JsonNode *ports_node = node->data_dict.at("ports"); + + if (ports_node->type != 'D') + log_error("JSON ports node is not a dictionary.\n"); + + for (int port_id = 1; port_id <= GetSize(ports_node->data_dict_keys); port_id++) + { + IdString port_name = RTLIL::escape_id(ports_node->data_dict_keys[port_id-1].c_str()); + JsonNode *port_node = ports_node->data_dict.at(ports_node->data_dict_keys[port_id-1]); + + if (port_node->type != 'D') + log_error("JSON port node '%s' is not a dictionary.\n", log_id(port_name)); + + if (port_node->data_dict.count("direction") == 0) + log_error("JSON port node '%s' has no direction attribute.\n", log_id(port_name)); + + if (port_node->data_dict.count("bits") == 0) + log_error("JSON port node '%s' has no bits attribute.\n", log_id(port_name)); + + JsonNode *port_direction_node = port_node->data_dict.at("direction"); + JsonNode *port_bits_node = port_node->data_dict.at("bits"); + + if (port_direction_node->type != 'S') + log_error("JSON port node '%s' has non-string direction attribute.\n", log_id(port_name)); + + if (port_bits_node->type != 'A') + log_error("JSON port node '%s' has non-array bits attribute.\n", log_id(port_name)); + + Wire *port_wire = module->wire(port_name); + + if (port_wire == nullptr) + port_wire = module->addWire(port_name, GetSize(port_bits_node->data_array)); + + if (port_direction_node->data_string == "input") { + port_wire->port_input = true; + } else + if (port_direction_node->data_string == "output") { + port_wire->port_output = true; + } else + if (port_direction_node->data_string == "inout") { + port_wire->port_input = true; + port_wire->port_output = true; + } else + log_error("JSON port node '%s' has invalid '%s' direction attribute.\n", log_id(port_name), port_direction_node->data_string.c_str()); + + port_wire->port_id = port_id; + + for (int i = 0; i < GetSize(port_bits_node->data_array); i++) + { + JsonNode *bitval_node = port_bits_node->data_array.at(i); + SigBit sigbit(port_wire, i); + + if (bitval_node->type == 'S') { + if (bitval_node->data_string == "0") + module->connect(sigbit, State::S0); + else if (bitval_node->data_string == "1") + module->connect(sigbit, State::S1); + else if (bitval_node->data_string == "x") + module->connect(sigbit, State::Sx); + else if (bitval_node->data_string == "z") + module->connect(sigbit, State::Sz); + else + log_error("JSON port node '%s' has invalid '%s' bit string value on bit %d.\n", + log_id(port_name), bitval_node->data_string.c_str(), i); + } else + if (bitval_node->type == 'N') { + int bitidx = bitval_node->data_number; + if (signal_bits.count(bitidx)) { + if (port_wire->port_output) { + module->connect(sigbit, signal_bits.at(bitidx)); + } else { + module->connect(signal_bits.at(bitidx), sigbit); + signal_bits[bitidx] = sigbit; + } + } else { + signal_bits[bitidx] = sigbit; + } + } else + log_error("JSON port node '%s' has invalid bit value on bit %d.\n", log_id(port_name), i); + } + } + + module->fixup_ports(); + } + + if (node->data_dict.count("netnames")) + { + JsonNode *netnames_node = node->data_dict.at("netnames"); + + if (netnames_node->type != 'D') + log_error("JSON netnames node is not a dictionary.\n"); + + for (auto &net : netnames_node->data_dict) + { + IdString net_name = RTLIL::escape_id(net.first.c_str()); + JsonNode *net_node = net.second; + + if (net_node->type != 'D') + log_error("JSON netname node '%s' is not a dictionary.\n", log_id(net_name)); + + if (net_node->data_dict.count("bits") == 0) + log_error("JSON netname node '%s' has no bits attribute.\n", log_id(net_name)); + + JsonNode *bits_node = net_node->data_dict.at("bits"); + + if (bits_node->type != 'A') + log_error("JSON netname node '%s' has non-array bits attribute.\n", log_id(net_name)); + + Wire *wire = module->wire(net_name); + + if (wire == nullptr) + wire = module->addWire(net_name, GetSize(bits_node->data_array)); + + for (int i = 0; i < GetSize(bits_node->data_array); i++) + { + JsonNode *bitval_node = bits_node->data_array.at(i); + SigBit sigbit(wire, i); + + if (bitval_node->type == 'S') { + if (bitval_node->data_string == "0") + module->connect(sigbit, State::S0); + else if (bitval_node->data_string == "1") + module->connect(sigbit, State::S1); + else if (bitval_node->data_string == "x") + module->connect(sigbit, State::Sx); + else if (bitval_node->data_string == "z") + module->connect(sigbit, State::Sz); + else + log_error("JSON netname node '%s' has invalid '%s' bit string value on bit %d.\n", + log_id(net_name), bitval_node->data_string.c_str(), i); + } else + if (bitval_node->type == 'N') { + int bitidx = bitval_node->data_number; + if (signal_bits.count(bitidx)) { + if (sigbit != signal_bits.at(bitidx)) + module->connect(sigbit, signal_bits.at(bitidx)); + } else { + signal_bits[bitidx] = sigbit; + } + } else + log_error("JSON netname node '%s' has invalid bit value on bit %d.\n", log_id(net_name), i); + } + + if (net_node->data_dict.count("attributes")) + json_parse_attr_param(wire->attributes, net_node->data_dict.at("attributes")); + } + } + + if (node->data_dict.count("cells")) + { + JsonNode *cells_node = node->data_dict.at("cells"); + + if (cells_node->type != 'D') + log_error("JSON cells node is not a dictionary.\n"); + + for (auto &cell_node_it : cells_node->data_dict) + { + IdString cell_name = RTLIL::escape_id(cell_node_it.first.c_str()); + JsonNode *cell_node = cell_node_it.second; + + if (cell_node->type != 'D') + log_error("JSON cells node '%s' is not a dictionary.\n", log_id(cell_name)); + + if (cell_node->data_dict.count("type") == 0) + log_error("JSON cells node '%s' has no type attribute.\n", log_id(cell_name)); + + JsonNode *type_node = cell_node->data_dict.at("type"); + + if (type_node->type != 'S') + log_error("JSON cells node '%s' has a non-string type.\n", log_id(cell_name)); + + IdString cell_type = RTLIL::escape_id(type_node->data_string.c_str()); + + Cell *cell = module->addCell(cell_name, cell_type); + + if (cell_node->data_dict.count("connections") == 0) + log_error("JSON cells node '%s' has no connections attribute.\n", log_id(cell_name)); + + JsonNode *connections_node = cell_node->data_dict.at("connections"); + + if (connections_node->type != 'D') + log_error("JSON cells node '%s' has non-dictionary connections attribute.\n", log_id(cell_name)); + + for (auto &conn_it : connections_node->data_dict) + { + IdString conn_name = RTLIL::escape_id(conn_it.first.c_str()); + JsonNode *conn_node = conn_it.second; + + if (conn_node->type != 'A') + log_error("JSON cells node '%s' connection '%s' is not an array.\n", log_id(cell_name), log_id(conn_name)); + + SigSpec sig; + + for (int i = 0; i < GetSize(conn_node->data_array); i++) + { + JsonNode *bitval_node = conn_node->data_array.at(i); + + if (bitval_node->type == 'S') { + if (bitval_node->data_string == "0") + sig.append(State::S0); + else if (bitval_node->data_string == "1") + sig.append(State::S1); + else if (bitval_node->data_string == "x") + sig.append(State::Sx); + else if (bitval_node->data_string == "z") + sig.append(State::Sz); + else + log_error("JSON cells node '%s' connection '%s' has invalid '%s' bit string value on bit %d.\n", + log_id(cell_name), log_id(conn_name), bitval_node->data_string.c_str(), i); + } else + if (bitval_node->type == 'N') { + int bitidx = bitval_node->data_number; + if (signal_bits.count(bitidx) == 0) + signal_bits[bitidx] = module->addWire(NEW_ID); + sig.append(signal_bits.at(bitidx)); + } else + log_error("JSON cells node '%s' connection '%s' has invalid bit value on bit %d.\n", + log_id(cell_name), log_id(conn_name), i); + + } + + cell->setPort(conn_name, sig); + } + + if (cell_node->data_dict.count("attributes")) + json_parse_attr_param(cell->attributes, cell_node->data_dict.at("attributes")); + + if (cell_node->data_dict.count("parameters")) + json_parse_attr_param(cell->parameters, cell_node->data_dict.at("parameters")); + } + } +} + +struct JsonFrontend : public Frontend { + JsonFrontend() : Frontend("json", "read JSON file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_json [filename]\n"); + log("\n"); + log("Load modules from a JSON file into the current design See \"help write_json\"\n"); + log("for a description of the file format.\n"); + log("\n"); + } + void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing JSON 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); + + JsonNode root(*f); + + if (root.type != 'D') + log_error("JSON root node is not a dictionary.\n"); + + if (root.data_dict.count("modules") != 0) + { + JsonNode *modules = root.data_dict.at("modules"); + + if (modules->type != 'D') + log_error("JSON modules node is not a dictionary.\n"); + + for (auto &it : modules->data_dict) + json_import(design, it.first, it.second); + } + } +} JsonFrontend; + +YOSYS_NAMESPACE_END + diff --git a/yosys/frontends/liberty/liberty.cc b/yosys/frontends/liberty/liberty.cc index 4666c818c..6e3cffaca 100644 --- a/yosys/frontends/liberty/liberty.cc +++ b/yosys/frontends/liberty/liberty.cc @@ -36,7 +36,8 @@ static RTLIL::SigSpec parse_func_identifier(RTLIL::Module *module, const char *& int id_len = 0; while (('a' <= expr[id_len] && expr[id_len] <= 'z') || ('A' <= expr[id_len] && expr[id_len] <= 'Z') || - ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || expr[id_len] == '_') id_len++; + ('0' <= expr[id_len] && expr[id_len] <= '9') || expr[id_len] == '.' || + expr[id_len] == '_' || expr[id_len] == '[' || expr[id_len] == ']') id_len++; if (id_len == 0) log_error("Expected identifier at `%s'.\n", expr); @@ -148,7 +149,7 @@ static bool parse_func_reduce(RTLIL::Module *module, std::vector &stack } if (0 <= top && stack[top].type == 2) { - if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(') + if (next_token.type == '*' || next_token.type == '&' || next_token.type == 0 || next_token.type == '(' || next_token.type == '!') return false; stack[top].type = 3; return true; @@ -188,7 +189,7 @@ static RTLIL::SigSpec parse_func_expr(RTLIL::Module *module, const char *expr) } token_t next_token(0); - if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|') + if (*expr == '(' || *expr == ')' || *expr == '\'' || *expr == '!' || *expr == '^' || *expr == '*' || *expr == '+' || *expr == '|' || *expr == '&') next_token = token_t(*(expr++)); else next_token = token_t(0, parse_func_identifier(module, expr)); @@ -290,7 +291,7 @@ static void create_ff(RTLIL::Module *module, LibertyAst *node) log_assert(!cell->type.empty()); } -static void create_latch(RTLIL::Module *module, LibertyAst *node) +static bool create_latch(RTLIL::Module *module, LibertyAst *node, bool flag_ignore_miss_data_latch) { 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)))); @@ -309,8 +310,14 @@ static void create_latch(RTLIL::Module *module, LibertyAst *node) 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)); + if (enable_sig.size() == 0 || data_sig.size() == 0) { + if (!flag_ignore_miss_data_latch) + log_error("Latch cell %s has no data_in and/or enable attribute.\n", log_id(module->name)); + else + log("Ignored latch cell %s with no data_in and/or enable attribute.\n", log_id(module->name)); + + return false; + } for (bool rerun_invert_rollback = true; rerun_invert_rollback;) { @@ -399,6 +406,8 @@ static void create_latch(RTLIL::Module *module, LibertyAst *node) cell->setPort("\\D", data_sig); cell->setPort("\\Q", iq_sig); cell->setPort("\\E", enable_sig); + + return true; } void parse_type_map(std::map> &type_map, LibertyAst *ast) @@ -444,7 +453,7 @@ void parse_type_map(std::map> &type_map, struct LibertyFrontend : public Frontend { LibertyFrontend() : Frontend("liberty", "read cells from liberty file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -455,9 +464,13 @@ struct LibertyFrontend : public Frontend { log(" -lib\n"); log(" only create empty blackbox modules\n"); log("\n"); - log(" -ignore_redef\n"); + log(" -nooverwrite\n"); log(" ignore re-definitions of modules. (the default behavior is to\n"); - log(" create an error message.)\n"); + log(" create an error message if the existing module is not a blackbox\n"); + log(" module, and overwrite the existing module if it is a blackbox module.)\n"); + log("\n"); + log(" -overwrite\n"); + log(" overwrite existing modules with the same name\n"); log("\n"); log(" -ignore_miss_func\n"); log(" ignore cells with missing function specification of outputs\n"); @@ -466,16 +479,21 @@ struct LibertyFrontend : public Frontend { log(" ignore cells with a missing or invalid direction\n"); log(" specification on a pin\n"); log("\n"); + log(" -ignore_miss_data_latch\n"); + log(" ignore latches with missing data and/or enable pins\n"); + log("\n"); log(" -setattr \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 args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_lib = false; - bool flag_ignore_redef = false; + bool flag_nooverwrite = false; + bool flag_overwrite = false; bool flag_ignore_miss_func = false; bool flag_ignore_miss_dir = false; + bool flag_ignore_miss_data_latch = false; std::vector attributes; log_header(design, "Executing Liberty frontend.\n"); @@ -487,8 +505,14 @@ struct LibertyFrontend : public Frontend { flag_lib = true; continue; } - if (arg == "-ignore_redef") { - flag_ignore_redef = true; + if (arg == "-ignore_redef" || arg == "-nooverwrite") { + flag_nooverwrite = true; + flag_overwrite = false; + continue; + } + if (arg == "-overwrite") { + flag_nooverwrite = false; + flag_overwrite = true; continue; } if (arg == "-ignore_miss_func") { @@ -499,6 +523,10 @@ struct LibertyFrontend : public Frontend { flag_ignore_miss_dir = true; continue; } + if (arg == "-ignore_miss_data_latch") { + flag_ignore_miss_data_latch = true; + continue; + } if (arg == "-setattr" && argidx+1 < args.size()) { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; @@ -521,9 +549,16 @@ struct LibertyFrontend : public Frontend { std::string cell_name = RTLIL::escape_id(cell->args.at(0)); if (design->has(cell_name)) { - if (flag_ignore_redef) + Module *existing_mod = design->module(cell_name); + if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { + log_error("Re-definition of of cell/module %s!\n", log_id(cell_name)); + } else if (flag_nooverwrite) { + log("Ignoring re-definition of module %s.\n", log_id(cell_name)); continue; - log_error("Duplicate definition of cell/module %s.\n", RTLIL::unescape_id(cell_name).c_str()); + } else { + log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", log_id(cell_name)); + design->remove(existing_mod); + } } // log("Processing cell type %s.\n", RTLIL::unescape_id(cell_name).c_str()); @@ -566,6 +601,12 @@ struct LibertyFrontend : public Frontend { LibertyAst *dir = node->find("direction"); + if (dir == nullptr) { + LibertyAst *pin = node->find("pin"); + if (pin != nullptr) + dir = pin->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)); @@ -575,7 +616,7 @@ struct LibertyFrontend : public Frontend { LibertyAst *bus_type_node = node->find("bus_type"); if (!bus_type_node || !type_map.count(bus_type_node->value)) - log_error("Unkown or unsupported type for bus interface %s on cell %s.\n", + log_error("Unknown or unsupported type for bus interface %s on cell %s.\n", node->args.at(0).c_str(), log_id(cell_name)); int bus_type_width = std::get<0>(type_map.at(bus_type_node->value)); @@ -594,15 +635,24 @@ struct LibertyFrontend : public Frontend { } } - for (auto node : cell->children) + if (!flag_lib) { - if (!flag_lib) { + // some liberty files do not put ff/latch at the beginning of a cell + // try to find "ff" or "latch" and create FF/latch _before_ processing all other nodes + for (auto node : cell->children) + { if (node->id == "ff" && node->args.size() == 2) create_ff(module, node); if (node->id == "latch" && node->args.size() == 2) - create_latch(module, node); + if (!create_latch(module, node, flag_ignore_miss_data_latch)) { + delete module; + goto skip_cell; + } } + } + for (auto node : cell->children) + { if (node->id == "pin" && node->args.size() == 1) { LibertyAst *dir = node->find("direction"); diff --git a/yosys/frontends/verific/Makefile.inc b/yosys/frontends/verific/Makefile.inc index 68ef9aed1..972f4f9f1 100644 --- a/yosys/frontends/verific/Makefile.inc +++ b/yosys/frontends/verific/Makefile.inc @@ -3,6 +3,8 @@ OBJS += frontends/verific/verific.o ifeq ($(ENABLE_VERIFIC),1) +OBJS += frontends/verific/verificsva.o + EXTRA_TARGETS += share/verific share/verific: @@ -11,6 +13,7 @@ share/verific: $(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) chmod -R a+rX share/verific.new $(Q) mv share/verific.new share/verific endif diff --git a/yosys/frontends/verific/README b/yosys/frontends/verific/README new file mode 100644 index 000000000..c76cdd637 --- /dev/null +++ b/yosys/frontends/verific/README @@ -0,0 +1,33 @@ + + +This directory contains Verific bindings for Yosys. +See http://www.verific.com/ for details. + + +Verific Features that should be enabled in your Verific library +=============================================================== + +database/DBCompileFlags.h: + DB_PRESERVE_INITIAL_VALUE + + +Testing Verific+Yosys+SymbiYosys for formal verification +======================================================== + +Install Yosys+Verific, SymbiYosys, and Yices2. Install instructions: +http://symbiyosys.readthedocs.io/en/latest/quickstart.html#installing + +Then run in the following command in this directory: + + sby -f example.sby + +This will generate approximately one page of text outpout. The last lines +should be something like this: + + SBY [example] summary: Elapsed clock time [H:MM:SS (secs)]: 0:00:00 (0) + SBY [example] summary: Elapsed process time [H:MM:SS (secs)]: 0:00:00 (0) + SBY [example] summary: engine_0 (smtbmc yices) returned PASS for basecase + SBY [example] summary: engine_0 (smtbmc yices) returned PASS for induction + SBY [example] summary: successful proof by k-induction. + SBY [example] DONE (PASS, rc=0) + diff --git a/yosys/frontends/verific/build_amd64.txt b/yosys/frontends/verific/build_amd64.txt deleted file mode 100644 index d6952820e..000000000 --- a/yosys/frontends/verific/build_amd64.txt +++ /dev/null @@ -1,30 +0,0 @@ - -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/yosys/frontends/verific/example.sby b/yosys/frontends/verific/example.sby new file mode 100644 index 000000000..ffbf33cab --- /dev/null +++ b/yosys/frontends/verific/example.sby @@ -0,0 +1,16 @@ +# Simple SymbiYosys example job utilizing Verific + +[options] +mode prove +depth 10 + +[engines] +smtbmc yices + +[script] +verific -sv example.sv +verific -import top +prep -top top + +[files] +example.sv diff --git a/yosys/frontends/verific/example.sv b/yosys/frontends/verific/example.sv new file mode 100644 index 000000000..21a5d42c8 --- /dev/null +++ b/yosys/frontends/verific/example.sv @@ -0,0 +1,18 @@ +module top ( + input clk, rst, + output reg [3:0] cnt +); + initial cnt = 0; + + always @(posedge clk) begin + if (rst) + cnt <= 0; + else + cnt <= cnt + 4'd 1; + end + + always @(posedge clk) begin + assume (cnt != 10); + assert (cnt != 15); + end +endmodule diff --git a/yosys/frontends/verific/test_navre.ys b/yosys/frontends/verific/test_navre.ys deleted file mode 100644 index a56b725ac..000000000 --- a/yosys/frontends/verific/test_navre.ys +++ /dev/null @@ -1,18 +0,0 @@ -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/yosys/frontends/verific/verific.cc b/yosys/frontends/verific/verific.cc index 7dd36a747..94138cdd6 100644 --- a/yosys/frontends/verific/verific.cc +++ b/yosys/frontends/verific/verific.cc @@ -29,29 +29,48 @@ # include #endif +#include "frontends/verific/verific.h" + USING_YOSYS_NAMESPACE #ifdef YOSYS_ENABLE_VERIFIC +#ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverloaded-virtual" +#endif #include "veri_file.h" #include "vhdl_file.h" +#include "hier_tree.h" #include "VeriModule.h" +#include "VeriWrite.h" #include "VhdlUnits.h" -#include "DataBase.h" #include "Message.h" +#ifdef __clang__ #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) +#ifdef VERIFIC_NAMESPACE +using namespace Verific; +#endif + +#endif + +#ifdef YOSYS_ENABLE_VERIFIC +YOSYS_NAMESPACE_BEGIN + +int verific_verbose; +bool verific_import_pending; +string verific_error_msg; +int verific_sva_fsm_limit; + +vector verific_incdirs, verific_libdirs; + +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] ", + string message_prefix = stringf("VERIFIC-%s [%s] ", msg_type == VERIFIC_NONE ? "NONE" : msg_type == VERIFIC_ERROR ? "ERROR" : msg_type == VERIFIC_WARNING ? "WARNING" : @@ -59,13 +78,68 @@ static void msg_func(msg_type_t msg_type, const char *message_id, linefile_type 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"); + + string message = linefile ? stringf("%s:%d: ", LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile)) : ""; + message += vstringf(msg, args); + + if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR) + log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str()); + else + log("%s%s\n", message_prefix.c_str(), message.c_str()); + + if (verific_error_msg.empty() && (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_PROGRAM_ERROR)) + verific_error_msg = message; } -static void import_attributes(dict &attributes, DesignObj *obj) +string get_full_netlist_name(Netlist *nl) +{ + if (nl->NumOfRefs() == 1) { + Instance *inst = (Instance*)nl->GetReferences()->GetLast(); + return get_full_netlist_name(inst->Owner()) + "." + inst->Name(); + } + + return nl->CellBaseName(); +} + +// ================================================================== + +VerificImporter::VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover) : + mode_gates(mode_gates), mode_keep(mode_keep), mode_nosva(mode_nosva), + mode_names(mode_names), mode_verific(mode_verific), mode_autocover(mode_autocover) +{ +} + +RTLIL::SigBit VerificImporter::net_map_at(Net *net) +{ + if (net->IsExternalTo(netlist)) + log_error("Found external reference to '%s.%s' in netlist '%s', please use -flatten or -extnets.\n", + get_full_netlist_name(net->Owner()).c_str(), net->Name(), get_full_netlist_name(netlist).c_str()); + + return net_map.at(net); +} + +bool is_blackbox(Netlist *nl) +{ + if (nl->IsBlackBox()) + return true; + + const char *attr = nl->GetAttValue("blackbox"); + if (attr != nullptr && strcmp(attr, "0")) + return true; + + return false; +} + +RTLIL::IdString VerificImporter::new_verific_id(Verific::DesignObj *obj) +{ + std::string s = stringf("$verific$%s", obj->Name()); + if (obj->Linefile()) + s += stringf("$%s:%d", Verific::LineFile::GetFileName(obj->Linefile()), Verific::LineFile::GetLineNo(obj->Linefile())); + s += stringf("$%d", autoidx++); + return s; +} + +void VerificImporter::import_attributes(dict &attributes, DesignObj *obj) { MapIter mi; Att *attr; @@ -74,44 +148,47 @@ static void import_attributes(dict &attributes, D attributes["\\src"] = stringf("%s:%d", LineFile::GetFileName(obj->Linefile()), LineFile::GetLineNo(obj->Linefile())); // FIXME: Parse numeric attributes - FOREACH_ATTRIBUTE(obj, mi, attr) + FOREACH_ATTRIBUTE(obj, mi, attr) { + if (attr->Key()[0] == ' ' || attr->Value() == nullptr) + continue; attributes[RTLIL::escape_id(attr->Key())] = RTLIL::Const(std::string(attr->Value())); + } } -static RTLIL::SigSpec operatorInput(Instance *inst, std::map &net_map) +RTLIL::SigSpec VerificImporter::operatorInput(Instance *inst) { 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))); + 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_map) +RTLIL::SigSpec VerificImporter::operatorInput1(Instance *inst) { 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))); + 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_map) +RTLIL::SigSpec VerificImporter::operatorInput2(Instance *inst) { 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))); + 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_map) +RTLIL::SigSpec VerificImporter::operatorInport(Instance *inst, const char *portname) { PortBus *portbus = inst->View()->GetPortBus(portname); if (portbus) { @@ -124,7 +201,7 @@ static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std:: else if (net->IsPwr()) sig.append(RTLIL::State::S1); else - sig.append(net_map.at(net)); + sig.append(net_map_at(net)); } else sig.append(RTLIL::State::Sz); } @@ -133,21 +210,21 @@ static RTLIL::SigSpec operatorInport(Instance *inst, const char *portname, std:: Port *port = inst->View()->GetPort(portname); log_assert(port != NULL); Net *net = inst->GetNet(port); - return net_map.at(net); + return net_map_at(net); } } -static RTLIL::SigSpec operatorOutput(Instance *inst, std::map &net_map, RTLIL::Module *module) +RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *any_all_nets) { 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))); + if (inst->GetOutputBit(i) && (!any_all_nets || !any_all_nets->count(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); + dummy_wire = module->addWire(new_verific_id(inst)); else dummy_wire->width++; sig.append(RTLIL::SigSpec(dummy_wire, dummy_wire->width - 1)); @@ -155,237 +232,270 @@ static RTLIL::SigSpec operatorOutput(Instance *inst, std::map &net_map, Instance *inst) +bool VerificImporter::import_netlist_instance_gates(Instance *inst, RTLIL::IdString inst_name) { 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())); + module->addAndGate(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())); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + module->addAndGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput())); return true; } 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())); + module->addOrGate(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())); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + module->addOrGate(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + module->addNotGate(inst_name, tmp, net_map_at(inst->GetOutput())); return true; } 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())); + module->addXorGate(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())); + module->addXnorGate(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())); + auto outnet = inst->GetOutput(); + if (!any_all_nets.count(outnet)) + module->addBufGate(inst_name, net_map_at(inst->GetInput()), net_map_at(outnet)); return true; } if (inst->Type() == PRIM_INV) { - module->addNotGate(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + module->addNotGate(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())); + module->addMuxGate(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())); + module->addMuxGate(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); + 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_verific_id(inst)); + RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst)); + RTLIL::SigSpec tmp1 = module->addWire(new_verific_id(inst)); + RTLIL::SigSpec tmp2 = module->addWire(new_verific_id(inst)); + RTLIL::SigSpec tmp3 = module->addWire(new_verific_id(inst)); + module->addXorGate(new_verific_id(inst), a, b, tmp1); + module->addXorGate(inst_name, tmp1, c, y); + module->addAndGate(new_verific_id(inst), tmp1, c, tmp2); + module->addAndGate(new_verific_id(inst), a, b, tmp3); + module->addOrGate(new_verific_id(inst), tmp2, tmp3, x); return true; } if (inst->Type() == PRIM_DFFRS) { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + 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())); + clocking.addDff(inst_name, 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); + clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), State::S0); 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); + clocking.addAdff(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), State::S1); 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())); + clocking.addDffsr(inst_name, 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_map, Instance *inst) +bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdString inst_name) { + RTLIL::Cell *cell = nullptr; + 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())); + cell = module->addAnd(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_NAND) { - RTLIL::SigSpec tmp = module->addWire(NEW_ID); - module->addAnd(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp); - module->addNot(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput())); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + cell = module->addAnd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + import_attributes(cell->attributes, inst); + cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_OR) { - module->addOr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput())); + cell = module->addOr(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_NOR) { - RTLIL::SigSpec tmp = module->addWire(NEW_ID); - module->addOr(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), tmp); - module->addNot(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetOutput())); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst)); + cell = module->addOr(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), tmp); + import_attributes(cell->attributes, inst); + cell = module->addNot(inst_name, tmp, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_XOR) { - module->addXor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput())); + cell = module->addXor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_XNOR) { - module->addXnor(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), net_map.at(inst->GetOutput())); + cell = module->addXnor(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_INV) { - module->addNot(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + cell = module->addNot(inst_name, net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_MUX) { - module->addMux(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())); + cell = module->addMux(inst_name, net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_TRI) { - module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::State::Sz, net_map.at(inst->GetInput()), net_map.at(inst->GetControl()), net_map.at(inst->GetOutput())); + cell = module->addMux(inst_name, RTLIL::State::Sz, net_map_at(inst->GetInput()), net_map_at(inst->GetControl()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_FADD) { - RTLIL::SigSpec a_plus_b = module->addWire(NEW_ID, 2); - RTLIL::SigSpec y = inst->GetOutput() ? net_map.at(inst->GetOutput()) : module->addWire(NEW_ID); + RTLIL::SigSpec a_plus_b = module->addWire(new_verific_id(inst), 2); + RTLIL::SigSpec y = inst->GetOutput() ? net_map_at(inst->GetOutput()) : module->addWire(new_verific_id(inst)); if (inst->GetCout()) - y.append(net_map.at(inst->GetCout())); - module->addAdd(NEW_ID, net_map.at(inst->GetInput1()), net_map.at(inst->GetInput2()), a_plus_b); - module->addAdd(RTLIL::escape_id(inst->Name()), a_plus_b, net_map.at(inst->GetCin()), y); + y.append(net_map_at(inst->GetCout())); + cell = module->addAdd(new_verific_id(inst), net_map_at(inst->GetInput1()), net_map_at(inst->GetInput2()), a_plus_b); + import_attributes(cell->attributes, inst); + cell = module->addAdd(inst_name, a_plus_b, net_map_at(inst->GetCin()), y); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_DFFRS) { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + 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())); + cell = clocking.addDff(inst_name, 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); + cell = clocking.addAdff(inst_name, net_map_at(inst->GetReset()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput()), RTLIL::State::S0); else if (inst->GetReset()->IsGnd()) - 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); + cell = clocking.addAdff(inst_name, 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())); + cell = clocking.addDffsr(inst_name, net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == PRIM_DLATCHRS) { if (inst->GetSet()->IsGnd() && inst->GetReset()->IsGnd()) - module->addDlatch(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetControl()), net_map.at(inst->GetInput()), net_map.at(inst->GetOutput())); + cell = module->addDlatch(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); else - module->addDlatchsr(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())); + cell = module->addDlatchsr(inst_name, net_map_at(inst->GetControl()), net_map_at(inst->GetSet()), net_map_at(inst->GetReset()), + net_map_at(inst->GetInput()), net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } - #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 IN operatorInput(inst) + #define IN1 operatorInput1(inst) + #define IN2 operatorInput2(inst) + #define OUT operatorOutput(inst) + #define FILTERED_OUT operatorOutput(inst, &any_all_nets) #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())); + out.append(net_map_at(inst->GetCout())); if (inst->GetCin()->IsGnd()) { - module->addAdd(RTLIL::escape_id(inst->Name()), IN1, IN2, out, SIGNED); + cell = module->addAdd(inst_name, IN1, IN2, out, SIGNED); + import_attributes(cell->attributes, inst); } else { - RTLIL::SigSpec tmp = module->addWire(NEW_ID, GetSize(out)); - module->addAdd(NEW_ID, IN1, IN2, tmp, SIGNED); - module->addAdd(RTLIL::escape_id(inst->Name()), tmp, net_map.at(inst->GetCin()), out, false); + RTLIL::SigSpec tmp = module->addWire(new_verific_id(inst), GetSize(out)); + cell = module->addAdd(new_verific_id(inst), IN1, IN2, tmp, SIGNED); + import_attributes(cell->attributes, inst); + cell = module->addAdd(inst_name, tmp, net_map_at(inst->GetCin()), out, false); + import_attributes(cell->attributes, inst); } return true; } if (inst->Type() == OPER_MULTIPLIER) { - module->addMul(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addMul(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_DIVIDER) { - module->addDiv(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addDiv(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_MODULO) { - module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REMAINDER) { - module->addMod(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addMod(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_SHIFT_LEFT) { - module->addShl(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false); + cell = module->addShl(inst_name, IN1, IN2, OUT, false); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_ENABLED_DECODER) { RTLIL::SigSpec vec; - vec.append(net_map.at(inst->GetControl())); + 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); + cell = module->addShl(inst_name, vec, IN, OUT, false); + import_attributes(cell->attributes, inst); return true; } @@ -395,7 +505,8 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::mapOutputSize(); i++) { vec.append(RTLIL::State::S0); } - module->addShl(RTLIL::escape_id(inst->Name()), vec, IN, OUT, false); + cell = module->addShl(inst_name, vec, IN, OUT, false); + import_attributes(cell->attributes, inst); return true; } @@ -403,112 +514,187 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::mapGetCin(); Net *net_a_msb = inst->GetInput1Bit(0); if (net_cin->IsGnd()) - module->addShr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, false); + cell = module->addShr(inst_name, IN1, IN2, OUT, false); else if (net_cin == net_a_msb) - module->addSshr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, true); + cell = module->addSshr(inst_name, IN1, IN2, OUT, true); else log_error("Can't import Verific OPER_SHIFT_RIGHT instance %s: carry_in is neither 0 nor msb of left input\n", inst->Name()); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_AND) { - module->addReduceAnd(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addReduceAnd(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_OR) { - module->addReduceOr(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_XOR) { - module->addReduceXor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addReduceXor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_REDUCE_XNOR) { - module->addReduceXnor(RTLIL::escape_id(inst->Name()), IN, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addReduceXnor(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == OPER_REDUCE_NOR) { + SigSpec t = module->ReduceOr(new_verific_id(inst), IN, SIGNED); + cell = module->addNot(inst_name, t, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_LESSTHAN) { Net *net_cin = inst->GetCin(); if (net_cin->IsGnd()) - module->addLt(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addLt(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); else if (net_cin->IsPwr()) - module->addLe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addLe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); else log_error("Can't import Verific OPER_LESSTHAN instance %s: carry_in is neither 0 nor 1\n", inst->Name()); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_AND) { - module->addAnd(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addAnd(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_OR) { - module->addOr(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addOr(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_XOR) { - module->addXor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addXor(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_XNOR) { - module->addXnor(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addXnor(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_BUF) { - module->addPos(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED); + cell = module->addPos(inst_name, IN, FILTERED_OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_INV) { - module->addNot(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED); + cell = module->addNot(inst_name, IN, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_MINUS) { - module->addSub(RTLIL::escape_id(inst->Name()), IN1, IN2, OUT, SIGNED); + cell = module->addSub(inst_name, IN1, IN2, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_UMINUS) { - module->addNeg(RTLIL::escape_id(inst->Name()), IN, OUT, SIGNED); + cell = module->addNeg(inst_name, IN, OUT, SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_EQUAL) { - module->addEq(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addEq(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_NEQUAL) { - module->addNe(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetOutput()), SIGNED); + cell = module->addNe(inst_name, IN1, IN2, net_map_at(inst->GetOutput()), SIGNED); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_MUX) { - module->addMux(RTLIL::escape_id(inst->Name()), IN1, IN2, net_map.at(inst->GetControl()), OUT); + cell = module->addMux(inst_name, IN1, IN2, net_map_at(inst->GetControl()), OUT); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == OPER_NTO1MUX) { + cell = module->addShr(inst_name, IN2, IN1, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == OPER_WIDE_NTO1MUX) + { + SigSpec data = IN2, out = OUT; + + int wordsize_bits = ceil_log2(GetSize(out)); + int wordsize = 1 << wordsize_bits; + + SigSpec sel = {IN1, SigSpec(State::S0, wordsize_bits)}; + + SigSpec padded_data; + for (int i = 0; i < GetSize(data); i += GetSize(out)) { + SigSpec d = data.extract(i, GetSize(out)); + d.extend_u0(wordsize); + padded_data.append(d); + } + + cell = module->addShr(inst_name, padded_data, sel, out); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == OPER_SELECTOR) + { + cell = module->addPmux(inst_name, State::S0, IN2, IN1, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); + return true; + } + + if (inst->Type() == OPER_WIDE_SELECTOR) + { + SigSpec out = OUT; + cell = module->addPmux(inst_name, SigSpec(State::S0, GetSize(out)), IN2, IN1, out); + import_attributes(cell->attributes, inst); return true; } if (inst->Type() == OPER_WIDE_TRI) { - module->addMux(RTLIL::escape_id(inst->Name()), RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map.at(inst->GetControl()), OUT); + cell = module->addMux(inst_name, RTLIL::SigSpec(RTLIL::State::Sz, inst->OutputSize()), IN, net_map_at(inst->GetControl()), OUT); + import_attributes(cell->attributes, inst); return true; } - 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 (inst->Type() == OPER_WIDE_DFFRS) + { + VerificClocking clocking(this, inst->GetClock()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + RTLIL::SigSpec sig_set = operatorInport(inst, "set"); + RTLIL::SigSpec sig_reset = operatorInport(inst, "reset"); + if (sig_set.is_fully_const() && !sig_set.as_bool() && sig_reset.is_fully_const() && !sig_reset.as_bool()) - module->addDff(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), IN, OUT); + cell = clocking.addDff(inst_name, IN, OUT); else - module->addDffsr(RTLIL::escape_id(inst->Name()), net_map.at(inst->GetClock()), sig_set, sig_reset, IN, OUT); + cell = clocking.addDffsr(inst_name, sig_set, sig_reset, IN, OUT); + import_attributes(cell->attributes, inst); + return true; } @@ -521,23 +707,95 @@ static bool import_netlist_instance_cells(RTLIL::Module *module, std::map &nl_todo, bool mode_gates) +void VerificImporter::merge_past_ffs_clock(pool &candidates, SigBit clock, bool clock_pol) +{ + bool keep_running = true; + SigMap sigmap; + + while (keep_running) + { + keep_running = false; + + dict> dbits_db; + SigSpec dbits; + + for (auto cell : candidates) { + SigBit bit = sigmap(cell->getPort("\\D")); + dbits_db[bit].insert(cell); + dbits.append(bit); + } + + dbits.sort_and_unify(); + + for (auto chunk : dbits.chunks()) + { + SigSpec sig_d = chunk; + + if (chunk.wire == nullptr || GetSize(sig_d) == 1) + continue; + + SigSpec sig_q = module->addWire(NEW_ID, GetSize(sig_d)); + RTLIL::Cell *new_ff = module->addDff(NEW_ID, clock, sig_d, sig_q, clock_pol); + + if (verific_verbose) + log(" merging single-bit past_ffs into new %d-bit ff %s.\n", GetSize(sig_d), log_id(new_ff)); + + for (int i = 0; i < GetSize(sig_d); i++) + for (auto old_ff : dbits_db[sig_d[i]]) + { + if (verific_verbose) + log(" replacing old ff %s on bit %d.\n", log_id(old_ff), i); + + SigBit old_q = old_ff->getPort("\\Q"); + SigBit new_q = sig_q[i]; + + sigmap.add(old_q, new_q); + module->connect(old_q, new_q); + candidates.erase(old_ff); + module->remove(old_ff); + keep_running = true; + } + } + } +} + +void VerificImporter::merge_past_ffs(pool &candidates) +{ + dict, pool> database; + + for (auto cell : candidates) + { + SigBit clock = cell->getPort("\\CLK"); + bool clock_pol = cell->getParam("\\CLK_POLARITY").as_bool(); + database[make_pair(clock, int(clock_pol))].insert(cell); + } + + for (auto it : database) + merge_past_ffs_clock(it.second, it.first.first, it.first.second); +} + +void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::set &nl_todo) { std::string module_name = nl->IsOperator() ? std::string("$verific$") + nl->Owner()->Name() : RTLIL::escape_id(nl->Owner()->Name()); + netlist = nl; + if (design->has(module_name)) { - if (!nl->IsOperator()) + if (!nl->IsOperator() && !is_blackbox(nl)) log_cmd_error("Re-definition of module `%s'.\n", nl->Owner()->Name()); return; } - RTLIL::Module *module = new 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_map; + if (is_blackbox(nl)) { + log("Importing blackbox module %s.\n", RTLIL::id2cstr(module->name)); + module->set_bool_attribute("\\blackbox"); + } else { + log("Importing module %s.\n", RTLIL::id2cstr(module->name)); + } SetIter si; MapIter mi, mi2; @@ -553,7 +811,8 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setBus()) continue; - // log(" importing port %s.\n", port->Name()); + if (verific_verbose) + log(" importing port %s.\n", port->Name()); RTLIL::Wire *wire = module->addWire(RTLIL::escape_id(port->Name())); import_attributes(wire->attributes, port); @@ -570,15 +829,16 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setport_input) - module->connect(net_map.at(net), wire); + module->connect(net_map_at(net), wire); else - module->connect(wire, net_map.at(net)); + module->connect(wire, net_map_at(net)); } } FOREACH_PORTBUS_OF_NETLIST(nl, mi, portbus) { - // log(" importing portbus %s.\n", portbus->Name()); + if (verific_verbose) + 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()); @@ -596,9 +856,9 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setport_input) - module->connect(net_map.at(net), bit); + module->connect(net_map_at(net), bit); else - module->connect(bit, net_map.at(net)); + module->connect(bit, net_map_at(net)); } if (i == portbus->RightIndex()) break; @@ -607,6 +867,11 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setfixup_ports(); + dict init_nets; + pool anyconst_nets, anyseq_nets; + pool allconst_nets, allseq_nets; + any_all_nets.clear(); + FOREACH_NET_OF_NETLIST(nl, mi, net) { if (net->IsRamNet()) @@ -633,20 +898,98 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setwidth = bits_in_word; memory->size = number_of_bits / bits_in_word; + + const char *ascii_initdata = net->GetWideInitialValue(); + if (ascii_initdata) { + while (*ascii_initdata != 0 && *ascii_initdata != '\'') + ascii_initdata++; + if (*ascii_initdata == '\'') + ascii_initdata++; + if (*ascii_initdata != 0) { + log_assert(*ascii_initdata == 'b'); + ascii_initdata++; + } + for (int word_idx = 0; word_idx < memory->size; word_idx++) { + Const initval = Const(State::Sx, memory->width); + bool initval_valid = false; + for (int bit_idx = memory->width-1; bit_idx >= 0; bit_idx--) { + if (*ascii_initdata == 0) + break; + if (*ascii_initdata == '0' || *ascii_initdata == '1') { + initval[bit_idx] = (*ascii_initdata == '0') ? State::S0 : State::S1; + initval_valid = true; + } + ascii_initdata++; + } + if (initval_valid) { + RTLIL::Cell *cell = module->addCell(new_verific_id(net), "$meminit"); + cell->parameters["\\WORDS"] = 1; + if (net->GetOrigTypeRange()->LeftRangeBound() < net->GetOrigTypeRange()->RightRangeBound()) + cell->setPort("\\ADDR", word_idx); + else + cell->setPort("\\ADDR", memory->size - word_idx - 1); + cell->setPort("\\DATA", initval); + cell->parameters["\\MEMID"] = RTLIL::Const(memory->name.str()); + cell->parameters["\\ABITS"] = 32; + cell->parameters["\\WIDTH"] = memory->width; + cell->parameters["\\PRIORITY"] = RTLIL::Const(autoidx-1); + } + } + } continue; } + if (net->GetInitialValue()) + init_nets[net] = net->GetInitialValue(); + + const char *rand_const_attr = net->GetAttValue(" rand_const"); + const char *rand_attr = net->GetAttValue(" rand"); + + const char *anyconst_attr = net->GetAttValue("anyconst"); + const char *anyseq_attr = net->GetAttValue("anyseq"); + + const char *allconst_attr = net->GetAttValue("allconst"); + const char *allseq_attr = net->GetAttValue("allseq"); + + if (rand_const_attr != nullptr && (!strcmp(rand_const_attr, "1") || !strcmp(rand_const_attr, "'1'"))) { + anyconst_nets.insert(net); + any_all_nets.insert(net); + } + else if (rand_attr != nullptr && (!strcmp(rand_attr, "1") || !strcmp(rand_attr, "'1'"))) { + anyseq_nets.insert(net); + any_all_nets.insert(net); + } + else if (anyconst_attr != nullptr && (!strcmp(anyconst_attr, "1") || !strcmp(anyconst_attr, "'1'"))) { + anyconst_nets.insert(net); + any_all_nets.insert(net); + } + else if (anyseq_attr != nullptr && (!strcmp(anyseq_attr, "1") || !strcmp(anyseq_attr, "'1'"))) { + anyseq_nets.insert(net); + any_all_nets.insert(net); + } + else if (allconst_attr != nullptr && (!strcmp(allconst_attr, "1") || !strcmp(allconst_attr, "'1'"))) { + allconst_nets.insert(net); + any_all_nets.insert(net); + } + else if (allseq_attr != nullptr && (!strcmp(allseq_attr, "1") || !strcmp(allseq_attr, "'1'"))) { + allseq_nets.insert(net); + any_all_nets.insert(net); + } + if (net_map.count(net)) { - // log(" skipping net %s.\n", net->Name()); + if (verific_verbose) + 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(mode_names || net->IsUserDeclared() ? RTLIL::escape_id(net->Name()) : new_verific_id(net)); + + if (verific_verbose) + log(" importing net %s as %s.\n", net->Name(), log_id(wire_name)); - RTLIL::IdString wire_name = module->uniquify(RTLIL::escape_id(net->Name())); RTLIL::Wire *wire = module->addWire(wire_name); import_attributes(wire->attributes, net); @@ -666,134 +1009,396 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setName()); + RTLIL::IdString wire_name = module->uniquify(mode_names || netbus->IsUserDeclared() ? RTLIL::escape_id(netbus->Name()) : new_verific_id(netbus)); + + if (verific_verbose) + log(" importing netbus %s as %s.\n", netbus->Name(), log_id(wire_name)); - 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)) { + RTLIL::Const initval = Const(State::Sx, GetSize(wire)); + bool initval_valid = false; + + for (int i = netbus->LeftIndex();; i += netbus->IsUp() ? +1 : -1) + { + if (netbus->ElementAtIndex(i)) + { + int bitidx = i - wire->start_offset; net = netbus->ElementAtIndex(i); - RTLIL::SigBit bit(wire, i - wire->start_offset); + RTLIL::SigBit bit(wire, bitidx); + + if (init_nets.count(net)) { + if (init_nets.at(net) == '0') + initval.bits.at(bitidx) = State::S0; + if (init_nets.at(net) == '1') + initval.bits.at(bitidx) = State::S1; + initval_valid = true; + init_nets.erase(net); + } + if (net_map.count(net) == 0) net_map[net] = bit; else - module->connect(bit, net_map.at(net)); + module->connect(bit, net_map_at(net)); } + if (i == netbus->RightIndex()) break; } + + if (initval_valid) + wire->attributes["\\init"] = initval; } else { - // log(" skipping netbus %s.\n", netbus->Name()); + if (verific_verbose) + log(" skipping netbus %s.\n", netbus->Name()); } + + SigSpec anyconst_sig; + SigSpec anyseq_sig; + SigSpec allconst_sig; + SigSpec allseq_sig; + + for (int i = netbus->RightIndex();; i += netbus->IsUp() ? -1 : +1) { + net = netbus->ElementAtIndex(i); + if (net != nullptr && anyconst_nets.count(net)) { + anyconst_sig.append(net_map_at(net)); + anyconst_nets.erase(net); + } + if (net != nullptr && anyseq_nets.count(net)) { + anyseq_sig.append(net_map_at(net)); + anyseq_nets.erase(net); + } + if (net != nullptr && allconst_nets.count(net)) { + allconst_sig.append(net_map_at(net)); + allconst_nets.erase(net); + } + if (net != nullptr && allseq_nets.count(net)) { + allseq_sig.append(net_map_at(net)); + allseq_nets.erase(net); + } + if (i == netbus->LeftIndex()) + break; + } + + if (GetSize(anyconst_sig)) + module->connect(anyconst_sig, module->Anyconst(new_verific_id(netbus), GetSize(anyconst_sig))); + + if (GetSize(anyseq_sig)) + module->connect(anyseq_sig, module->Anyseq(new_verific_id(netbus), GetSize(anyseq_sig))); + + if (GetSize(allconst_sig)) + module->connect(allconst_sig, module->Allconst(new_verific_id(netbus), GetSize(allconst_sig))); + + if (GetSize(allseq_sig)) + module->connect(allseq_sig, module->Allseq(new_verific_id(netbus), GetSize(allseq_sig))); } + for (auto it : init_nets) + { + Const initval; + SigBit bit = net_map_at(it.first); + log_assert(bit.wire); + + if (bit.wire->attributes.count("\\init")) + initval = bit.wire->attributes.at("\\init"); + + while (GetSize(initval) < GetSize(bit.wire)) + initval.bits.push_back(State::Sx); + + if (it.second == '0') + initval.bits.at(bit.offset) = State::S0; + if (it.second == '1') + initval.bits.at(bit.offset) = State::S1; + + bit.wire->attributes["\\init"] = initval; + } + + for (auto net : anyconst_nets) + module->connect(net_map_at(net), module->Anyconst(new_verific_id(net))); + + for (auto net : anyseq_nets) + module->connect(net_map_at(net), module->Anyseq(new_verific_id(net))); + + pool sva_asserts; + pool sva_assumes; + pool sva_covers; + pool sva_triggers; + + pool past_ffs; + FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) { - // log(" importing cell %s (%s).\n", inst->Name(), inst->View()->Owner()->Name()); + RTLIL::IdString inst_name = module->uniquify(mode_names || inst->IsUserDeclared() ? RTLIL::escape_id(inst->Name()) : new_verific_id(inst)); + + if (verific_verbose) + log(" importing cell %s (%s) as %s.\n", inst->Name(), inst->View()->Owner()->Name(), log_id(inst_name)); + + if (mode_verific) + goto import_verific_cells; if (inst->Type() == PRIM_PWR) { - module->connect(net_map.at(inst->GetOutput()), RTLIL::State::S1); + 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); + 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())); + auto outnet = inst->GetOutput(); + if (!any_all_nets.count(outnet)) + module->addBufGate(inst_name, net_map_at(inst->GetInput()), net_map_at(outnet)); continue; } if (inst->Type() == PRIM_X) { - module->connect(net_map.at(inst->GetOutput()), RTLIL::State::Sx); + 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); + 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()); + int numchunks = int(inst->OutputSize()) / memory->width; + int chunksbits = ceil_log2(numchunks); - RTLIL::SigSpec addr = operatorInput1(inst, net_map); - RTLIL::SigSpec data = operatorOutput(inst, net_map, module); + if ((numchunks * memory->width) != int(inst->OutputSize()) || (numchunks & (numchunks - 1)) != 0) + log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name()); - 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); + for (int i = 0; i < numchunks; i++) + { + RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; + RTLIL::SigSpec data = operatorOutput(inst).extract(i * memory->width, memory->width); + + RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : + RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), "$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()); + int numchunks = int(inst->Input2Size()) / memory->width; + int chunksbits = ceil_log2(numchunks); - RTLIL::SigSpec addr = operatorInput1(inst, net_map); - RTLIL::SigSpec data = operatorInput2(inst, net_map); + if ((numchunks * memory->width) != int(inst->Input2Size()) || (numchunks & (numchunks - 1)) != 0) + log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name()); - 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); + for (int i = 0; i < numchunks; i++) + { + RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)}; + RTLIL::SigSpec data = operatorInput2(inst).extract(i * memory->width, memory->width); - if (inst->Type() == OPER_CLOCKED_WRITE_PORT) { - cell->parameters["\\CLK_ENABLE"] = true; - cell->setPort("\\CLK", net_map.at(inst->GetClock())); + RTLIL::Cell *cell = module->addCell(numchunks == 1 ? inst_name : + RTLIL::IdString(stringf("%s_%d", inst_name.c_str(), i)), "$memwr"); + cell->parameters["\\MEMID"] = memory->name.str(); + cell->parameters["\\CLK_ENABLE"] = false; + cell->parameters["\\CLK_POLARITY"] = true; + cell->parameters["\\PRIORITY"] = 0; + cell->parameters["\\ABITS"] = GetSize(addr); + cell->parameters["\\WIDTH"] = GetSize(data); + cell->setPort("\\EN", RTLIL::SigSpec(net_map_at(inst->GetControl())).repeat(GetSize(data))); + cell->setPort("\\CLK", RTLIL::State::S0); + cell->setPort("\\ADDR", addr); + cell->setPort("\\DATA", data); + + 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)) + if (import_netlist_instance_cells(inst, inst_name)) continue; - if (inst->IsOperator()) + if (inst->IsOperator() && !verific_sva_prims.count(inst->Type())) 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)) + if (import_netlist_instance_gates(inst, inst_name)) continue; } - if (inst->IsPrimitive()) - log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); + if (inst->Type() == PRIM_SVA_ASSERT || inst->Type() == PRIM_SVA_IMMEDIATE_ASSERT) + sva_asserts.insert(inst); + if (inst->Type() == PRIM_SVA_ASSUME || inst->Type() == PRIM_SVA_IMMEDIATE_ASSUME) + sva_assumes.insert(inst); + + if (inst->Type() == PRIM_SVA_COVER || inst->Type() == PRIM_SVA_IMMEDIATE_COVER) + sva_covers.insert(inst); + + if (inst->Type() == PRIM_SVA_TRIGGERED) + sva_triggers.insert(inst); + + if (inst->Type() == OPER_SVA_STABLE) + { + VerificClocking clocking(this, inst->GetInput2Bit(0)); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + log_assert(inst->Input1Size() == inst->OutputSize()); + + SigSpec sig_d, sig_q, sig_o; + sig_q = module->addWire(new_verific_id(inst), inst->Input1Size()); + + for (int i = int(inst->Input1Size())-1; i >= 0; i--){ + sig_d.append(net_map_at(inst->GetInput1Bit(i))); + sig_o.append(net_map_at(inst->GetOutputBit(i))); + } + + if (verific_verbose) { + log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", + log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); + log(" XNOR with A=%s, B=%s, Y=%s.\n", + log_signal(sig_d), log_signal(sig_q), log_signal(sig_o)); + } + + clocking.addDff(new_verific_id(inst), sig_d, sig_q); + module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o); + + if (!mode_keep) + continue; + } + + if (inst->Type() == PRIM_SVA_STABLE) + { + VerificClocking clocking(this, inst->GetInput2()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + SigSpec sig_d = net_map_at(inst->GetInput1()); + SigSpec sig_o = net_map_at(inst->GetOutput()); + SigSpec sig_q = module->addWire(new_verific_id(inst)); + + if (verific_verbose) { + log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", + log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); + log(" XNOR with A=%s, B=%s, Y=%s.\n", + log_signal(sig_d), log_signal(sig_q), log_signal(sig_o)); + } + + clocking.addDff(new_verific_id(inst), sig_d, sig_q); + module->addXnor(new_verific_id(inst), sig_d, sig_q, sig_o); + + if (!mode_keep) + continue; + } + + if (inst->Type() == PRIM_SVA_PAST) + { + VerificClocking clocking(this, inst->GetInput2()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + SigBit sig_d = net_map_at(inst->GetInput1()); + SigBit sig_q = net_map_at(inst->GetOutput()); + + if (verific_verbose) + log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", + log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); + + past_ffs.insert(clocking.addDff(new_verific_id(inst), sig_d, sig_q)); + + if (!mode_keep) + continue; + } + + if ((inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL)) + { + VerificClocking clocking(this, inst->GetInput2()); + log_assert(clocking.disable_sig == State::S0); + log_assert(clocking.body_net == nullptr); + + SigBit sig_d = net_map_at(inst->GetInput1()); + SigBit sig_o = net_map_at(inst->GetOutput()); + SigBit sig_q = module->addWire(new_verific_id(inst)); + + if (verific_verbose) + log(" %sedge FF with D=%s, Q=%s, C=%s.\n", clocking.posedge ? "pos" : "neg", + log_signal(sig_d), log_signal(sig_q), log_signal(clocking.clock_sig)); + + clocking.addDff(new_verific_id(inst), sig_d, sig_q); + module->addEq(new_verific_id(inst), {sig_q, sig_d}, Const(inst->Type() == PRIM_SVA_ROSE ? 1 : 2, 2), sig_o); + + if (!mode_keep) + continue; + } + + if (!mode_keep && verific_sva_prims.count(inst->Type())) { + if (verific_verbose) + log(" skipping SVA cell in non k-mode\n"); + continue; + } + + if (inst->Type() == PRIM_HDL_ASSERTION) + { + SigBit cond = net_map_at(inst->GetInput()); + + if (verific_verbose) + log(" assert condition %s.\n", log_signal(cond)); + + const char *assume_attr = nullptr; // inst->GetAttValue("assume"); + + Cell *cell = nullptr; + if (assume_attr != nullptr && !strcmp(assume_attr, "1")) + cell = module->addAssume(new_verific_id(inst), cond, State::S1); + else + cell = module->addAssert(new_verific_id(inst), cond, State::S1); + + import_attributes(cell->attributes, inst); + continue; + } + + if (inst->IsPrimitive()) + { + if (!mode_keep) + log_error("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); + + if (!verific_sva_prims.count(inst->Type())) + log_warning("Unsupported Verific primitive %s of type %s\n", inst->Name(), inst->View()->Owner()->Name()); + } + + import_verific_cells: nl_todo.insert(inst->View()); - RTLIL::Cell *cell = module->addCell(RTLIL::escape_id(inst->Name()), inst->IsOperator() ? + RTLIL::Cell *cell = module->addCell(inst_name, inst->IsOperator() ? std::string("$verific$") + inst->View()->Owner()->Name() : RTLIL::escape_id(inst->View()->Owner()->Name())); + if (inst->IsPrimitive() && mode_keep) + cell->attributes["\\keep"] = 1; + dict> cell_port_conns; + if (verific_verbose) + log(" ports in verific db:\n"); + FOREACH_PORTREF_OF_INST(inst, mi2, pr) { - // log(" .%s(%s)\n", pr->GetPort()->Name(), pr->GetNet()->Name()); + if (verific_verbose) + 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()) { @@ -804,131 +1409,704 @@ static void import_netlist(RTLIL::Design *design, Netlist *nl, std::setaddWire(NEW_ID, port_offset+1-GetSize(sigvec)); + SigSpec zwires = module->addWire(new_verific_id(inst), port_offset+1-GetSize(sigvec)); for (auto bit : zwires) sigvec.push_back(bit); } - sigvec[port_offset] = net_map.at(pr->GetNet()); + sigvec[port_offset] = net_map_at(pr->GetNet()); } + if (verific_verbose) + log(" ports in yosys db:\n"); + for (auto &it : cell_port_conns) { - // log(" .%s(%s)\n", log_id(it.first), log_signal(it.second)); + if (verific_verbose) + log(" .%s(%s)\n", log_id(it.first), log_signal(it.second)); cell->setPort(it.first, it.second); } } + + if (!mode_nosva) + { + for (auto inst : sva_asserts) { + if (mode_autocover) + verific_import_sva_cover(this, inst); + verific_import_sva_assert(this, inst); + } + + for (auto inst : sva_assumes) + verific_import_sva_assume(this, inst); + + for (auto inst : sva_covers) + verific_import_sva_cover(this, inst); + + for (auto inst : sva_triggers) + verific_import_sva_trigger(this, inst); + + merge_past_ffs(past_ffs); + } } +// ================================================================== + +VerificClocking::VerificClocking(VerificImporter *importer, Net *net, bool sva_at_only) +{ + module = importer->module; + + log_assert(importer != nullptr); + log_assert(net != nullptr); + + Instance *inst = net->Driver(); + + if (inst != nullptr && inst->Type() == PRIM_SVA_AT) + { + net = inst->GetInput1(); + body_net = inst->GetInput2(); + + inst = net->Driver(); + + Instance *body_inst = body_net->Driver(); + if (body_inst != nullptr && body_inst->Type() == PRIM_SVA_DISABLE_IFF) { + disable_net = body_inst->GetInput1(); + disable_sig = importer->net_map_at(disable_net); + body_net = body_inst->GetInput2(); + } + } + else + { + if (sva_at_only) + return; + } + + // Use while() instead of if() to work around VIPER #13453 + while (inst != nullptr && inst->Type() == PRIM_SVA_POSEDGE) + { + net = inst->GetInput(); + inst = net->Driver();; + } + + if (inst != nullptr && inst->Type() == PRIM_INV) + { + net = inst->GetInput(); + inst = net->Driver();; + posedge = false; + } + + // Detect clock-enable circuit + do { + if (inst == nullptr || inst->Type() != PRIM_AND) + break; + + Net *net_dlatch = inst->GetInput1(); + Instance *inst_dlatch = net_dlatch->Driver(); + + if (inst_dlatch == nullptr || inst_dlatch->Type() != PRIM_DLATCHRS) + break; + + if (!inst_dlatch->GetSet()->IsGnd() || !inst_dlatch->GetReset()->IsGnd()) + break; + + Net *net_enable = inst_dlatch->GetInput(); + Net *net_not_clock = inst_dlatch->GetControl(); + + if (net_enable == nullptr || net_not_clock == nullptr) + break; + + Instance *inst_not_clock = net_not_clock->Driver(); + + if (inst_not_clock == nullptr || inst_not_clock->Type() != PRIM_INV) + break; + + Net *net_clock1 = inst_not_clock->GetInput(); + Net *net_clock2 = inst->GetInput2(); + + if (net_clock1 == nullptr || net_clock1 != net_clock2) + break; + + enable_net = net_enable; + enable_sig = importer->net_map_at(enable_net); + + net = net_clock1; + inst = net->Driver();; + } while (0); + + // Detect condition expression + do { + if (body_net == nullptr) + break; + + Instance *inst_mux = body_net->Driver(); + + if (inst_mux == nullptr || inst_mux->Type() != PRIM_MUX) + break; + + if (!inst_mux->GetInput1()->IsPwr()) + break; + + Net *sva_net = inst_mux->GetInput2(); + if (!verific_is_sva_net(importer, sva_net)) + break; + + body_net = sva_net; + cond_net = inst_mux->GetControl(); + } while (0); + + clock_net = net; + clock_sig = importer->net_map_at(clock_net); + + const char *gclk_attr = clock_net->GetAttValue("gclk"); + if (gclk_attr != nullptr && (!strcmp(gclk_attr, "1") || !strcmp(gclk_attr, "'1'"))) + gclk = true; +} + +Cell *VerificClocking::addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value) +{ + log_assert(GetSize(sig_d) == GetSize(sig_q)); + + if (GetSize(init_value) != 0) { + log_assert(GetSize(sig_q) == GetSize(init_value)); + if (sig_q.is_wire()) { + sig_q.as_wire()->attributes["\\init"] = init_value; + } else { + Wire *w = module->addWire(NEW_ID, GetSize(sig_q)); + w->attributes["\\init"] = init_value; + module->connect(sig_q, w); + sig_q = w; + } + } + + if (enable_sig != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); + + if (disable_sig != State::S0) { + log_assert(gclk == false); + log_assert(GetSize(sig_q) == GetSize(init_value)); + return module->addAdff(name, clock_sig, disable_sig, sig_d, sig_q, init_value, posedge); + } + + if (gclk) + return module->addFf(name, sig_d, sig_q); + + return module->addDff(name, clock_sig, sig_d, sig_q, posedge); +} + +Cell *VerificClocking::addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value) +{ + log_assert(gclk == false); + log_assert(disable_sig == State::S0); + + if (enable_sig != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); + + return module->addAdff(name, clock_sig, sig_arst, sig_d, sig_q, arst_value, posedge); +} + +Cell *VerificClocking::addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q) +{ + log_assert(gclk == false); + log_assert(disable_sig == State::S0); + + if (enable_sig != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, enable_sig); + + return module->addDffsr(name, clock_sig, sig_set, sig_clr, sig_d, sig_q, posedge); +} + +// ================================================================== + +struct VerificExtNets +{ + int portname_cnt = 0; + + // a map from Net to the same Net one level up in the design hierarchy + std::map net_level_up; + + Net *get_net_level_up(Net *net) + { + if (net_level_up.count(net) == 0) + { + Netlist *nl = net->Owner(); + + // Simply return if Netlist is not unique + if (nl->NumOfRefs() != 1) + return net; + + Instance *up_inst = (Instance*)nl->GetReferences()->GetLast(); + Netlist *up_nl = up_inst->Owner(); + + // create new Port + string name = stringf("___extnets_%d", portname_cnt++); + Port *new_port = new Port(name.c_str(), DIR_OUT); + nl->Add(new_port); + net->Connect(new_port); + + // create new Net in up Netlist + Net *new_net = new Net(name.c_str()); + up_nl->Add(new_net); + up_inst->Connect(new_port, new_net); + + net_level_up[net] = new_net; + } + + return net_level_up.at(net); + } + + void run(Netlist *nl) + { + MapIter mi, mi2; + Instance *inst; + PortRef *pr; + + vector> todo_connect; + + FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) + run(inst->View()); + + FOREACH_INSTANCE_OF_NETLIST(nl, mi, inst) + FOREACH_PORTREF_OF_INST(inst, mi2, pr) + { + Port *port = pr->GetPort(); + Net *net = pr->GetNet(); + + if (!net->IsExternalTo(nl)) + continue; + + if (verific_verbose) + log("Fixing external net reference on port %s.%s.%s:\n", get_full_netlist_name(nl).c_str(), inst->Name(), port->Name()); + + while (net->IsExternalTo(nl)) + { + Net *newnet = get_net_level_up(net); + if (newnet == net) break; + + if (verific_verbose) + log(" external net: %s.%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name()); + net = newnet; + } + + if (verific_verbose) + log(" final net: %s.%s%s\n", get_full_netlist_name(net->Owner()).c_str(), net->Name(), net->IsExternalTo(nl) ? " (external)" : ""); + todo_connect.push_back(tuple(inst, port, net)); + } + + for (auto it : todo_connect) { + get<0>(it)->Disconnect(get<1>(it)); + get<0>(it)->Connect(get<1>(it), get<2>(it)); + } + } +}; + +void verific_import(Design *design, std::string top) +{ + verific_sva_fsm_limit = 16; + + std::set nl_todo, nl_done; + + { + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary("work", 1); + VeriLibrary *veri_lib = veri_file::GetLibrary("work", 1); + + Array veri_libs, vhdl_libs; + if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); + if (veri_lib) veri_libs.InsertLast(veri_lib); + + Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs); + Netlist *nl; + int i; + + FOREACH_ARRAY_ITEM(netlists, i, nl) { + if (top.empty() || nl->Owner()->Name() == top) + nl_todo.insert(nl); + } + + delete netlists; + } + + if (!verific_error_msg.empty()) + log_error("%s\n", verific_error_msg.c_str()); + + VerificExtNets worker; + for (auto nl : nl_todo) + worker.run(nl); + + while (!nl_todo.empty()) { + Netlist *nl = *nl_todo.begin(); + if (nl_done.count(nl) == 0) { + VerificImporter importer(false, false, false, false, false, false); + importer.import_netlist(design, nl, nl_todo); + } + nl_todo.erase(nl); + nl_done.insert(nl); + } + + veri_file::Reset(); + vhdl_file::Reset(); + Libset::Reset(); + verific_incdirs.clear(); + verific_libdirs.clear(); + verific_import_pending = false; + + if (!verific_error_msg.empty()) + log_error("%s\n", verific_error_msg.c_str()); +} + +YOSYS_NAMESPACE_END #endif /* YOSYS_ENABLE_VERIFIC */ -YOSYS_NAMESPACE_BEGIN +PRIVATE_NAMESPACE_BEGIN + +#ifdef YOSYS_ENABLE_VERIFIC +bool check_noverific_env() +{ + const char *e = getenv("YOSYS_NOVERIFIC"); + if (e == nullptr) + return false; + if (atoi(e) == 0) + return false; + return true; +} +#endif struct VerificPass : public Pass { VerificPass() : Pass("verific", "load Verilog and VHDL designs using Verific") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv} ..\n"); + log(" verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} ..\n"); log("\n"); log("Load the specified Verilog/SystemVerilog files into Verific.\n"); log("\n"); + log("All files specified in one call to this command are one compilation unit.\n"); + log("Files passed to different calls to this command are treated as belonging to\n"); + log("different compilation units.\n"); log("\n"); - log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} ..\n"); + log("Additional -D[=] options may be added after the option indicating\n"); + log("the language version (and before file names) to set additional verilog defines.\n"); + log("The macros SYNTHESIS and VERIFIC are defined implicitly.\n"); + log("\n"); + log("\n"); + log(" verific -formal ..\n"); + log("\n"); + log("Like -sv, but define FORMAL instead of SYNTHESIS.\n"); + log("\n"); + log("\n"); + log(" verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); log("\n"); log("Load the specified VHDL files into Verific.\n"); log("\n"); log("\n"); - log(" verific -import [-gates] {-all | ..}\n"); + log(" verific -work {-sv|-vhdl|...} \n"); + log("\n"); + log("Load the specified Verilog/SystemVerilog/VHDL file into the specified library.\n"); + log("(default library when -work is not present: \"work\")\n"); + log("\n"); + log("\n"); + log(" verific -vlog-incdir ..\n"); + log("\n"); + log("Add Verilog include directories.\n"); + log("\n"); + log("\n"); + log(" verific -vlog-libdir ..\n"); + log("\n"); + log("Add Verilog library directories. Verific will search in this directories to\n"); + log("find undefined modules.\n"); + log("\n"); + log("\n"); + log(" verific -vlog-define [=]..\n"); + log("\n"); + log("Add Verilog defines.\n"); + log("\n"); + log("\n"); + log(" verific -vlog-undef ..\n"); + log("\n"); + log("Remove Verilog defines previously set with -vlog-define.\n"); + log("\n"); + log("\n"); + log(" verific -set-error ..\n"); + log(" verific -set-warning ..\n"); + log(" verific -set-info ..\n"); + log(" verific -set-ignore ..\n"); + log("\n"); + log("Set message severity. is the string in square brackets when a message\n"); + log("is printed, such as VERI-1209.\n"); + log("\n"); + log("\n"); + log(" verific -import [options] ..\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("reset the internal state of Verific.\n"); + log("\n"); + log("Import options:\n"); + log("\n"); + log(" -all\n"); + log(" Elaborate all modules, not just the hierarchy below the given top\n"); + log(" modules. With this option the list of modules to import is optional.\n"); + log("\n"); + log(" -gates\n"); + log(" Create a gate-level netlist.\n"); + log("\n"); + log(" -flatten\n"); + log(" Flatten the design in Verific before importing.\n"); + log("\n"); + log(" -extnets\n"); + log(" Resolve references to external nets by adding module ports as needed.\n"); + log("\n"); + log(" -autocover\n"); + log(" Generate automatic cover statements for all asserts\n"); + log("\n"); + log(" -v, -vv\n"); + log(" Verbose log messages. (-vv is even more verbose than -v.)\n"); + log("\n"); + log("The following additional import options are useful for debugging the Verific\n"); + log("bindings (for Yosys and/or Verific developers):\n"); + log("\n"); + log(" -k\n"); + log(" Keep going after an unsupported verific primitive is found. The\n"); + log(" unsupported primitive is added as blockbox module to the design.\n"); + log(" This will also add all SVA related cells to the design parallel to\n"); + log(" the checker logic inferred by it.\n"); + log("\n"); + log(" -V\n"); + log(" Import Verific netlist as-is without translating to Yosys cell types. \n"); + log("\n"); + log(" -nosva\n"); + log(" Ignore SVA properties, do not infer checker logic.\n"); + log("\n"); + log(" -L \n"); + log(" Maximum number of ctrl bits for SVA checker FSMs (default=16).\n"); + log("\n"); + log(" -n\n"); + log(" Keep all Verific names on instances and nets. By default only\n"); + log(" user-declared names are preserved.\n"); + log("\n"); + log(" -d \n"); + log(" Dump the Verific netlist as a verilog file.\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 args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - log_header(design, "Executing VERIFIC (loading Verilog and VHDL designs using Verific).\n"); + static bool set_verific_global_flags = true; - Message::SetConsoleOutput(0); - Message::RegisterCallBackMsg(msg_func); + if (check_noverific_env()) + log_cmd_error("This version of Yosys is built without Verific support.\n"); - 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; + log_header(design, "Executing VERIFIC (loading SystemVerilog and VHDL designs using Verific).\n"); + + if (set_verific_global_flags) + { + Message::SetConsoleOutput(0); + Message::RegisterCallBackMsg(msg_func); + + RuntimeFlags::SetVar("db_preserve_user_nets", 1); + RuntimeFlags::SetVar("db_allow_external_nets", 1); + RuntimeFlags::SetVar("db_infer_wide_operators", 1); + + RuntimeFlags::SetVar("veri_extract_dualport_rams", 0); + RuntimeFlags::SetVar("veri_extract_multiport_rams", 1); + + RuntimeFlags::SetVar("vhdl_extract_dualport_rams", 0); + RuntimeFlags::SetVar("vhdl_extract_multiport_rams", 1); + + RuntimeFlags::SetVar("vhdl_support_variable_slice", 1); + RuntimeFlags::SetVar("vhdl_ignore_assertion_statements", 0); + + // Workaround for VIPER #13851 + RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); + + // WARNING: instantiating unknown module 'XYZ' (VERI-1063) + Message::SetMessageType("VERI-1063", VERIFIC_ERROR); + + set_verific_global_flags = false; } - 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; + verific_verbose = 0; + verific_sva_fsm_limit = 16; + + const char *release_str = Message::ReleaseString(); + time_t release_time = Message::ReleaseDate(); + char *release_tmstr = ctime(&release_time); + + if (release_str == nullptr) + release_str = "(no release string)"; + + for (char *p = release_tmstr; *p; p++) + if (*p == '\n') *p = 0; + + log("Built with Verific %s, released at %s.\n", release_str, release_tmstr); + + int argidx = 1; + std::string work = "work"; + + if (GetSize(args) > argidx && (args[argidx] == "-set-error" || args[argidx] == "-set-warning" || + args[argidx] == "-set-info" || args[argidx] == "-set-ignore")) + { + msg_type_t new_type; + + if (args[argidx] == "-set-error") + new_type = VERIFIC_ERROR; + else if (args[argidx] == "-set-warning") + new_type = VERIFIC_WARNING; + else if (args[argidx] == "-set-info") + new_type = VERIFIC_INFO; + else if (args[argidx] == "-set-ignore") + new_type = VERIFIC_IGNORE; + else + log_abort(); + + for (argidx++; argidx < GetSize(args); argidx++) + Message::SetMessageType(args[argidx].c_str(), new_type); + + goto check_error; } - 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 (GetSize(args) > argidx && args[argidx] == "-vlog-incdir") { + for (argidx++; argidx < GetSize(args); argidx++) + verific_incdirs.push_back(args[argidx]); + goto check_error; } - 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 (GetSize(args) > argidx && args[argidx] == "-vlog-libdir") { + for (argidx++; argidx < GetSize(args); argidx++) + verific_libdirs.push_back(args[argidx]); + goto check_error; } - 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 (GetSize(args) > argidx && args[argidx] == "-vlog-define") { + for (argidx++; argidx < GetSize(args); argidx++) { + string name = args[argidx]; + size_t equal = name.find('='); + if (equal != std::string::npos) { + string value = name.substr(equal+1); + name = name.substr(0, equal); + veri_file::DefineCmdLineMacro(name.c_str(), value.c_str()); + } else { + veri_file::DefineCmdLineMacro(name.c_str()); + } + } + goto check_error; } - if (args.size() > 1 && args[1] == "-vhdl87") { + if (GetSize(args) > argidx && args[argidx] == "-vlog-undef") { + for (argidx++; argidx < GetSize(args); argidx++) { + string name = args[argidx]; + veri_file::UndefineMacro(name.c_str()); + } + goto check_error; + } + + for (; argidx < GetSize(args); argidx++) + { + if (args[argidx] == "-work" && argidx+1 < GetSize(args)) { + work = args[++argidx]; + continue; + } + break; + } + + if (GetSize(args) > argidx && (args[argidx] == "-vlog95" || args[argidx] == "-vlog2k" || args[argidx] == "-sv2005" || + args[argidx] == "-sv2009" || args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal")) + { + Array file_names; + unsigned verilog_mode; + + if (args[argidx] == "-vlog95") + verilog_mode = veri_file::VERILOG_95; + else if (args[argidx] == "-vlog2k") + verilog_mode = veri_file::VERILOG_2K; + else if (args[argidx] == "-sv2005") + verilog_mode = veri_file::SYSTEM_VERILOG_2005; + else if (args[argidx] == "-sv2009") + verilog_mode = veri_file::SYSTEM_VERILOG_2009; + else if (args[argidx] == "-sv2012" || args[argidx] == "-sv" || args[argidx] == "-formal") + verilog_mode = veri_file::SYSTEM_VERILOG; + else + log_abort(); + + veri_file::DefineMacro("VERIFIC"); + veri_file::DefineMacro(args[argidx] == "-formal" ? "FORMAL" : "SYNTHESIS"); + + for (argidx++; argidx < GetSize(args) && GetSize(args[argidx]) >= 2 && args[argidx].substr(0, 2) == "-D"; argidx++) { + std::string name = args[argidx].substr(2); + if (args[argidx] == "-D") { + if (++argidx >= GetSize(args)) + break; + name = args[argidx]; + } + size_t equal = name.find('='); + if (equal != std::string::npos) { + string value = name.substr(equal+1); + name = name.substr(0, equal); + veri_file::DefineMacro(name.c_str(), value.c_str()); + } else { + veri_file::DefineMacro(name.c_str()); + } + } + + for (auto &dir : verific_incdirs) + veri_file::AddIncludeDir(dir.c_str()); + for (auto &dir : verific_libdirs) + veri_file::AddYDir(dir.c_str()); + + while (argidx < GetSize(args)) + file_names.Insert(args[argidx++].c_str()); + + if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU)) + log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n"); + + verific_import_pending = true; + goto check_error; + } + + if (GetSize(args) > argidx && args[argidx] == "-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)) + for (argidx++; argidx < GetSize(args); argidx++) + if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_87)) log_cmd_error("Reading `%s' in VHDL_87 mode failed.\n", args[argidx].c_str()); - return; + verific_import_pending = true; + goto check_error; } - if (args.size() > 1 && args[1] == "-vhdl93") { + if (GetSize(args) > argidx && args[argidx] == "-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)) + for (argidx++; argidx < GetSize(args); argidx++) + if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_93)) log_cmd_error("Reading `%s' in VHDL_93 mode failed.\n", args[argidx].c_str()); - return; + verific_import_pending = true; + goto check_error; } - if (args.size() > 1 && args[1] == "-vhdl2k") { + if (GetSize(args) > argidx && args[argidx] == "-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)) + for (argidx++; argidx < GetSize(args); argidx++) + if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2K)) log_cmd_error("Reading `%s' in VHDL_2K mode failed.\n", args[argidx].c_str()); - return; + verific_import_pending = true; + goto check_error; } - if (args.size() > 1 && args[1] == "-vhdl2008") { + if (GetSize(args) > argidx && (args[argidx] == "-vhdl2008" || args[argidx] == "-vhdl")) { 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)) + for (argidx++; argidx < GetSize(args); argidx++) + if (!vhdl_file::Analyze(args[argidx].c_str(), work.c_str(), vhdl_file::VHDL_2008)) log_cmd_error("Reading `%s' in VHDL_2008 mode failed.\n", args[argidx].c_str()); - return; + verific_import_pending = true; + goto check_error; } - if (args.size() > 1 && args[1] == "-import") + if (GetSize(args) > argidx && args[argidx] == "-import") { std::set nl_todo, nl_done; - bool mode_all = false, mode_gates = false; + bool mode_all = false, mode_gates = false, mode_keep = false; + bool mode_nosva = false, mode_names = false, mode_verific = false; + bool mode_autocover = false; + bool flatten = false, extnets = false; + string dumpfile; - size_t argidx = 2; - for (; argidx < args.size(); argidx++) { + for (argidx++; argidx < GetSize(args); argidx++) { if (args[argidx] == "-all") { mode_all = true; continue; @@ -937,69 +2115,293 @@ struct VerificPass : public Pass { mode_gates = true; continue; } + if (args[argidx] == "-flatten") { + flatten = true; + continue; + } + if (args[argidx] == "-extnets") { + extnets = true; + continue; + } + if (args[argidx] == "-k") { + mode_keep = true; + continue; + } + if (args[argidx] == "-nosva") { + mode_nosva = true; + continue; + } + if (args[argidx] == "-L" && argidx+1 < GetSize(args)) { + verific_sva_fsm_limit = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-n") { + mode_names = true; + continue; + } + if (args[argidx] == "-autocover") { + mode_autocover = true; + continue; + } + if (args[argidx] == "-V") { + mode_verific = true; + continue; + } + if (args[argidx] == "-v") { + verific_verbose = 1; + continue; + } + if (args[argidx] == "-vv") { + verific_verbose = 2; + continue; + } + if (args[argidx] == "-d" && argidx+1 < GetSize(args)) { + dumpfile = args[++argidx]; + continue; + } break; } - if (argidx > args.size() && args[argidx].substr(0, 1) == "-") + if (argidx > GetSize(args) && 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"); + log("Running hier_tree::ElaborateAll().\n"); - MapIter m1, m2, m3; - VeriModule *mod; - FOREACH_VERILOG_MODULE(m1, mod) - args.push_back(mod->Name()); + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); + VeriLibrary *veri_lib = veri_file::GetLibrary(work.c_str(), 1); - 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()); - } + Array veri_libs, vhdl_libs; + if (vhdl_lib) vhdl_libs.InsertLast(vhdl_lib); + if (veri_lib) veri_libs.InsertLast(veri_lib); + + Array *netlists = hier_tree::ElaborateAll(&veri_libs, &vhdl_libs); + Netlist *nl; + int i; + + FOREACH_ARRAY_ITEM(netlists, i, nl) + nl_todo.insert(nl); + delete netlists; } else - if (argidx == args.size()) + { + if (argidx == GetSize(args)) 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()); + Array veri_modules, vhdl_units; + for (; argidx < GetSize(args); argidx++) + { + const char *name = args[argidx].c_str(); + + VeriModule *veri_module = veri_file::GetModule(name); + if (veri_module) { + log("Adding Verilog module '%s' to elaboration queue.\n", name); + veri_modules.InsertLast(veri_module); + continue; + } + + VhdlLibrary *vhdl_lib = vhdl_file::GetLibrary(work.c_str(), 1); + VhdlDesignUnit *vhdl_unit = vhdl_lib->GetPrimUnit(name); + if (vhdl_unit) { + log("Adding VHDL unit '%s' to elaboration queue.\n", name); + vhdl_units.InsertLast(vhdl_unit); + continue; + } + + log_error("Can't find module/unit '%s'.\n", name); } + + log("Running hier_tree::Elaborate().\n"); + Array *netlists = hier_tree::Elaborate(&veri_modules, &vhdl_units); + Netlist *nl; + int i; + + FOREACH_ARRAY_ITEM(netlists, i, nl) + nl_todo.insert(nl); + delete netlists; + } + + if (!verific_error_msg.empty()) + goto check_error; + + if (flatten) { + for (auto nl : nl_todo) + nl->Flatten(); + } + + if (extnets) { + VerificExtNets worker; + for (auto nl : nl_todo) + worker.run(nl); + } + + if (!dumpfile.empty()) { + VeriWrite veri_writer; + veri_writer.WriteFile(dumpfile.c_str(), 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); + if (nl_done.count(nl) == 0) { + VerificImporter importer(mode_gates, mode_keep, mode_nosva, + mode_names, mode_verific, mode_autocover); + importer.import_netlist(design, nl, nl_todo); + } nl_todo.erase(nl); nl_done.insert(nl); } + veri_file::Reset(); + vhdl_file::Reset(); Libset::Reset(); - return; + verific_incdirs.clear(); + verific_libdirs.clear(); + verific_import_pending = false; + goto check_error; } log_cmd_error("Missing or unsupported mode parameter.\n"); + + check_error: + if (!verific_error_msg.empty()) + log_error("%s\n", verific_error_msg.c_str()); + } #else /* YOSYS_ENABLE_VERIFIC */ - virtual void execute(std::vector, RTLIL::Design *) { + void execute(std::vector, RTLIL::Design *) YS_OVERRIDE { log_cmd_error("This version of Yosys is built without Verific support.\n"); } #endif } VerificPass; -YOSYS_NAMESPACE_END +struct ReadPass : public Pass { + ReadPass() : Pass("read", "load HDL designs") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal} ..\n"); + log("\n"); + log("Load the specified Verilog/SystemVerilog files. (Full SystemVerilog support\n"); + log("is only available via Verific.)\n"); + log("\n"); + log("Additional -D[=] options may be added after the option indicating\n"); + log("the language version (and before file names) to set additional verilog defines.\n"); + log("\n"); + log("\n"); + log(" read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} ..\n"); + log("\n"); + log("Load the specified VHDL files. (Requires Verific.)\n"); + log("\n"); + log("\n"); + log(" read -define [=]..\n"); + log("\n"); + log("Set global Verilog/SystemVerilog defines.\n"); + log("\n"); + log("\n"); + log(" read -undef ..\n"); + log("\n"); + log("Unset global Verilog/SystemVerilog defines.\n"); + log("\n"); + log("\n"); + log(" read -incdir \n"); + log("\n"); + log("Add directory to global Verilog/SystemVerilog include directories.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + if (args.size() < 2) + log_cmd_error("Missing mode parameter.\n"); + if (args.size() < 3) + log_cmd_error("Missing file name parameter.\n"); + +#ifdef YOSYS_ENABLE_VERIFIC + bool use_verific = !check_noverific_env(); +#else + bool use_verific = false; +#endif + + if (args[1] == "-vlog95" || args[1] == "-vlog2k") { + if (use_verific) { + args[0] = "verific"; + } else { + args[0] = "read_verilog"; + args.erase(args.begin()+1, args.begin()+2); + } + Pass::call(design, args); + return; + } + + if (args[1] == "-sv2005" || args[1] == "-sv2009" || args[1] == "-sv2012" || args[1] == "-sv" || args[1] == "-formal") { + if (use_verific) { + args[0] = "verific"; + } else { + args[0] = "read_verilog"; + if (args[1] == "-formal") + args.insert(args.begin()+1, std::string()); + args[1] = "-sv"; + } + Pass::call(design, args); + return; + } + + if (args[1] == "-vhdl87" || args[1] == "-vhdl93" || args[1] == "-vhdl2k" || args[1] == "-vhdl2008" || args[1] == "-vhdl") { + if (use_verific) { + args[0] = "verific"; + Pass::call(design, args); + } else { + log_cmd_error("This version of Yosys is built without Verific support.\n"); + } + return; + } + + if (args[1] == "-define") { + if (use_verific) { + args[0] = "verific"; + args[1] = "-vlog-define"; + Pass::call(design, args); + } + args[0] = "verilog_defines"; + args.erase(args.begin()+1, args.begin()+2); + for (int i = 1; i < GetSize(args); i++) + args[i] = "-D" + args[i]; + Pass::call(design, args); + return; + } + + if (args[1] == "-undef") { + if (use_verific) { + args[0] = "verific"; + args[1] = "-vlog-undef"; + Pass::call(design, args); + } + args[0] = "verilog_defines"; + args.erase(args.begin()+1, args.begin()+2); + for (int i = 1; i < GetSize(args); i++) + args[i] = "-U" + args[i]; + Pass::call(design, args); + return; + } + + if (args[1] == "-incdir") { + if (use_verific) { + args[0] = "verific"; + args[1] = "-vlog-incdir"; + Pass::call(design, args); + } + args[0] = "verilog_defaults"; + args[1] = "-add"; + for (int i = 2; i < GetSize(args); i++) + args[i] = "-I" + args[i]; + Pass::call(design, args); + return; + } + + log_cmd_error("Missing or unsupported mode parameter.\n"); + } +} ReadPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/frontends/verific/verific.h b/yosys/frontends/verific/verific.h new file mode 100644 index 000000000..b331dd4b9 --- /dev/null +++ b/yosys/frontends/verific/verific.h @@ -0,0 +1,109 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ + +#ifdef YOSYS_ENABLE_VERIFIC + +#include "DataBase.h" + +YOSYS_NAMESPACE_BEGIN + +extern int verific_verbose; + +extern bool verific_import_pending; +extern void verific_import(Design *design, std::string top = std::string()); + +extern pool verific_sva_prims; + +struct VerificImporter; + +struct VerificClocking { + RTLIL::Module *module = nullptr; + Verific::Net *clock_net = nullptr; + Verific::Net *enable_net = nullptr; + Verific::Net *disable_net = nullptr; + Verific::Net *body_net = nullptr; + Verific::Net *cond_net = nullptr; + SigBit clock_sig = State::Sx; + SigBit enable_sig = State::S1; + SigBit disable_sig = State::S0; + bool posedge = true; + bool gclk = false; + + VerificClocking() { } + VerificClocking(VerificImporter *importer, Verific::Net *net, bool sva_at_only = false); + RTLIL::Cell *addDff(IdString name, SigSpec sig_d, SigSpec sig_q, Const init_value = Const()); + RTLIL::Cell *addAdff(IdString name, RTLIL::SigSpec sig_arst, SigSpec sig_d, SigSpec sig_q, Const arst_value); + RTLIL::Cell *addDffsr(IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, SigSpec sig_d, SigSpec sig_q); + + bool property_matches_sequence(const VerificClocking &seq) const { + if (clock_net != seq.clock_net) + return false; + if (enable_net != seq.enable_net) + return false; + if (posedge != seq.posedge) + return false; + return true; + } +}; + +struct VerificImporter +{ + RTLIL::Module *module; + Verific::Netlist *netlist; + + std::map net_map; + std::map sva_posedge_map; + pool any_all_nets; + + bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific; + bool mode_autocover; + + VerificImporter(bool mode_gates, bool mode_keep, bool mode_nosva, bool mode_names, bool mode_verific, bool mode_autocover); + + RTLIL::SigBit net_map_at(Verific::Net *net); + + RTLIL::IdString new_verific_id(Verific::DesignObj *obj); + void import_attributes(dict &attributes, Verific::DesignObj *obj); + + RTLIL::SigSpec operatorInput(Verific::Instance *inst); + RTLIL::SigSpec operatorInput1(Verific::Instance *inst); + RTLIL::SigSpec operatorInput2(Verific::Instance *inst); + RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname); + RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool *any_all_nets = nullptr); + + bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name); + bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name); + + void merge_past_ffs_clock(pool &candidates, SigBit clock, bool clock_pol); + void merge_past_ffs(pool &candidates); + + void import_netlist(RTLIL::Design *design, Verific::Netlist *nl, std::set &nl_todo); +}; + +void verific_import_sva_assert(VerificImporter *importer, Verific::Instance *inst); +void verific_import_sva_assume(VerificImporter *importer, Verific::Instance *inst); +void verific_import_sva_cover(VerificImporter *importer, Verific::Instance *inst); +void verific_import_sva_trigger(VerificImporter *importer, Verific::Instance *inst); +bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net); + +extern int verific_sva_fsm_limit; + +YOSYS_NAMESPACE_END + +#endif diff --git a/yosys/frontends/verific/verificsva.cc b/yosys/frontends/verific/verificsva.cc new file mode 100644 index 000000000..6681115df --- /dev/null +++ b/yosys/frontends/verific/verificsva.cc @@ -0,0 +1,1815 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ + + +// Currently supported SVA sequence and property syntax: +// http://symbiyosys.readthedocs.io/en/latest/verific.html +// +// Next gen property syntax: +// basic_property +// [antecedent_condition] property +// [antecedent_condition] always.. property +// [antecedent_condition] eventually.. basic_property +// [antecedent_condition] property until.. expression +// [antecedent_condition] basic_property until.. basic_property (assert/assume only) +// +// antecedent_condition: +// sequence |-> +// sequence |=> +// +// basic_property: +// sequence +// not basic_property +// sequence #-# basic_property +// sequence #=# basic_property +// basic_property or basic_property (cover only) +// basic_property and basic_property (assert/assume only) +// basic_property implies basic_property +// basic_property iff basic_property +// +// sequence: +// expression +// sequence ##N sequence +// sequence ##[*] sequence +// sequence ##[+] sequence +// sequence ##[N:M] sequence +// sequence ##[N:$] sequence +// expression [*] +// expression [+] +// expression [*N] +// expression [*N:M] +// expression [*N:$] +// sequence or sequence +// sequence and sequence +// expression throughout sequence +// sequence intersect sequence +// sequence within sequence +// first_match( sequence ) +// expression [=N] +// expression [=N:M] +// expression [=N:$] +// expression [->N] +// expression [->N:M] +// expression [->N:$] + + +#include "kernel/yosys.h" +#include "frontends/verific/verific.h" + +USING_YOSYS_NAMESPACE + +#ifdef VERIFIC_NAMESPACE +using namespace Verific; +#endif + +PRIVATE_NAMESPACE_BEGIN + +// Non-deterministic FSM +struct SvaNFsmNode +{ + // Edge: Activate the target node if ctrl signal is true, consumes clock cycle + // Link: Activate the target node if ctrl signal is true, doesn't consume clock cycle + vector> edges, links; + bool is_cond_node; +}; + +// Non-deterministic FSM after resolving links +struct SvaUFsmNode +{ + // Edge: Activate the target node if all bits in ctrl signal are true, consumes clock cycle + // Accept: This node functions as an accept node if all bits in ctrl signal are true + vector> edges; + vector accept, cond; + bool reachable; +}; + +// Deterministic FSM +struct SvaDFsmNode +{ + // A DFSM state corresponds to a set of NFSM states. We represent DFSM states as sorted vectors + // of NFSM state node ids. Edge/accept controls are constants matched against the ctrl sigspec. + SigSpec ctrl; + vector, Const>> edges; + vector accept, reject; + + // additional temp data for getReject() + Wire *ffoutwire; + SigBit statesig; + SigSpec nextstate; + + // additional temp data for getDFsm() + int outnode; +}; + +struct SvaFsm +{ + Module *module; + VerificClocking clocking; + + SigBit trigger_sig = State::S1, disable_sig; + SigBit throughout_sig = State::S1; + bool in_cond_mode = false; + + vector disable_stack; + vector throughout_stack; + + int startNode, acceptNode, condNode; + vector nodes; + + vector unodes; + dict, SvaDFsmNode> dnodes; + dict, SigBit> cond_eq_cache; + bool materialized = false; + + SigBit final_accept_sig = State::Sx; + SigBit final_reject_sig = State::Sx; + + SvaFsm(const VerificClocking &clking, SigBit trig = State::S1) + { + module = clking.module; + clocking = clking; + trigger_sig = trig; + + startNode = createNode(); + acceptNode = createNode(); + + in_cond_mode = true; + condNode = createNode(); + in_cond_mode = false; + } + + void pushDisable(SigBit sig) + { + log_assert(!materialized); + + disable_stack.push_back(disable_sig); + + if (disable_sig == State::S0) + disable_sig = sig; + else + disable_sig = module->Or(NEW_ID, disable_sig, sig); + } + + void popDisable() + { + log_assert(!materialized); + log_assert(!disable_stack.empty()); + + disable_sig = disable_stack.back(); + disable_stack.pop_back(); + } + + void pushThroughout(SigBit sig) + { + log_assert(!materialized); + + throughout_stack.push_back(throughout_sig); + + if (throughout_sig == State::S1) + throughout_sig = sig; + else + throughout_sig = module->And(NEW_ID, throughout_sig, sig); + } + + void popThroughout() + { + log_assert(!materialized); + log_assert(!throughout_stack.empty()); + + throughout_sig = throughout_stack.back(); + throughout_stack.pop_back(); + } + + int createNode(int link_node = -1) + { + log_assert(!materialized); + + int idx = GetSize(nodes); + nodes.push_back(SvaNFsmNode()); + nodes.back().is_cond_node = in_cond_mode; + if (link_node >= 0) + createLink(link_node, idx); + return idx; + } + + int createStartNode() + { + return createNode(startNode); + } + + void createEdge(int from_node, int to_node, SigBit ctrl = State::S1) + { + log_assert(!materialized); + log_assert(0 <= from_node && from_node < GetSize(nodes)); + log_assert(0 <= to_node && to_node < GetSize(nodes)); + log_assert(from_node != acceptNode); + log_assert(to_node != acceptNode); + log_assert(from_node != condNode); + log_assert(to_node != condNode); + log_assert(to_node != startNode); + + if (from_node != startNode) + log_assert(nodes.at(from_node).is_cond_node == nodes.at(to_node).is_cond_node); + + if (throughout_sig != State::S1) { + if (ctrl != State::S1) + ctrl = module->And(NEW_ID, throughout_sig, ctrl); + else + ctrl = throughout_sig; + } + + nodes[from_node].edges.push_back(make_pair(to_node, ctrl)); + } + + void createLink(int from_node, int to_node, SigBit ctrl = State::S1) + { + log_assert(!materialized); + log_assert(0 <= from_node && from_node < GetSize(nodes)); + log_assert(0 <= to_node && to_node < GetSize(nodes)); + log_assert(from_node != acceptNode); + log_assert(from_node != condNode); + log_assert(to_node != startNode); + + if (from_node != startNode) + log_assert(nodes.at(from_node).is_cond_node == nodes.at(to_node).is_cond_node); + + if (throughout_sig != State::S1) { + if (ctrl != State::S1) + ctrl = module->And(NEW_ID, throughout_sig, ctrl); + else + ctrl = throughout_sig; + } + + nodes[from_node].links.push_back(make_pair(to_node, ctrl)); + } + + void make_link_order(vector &order, int node, int min) + { + order[node] = std::max(order[node], min); + for (auto &it : nodes[node].links) + make_link_order(order, it.first, order[node]+1); + } + + // ---------------------------------------------------- + // Generating NFSM circuit to acquire accept signal + + SigBit getAccept() + { + log_assert(!materialized); + materialized = true; + + vector state_wire(GetSize(nodes)); + vector state_sig(GetSize(nodes)); + vector next_state_sig(GetSize(nodes)); + + // Create state signals + + { + SigBit not_disable = State::S1; + + if (disable_sig != State::S0) + not_disable = module->Not(NEW_ID, disable_sig); + + for (int i = 0; i < GetSize(nodes); i++) + { + Wire *w = module->addWire(NEW_ID); + state_wire[i] = w; + state_sig[i] = w; + + if (i == startNode) + state_sig[i] = module->Or(NEW_ID, state_sig[i], trigger_sig); + + if (disable_sig != State::S0) + state_sig[i] = module->And(NEW_ID, state_sig[i], not_disable); + } + } + + // Follow Links + + { + vector node_order(GetSize(nodes)); + vector> order_to_nodes; + + for (int i = 0; i < GetSize(nodes); i++) + make_link_order(node_order, i, 0); + + for (int i = 0; i < GetSize(nodes); i++) { + if (node_order[i] >= GetSize(order_to_nodes)) + order_to_nodes.resize(node_order[i]+1); + order_to_nodes[node_order[i]].push_back(i); + } + + for (int order = 0; order < GetSize(order_to_nodes); order++) + for (int node : order_to_nodes[order]) + { + for (auto &it : nodes[node].links) + { + int target = it.first; + SigBit ctrl = state_sig[node]; + + if (it.second != State::S1) + ctrl = module->And(NEW_ID, ctrl, it.second); + + state_sig[target] = module->Or(NEW_ID, state_sig[target], ctrl); + } + } + } + + // Construct activations + + { + vector activate_sig(GetSize(nodes)); + vector activate_bit(GetSize(nodes)); + + for (int i = 0; i < GetSize(nodes); i++) { + for (auto &it : nodes[i].edges) + activate_sig[it.first].append(module->And(NEW_ID, state_sig[i], it.second)); + } + + for (int i = 0; i < GetSize(nodes); i++) { + if (GetSize(activate_sig[i]) == 0) + next_state_sig[i] = State::S0; + else if (GetSize(activate_sig[i]) == 1) + next_state_sig[i] = activate_sig[i]; + else + next_state_sig[i] = module->ReduceOr(NEW_ID, activate_sig[i]); + } + } + + // Create state FFs + + for (int i = 0; i < GetSize(nodes); i++) + { + if (next_state_sig[i] != State::S0) { + clocking.addDff(NEW_ID, next_state_sig[i], state_wire[i], Const(0, 1)); + } else { + module->connect(state_wire[i], State::S0); + } + } + + final_accept_sig = state_sig[acceptNode]; + return final_accept_sig; + } + + // ---------------------------------------------------- + // Generating quantifier-based NFSM circuit to acquire reject signal + + SigBit getAnyAllRejectWorker(bool /* allMode */) + { + // FIXME + log_abort(); + } + + SigBit getAnyReject() + { + return getAnyAllRejectWorker(false); + } + + SigBit getAllReject() + { + return getAnyAllRejectWorker(true); + } + + // ---------------------------------------------------- + // Generating DFSM circuit to acquire reject signal + + void node_to_unode(int node, int unode, SigSpec ctrl) + { + if (node == acceptNode) + unodes[unode].accept.push_back(ctrl); + + if (node == condNode) + unodes[unode].cond.push_back(ctrl); + + for (auto &it : nodes[node].edges) { + if (it.second != State::S1) { + SigSpec s = {ctrl, it.second}; + s.sort_and_unify(); + unodes[unode].edges.push_back(make_pair(it.first, s)); + } else { + unodes[unode].edges.push_back(make_pair(it.first, ctrl)); + } + } + + for (auto &it : nodes[node].links) { + if (it.second != State::S1) { + SigSpec s = {ctrl, it.second}; + s.sort_and_unify(); + node_to_unode(it.first, unode, s); + } else { + node_to_unode(it.first, unode, ctrl); + } + } + } + + void mark_reachable_unode(int unode) + { + if (unodes[unode].reachable) + return; + + unodes[unode].reachable = true; + for (auto &it : unodes[unode].edges) + mark_reachable_unode(it.first); + } + + void usortint(vector &vec) + { + vector newvec; + std::sort(vec.begin(), vec.end()); + for (int i = 0; i < GetSize(vec); i++) + if (i == GetSize(vec)-1 || vec[i] != vec[i+1]) + newvec.push_back(vec[i]); + vec.swap(newvec); + } + + bool cmp_ctrl(const pool &ctrl_bits, const SigSpec &ctrl) + { + for (int i = 0; i < GetSize(ctrl); i++) + if (ctrl_bits.count(ctrl[i]) == 0) + return false; + return true; + } + + void create_dnode(const vector &state, bool firstmatch, bool condaccept) + { + if (dnodes.count(state) != 0) + return; + + SvaDFsmNode dnode; + dnodes[state] = SvaDFsmNode(); + + for (int unode : state) { + log_assert(unodes[unode].reachable); + for (auto &it : unodes[unode].edges) + dnode.ctrl.append(it.second); + for (auto &it : unodes[unode].accept) + dnode.ctrl.append(it); + for (auto &it : unodes[unode].cond) + dnode.ctrl.append(it); + } + + dnode.ctrl.sort_and_unify(); + + if (GetSize(dnode.ctrl) > verific_sva_fsm_limit) { + if (verific_verbose >= 2) { + log(" detected state explosion in DFSM generation:\n"); + dump(); + log(" ctrl signal: %s\n", log_signal(dnode.ctrl)); + } + log_error("SVA DFSM state ctrl signal has %d (>%d) bits. Stopping to prevent exponential design size explosion.\n", + GetSize(dnode.ctrl), verific_sva_fsm_limit); + } + + for (int i = 0; i < (1 << GetSize(dnode.ctrl)); i++) + { + Const ctrl_val(i, GetSize(dnode.ctrl)); + pool ctrl_bits; + + for (int i = 0; i < GetSize(dnode.ctrl); i++) + if (ctrl_val[i] == State::S1) + ctrl_bits.insert(dnode.ctrl[i]); + + vector new_state; + bool accept = false, cond = false; + + for (int unode : state) { + for (auto &it : unodes[unode].accept) + if (cmp_ctrl(ctrl_bits, it)) + accept = true; + for (auto &it : unodes[unode].cond) + if (cmp_ctrl(ctrl_bits, it)) + cond = true; + } + + bool new_state_cond = false; + bool new_state_noncond = false; + + if (accept && condaccept) + accept = cond; + + if (!accept || !firstmatch) { + for (int unode : state) + for (auto &it : unodes[unode].edges) + if (cmp_ctrl(ctrl_bits, it.second)) { + if (nodes.at(it.first).is_cond_node) + new_state_cond = true; + else + new_state_noncond = true; + new_state.push_back(it.first); + } + } + + if (accept) + dnode.accept.push_back(ctrl_val); + + if (condaccept && (!new_state_cond || !new_state_noncond)) + new_state.clear(); + + if (new_state.empty()) { + if (!accept) + dnode.reject.push_back(ctrl_val); + } else { + usortint(new_state); + dnode.edges.push_back(make_pair(new_state, ctrl_val)); + create_dnode(new_state, firstmatch, condaccept); + } + } + + dnodes[state] = dnode; + } + + void optimize_cond(vector &values) + { + bool did_something = true; + + while (did_something) + { + did_something = false; + + for (int i = 0; i < GetSize(values); i++) + for (int j = 0; j < GetSize(values); j++) + { + if (i == j) + continue; + + log_assert(GetSize(values[i]) == GetSize(values[j])); + + int delta_pos = -1; + bool i_within_j = true; + bool j_within_i = true; + + for (int k = 0; k < GetSize(values[i]); k++) { + if (values[i][k] == State::Sa && values[j][k] != State::Sa) { + i_within_j = false; + continue; + } + if (values[i][k] != State::Sa && values[j][k] == State::Sa) { + j_within_i = false; + continue; + } + if (values[i][k] == values[j][k]) + continue; + if (delta_pos >= 0) + goto next_pair; + delta_pos = k; + } + + if (delta_pos >= 0 && i_within_j && j_within_i) { + did_something = true; + values[i][delta_pos] = State::Sa; + values[j] = values.back(); + values.pop_back(); + goto next_pair; + } + + if (delta_pos < 0 && i_within_j) { + did_something = true; + values[i] = values.back(); + values.pop_back(); + goto next_pair; + } + + if (delta_pos < 0 && j_within_i) { + did_something = true; + values[j] = values.back(); + values.pop_back(); + goto next_pair; + } + next_pair:; + } + } + } + + SigBit make_cond_eq(const SigSpec &ctrl, const Const &value, SigBit enable = State::S1) + { + SigSpec sig_a, sig_b; + + log_assert(GetSize(ctrl) == GetSize(value)); + + for (int i = 0; i < GetSize(ctrl); i++) + if (value[i] != State::Sa) { + sig_a.append(ctrl[i]); + sig_b.append(value[i]); + } + + if (GetSize(sig_a) == 0) + return enable; + + if (enable != State::S1) { + sig_a.append(enable); + sig_b.append(State::S1); + } + + auto key = make_pair(sig_a, sig_b); + + if (cond_eq_cache.count(key) == 0) + { + if (sig_b == State::S1) + cond_eq_cache[key] = sig_a; + else if (sig_b == State::S0) + cond_eq_cache[key] = module->Not(NEW_ID, sig_a); + else + cond_eq_cache[key] = module->Eq(NEW_ID, sig_a, sig_b); + + if (verific_verbose >= 2) { + log(" Cond: %s := %s == %s\n", log_signal(cond_eq_cache[key]), + log_signal(sig_a), log_signal(sig_b)); + } + } + + return cond_eq_cache.at(key); + } + + void getFirstAcceptReject(SigBit *accept_p, SigBit *reject_p) + { + log_assert(!materialized); + materialized = true; + + // Create unlinked NFSM + + unodes.resize(GetSize(nodes)); + + for (int node = 0; node < GetSize(nodes); node++) + node_to_unode(node, node, SigSpec()); + + mark_reachable_unode(startNode); + + // Create DFSM + + create_dnode(vector{startNode}, true, false); + dnodes.sort(); + + // Create DFSM Circuit + + SigSpec accept_sig, reject_sig; + + for (auto &it : dnodes) + { + SvaDFsmNode &dnode = it.second; + dnode.ffoutwire = module->addWire(NEW_ID); + dnode.statesig = dnode.ffoutwire; + + if (it.first == vector{startNode}) + dnode.statesig = module->Or(NEW_ID, dnode.statesig, trigger_sig); + } + + for (auto &it : dnodes) + { + SvaDFsmNode &dnode = it.second; + dict, vector> edge_cond; + + for (auto &edge : dnode.edges) + edge_cond[edge.first].push_back(edge.second); + + for (auto &it : edge_cond) { + optimize_cond(it.second); + for (auto &value : it.second) + dnodes.at(it.first).nextstate.append(make_cond_eq(dnode.ctrl, value, dnode.statesig)); + } + + if (accept_p) { + vector accept_cond = dnode.accept; + optimize_cond(accept_cond); + for (auto &value : accept_cond) + accept_sig.append(make_cond_eq(dnode.ctrl, value, dnode.statesig)); + } + + if (reject_p) { + vector reject_cond = dnode.reject; + optimize_cond(reject_cond); + for (auto &value : reject_cond) + reject_sig.append(make_cond_eq(dnode.ctrl, value, dnode.statesig)); + } + } + + for (auto &it : dnodes) + { + SvaDFsmNode &dnode = it.second; + if (GetSize(dnode.nextstate) == 0) { + module->connect(dnode.ffoutwire, State::S0); + } else + if (GetSize(dnode.nextstate) == 1) { + clocking.addDff(NEW_ID, dnode.nextstate, dnode.ffoutwire, State::S0); + } else { + SigSpec nextstate = module->ReduceOr(NEW_ID, dnode.nextstate); + clocking.addDff(NEW_ID, nextstate, dnode.ffoutwire, State::S0); + } + } + + if (accept_p) + { + if (GetSize(accept_sig) == 0) + final_accept_sig = State::S0; + else if (GetSize(accept_sig) == 1) + final_accept_sig = accept_sig; + else + final_accept_sig = module->ReduceOr(NEW_ID, accept_sig); + *accept_p = final_accept_sig; + } + + if (reject_p) + { + if (GetSize(reject_sig) == 0) + final_reject_sig = State::S0; + else if (GetSize(reject_sig) == 1) + final_reject_sig = reject_sig; + else + final_reject_sig = module->ReduceOr(NEW_ID, reject_sig); + *reject_p = final_reject_sig; + } + } + + SigBit getFirstAccept() + { + SigBit accept; + getFirstAcceptReject(&accept, nullptr); + return accept; + } + + SigBit getReject() + { + SigBit reject; + getFirstAcceptReject(nullptr, &reject); + return reject; + } + + void getDFsm(SvaFsm &output_fsm, int output_start_node, int output_accept_node, int output_reject_node = -1, bool firstmatch = true, bool condaccept = false) + { + log_assert(!materialized); + materialized = true; + + // Create unlinked NFSM + + unodes.resize(GetSize(nodes)); + + for (int node = 0; node < GetSize(nodes); node++) + node_to_unode(node, node, SigSpec()); + + mark_reachable_unode(startNode); + + // Create DFSM + + create_dnode(vector{startNode}, firstmatch, condaccept); + dnodes.sort(); + + // Create DFSM Graph + + for (auto &it : dnodes) + { + SvaDFsmNode &dnode = it.second; + dnode.outnode = output_fsm.createNode(); + + if (it.first == vector{startNode}) + output_fsm.createLink(output_start_node, dnode.outnode); + + if (output_accept_node >= 0) { + vector accept_cond = dnode.accept; + optimize_cond(accept_cond); + for (auto &value : accept_cond) + output_fsm.createLink(it.second.outnode, output_accept_node, make_cond_eq(dnode.ctrl, value)); + } + + if (output_reject_node >= 0) { + vector reject_cond = dnode.reject; + optimize_cond(reject_cond); + for (auto &value : reject_cond) + output_fsm.createLink(it.second.outnode, output_reject_node, make_cond_eq(dnode.ctrl, value)); + } + } + + for (auto &it : dnodes) + { + SvaDFsmNode &dnode = it.second; + dict, vector> edge_cond; + + for (auto &edge : dnode.edges) + edge_cond[edge.first].push_back(edge.second); + + for (auto &it : edge_cond) { + optimize_cond(it.second); + for (auto &value : it.second) + output_fsm.createEdge(dnode.outnode, dnodes.at(it.first).outnode, make_cond_eq(dnode.ctrl, value)); + } + } + } + + // ---------------------------------------------------- + // State dump for verbose log messages + + void dump_nodes() + { + if (nodes.empty()) + return; + + log(" non-deterministic encoding:\n"); + for (int i = 0; i < GetSize(nodes); i++) + { + log(" node %d:%s\n", i, + i == startNode ? " [start]" : + i == acceptNode ? " [accept]" : + i == condNode ? " [cond]" : ""); + + for (auto &it : nodes[i].edges) { + if (it.second != State::S1) + log(" edge %s -> %d\n", log_signal(it.second), it.first); + else + log(" edge -> %d\n", it.first); + } + + for (auto &it : nodes[i].links) { + if (it.second != State::S1) + log(" link %s -> %d\n", log_signal(it.second), it.first); + else + log(" link -> %d\n", it.first); + } + } + } + + void dump_unodes() + { + if (unodes.empty()) + return; + + log(" unlinked non-deterministic encoding:\n"); + for (int i = 0; i < GetSize(unodes); i++) + { + if (!unodes[i].reachable) + continue; + + log(" unode %d:%s\n", i, i == startNode ? " [start]" : ""); + + for (auto &it : unodes[i].edges) { + if (!it.second.empty()) + log(" edge %s -> %d\n", log_signal(it.second), it.first); + else + log(" edge -> %d\n", it.first); + } + + for (auto &ctrl : unodes[i].accept) { + if (!ctrl.empty()) + log(" accept %s\n", log_signal(ctrl)); + else + log(" accept\n"); + } + + for (auto &ctrl : unodes[i].cond) { + if (!ctrl.empty()) + log(" cond %s\n", log_signal(ctrl)); + else + log(" cond\n"); + } + } + } + + void dump_dnodes() + { + if (dnodes.empty()) + return; + + log(" deterministic encoding:\n"); + for (auto &it : dnodes) + { + log(" dnode {"); + for (int i = 0; i < GetSize(it.first); i++) + log("%s%d", i ? "," : "", it.first[i]); + log("}:%s\n", GetSize(it.first) == 1 && it.first[0] == startNode ? " [start]" : ""); + + log(" ctrl %s\n", log_signal(it.second.ctrl)); + + for (auto &edge : it.second.edges) { + log(" edge %s -> {", log_signal(edge.second)); + for (int i = 0; i < GetSize(edge.first); i++) + log("%s%d", i ? "," : "", edge.first[i]); + log("}\n"); + } + + for (auto &value : it.second.accept) + log(" accept %s\n", log_signal(value)); + + for (auto &value : it.second.reject) + log(" reject %s\n", log_signal(value)); + } + } + + void dump() + { + if (!nodes.empty()) + log(" number of NFSM states: %d\n", GetSize(nodes)); + + if (!unodes.empty()) { + int count = 0; + for (auto &unode : unodes) + if (unode.reachable) + count++; + log(" number of reachable UFSM states: %d\n", count); + } + + if (!dnodes.empty()) + log(" number of DFSM states: %d\n", GetSize(dnodes)); + + if (verific_verbose >= 2) { + dump_nodes(); + dump_unodes(); + dump_dnodes(); + } + + if (trigger_sig != State::S1) + log(" trigger signal: %s\n", log_signal(trigger_sig)); + + if (final_accept_sig != State::Sx) + log(" accept signal: %s\n", log_signal(final_accept_sig)); + + if (final_reject_sig != State::Sx) + log(" reject signal: %s\n", log_signal(final_reject_sig)); + } +}; + +PRIVATE_NAMESPACE_END + +YOSYS_NAMESPACE_BEGIN + +pool verific_sva_prims = { + // Copy&paste from Verific 3.16_484_32_170630 Netlist.h + PRIM_SVA_IMMEDIATE_ASSERT, PRIM_SVA_ASSERT, PRIM_SVA_COVER, PRIM_SVA_ASSUME, + PRIM_SVA_EXPECT, PRIM_SVA_POSEDGE, PRIM_SVA_NOT, PRIM_SVA_FIRST_MATCH, + PRIM_SVA_ENDED, PRIM_SVA_MATCHED, PRIM_SVA_CONSECUTIVE_REPEAT, + PRIM_SVA_NON_CONSECUTIVE_REPEAT, PRIM_SVA_GOTO_REPEAT, + PRIM_SVA_MATCH_ITEM_TRIGGER, PRIM_SVA_AND, PRIM_SVA_OR, PRIM_SVA_SEQ_AND, + PRIM_SVA_SEQ_OR, PRIM_SVA_EVENT_OR, PRIM_SVA_OVERLAPPED_IMPLICATION, + PRIM_SVA_NON_OVERLAPPED_IMPLICATION, PRIM_SVA_OVERLAPPED_FOLLOWED_BY, + PRIM_SVA_NON_OVERLAPPED_FOLLOWED_BY, PRIM_SVA_INTERSECT, PRIM_SVA_THROUGHOUT, + PRIM_SVA_WITHIN, PRIM_SVA_AT, PRIM_SVA_DISABLE_IFF, PRIM_SVA_SAMPLED, + PRIM_SVA_ROSE, PRIM_SVA_FELL, PRIM_SVA_STABLE, PRIM_SVA_PAST, + PRIM_SVA_MATCH_ITEM_ASSIGN, PRIM_SVA_SEQ_CONCAT, PRIM_SVA_IF, + PRIM_SVA_RESTRICT, PRIM_SVA_TRIGGERED, PRIM_SVA_STRONG, PRIM_SVA_WEAK, + PRIM_SVA_NEXTTIME, PRIM_SVA_S_NEXTTIME, PRIM_SVA_ALWAYS, PRIM_SVA_S_ALWAYS, + PRIM_SVA_S_EVENTUALLY, PRIM_SVA_EVENTUALLY, PRIM_SVA_UNTIL, PRIM_SVA_S_UNTIL, + PRIM_SVA_UNTIL_WITH, PRIM_SVA_S_UNTIL_WITH, PRIM_SVA_IMPLIES, PRIM_SVA_IFF, + PRIM_SVA_ACCEPT_ON, PRIM_SVA_REJECT_ON, PRIM_SVA_SYNC_ACCEPT_ON, + PRIM_SVA_SYNC_REJECT_ON, PRIM_SVA_GLOBAL_CLOCKING_DEF, + PRIM_SVA_GLOBAL_CLOCKING_REF, PRIM_SVA_IMMEDIATE_ASSUME, + PRIM_SVA_IMMEDIATE_COVER, OPER_SVA_SAMPLED, OPER_SVA_STABLE +}; + +struct VerificSvaImporter +{ + VerificImporter *importer = nullptr; + Module *module = nullptr; + + Netlist *netlist = nullptr; + Instance *root = nullptr; + + VerificClocking clocking; + + bool mode_assert = false; + bool mode_assume = false; + bool mode_cover = false; + bool mode_trigger = false; + + Instance *net_to_ast_driver(Net *n) + { + if (n == nullptr) + return nullptr; + + if (n->IsMultipleDriven()) + return nullptr; + + Instance *inst = n->Driver(); + + if (inst == nullptr) + return nullptr; + + if (!verific_sva_prims.count(inst->Type())) + return nullptr; + + if (inst->Type() == PRIM_SVA_ROSE || inst->Type() == PRIM_SVA_FELL || + inst->Type() == PRIM_SVA_STABLE || inst->Type() == OPER_SVA_STABLE || + inst->Type() == PRIM_SVA_PAST || inst->Type() == PRIM_SVA_TRIGGERED) + return nullptr; + + return inst; + } + + Instance *get_ast_input(Instance *inst) { return net_to_ast_driver(inst->GetInput()); } + Instance *get_ast_input1(Instance *inst) { return net_to_ast_driver(inst->GetInput1()); } + Instance *get_ast_input2(Instance *inst) { return net_to_ast_driver(inst->GetInput2()); } + Instance *get_ast_input3(Instance *inst) { return net_to_ast_driver(inst->GetInput3()); } + Instance *get_ast_control(Instance *inst) { return net_to_ast_driver(inst->GetControl()); } + + // ---------------------------------------------------------- + // SVA Importer + + struct ParserErrorException { + }; + + [[noreturn]] void parser_error(std::string errmsg) + { + if (!importer->mode_keep) + log_error("%s", errmsg.c_str()); + log_warning("%s", errmsg.c_str()); + throw ParserErrorException(); + } + + [[noreturn]] void parser_error(std::string errmsg, linefile_type loc) + { + parser_error(stringf("%s at %s:%d.\n", errmsg.c_str(), LineFile::GetFileName(loc), LineFile::GetLineNo(loc))); + } + + [[noreturn]] void parser_error(std::string errmsg, Instance *inst) + { + parser_error(stringf("%s at %s (%s)", errmsg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); + } + + [[noreturn]] void parser_error(Instance *inst) + { + parser_error(stringf("Verific SVA primitive %s (%s) is currently unsupported in this context", + inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); + } + + dict check_expression_cache; + + bool check_expression(Net *net, bool raise_error = false) + { + while (!check_expression_cache.count(net)) + { + Instance *inst = net_to_ast_driver(net); + + if (inst == nullptr) { + check_expression_cache[net] = true; + break; + } + + if (inst->Type() == PRIM_SVA_AT) + { + VerificClocking new_clocking(importer, net); + log_assert(new_clocking.cond_net == nullptr); + if (!clocking.property_matches_sequence(new_clocking)) + parser_error("Mixed clocking is currently not supported", inst); + check_expression_cache[net] = check_expression(new_clocking.body_net, raise_error); + break; + } + + if (inst->Type() == PRIM_SVA_FIRST_MATCH || inst->Type() == PRIM_SVA_NOT) + { + check_expression_cache[net] = check_expression(inst->GetInput(), raise_error); + break; + } + + if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_INTERSECT || + inst->Type() == PRIM_SVA_WITHIN || inst->Type() == PRIM_SVA_THROUGHOUT || + inst->Type() == PRIM_SVA_OR || inst->Type() == PRIM_SVA_AND) + { + check_expression_cache[net] = check_expression(inst->GetInput1(), raise_error) && check_expression(inst->GetInput2(), raise_error); + break; + } + + if (inst->Type() == PRIM_SVA_SEQ_CONCAT) + { + const char *sva_low_s = inst->GetAttValue("sva:low"); + const char *sva_high_s = inst->GetAttValue("sva:high"); + + int sva_low = atoi(sva_low_s); + int sva_high = atoi(sva_high_s); + bool sva_inf = !strcmp(sva_high_s, "$"); + + if (sva_low == 0 && sva_high == 0 && !sva_inf) + check_expression_cache[net] = check_expression(inst->GetInput1(), raise_error) && check_expression(inst->GetInput2(), raise_error); + else + check_expression_cache[net] = false; + break; + } + + check_expression_cache[net] = false; + } + + if (raise_error && !check_expression_cache.at(net)) + parser_error(net_to_ast_driver(net)); + return check_expression_cache.at(net); + } + + SigBit parse_expression(Net *net) + { + check_expression(net, true); + + Instance *inst = net_to_ast_driver(net); + + if (inst == nullptr) { + return importer->net_map_at(net); + } + + if (inst->Type() == PRIM_SVA_AT) + { + VerificClocking new_clocking(importer, net); + log_assert(new_clocking.cond_net == nullptr); + if (!clocking.property_matches_sequence(new_clocking)) + parser_error("Mixed clocking is currently not supported", inst); + return parse_expression(new_clocking.body_net); + } + + if (inst->Type() == PRIM_SVA_FIRST_MATCH) + return parse_expression(inst->GetInput()); + + if (inst->Type() == PRIM_SVA_NOT) + return module->Not(NEW_ID, parse_expression(inst->GetInput())); + + if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_OR) + return module->Or(NEW_ID, parse_expression(inst->GetInput1()), parse_expression(inst->GetInput2())); + + if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND || inst->Type() == PRIM_SVA_INTERSECT || + inst->Type() == PRIM_SVA_WITHIN || inst->Type() == PRIM_SVA_THROUGHOUT || inst->Type() == PRIM_SVA_SEQ_CONCAT) + return module->And(NEW_ID, parse_expression(inst->GetInput1()), parse_expression(inst->GetInput2())); + + log_abort(); + } + + bool check_zero_consecutive_repeat(Net *net) + { + Instance *inst = net_to_ast_driver(net); + + if (inst == nullptr) + return false; + + if (inst->Type() != PRIM_SVA_CONSECUTIVE_REPEAT) + return false; + + const char *sva_low_s = inst->GetAttValue("sva:low"); + int sva_low = atoi(sva_low_s); + + return sva_low == 0; + } + + int parse_consecutive_repeat(SvaFsm &fsm, int start_node, Net *net, bool add_pre_delay, bool add_post_delay) + { + Instance *inst = net_to_ast_driver(net); + + log_assert(inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT); + + const char *sva_low_s = inst->GetAttValue("sva:low"); + const char *sva_high_s = inst->GetAttValue("sva:high"); + + int sva_low = atoi(sva_low_s); + int sva_high = atoi(sva_high_s); + bool sva_inf = !strcmp(sva_high_s, "$"); + + Net *body_net = inst->GetInput(); + + if (add_pre_delay || add_post_delay) + log_assert(sva_low == 0); + + if (sva_low == 0) { + if (!add_pre_delay && !add_post_delay) + parser_error("Possibly zero-length consecutive repeat must follow or precede a delay of at least one cycle", inst); + sva_low++; + } + + int node = fsm.createNode(start_node); + start_node = node; + + if (add_pre_delay) { + node = fsm.createNode(start_node); + fsm.createEdge(start_node, node); + } + + int prev_node = node; + node = parse_sequence(fsm, node, body_net); + + for (int i = 1; i < sva_low; i++) + { + int next_node = fsm.createNode(); + fsm.createEdge(node, next_node); + + prev_node = node; + node = parse_sequence(fsm, next_node, body_net); + } + + if (sva_inf) + { + log_assert(prev_node >= 0); + fsm.createEdge(node, prev_node); + } + else + { + for (int i = sva_low; i < sva_high; i++) + { + int next_node = fsm.createNode(); + fsm.createEdge(node, next_node); + + prev_node = node; + node = parse_sequence(fsm, next_node, body_net); + + fsm.createLink(prev_node, node); + } + } + + if (add_post_delay) { + int next_node = fsm.createNode(); + fsm.createEdge(node, next_node); + node = next_node; + } + + if (add_pre_delay || add_post_delay) + fsm.createLink(start_node, node); + + return node; + } + + int parse_sequence(SvaFsm &fsm, int start_node, Net *net) + { + if (check_expression(net)) { + int node = fsm.createNode(); + fsm.createLink(start_node, node, parse_expression(net)); + return node; + } + + Instance *inst = net_to_ast_driver(net); + + if (inst->Type() == PRIM_SVA_AT) + { + VerificClocking new_clocking(importer, net); + log_assert(new_clocking.cond_net == nullptr); + if (!clocking.property_matches_sequence(new_clocking)) + parser_error("Mixed clocking is currently not supported", inst); + return parse_sequence(fsm, start_node, new_clocking.body_net); + } + + if (inst->Type() == PRIM_SVA_FIRST_MATCH) + { + SvaFsm match_fsm(clocking); + match_fsm.createLink(parse_sequence(match_fsm, match_fsm.createStartNode(), inst->GetInput()), match_fsm.acceptNode); + + int node = fsm.createNode(); + match_fsm.getDFsm(fsm, start_node, node); + + if (verific_verbose) { + log(" First Match FSM:\n"); + match_fsm.dump(); + } + + return node; + } + + if (inst->Type() == PRIM_SVA_SEQ_CONCAT) + { + const char *sva_low_s = inst->GetAttValue("sva:low"); + const char *sva_high_s = inst->GetAttValue("sva:high"); + + int sva_low = atoi(sva_low_s); + int sva_high = atoi(sva_high_s); + bool sva_inf = !strcmp(sva_high_s, "$"); + + int node = -1; + bool past_add_delay = false; + + if (check_zero_consecutive_repeat(inst->GetInput1()) && sva_low > 0) { + node = parse_consecutive_repeat(fsm, start_node, inst->GetInput1(), false, true); + sva_low--, sva_high--; + } else { + node = parse_sequence(fsm, start_node, inst->GetInput1()); + } + + if (check_zero_consecutive_repeat(inst->GetInput2()) && sva_low > 0) { + past_add_delay = true; + sva_low--, sva_high--; + } + + for (int i = 0; i < sva_low; i++) { + int next_node = fsm.createNode(); + fsm.createEdge(node, next_node); + node = next_node; + } + + if (sva_inf) + { + fsm.createEdge(node, node); + } + else + { + for (int i = sva_low; i < sva_high; i++) + { + int next_node = fsm.createNode(); + fsm.createEdge(node, next_node); + fsm.createLink(node, next_node); + node = next_node; + } + } + + if (past_add_delay) + node = parse_consecutive_repeat(fsm, node, inst->GetInput2(), true, false); + else + node = parse_sequence(fsm, node, inst->GetInput2()); + + return node; + } + + if (inst->Type() == PRIM_SVA_CONSECUTIVE_REPEAT) + { + return parse_consecutive_repeat(fsm, start_node, net, false, false); + } + + if (inst->Type() == PRIM_SVA_NON_CONSECUTIVE_REPEAT || inst->Type() == PRIM_SVA_GOTO_REPEAT) + { + const char *sva_low_s = inst->GetAttValue("sva:low"); + const char *sva_high_s = inst->GetAttValue("sva:high"); + + int sva_low = atoi(sva_low_s); + int sva_high = atoi(sva_high_s); + bool sva_inf = !strcmp(sva_high_s, "$"); + + Net *body_net = inst->GetInput(); + int node = fsm.createNode(start_node); + + SigBit cond = parse_expression(body_net); + SigBit not_cond = module->Not(NEW_ID, cond); + + for (int i = 0; i < sva_low; i++) + { + int wait_node = fsm.createNode(); + fsm.createEdge(wait_node, wait_node, not_cond); + + if (i == 0) + fsm.createLink(node, wait_node); + else + fsm.createEdge(node, wait_node); + + int next_node = fsm.createNode(); + fsm.createLink(wait_node, next_node, cond); + + node = next_node; + } + + if (sva_inf) + { + int wait_node = fsm.createNode(); + fsm.createEdge(wait_node, wait_node, not_cond); + fsm.createEdge(node, wait_node); + fsm.createLink(wait_node, node, cond); + } + else + { + for (int i = sva_low; i < sva_high; i++) + { + int wait_node = fsm.createNode(); + fsm.createEdge(wait_node, wait_node, not_cond); + + if (i == 0) + fsm.createLink(node, wait_node); + else + fsm.createEdge(node, wait_node); + + int next_node = fsm.createNode(); + fsm.createLink(wait_node, next_node, cond); + + fsm.createLink(node, next_node); + node = next_node; + } + } + + if (inst->Type() == PRIM_SVA_NON_CONSECUTIVE_REPEAT) + fsm.createEdge(node, node); + + return node; + } + + if (inst->Type() == PRIM_SVA_SEQ_OR || inst->Type() == PRIM_SVA_OR) + { + int node = parse_sequence(fsm, start_node, inst->GetInput1()); + int node2 = parse_sequence(fsm, start_node, inst->GetInput2()); + fsm.createLink(node2, node); + return node; + } + + if (inst->Type() == PRIM_SVA_SEQ_AND || inst->Type() == PRIM_SVA_AND) + { + SvaFsm fsm1(clocking); + fsm1.createLink(parse_sequence(fsm1, fsm1.createStartNode(), inst->GetInput1()), fsm1.acceptNode); + + SvaFsm fsm2(clocking); + fsm2.createLink(parse_sequence(fsm2, fsm2.createStartNode(), inst->GetInput2()), fsm2.acceptNode); + + SvaFsm combined_fsm(clocking); + fsm1.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode); + fsm2.getDFsm(combined_fsm, combined_fsm.createStartNode(), -1, combined_fsm.acceptNode); + + int node = fsm.createNode(); + combined_fsm.getDFsm(fsm, start_node, -1, node); + + if (verific_verbose) + { + log(" Left And FSM:\n"); + fsm1.dump(); + + log(" Right And FSM:\n"); + fsm1.dump(); + + log(" Combined And FSM:\n"); + combined_fsm.dump(); + } + + return node; + } + + if (inst->Type() == PRIM_SVA_INTERSECT || inst->Type() == PRIM_SVA_WITHIN) + { + SvaFsm intersect_fsm(clocking); + + if (inst->Type() == PRIM_SVA_INTERSECT) + { + intersect_fsm.createLink(parse_sequence(intersect_fsm, intersect_fsm.createStartNode(), inst->GetInput1()), intersect_fsm.acceptNode); + } + else + { + int n = intersect_fsm.createNode(); + intersect_fsm.createLink(intersect_fsm.createStartNode(), n); + intersect_fsm.createEdge(n, n); + + n = parse_sequence(intersect_fsm, n, inst->GetInput1()); + + intersect_fsm.createLink(n, intersect_fsm.acceptNode); + intersect_fsm.createEdge(n, n); + } + + intersect_fsm.in_cond_mode = true; + intersect_fsm.createLink(parse_sequence(intersect_fsm, intersect_fsm.createStartNode(), inst->GetInput2()), intersect_fsm.condNode); + intersect_fsm.in_cond_mode = false; + + int node = fsm.createNode(); + intersect_fsm.getDFsm(fsm, start_node, node, -1, false, true); + + if (verific_verbose) { + log(" Intersect FSM:\n"); + intersect_fsm.dump(); + } + + return node; + } + + if (inst->Type() == PRIM_SVA_THROUGHOUT) + { + SigBit expr = parse_expression(inst->GetInput1()); + + fsm.pushThroughout(expr); + int node = parse_sequence(fsm, start_node, inst->GetInput2()); + fsm.popThroughout(); + + return node; + } + + parser_error(inst); + } + + void get_fsm_accept_reject(SvaFsm &fsm, SigBit *accept_p, SigBit *reject_p, bool swap_accept_reject = false) + { + log_assert(accept_p != nullptr || reject_p != nullptr); + + if (swap_accept_reject) + get_fsm_accept_reject(fsm, reject_p, accept_p); + else if (reject_p == nullptr) + *accept_p = fsm.getAccept(); + else if (accept_p == nullptr) + *reject_p = fsm.getReject(); + else + fsm.getFirstAcceptReject(accept_p, reject_p); + } + + bool eventually_property(Net *&net, SigBit &trig) + { + Instance *inst = net_to_ast_driver(net); + + if (inst == nullptr) + return false; + + if (clocking.cond_net != nullptr) + trig = importer->net_map_at(clocking.cond_net); + else + trig = State::S1; + + if (inst->Type() == PRIM_SVA_S_EVENTUALLY || inst->Type() == PRIM_SVA_EVENTUALLY) + { + if (mode_cover || mode_trigger) + parser_error(inst); + + net = inst->GetInput(); + clocking.cond_net = nullptr; + + return true; + } + + if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION || + inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) + { + Net *antecedent_net = inst->GetInput1(); + Net *consequent_net = inst->GetInput2(); + + Instance *consequent_inst = net_to_ast_driver(consequent_net); + + if (consequent_inst == nullptr) + return false; + + if (consequent_inst->Type() != PRIM_SVA_S_EVENTUALLY && consequent_inst->Type() != PRIM_SVA_EVENTUALLY) + return false; + + if (mode_cover || mode_trigger) + parser_error(consequent_inst); + + int node; + + SvaFsm antecedent_fsm(clocking, trig); + node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net); + if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { + int next_node = antecedent_fsm.createNode(); + antecedent_fsm.createEdge(node, next_node); + node = next_node; + } + antecedent_fsm.createLink(node, antecedent_fsm.acceptNode); + + trig = antecedent_fsm.getAccept(); + net = consequent_inst->GetInput(); + clocking.cond_net = nullptr; + + if (verific_verbose) { + log(" Eventually Antecedent FSM:\n"); + antecedent_fsm.dump(); + } + + return true; + } + + return false; + } + + void parse_property(Net *net, SigBit *accept_p, SigBit *reject_p) + { + Instance *inst = net_to_ast_driver(net); + + SigBit trig = State::S1; + + if (clocking.cond_net != nullptr) + trig = importer->net_map_at(clocking.cond_net); + + if (inst == nullptr) + { + log_assert(trig == State::S1); + + if (accept_p != nullptr) + *accept_p = importer->net_map_at(net); + if (reject_p != nullptr) + *reject_p = module->Not(NEW_ID, importer->net_map_at(net)); + } + else + if (inst->Type() == PRIM_SVA_OVERLAPPED_IMPLICATION || + inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) + { + Net *antecedent_net = inst->GetInput1(); + Net *consequent_net = inst->GetInput2(); + int node; + + SvaFsm antecedent_fsm(clocking, trig); + node = parse_sequence(antecedent_fsm, antecedent_fsm.createStartNode(), antecedent_net); + if (inst->Type() == PRIM_SVA_NON_OVERLAPPED_IMPLICATION) { + int next_node = antecedent_fsm.createNode(); + antecedent_fsm.createEdge(node, next_node); + node = next_node; + } + + Instance *consequent_inst = net_to_ast_driver(consequent_net); + + if (consequent_inst && (consequent_inst->Type() == PRIM_SVA_UNTIL || consequent_inst->Type() == PRIM_SVA_S_UNTIL || + consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH)) + { + bool until_with = consequent_inst->Type() == PRIM_SVA_UNTIL_WITH || consequent_inst->Type() == PRIM_SVA_S_UNTIL_WITH; + + Net *until_net = consequent_inst->GetInput2(); + consequent_net = consequent_inst->GetInput1(); + consequent_inst = net_to_ast_driver(consequent_net); + + SigBit until_sig = parse_expression(until_net); + SigBit not_until_sig = module->Not(NEW_ID, until_sig); + antecedent_fsm.createEdge(node, node, not_until_sig); + + antecedent_fsm.createLink(node, antecedent_fsm.acceptNode, until_with ? State::S1 : not_until_sig); + } + else + { + antecedent_fsm.createLink(node, antecedent_fsm.acceptNode); + } + + SigBit antecedent_match = antecedent_fsm.getAccept(); + + if (verific_verbose) { + log(" Antecedent FSM:\n"); + antecedent_fsm.dump(); + } + + bool consequent_not = false; + if (consequent_inst && consequent_inst->Type() == PRIM_SVA_NOT) { + consequent_not = true; + consequent_net = consequent_inst->GetInput(); + consequent_inst = net_to_ast_driver(consequent_net); + } + + SvaFsm consequent_fsm(clocking, antecedent_match); + node = parse_sequence(consequent_fsm, consequent_fsm.createStartNode(), consequent_net); + consequent_fsm.createLink(node, consequent_fsm.acceptNode); + + get_fsm_accept_reject(consequent_fsm, accept_p, reject_p, consequent_not); + + if (verific_verbose) { + log(" Consequent FSM:\n"); + consequent_fsm.dump(); + } + } + else + { + bool prop_not = inst->Type() == PRIM_SVA_NOT; + if (prop_not) { + net = inst->GetInput(); + inst = net_to_ast_driver(net); + } + + SvaFsm fsm(clocking, trig); + int node = parse_sequence(fsm, fsm.createStartNode(), net); + fsm.createLink(node, fsm.acceptNode); + + get_fsm_accept_reject(fsm, accept_p, reject_p, prop_not); + + if (verific_verbose) { + log(" Sequence FSM:\n"); + fsm.dump(); + } + } + } + + void import() + { + try + { + module = importer->module; + netlist = root->Owner(); + + if (verific_verbose) + log(" importing SVA property at root cell %s (%s) at %s:%d.\n", root->Name(), root->View()->Owner()->Name(), + LineFile::GetFileName(root->Linefile()), LineFile::GetLineNo(root->Linefile())); + + RTLIL::IdString root_name = module->uniquify(importer->mode_names || root->IsUserDeclared() ? RTLIL::escape_id(root->Name()) : NEW_ID); + + // parse SVA sequence into trigger signal + + clocking = VerificClocking(importer, root->GetInput(), true); + SigBit accept_bit = State::S0, reject_bit = State::S0; + + if (clocking.body_net == nullptr) + { + if (clocking.clock_net != nullptr || clocking.enable_net != nullptr || clocking.disable_net != nullptr || clocking.cond_net != nullptr) + parser_error(stringf("Failed to parse SVA clocking"), root); + + if (mode_assert || mode_assume) { + reject_bit = module->Not(NEW_ID, parse_expression(root->GetInput())); + } else { + accept_bit = parse_expression(root->GetInput()); + } + } + else + { + Net *net = clocking.body_net; + SigBit trig; + + if (eventually_property(net, trig)) + { + SigBit sig_a, sig_en = trig; + parse_property(net, &sig_a, nullptr); + + // add final FF stage + + SigBit sig_a_q, sig_en_q; + + if (clocking.body_net == nullptr) { + sig_a_q = sig_a; + sig_en_q = sig_en; + } else { + sig_a_q = module->addWire(NEW_ID); + sig_en_q = module->addWire(NEW_ID); + clocking.addDff(NEW_ID, sig_a, sig_a_q, State::S0); + clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0); + } + + // generate fair/live cell + + RTLIL::Cell *c = nullptr; + + if (mode_assert) c = module->addLive(root_name, sig_a_q, sig_en_q); + if (mode_assume) c = module->addFair(root_name, sig_a_q, sig_en_q); + + importer->import_attributes(c->attributes, root); + + return; + } + else + { + if (mode_assert || mode_assume) { + parse_property(net, nullptr, &reject_bit); + } else { + parse_property(net, &accept_bit, nullptr); + } + } + } + + if (mode_trigger) + { + module->connect(importer->net_map_at(root->GetOutput()), accept_bit); + } + else + { + SigBit sig_a = module->Not(NEW_ID, reject_bit); + SigBit sig_en = module->Or(NEW_ID, accept_bit, reject_bit); + + // add final FF stage + + SigBit sig_a_q, sig_en_q; + + if (clocking.body_net == nullptr) { + sig_a_q = sig_a; + sig_en_q = sig_en; + } else { + sig_a_q = module->addWire(NEW_ID); + sig_en_q = module->addWire(NEW_ID); + clocking.addDff(NEW_ID, sig_a, sig_a_q, State::S0); + clocking.addDff(NEW_ID, sig_en, sig_en_q, State::S0); + } + + // generate assert/assume/cover cell + + RTLIL::Cell *c = nullptr; + + if (mode_assert) c = module->addAssert(root_name, sig_a_q, sig_en_q); + if (mode_assume) c = module->addAssume(root_name, sig_a_q, sig_en_q); + if (mode_cover) c = module->addCover(root_name, sig_a_q, sig_en_q); + + importer->import_attributes(c->attributes, root); + } + } + catch (ParserErrorException) + { + } + } +}; + +void verific_import_sva_assert(VerificImporter *importer, Instance *inst) +{ + VerificSvaImporter worker; + worker.importer = importer; + worker.root = inst; + worker.mode_assert = true; + worker.import(); +} + +void verific_import_sva_assume(VerificImporter *importer, Instance *inst) +{ + VerificSvaImporter worker; + worker.importer = importer; + worker.root = inst; + worker.mode_assume = true; + worker.import(); +} + +void verific_import_sva_cover(VerificImporter *importer, Instance *inst) +{ + VerificSvaImporter worker; + worker.importer = importer; + worker.root = inst; + worker.mode_cover = true; + worker.import(); +} + +void verific_import_sva_trigger(VerificImporter *importer, Instance *inst) +{ + VerificSvaImporter worker; + worker.importer = importer; + worker.root = inst; + worker.mode_trigger = true; + worker.import(); +} + +bool verific_is_sva_net(VerificImporter *importer, Verific::Net *net) +{ + VerificSvaImporter worker; + worker.importer = importer; + return worker.net_to_ast_driver(net) != nullptr; +} + +YOSYS_NAMESPACE_END diff --git a/yosys/frontends/verilog/.gitignore b/yosys/frontends/verilog/.gitignore index 1d4ae9e5c..aadbcdcdd 100644 --- a/yosys/frontends/verilog/.gitignore +++ b/yosys/frontends/verilog/.gitignore @@ -1,4 +1,4 @@ verilog_lexer.cc verilog_parser.output verilog_parser.tab.cc -verilog_parser.tab.h +verilog_parser.tab.hh diff --git a/yosys/frontends/verilog/Makefile.inc b/yosys/frontends/verilog/Makefile.inc index a06c1d5ab..dbaace585 100644 --- a/yosys/frontends/verilog/Makefile.inc +++ b/yosys/frontends/verilog/Makefile.inc @@ -1,15 +1,14 @@ GENFILES += frontends/verilog/verilog_parser.tab.cc -GENFILES += frontends/verilog/verilog_parser.tab.h +GENFILES += frontends/verilog/verilog_parser.tab.hh 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 + $(P) $(BISON) -o $@ -d -r all -b frontends/verilog/verilog_parser $< -frontends/verilog/verilog_parser.tab.h: frontends/verilog/verilog_parser.tab.cc +frontends/verilog/verilog_parser.tab.hh: frontends/verilog/verilog_parser.tab.cc frontends/verilog/verilog_lexer.cc: frontends/verilog/verilog_lexer.l $(Q) mkdir -p $(dir $@) diff --git a/yosys/frontends/verilog/const2ast.cc b/yosys/frontends/verilog/const2ast.cc index 4a58357bf..7848c626d 100644 --- a/yosys/frontends/verilog/const2ast.cc +++ b/yosys/frontends/verilog/const2ast.cc @@ -49,8 +49,7 @@ static int my_decimal_div_by_two(std::vector &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()); + log_file_error(current_filename, get_line_num(), "Invalid use of [a-fxz?] in decimal constant.\n"); digits[i] += carry * 10; carry = digits[i] % 2; digits[i] /= 2; @@ -105,8 +104,8 @@ static void my_strtobin(std::vector &data, const char *str, int le 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()); + log_file_error(current_filename, get_line_num(), "Digit larger than %d used in in base-%d constant.\n", + base-1, base); for (int i = 0; i < bits_per_digit; i++) { int bitmask = 1 << i; if (*it == 0xf0) @@ -238,4 +237,3 @@ AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn } YOSYS_NAMESPACE_END - diff --git a/yosys/frontends/verilog/preproc.cc b/yosys/frontends/verilog/preproc.cc index 997920b89..dea22ee8a 100644 --- a/yosys/frontends/verilog/preproc.cc +++ b/yosys/frontends/verilog/preproc.cc @@ -182,15 +182,20 @@ static std::string next_token(bool pass_newline = false) { 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; - } + { + char first = ch; + ch = next_char(); + if (first == '`' && (ch == '"' || ch == '`')) { token += ch; - } + } else do { + if (strchr(ok, ch) == NULL) { + return_char(ch); + break; + } + token += ch; + } while ((ch = next_char()) != 0); + } } - return token; } @@ -210,10 +215,69 @@ static void input_file(std::istream &f, std::string filename) input_buffer.insert(it, "\n`file_pop\n"); } -std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map pre_defines_map, const std::list include_dirs) + +static bool try_expand_macro(std::set &defines_with_args, + std::map &defines_map, + std::string &tok + ) +{ + if (tok == "`\"") { + std::string literal("\""); + // Expand string literal + while (!input_buffer.empty()) { + std::string ntok = next_token(); + if (ntok == "`\"") { + insert_input(literal+"\""); + return true; + } else if (!try_expand_macro(defines_with_args, defines_map, ntok)) { + literal += ntok; + } + } + return false; // error - unmatched `" + } else 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 args; + args.push_back(std::string()); + while (1) + { + skip_spaces(); + 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]); + return true; + } else if (tok == "``") { + // Swallow `` in macro expansion + return true; + } else return false; +} + +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map &pre_defines_map, + dict> &global_defines_cache, const std::list &include_dirs) { std::set defines_with_args; std::map defines_map(pre_defines_map); + std::vector filename_stack; int ifdef_fail_level = 0; bool in_elseif = false; @@ -222,9 +286,19 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons input_buffer_charp = 0; input_file(f, filename); + defines_map["YOSYS"] = "1"; defines_map[formal_mode ? "FORMAL" : "SYNTHESIS"] = "1"; + for (auto &it : pre_defines_map) + defines_map[it.first] = it.second; + + for (auto &it : global_defines_cache) { + if (it.second.second) + defines_with_args.insert(it.first); + defines_map[it.first] = it.second.first; + } + while (!input_buffer.empty()) { std::string tok = next_token(); @@ -281,6 +355,9 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons if (tok == "`include") { skip_spaces(); std::string fn = next_token(true); + while (try_expand_macro(defines_with_args, defines_map, fn)) { + fn = next_token(); + } while (1) { size_t pos = fn.find('"'); if (pos == std::string::npos) @@ -292,26 +369,66 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons } std::ifstream ff; ff.clear(); - ff.open(fn.c_str()); - if (ff.fail() && fn.size() > 0 && fn[0] != '/' && filename.find('/') != std::string::npos) { + std::string fixed_fn = fn; + ff.open(fixed_fn.c_str()); + + bool filename_path_sep_found; + bool fn_relative; +#ifdef _WIN32 + // Both forward and backslash are acceptable separators on Windows. + filename_path_sep_found = (filename.find_first_of("/\\") != std::string::npos); + // Easier just to invert the check for an absolute path (e.g. C:\ or C:/) + fn_relative = !(fn[1] == ':' && (fn[2] == '/' || fn[2] == '\\')); +#else + filename_path_sep_found = (filename.find('/') != std::string::npos); + fn_relative = (fn[0] != '/'); +#endif + + if (ff.fail() && fn.size() > 0 && fn_relative && filename_path_sep_found) { // 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); +#ifdef _WIN32 + fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn; +#else + fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn; +#endif + ff.open(fixed_fn); } - if (ff.fail() && fn.size() > 0 && fn[0] != '/') { + if (ff.fail() && fn.size() > 0 && fn_relative) { // 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); + fixed_fn = incdir + '/' + fn; + ff.open(fixed_fn); if (!ff.fail()) break; } } - if (ff.fail()) + if (ff.fail()) { output_code.push_back("`file_notfound " + fn); - else - input_file(ff, fn); + } else { + input_file(ff, fixed_fn); + yosys_input_files.insert(fixed_fn); + } + continue; + } + + if (tok == "`file_push") { + skip_spaces(); + std::string fn = next_token(true); + if (!fn.empty() && fn.front() == '"' && fn.back() == '"') + fn = fn.substr(1, fn.size()-2); + output_code.push_back(tok + " \"" + fn + "\""); + filename_stack.push_back(filename); + filename = fn; + continue; + } + + if (tok == "`file_pop") { + output_code.push_back(tok); + filename = filename_stack.back(); + filename_stack.pop_back(); continue; } @@ -379,6 +496,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons defines_with_args.insert(name); else defines_with_args.erase(name); + global_defines_cache[name] = std::pair(value, state == 2); continue; } @@ -389,6 +507,7 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons // printf("undef: >>%s<<\n", name.c_str()); defines_map.erase(name); defines_with_args.erase(name); + global_defines_cache.erase(name); continue; } @@ -401,39 +520,16 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons 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 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]); + if (tok == "`resetall") { + defines_map.clear(); + defines_with_args.clear(); + global_defines_cache.clear(); continue; } + if (try_expand_macro(defines_with_args, defines_map, tok)) + continue; + output_code.push_back(tok); } @@ -449,4 +545,3 @@ std::string frontend_verilog_preproc(std::istream &f, std::string filename, cons } YOSYS_NAMESPACE_END - diff --git a/yosys/frontends/verilog/verilog_frontend.cc b/yosys/frontends/verilog/verilog_frontend.cc index 894723c85..aeea36a2b 100644 --- a/yosys/frontends/verilog/verilog_frontend.cc +++ b/yosys/frontends/verilog/verilog_frontend.cc @@ -42,14 +42,14 @@ static std::list> 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); + log_file_error(node->filename, node->linenum, "Found DPI function %s.\n", node->str.c_str()); 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() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -66,18 +66,30 @@ struct VerilogFrontend : public Frontend { log(" enable support for SystemVerilog assertions and some Yosys extensions\n"); log(" replace the implicit -D SYNTHESIS with -D FORMAL\n"); log("\n"); + log(" -noassert\n"); + log(" ignore assert() statements\n"); + log("\n"); + log(" -noassume\n"); + log(" ignore assume() statements\n"); + log("\n"); log(" -norestrict\n"); - log(" ignore restrict() assertions\n"); + log(" ignore restrict() statements\n"); log("\n"); log(" -assume-asserts\n"); log(" treat all assert() statements like assume() statements\n"); log("\n"); + log(" -assert-assumes\n"); + log(" treat all assume() statements like assert() statements\n"); + log("\n"); log(" -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(" -no_dump_ptr\n"); + log(" do not include hex memory addresses in dump (easier to diff dumps)\n"); + log("\n"); log(" -dump_vlog\n"); log(" dump ast as Verilog code (after simplification)\n"); log("\n"); @@ -137,9 +149,13 @@ struct VerilogFrontend : public Frontend { log(" -icells\n"); log(" interpret cell types starting with '$' as internal cell types\n"); log("\n"); - log(" -ignore_redef\n"); + log(" -nooverwrite\n"); log(" ignore re-definitions of modules. (the default behavior is to\n"); - log(" create an error message.)\n"); + log(" create an error message if the existing module is not a black box\n"); + log(" module, and overwrite the existing module otherwise.)\n"); + log("\n"); + log(" -overwrite\n"); + log(" overwrite existing modules with the same name\n"); log("\n"); log(" -defer\n"); log(" only read the abstract syntax tree and defer actual compilation\n"); @@ -168,14 +184,19 @@ struct VerilogFrontend : public Frontend { 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("Depending on if read_verilog is run in -formal mode, either the macro\n"); + log("SYNTHESIS or FORMAL is defined automatically. In addition, read_verilog\n"); + log("always defines the macro YOSYS.\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 args, RTLIL::Design *design) + void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_dump_ast1 = false; bool flag_dump_ast2 = false; + bool flag_no_dump_ptr = false; bool flag_dump_vlog = false; bool flag_dump_rtlil = false; bool flag_nolatches = false; @@ -187,7 +208,8 @@ struct VerilogFrontend : public Frontend { bool flag_nodpi = false; bool flag_noopt = false; bool flag_icells = false; - bool flag_ignore_redef = false; + bool flag_nooverwrite = false; + bool flag_overwrite = false; bool flag_defer = false; std::map defines_map; std::list include_dirs; @@ -216,6 +238,14 @@ struct VerilogFrontend : public Frontend { formal_mode = true; continue; } + if (arg == "-noassert") { + noassert_mode = true; + continue; + } + if (arg == "-noassume") { + noassume_mode = true; + continue; + } if (arg == "-norestrict") { norestrict_mode = true; continue; @@ -224,6 +254,10 @@ struct VerilogFrontend : public Frontend { assume_asserts_mode = true; continue; } + if (arg == "-assert-assumes") { + assert_assumes_mode = true; + continue; + } if (arg == "-dump_ast1") { flag_dump_ast1 = true; continue; @@ -232,6 +266,10 @@ struct VerilogFrontend : public Frontend { flag_dump_ast2 = true; continue; } + if (arg == "-no_dump_ptr") { + flag_no_dump_ptr = true; + continue; + } if (arg == "-dump_vlog") { flag_dump_vlog = true; continue; @@ -285,8 +323,14 @@ struct VerilogFrontend : public Frontend { flag_icells = true; continue; } - if (arg == "-ignore_redef") { - flag_ignore_redef = true; + if (arg == "-ignore_redef" || arg == "-nooverwrite") { + flag_nooverwrite = true; + flag_overwrite = false; + continue; + } + if (arg == "-overwrite") { + flag_nooverwrite = false; + flag_overwrite = true; continue; } if (arg == "-defer") { @@ -303,10 +347,10 @@ struct VerilogFrontend : public Frontend { } if (arg == "-D" && argidx+1 < args.size()) { std::string name = args[++argidx], value; - size_t equal = name.find('=', 2); + size_t equal = name.find('='); if (equal != std::string::npos) { - value = arg.substr(equal+1); - name = arg.substr(0, equal); + value = name.substr(equal+1); + name = name.substr(0, equal); } defines_map[name] = value; continue; @@ -345,7 +389,7 @@ struct VerilogFrontend : public Frontend { std::string code_after_preproc; if (!flag_nopp) { - code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, include_dirs); + code_after_preproc = frontend_verilog_preproc(*f, filename, defines_map, design->verilog_defines, 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); @@ -366,7 +410,7 @@ struct VerilogFrontend : public Frontend { if (flag_nodpi) error_on_dpi_function(current_ast); - AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_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); + AST::process(design, current_ast, flag_dump_ast1, flag_dump_ast2, flag_no_dump_ptr, flag_dump_vlog, flag_dump_rtlil, flag_nolatches, flag_nomeminit, flag_nomem2reg, flag_mem2reg, lib_mode, flag_noopt, flag_icells, flag_nooverwrite, flag_overwrite, flag_defer, default_nettype_wire); if (!flag_nopp) delete lexin; @@ -380,7 +424,7 @@ struct VerilogFrontend : public Frontend { struct VerilogDefaults : public Pass { VerilogDefaults() : Pass("verilog_defaults", "set default options for read_verilog") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -401,9 +445,9 @@ struct VerilogDefaults : public Pass { log("not imply -clear.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design*) + void execute(std::vector args, RTLIL::Design*) YS_OVERRIDE { - if (args.size() == 0) + if (args.size() < 2) cmd_error(args, 1, "Missing argument."); if (args[1] == "-add") { @@ -436,6 +480,66 @@ struct VerilogDefaults : public Pass { } } VerilogDefaults; +struct VerilogDefines : public Pass { + VerilogDefines() : Pass("verilog_defines", "define and undefine verilog defines") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" verilog_defines [options]\n"); + log("\n"); + log("Define and undefine verilog preprocessor macros.\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(" -Uname[=definition]\n"); + log(" undefine the preprocessor symbol 'name'\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-D" && argidx+1 < args.size()) { + std::string name = args[++argidx], value; + size_t equal = name.find('='); + if (equal != std::string::npos) { + value = name.substr(equal+1); + name = name.substr(0, equal); + } + design->verilog_defines[name] = std::pair(value, false); + 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); + design->verilog_defines[name] = std::pair(value, false); + continue; + } + if (arg == "-U" && argidx+1 < args.size()) { + std::string name = args[++argidx]; + design->verilog_defines.erase(name); + continue; + } + if (arg.compare(0, 2, "-U") == 0) { + std::string name = arg.substr(2); + design->verilog_defines.erase(name); + continue; + } + break; + } + + if (args.size() != argidx) + cmd_error(args, argidx, "Extra argument."); + } +} VerilogDefines; + YOSYS_NAMESPACE_END // the yyerror function used by bison to report parser errors @@ -444,13 +548,11 @@ 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); + YOSYS_NAMESPACE_PREFIX log_file_error(YOSYS_NAMESPACE_PREFIX AST::current_filename, frontend_verilog_yyget_lineno(), + "%s", buffer); exit(1); } - diff --git a/yosys/frontends/verilog/verilog_frontend.h b/yosys/frontends/verilog/verilog_frontend.h index 606ec20a2..523bbc897 100644 --- a/yosys/frontends/verilog/verilog_frontend.h +++ b/yosys/frontends/verilog/verilog_frontend.h @@ -54,12 +54,21 @@ namespace VERILOG_FRONTEND // running in -formal mode extern bool formal_mode; + // running in -noassert mode + extern bool noassert_mode; + + // running in -noassume mode + extern bool noassume_mode; + // running in -norestrict mode extern bool norestrict_mode; // running in -assume-asserts mode extern bool assume_asserts_mode; + // running in -assert-assumes mode + extern bool assert_assumes_mode; + // running in -lib mode extern bool lib_mode; @@ -68,7 +77,8 @@ namespace VERILOG_FRONTEND } // the pre-processor -std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map pre_defines_map, const std::list include_dirs); +std::string frontend_verilog_preproc(std::istream &f, std::string filename, const std::map &pre_defines_map, + dict> &global_defines_cache, const std::list &include_dirs); YOSYS_NAMESPACE_END diff --git a/yosys/frontends/verilog/verilog_lexer.l b/yosys/frontends/verilog/verilog_lexer.l index 405aeb975..1b1873e24 100644 --- a/yosys/frontends/verilog/verilog_lexer.l +++ b/yosys/frontends/verilog/verilog_lexer.l @@ -42,7 +42,7 @@ #include "kernel/log.h" #include "frontends/verilog/verilog_frontend.h" #include "frontends/ast/ast.h" -#include "verilog_parser.tab.h" +#include "verilog_parser.tab.hh" USING_YOSYS_NAMESPACE using namespace AST; @@ -135,6 +135,9 @@ YOSYS_NAMESPACE_END frontend_verilog_yyerror("Unsupported default nettype: %s", p); } +"`protect"[^\n]* /* ignore `protect*/ +"`endprotect"[^\n]* /* ignore `endprotect*/ + "`"[a-zA-Z_$][a-zA-Z0-9_$]* { frontend_verilog_yyerror("Unimplemented compiler directive or undefined macro %s.", yytext); } @@ -145,8 +148,14 @@ YOSYS_NAMESPACE_END "endfunction" { return TOK_ENDFUNCTION; } "task" { return TOK_TASK; } "endtask" { return TOK_ENDTASK; } +"specify" { return TOK_SPECIFY; } +"endspecify" { return TOK_ENDSPECIFY; } +"specparam" { return TOK_SPECPARAM; } "package" { SV_KEYWORD(TOK_PACKAGE); } "endpackage" { SV_KEYWORD(TOK_ENDPACKAGE); } +"interface" { SV_KEYWORD(TOK_INTERFACE); } +"endinterface" { SV_KEYWORD(TOK_ENDINTERFACE); } +"modport" { SV_KEYWORD(TOK_MODPORT); } "parameter" { return TOK_PARAMETER; } "localparam" { return TOK_LOCALPARAM; } "defparam" { return TOK_DEFPARAM; } @@ -170,17 +179,30 @@ YOSYS_NAMESPACE_END "endgenerate" { return TOK_ENDGENERATE; } "while" { return TOK_WHILE; } "repeat" { return TOK_REPEAT; } +"automatic" { return TOK_AUTOMATIC; } + +"unique" { SV_KEYWORD(TOK_UNIQUE); } +"unique0" { SV_KEYWORD(TOK_UNIQUE); } +"priority" { SV_KEYWORD(TOK_PRIORITY); } "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); } +"assert" { if (formal_mode) return TOK_ASSERT; SV_KEYWORD(TOK_ASSERT); } +"assume" { if (formal_mode) return TOK_ASSUME; SV_KEYWORD(TOK_ASSUME); } +"cover" { if (formal_mode) return TOK_COVER; SV_KEYWORD(TOK_COVER); } +"restrict" { if (formal_mode) return TOK_RESTRICT; SV_KEYWORD(TOK_RESTRICT); } +"property" { if (formal_mode) return TOK_PROPERTY; SV_KEYWORD(TOK_PROPERTY); } +"rand" { if (formal_mode) return TOK_RAND; SV_KEYWORD(TOK_RAND); } +"const" { if (formal_mode) return TOK_CONST; SV_KEYWORD(TOK_CONST); } +"checker" { if (formal_mode) return TOK_CHECKER; SV_KEYWORD(TOK_CHECKER); } +"endchecker" { if (formal_mode) return TOK_ENDCHECKER; SV_KEYWORD(TOK_ENDCHECKER); } +"logic" { SV_KEYWORD(TOK_LOGIC); } +"bit" { SV_KEYWORD(TOK_REG); } + +"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } +"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } "input" { return TOK_INPUT; } "output" { return TOK_OUTPUT; } @@ -192,14 +214,17 @@ YOSYS_NAMESPACE_END "genvar" { return TOK_GENVAR; } "real" { return TOK_REAL; } +"enum" { SV_KEYWORD(TOK_ENUM); } +"typedef" { SV_KEYWORD(TOK_TYPEDEF); } + [0-9][0-9_]* { frontend_verilog_yylval.string = new std::string(yytext); - return TOK_CONST; + return TOK_CONSTVAL; } [0-9]*[ \t]*\'s?[bodhBODH][ \t\r\n]*[0-9a-fA-FzxZX?_]+ { frontend_verilog_yylval.string = new std::string(yytext); - return TOK_CONST; + return TOK_CONSTVAL; } [0-9][0-9_]*\.[0-9][0-9_]*([eE][-+]?[0-9_]+)? { @@ -222,10 +247,18 @@ YOSYS_NAMESPACE_END while (yystr[i]) { if (yystr[i] == '\\' && yystr[i + 1]) { i++; - if (yystr[i] == 'n') + if (yystr[i] == 'a') + yystr[i] = '\a'; + else if (yystr[i] == 'f') + yystr[i] = '\f'; + else if (yystr[i] == 'n') yystr[i] = '\n'; + else if (yystr[i] == 'r') + yystr[i] = '\r'; else if (yystr[i] == 't') yystr[i] = '\t'; + else if (yystr[i] == 'v') + yystr[i] = '\v'; else if ('0' <= yystr[i] && yystr[i] <= '7') { yystr[i] = yystr[i] - '0'; if ('0' <= yystr[i + 1] && yystr[i + 1] <= '7') { @@ -241,7 +274,7 @@ YOSYS_NAMESPACE_END yystr[j++] = yystr[i++]; } yystr[j] = 0; - frontend_verilog_yylval.string = new std::string(yystr); + frontend_verilog_yylval.string = new std::string(yystr, j); free(yystr); return TOK_STRING; } @@ -268,6 +301,11 @@ supply1 { return TOK_SUPPLY1; } return TOK_ID; } +[a-zA-Z_$][a-zA-Z0-9_$\.]* { + frontend_verilog_yylval.string = new std::string(std::string("\\") + yytext); + return TOK_ID; +} + "/*"[ \t]*(synopsys|synthesis)[ \t]*translate_off[ \t]*"*/" { static bool printed_warning = false; if (!printed_warning) { @@ -358,7 +396,9 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "<<<" { return OP_SSHL; } ">>>" { return OP_SSHR; } -"::" { SV_KEYWORD(TOK_PACKAGESEP); } +"::" { return TOK_PACKAGESEP; } +"++" { return TOK_INCREMENT; } +"--" { return TOK_DECREMENT; } "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } @@ -372,10 +412,6 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { \\[\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; } %% diff --git a/yosys/frontends/verilog/verilog_parser.y b/yosys/frontends/verilog/verilog_parser.y index 5bbda5355..a6718b020 100644 --- a/yosys/frontends/verilog/verilog_parser.y +++ b/yosys/frontends/verilog/verilog_parser.y @@ -35,6 +35,7 @@ %{ #include +#include #include #include "frontends/verilog/verilog_frontend.h" #include "kernel/log.h" @@ -47,7 +48,8 @@ YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { int port_counter; std::map port_stubs; - std::map attr_list, default_attr_list; + std::map *attr_list, default_attr_list; + std::stack *> attr_list_stack; std::map *albuf; std::vector ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; @@ -58,7 +60,10 @@ namespace VERILOG_FRONTEND { bool do_not_require_port_stubs; bool default_nettype_wire; bool sv_mode, formal_mode, lib_mode; - bool norestrict_mode, assume_asserts_mode; + bool noassert_mode, noassume_mode, norestrict_mode; + bool assume_asserts_mode, assert_assumes_mode; + bool current_wire_rand, current_wire_const; + bool current_modport_input, current_modport_output; std::istream *lexin; } YOSYS_NAMESPACE_END @@ -100,27 +105,30 @@ static void free_attr(std::map *al) bool boolean; } -%token TOK_STRING TOK_ID TOK_CONST TOK_REALVAL TOK_PRIMITIVE +%token TOK_STRING TOK_ID TOK_CONSTVAL 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_INTERFACE TOK_ENDINTERFACE TOK_MODPORT +%token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_BEGIN TOK_END TOK_IF TOK_ELSE TOK_FOR TOK_WHILE TOK_REPEAT -%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR +%token TOK_DPI_FUNCTION TOK_POSEDGE TOK_NEGEDGE TOK_OR TOK_AUTOMATIC %token TOK_CASE TOK_CASEX TOK_CASEZ TOK_ENDCASE TOK_DEFAULT -%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK +%token TOK_FUNCTION TOK_ENDFUNCTION TOK_TASK TOK_ENDTASK TOK_SPECIFY TOK_ENDSPECIFY TOK_SPECPARAM %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 +%token TOK_RESTRICT TOK_COVER TOK_PROPERTY TOK_ENUM TOK_TYPEDEF +%token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY +%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY %type range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int %type wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list %type opt_label tok_prim_wrapper hierarchical_id -%type opt_signed -%type attr +%type opt_signed opt_property unique_case_attr +%type attr case_attr // operator precedence from low to high %left OP_LOR @@ -139,7 +147,9 @@ static void free_attr(std::map *al) %define parse.error verbose %define parse.lac full -%expect 2 +%nonassoc FAKE_THEN +%nonassoc TOK_ELSE + %debug %% @@ -162,19 +172,23 @@ design: param_decl design | localparam_decl design | package design | + interface design | /* empty */; attr: { - for (auto &it : attr_list) - delete it.second; - attr_list.clear(); + if (attr_list != nullptr) + attr_list_stack.push(attr_list); + attr_list = new std::map; for (auto &it : default_attr_list) - attr_list[it.first] = it.second->clone(); + (*attr_list)[it.first] = it.second->clone(); } attr_opt { - std::map *al = new std::map; - al->swap(attr_list); - $$ = al; + $$ = attr_list; + if (!attr_list_stack.empty()) { + attr_list = attr_list_stack.top(); + attr_list_stack.pop(); + } else + attr_list = nullptr; }; attr_opt: @@ -183,15 +197,20 @@ attr_opt: defattr: DEFATTR_BEGIN { + if (attr_list != nullptr) + attr_list_stack.push(attr_list); + attr_list = new std::map; for (auto &it : default_attr_list) delete it.second; default_attr_list.clear(); - for (auto &it : attr_list) - delete it.second; - attr_list.clear(); } opt_attr_list { - default_attr_list = attr_list; - attr_list.clear(); + attr_list->swap(default_attr_list); + delete attr_list; + if (!attr_list_stack.empty()) { + attr_list = attr_list_stack.top(); + attr_list_stack.pop(); + } else + attr_list = nullptr; } DEFATTR_END; opt_attr_list: @@ -203,15 +222,15 @@ attr_list: attr_assign: hierarchical_id { - if (attr_list.count(*$1) != 0) - delete attr_list[*$1]; - attr_list[*$1] = AstNode::mkconst_int(1, false); + if (attr_list->count(*$1) != 0) + delete (*attr_list)[*$1]; + (*attr_list)[*$1] = AstNode::mkconst_int(1, false); delete $1; } | hierarchical_id '=' expr { - if (attr_list.count(*$1) != 0) - delete attr_list[*$1]; - attr_list[*$1] = $3; + if (attr_list->count(*$1) != 0) + delete (*attr_list)[*$1]; + (*attr_list)[*$1] = $3; delete $1; }; @@ -269,7 +288,13 @@ single_module_para: 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; + } param_signed param_integer param_range single_param_decl | + TOK_LOCALPARAM { + if (astbuf1) delete astbuf1; + astbuf1 = new AstNode(AST_LOCALPARAM); + 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 ')'; @@ -290,7 +315,7 @@ module_arg_opt_assignment: else ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, $2)); } else - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("SystemVerilog interface in module port list cannot have a default value."); } | /* empty */; @@ -308,6 +333,21 @@ module_arg: } delete $1; } module_arg_opt_assignment | + TOK_ID { + astbuf1 = new AstNode(AST_INTERFACEPORT); + astbuf1->children.push_back(new AstNode(AST_INTERFACEPORTTYPE)); + astbuf1->children[0]->str = *$1; + delete $1; + } TOK_ID { /* SV interfaces */ + if (!sv_mode) + frontend_verilog_yyerror("Interface found in port list (%s). This is not supported unless read_verilog is called with -sv!", $3->c_str()); + astbuf2 = astbuf1->clone(); // really only needed if multiple instances of same type. + astbuf2->str = *$3; + delete $3; + astbuf2->port_id = ++port_counter; + ast_stack.back()->children.push_back(astbuf2); + delete astbuf1; // really only needed if multiple instances of same type. + } module_arg_opt_assignment | attr wire_type range TOK_ID { AstNode *node = $2; node->str = *$4; @@ -345,7 +385,37 @@ package_body: package_body_stmt: localparam_decl; +interface: + TOK_INTERFACE TOK_ID { + do_not_require_port_stubs = false; + AstNode *intf = new AstNode(AST_INTERFACE); + ast_stack.back()->children.push_back(intf); + ast_stack.push_back(intf); + current_ast_mod = intf; + port_stubs.clear(); + port_counter = 0; + intf->str = *$2; + delete $2; + } module_para_opt module_args_opt ';' interface_body TOK_ENDINTERFACE { + if (port_stubs.size() != 0) + frontend_verilog_yyerror("Missing details for module port `%s'.", + port_stubs.begin()->first.c_str()); + ast_stack.pop_back(); + log_assert(ast_stack.size() == 1); + current_ast_mod = NULL; + }; + +interface_body: + interface_body interface_body_stmt |; + +interface_body_stmt: + param_decl | localparam_decl | defparam_decl | wire_decl | always_stmt | assign_stmt | + modport_stmt; + non_opt_delay: + '#' TOK_ID { delete $2; } | + '#' TOK_CONSTVAL { delete $2; } | + '#' TOK_REALVAL { delete $2; } | '#' '(' expr ')' { delete $3; } | '#' '(' expr ':' expr ':' expr ')' { delete $3; delete $5; delete $7; }; @@ -355,14 +425,17 @@ delay: wire_type: { astbuf3 = new AstNode(AST_WIRE); + current_wire_rand = false; + current_wire_const = false; } wire_type_token_list delay { $$ = astbuf3; }; wire_type_token_list: - wire_type_token | wire_type_token_list wire_type_token; + wire_type_token | wire_type_token_list wire_type_token | + wire_type_token_io ; -wire_type_token: +wire_type_token_io: TOK_INPUT { astbuf3->is_input = true; } | @@ -372,12 +445,17 @@ wire_type_token: TOK_INOUT { astbuf3->is_input = true; astbuf3->is_output = true; - } | + }; + +wire_type_token: TOK_WIRE { } | TOK_REG { astbuf3->is_reg = true; } | + TOK_LOGIC { + astbuf3->is_logic = true; + } | TOK_INTEGER { astbuf3->is_reg = true; astbuf3->range_left = 31; @@ -392,6 +470,12 @@ wire_type_token: } | TOK_SIGNED { astbuf3->is_signed = true; + } | + TOK_RAND { + current_wire_rand = true; + } | + TOK_CONST { + current_wire_const = true; }; non_opt_range: @@ -454,8 +538,19 @@ module_body: /* 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 | specify_block |param_decl | localparam_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | + always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl; + +checker_decl: + TOK_CHECKER TOK_ID ';' { + AstNode *node = new AstNode(AST_GENBLOCK); + node->str = *$2; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } module_body TOK_ENDCHECKER { + delete $2; + ast_stack.pop_back(); + }; task_func_decl: attr TOK_DPI_FUNCTION TOK_ID TOK_ID { @@ -491,35 +586,36 @@ task_func_decl: } opt_dpi_function_args ';' { current_function_or_task = NULL; } | - attr TOK_TASK TOK_ID { + attr TOK_TASK opt_automatic TOK_ID { current_function_or_task = new AstNode(AST_TASK); - current_function_or_task->str = *$3; + current_function_or_task->str = *$4; 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; + delete $4; } 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 { + attr TOK_FUNCTION opt_automatic opt_signed range_or_signed_int TOK_ID { current_function_or_task = new AstNode(AST_FUNCTION); - current_function_or_task->str = *$5; + current_function_or_task->str = *$6; 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; + outreg->str = *$6; + outreg->is_signed = $4; + outreg->is_reg = true; + if ($5 != NULL) { + outreg->children.push_back($5); + outreg->is_signed = $4 || $5->is_signed; + $5->is_signed = false; } current_function_or_task->children.push_back(outreg); current_function_or_task_port_id = 1; - delete $5; + delete $6; } task_func_args_opt ';' task_func_body TOK_ENDFUNCTION { current_function_or_task = NULL; ast_stack.pop_back(); @@ -546,6 +642,10 @@ dpi_function_args: dpi_function_arg | /* empty */; +opt_automatic: + TOK_AUTOMATIC | + /* empty */; + opt_signed: TOK_SIGNED { $$ = true; @@ -582,7 +682,7 @@ task_func_port: astbuf2 = $3; if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { if (astbuf2) { - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions (task/function arguments)"); } else { astbuf2 = new AstNode(AST_RANGE); astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); @@ -590,13 +690,201 @@ task_func_port: } } if (astbuf2 && astbuf2->children.size() != 2) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("task/function argument range must be of the form: [:], [+:], or [-:]"); } wire_name | wire_name; task_func_body: task_func_body behavioral_stmt | /* empty */; +specify_block: + TOK_SPECIFY specify_item_opt TOK_ENDSPECIFY | + TOK_SPECIFY TOK_ENDSPECIFY ; + +specify_item_opt: + specify_item_opt specify_item | + specify_item ; + +specify_item: + specparam_declaration + // | pulsestyle_declaration + // | showcancelled_declaration + | path_declaration + | system_timing_declaration + ; + +specparam_declaration: + TOK_SPECPARAM list_of_specparam_assignments ';' | + TOK_SPECPARAM specparam_range list_of_specparam_assignments ';' ; + +// IEEE 1364-2005 calls this sinmply 'range' but the current 'range' rule allows empty match +// and the 'non_opt_range' rule allows index ranges not allowed by 1364-2005 +// exxxxtending this for SV specparam would change this anyhow +specparam_range: + '[' constant_expression ':' constant_expression ']' ; + +list_of_specparam_assignments: + specparam_assignment | list_of_specparam_assignments ',' specparam_assignment; + +specparam_assignment: + TOK_ID '=' constant_mintypmax_expression ; + +/* +pulsestyle_declaration : + ; + +showcancelled_declaration : + ; +*/ + +path_declaration : + simple_path_declaration ';' + // | edge_sensitive_path_declaration + // | state_dependent_path_declaration + ; + +simple_path_declaration : + parallel_path_description '=' path_delay_value | + full_path_description '=' path_delay_value + ; + +path_delay_value : + '(' path_delay_expression list_of_path_delay_extra_expressions ')' + | path_delay_expression + | path_delay_expression list_of_path_delay_extra_expressions + ; + +list_of_path_delay_extra_expressions : +/* + t_path_delay_expression + | trise_path_delay_expression ',' tfall_path_delay_expression + | trise_path_delay_expression ',' tfall_path_delay_expression ',' tz_path_delay_expression + | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ',' + tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression + | t01_path_delay_expression ',' t10_path_delay_expression ',' t0z_path_delay_expression ',' + tz1_path_delay_expression ',' t1z_path_delay_expression ',' tz0_path_delay_expression ',' + t0x_path_delay_expression ',' tx1_path_delay_expression ',' t1x_path_delay_expression ',' + tx0_path_delay_expression ',' txz_path_delay_expression ',' tzx_path_delay_expression +*/ + ',' path_delay_expression + | ',' path_delay_expression ',' path_delay_expression + | ',' path_delay_expression ',' path_delay_expression ',' + path_delay_expression ',' path_delay_expression ',' path_delay_expression + | ',' path_delay_expression ',' path_delay_expression ',' + path_delay_expression ',' path_delay_expression ',' path_delay_expression ',' + path_delay_expression ',' path_delay_expression ',' path_delay_expression ',' + path_delay_expression ',' path_delay_expression ',' path_delay_expression + ; + +parallel_path_description : + '(' specify_input_terminal_descriptor opt_polarity_operator '=' '>' specify_output_terminal_descriptor ')' ; + +full_path_description : + '(' list_of_path_inputs '*' '>' list_of_path_outputs ')' ; + +// This was broken into 2 rules to solve shift/reduce conflicts +list_of_path_inputs : + specify_input_terminal_descriptor opt_polarity_operator | + specify_input_terminal_descriptor more_path_inputs opt_polarity_operator ; + +more_path_inputs : + ',' specify_input_terminal_descriptor | + more_path_inputs ',' specify_input_terminal_descriptor ; + +list_of_path_outputs : + specify_output_terminal_descriptor | + list_of_path_outputs ',' specify_output_terminal_descriptor ; + +opt_polarity_operator : + '+' + | '-' + | ; + +// Good enough for the time being +specify_input_terminal_descriptor : + TOK_ID ; + +// Good enough for the time being +specify_output_terminal_descriptor : + TOK_ID ; + +system_timing_declaration : + TOK_ID '(' system_timing_args ')' ';' ; + +system_timing_arg : + TOK_POSEDGE TOK_ID | + TOK_NEGEDGE TOK_ID | + expr ; + +system_timing_args : + system_timing_arg | + system_timing_args ',' system_timing_arg ; + +/* +t_path_delay_expression : + path_delay_expression; + +trise_path_delay_expression : + path_delay_expression; + +tfall_path_delay_expression : + path_delay_expression; + +tz_path_delay_expression : + path_delay_expression; + +t01_path_delay_expression : + path_delay_expression; + +t10_path_delay_expression : + path_delay_expression; + +t0z_path_delay_expression : + path_delay_expression; + +tz1_path_delay_expression : + path_delay_expression; + +t1z_path_delay_expression : + path_delay_expression; + +tz0_path_delay_expression : + path_delay_expression; + +t0x_path_delay_expression : + path_delay_expression; + +tx1_path_delay_expression : + path_delay_expression; + +t1x_path_delay_expression : + path_delay_expression; + +tx0_path_delay_expression : + path_delay_expression; + +txz_path_delay_expression : + path_delay_expression; + +tzx_path_delay_expression : + path_delay_expression; +*/ + +path_delay_expression : + constant_expression; + +constant_mintypmax_expression : + constant_expression + | constant_expression ':' constant_expression ':' constant_expression + ; + +// for the time being this is OK, but we may write our own expr here. +// as I'm not sure it is legal to use a full expr here (probably not) +// On the other hand, other rules requiring constant expressions also use 'expr' +// (such as param assignment), so we may leave this as-is, perhaps adding runtime checks for constant-ness +constant_expression: + expr ; + param_signed: TOK_SIGNED { astbuf1->is_signed = true; @@ -605,7 +893,7 @@ param_signed: param_integer: TOK_INTEGER { if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Internal error in param_integer - should not happen?"); astbuf1->children.push_back(new AstNode(AST_RANGE)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(31, true)); astbuf1->children.back()->children.push_back(AstNode::mkconst_int(0, true)); @@ -615,7 +903,7 @@ param_integer: param_real: TOK_REAL { if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Parameter already declared as integer, cannot set to real."); astbuf1->children.push_back(new AstNode(AST_REALVALUE)); } | /* empty */; @@ -623,7 +911,7 @@ param_range: range { if ($1 != NULL) { if (astbuf1->children.size() != 1) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("integer/real parameters should not have a range."); astbuf1->children.push_back($1); } }; @@ -649,9 +937,15 @@ param_decl_list: single_param_decl: TOK_ID '=' expr { - if (astbuf1 == nullptr) - frontend_verilog_yyerror("syntax error"); - AstNode *node = astbuf1->clone(); + AstNode *node; + if (astbuf1 == nullptr) { + if (!sv_mode) + frontend_verilog_yyerror("In pure Verilog (not SystemVerilog), parameter/localparam with an initializer must use the parameter/localparam keyword"); + node = new AstNode(AST_PARAMETER); + node->children.push_back(AstNode::mkconst_int(0, true)); + } else { + node = astbuf1->clone(); + } node->str = *$1; delete node->children[0]; node->children[0] = $3; @@ -666,14 +960,13 @@ defparam_decl_list: single_defparam_decl | defparam_decl_list ',' single_defparam_decl; single_defparam_decl: - range hierarchical_id '=' expr { + range rvalue '=' expr { AstNode *node = new AstNode(AST_DEFPARAM); - node->str = *$2; + node->children.push_back($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: @@ -683,7 +976,7 @@ wire_decl: astbuf2 = $3; if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { if (astbuf2) { - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); } else { astbuf2 = new AstNode(AST_RANGE); astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); @@ -691,7 +984,7 @@ wire_decl: } } if (astbuf2 && astbuf2->children.size() != 2) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [:], [+:], or [-:]"); } wire_name_list { delete astbuf1; if (astbuf2 != NULL) @@ -731,7 +1024,48 @@ 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 { + bool attr_anyconst = false; + bool attr_anyseq = false; + bool attr_allconst = false; + bool attr_allseq = false; + if (ast_stack.back()->children.back()->get_bool_attribute("\\anyconst")) { + delete ast_stack.back()->children.back()->attributes.at("\\anyconst"); + ast_stack.back()->children.back()->attributes.erase("\\anyconst"); + attr_anyconst = true; + } + if (ast_stack.back()->children.back()->get_bool_attribute("\\anyseq")) { + delete ast_stack.back()->children.back()->attributes.at("\\anyseq"); + ast_stack.back()->children.back()->attributes.erase("\\anyseq"); + attr_anyseq = true; + } + if (ast_stack.back()->children.back()->get_bool_attribute("\\allconst")) { + delete ast_stack.back()->children.back()->attributes.at("\\allconst"); + ast_stack.back()->children.back()->attributes.erase("\\allconst"); + attr_allconst = true; + } + if (ast_stack.back()->children.back()->get_bool_attribute("\\allseq")) { + delete ast_stack.back()->children.back()->attributes.at("\\allseq"); + ast_stack.back()->children.back()->attributes.erase("\\allseq"); + attr_allseq = true; + } + if (current_wire_rand || attr_anyconst || attr_anyseq || attr_allconst || attr_allseq) { + AstNode *wire = new AstNode(AST_IDENTIFIER); + AstNode *fcall = new AstNode(AST_FCALL); + wire->str = ast_stack.back()->children.back()->str; + fcall->str = current_wire_const ? "\\$anyconst" : "\\$anyseq"; + if (attr_anyconst) + fcall->str = "\\$anyconst"; + if (attr_anyseq) + fcall->str = "\\$anyseq"; + if (attr_allconst) + fcall->str = "\\$allconst"; + if (attr_allseq) + fcall->str = "\\$allseq"; + fcall->attributes["\\reg"] = AstNode::mkconst_str(RTLIL::unescape_id(wire->str)); + ast_stack.back()->children.push_back(new AstNode(AST_ASSIGN, wire, fcall)); + } + } | wire_name '=' expr { AstNode *wire = new AstNode(AST_IDENTIFIER); wire->str = ast_stack.back()->children.back()->str; @@ -744,7 +1078,7 @@ wire_name_and_opt_assign: wire_name: TOK_ID range_or_multirange { if (astbuf1 == nullptr) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Internal error - should not happen - no AST_WIRE node."); AstNode *node = astbuf1->clone(); node->str = *$1; append_attr_clone(node, albuf); @@ -752,7 +1086,7 @@ wire_name: node->children.push_back(astbuf2->clone()); if ($2 != NULL) { if (node->is_input || node->is_output) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); if (!astbuf2) { AstNode *rng = new AstNode(AST_RANGE); rng->children.push_back(AstNode::mkconst_int(0, true)); @@ -782,6 +1116,7 @@ wire_name: node->port_id = current_function_or_task_port_id++; } ast_stack.back()->children.push_back(node); + delete $1; }; @@ -994,18 +1329,101 @@ opt_label: $$ = NULL; }; +opt_property: + TOK_PROPERTY { + $$ = true; + } | + /* empty */ { + $$ = false; + }; + +opt_stmt_label: + TOK_ID ':' | /* empty */; + +modport_stmt: + TOK_MODPORT TOK_ID { + AstNode *modport = new AstNode(AST_MODPORT); + ast_stack.back()->children.push_back(modport); + ast_stack.push_back(modport); + modport->str = *$2; + delete $2; + } modport_args_opt { + ast_stack.pop_back(); + log_assert(ast_stack.size() == 2); + } ';' + +modport_args_opt: + '(' ')' | '(' modport_args optional_comma ')'; + +modport_args: + modport_arg | modport_args ',' modport_arg; + +modport_arg: + modport_type_token modport_member | + modport_member + +modport_member: + TOK_ID { + AstNode *modport_member = new AstNode(AST_MODPORTMEMBER); + ast_stack.back()->children.push_back(modport_member); + modport_member->str = *$1; + modport_member->is_input = current_modport_input; + modport_member->is_output = current_modport_output; + delete $1; + } + +modport_type_token: + TOK_INPUT {current_modport_input = 1; current_modport_output = 0;} | TOK_OUTPUT {current_modport_input = 0; current_modport_output = 1;} + assert: - 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; + opt_stmt_label TOK_ASSERT opt_property '(' expr ')' ';' { + if (noassert_mode) + delete $5; else - ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $3)); + ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_ASSUME : AST_ASSERT, $5)); + } | + opt_stmt_label TOK_ASSUME opt_property '(' expr ')' ';' { + if (noassume_mode) + delete $5; + else + ast_stack.back()->children.push_back(new AstNode(assert_assumes_mode ? AST_ASSERT : AST_ASSUME, $5)); + } | + opt_stmt_label TOK_ASSERT opt_property '(' TOK_EVENTUALLY expr ')' ';' { + if (noassert_mode) + delete $6; + else + ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $6)); + } | + opt_stmt_label TOK_ASSUME opt_property '(' TOK_EVENTUALLY expr ')' ';' { + if (noassume_mode) + delete $6; + else + ast_stack.back()->children.push_back(new AstNode(assert_assumes_mode ? AST_LIVE : AST_FAIR, $6)); + } | + opt_stmt_label TOK_COVER opt_property '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_COVER, $5)); + } | + opt_stmt_label TOK_COVER opt_property '(' ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false))); + } | + opt_stmt_label TOK_COVER ';' { + ast_stack.back()->children.push_back(new AstNode(AST_COVER, AstNode::mkconst_int(1, false))); + } | + opt_stmt_label TOK_RESTRICT opt_property '(' expr ')' ';' { + if (norestrict_mode) + delete $5; + else + ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $5)); + if (!$3) + log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); + } | + opt_stmt_label TOK_RESTRICT opt_property '(' TOK_EVENTUALLY expr ')' ';' { + if (norestrict_mode) + delete $6; + else + ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $6)); + if (!$3) + log_file_warning(current_filename, get_line_num(), "SystemVerilog does not allow \"restrict\" without \"property\".\n"); }; assert_property: @@ -1015,11 +1433,26 @@ assert_property: TOK_ASSUME TOK_PROPERTY '(' expr ')' ';' { ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4)); } | + TOK_ASSERT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(assume_asserts_mode ? AST_FAIR : AST_LIVE, $5)); + } | + TOK_ASSUME TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5)); + } | + TOK_COVER TOK_PROPERTY '(' expr ')' ';' { + ast_stack.back()->children.push_back(new AstNode(AST_COVER, $4)); + } | TOK_RESTRICT TOK_PROPERTY '(' expr ')' ';' { if (norestrict_mode) delete $4; else ast_stack.back()->children.push_back(new AstNode(AST_ASSUME, $4)); + } | + TOK_RESTRICT TOK_PROPERTY '(' TOK_EVENTUALLY expr ')' ';' { + if (norestrict_mode) + delete $5; + else + ast_stack.back()->children.push_back(new AstNode(AST_FAIR, $5)); }; simple_behavioral_stmt: @@ -1027,6 +1460,14 @@ simple_behavioral_stmt: AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $4); ast_stack.back()->children.push_back(node); } | + lvalue TOK_INCREMENT { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_ADD, $1->clone(), AstNode::mkconst_int(1, true))); + ast_stack.back()->children.push_back(node); + } | + lvalue TOK_DECREMENT { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_SUB, $1->clone(), AstNode::mkconst_int(1, true))); + 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); @@ -1056,7 +1497,7 @@ behavioral_stmt: node->str = *$3; } behavioral_stmt_list TOK_END opt_label { if ($3 != NULL && $7 != NULL && *$3 != *$7) - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $3->c_str()+1, $7->c_str()+1); if ($3 != NULL) delete $3; if ($7 != NULL) @@ -1118,7 +1559,7 @@ behavioral_stmt: ast_stack.pop_back(); ast_stack.pop_back(); } | - attr case_type '(' expr ')' { + case_attr case_type '(' expr ')' { AstNode *node = new AstNode(AST_CASE, $4); ast_stack.back()->children.push_back(node); ast_stack.push_back(node); @@ -1128,6 +1569,23 @@ behavioral_stmt: ast_stack.pop_back(); }; +unique_case_attr: + /* empty */ { + $$ = false; + } | + TOK_PRIORITY case_attr { + $$ = $2; + } | + TOK_UNIQUE case_attr { + $$ = true; + }; + +case_attr: + attr unique_case_attr { + if ($2) (*$1)["\\parallel_case"] = AstNode::mkconst_int(1, false); + $$ = $1; + }; + case_type: TOK_CASE { case_type_stack.push_back(0); @@ -1162,7 +1620,7 @@ optional_else: ast_stack.back()->children.push_back(cond); ast_stack.push_back(block); } behavioral_stmt | - /* empty */; + /* empty */ %prec FAKE_THEN; case_body: case_body case_item | @@ -1229,7 +1687,9 @@ rvalue: $$ = new AstNode(AST_IDENTIFIER, $2); $$->str = *$1; delete $1; - if ($2 == nullptr && formal_mode && ($$->str == "\\$initstate" || $$->str == "\\$anyconst" || $$->str == "\\$anyseq")) + if ($2 == nullptr && ($$->str == "\\$initstate" || + $$->str == "\\$anyconst" || $$->str == "\\$anyseq" || + $$->str == "\\$allconst" || $$->str == "\\$allseq")) $$->type = AST_FCALL; } | hierarchical_id non_opt_multirange { @@ -1333,7 +1793,7 @@ gen_stmt_or_null: gen_stmt_block | ';'; opt_gen_else: - TOK_ELSE gen_stmt_or_null | /* empty */; + TOK_ELSE gen_stmt_or_null | /* empty */ %prec FAKE_THEN; expr: basic_expr { @@ -1351,9 +1811,9 @@ basic_expr: rvalue { $$ = $1; } | - '(' expr ')' TOK_CONST { + '(' expr ')' TOK_CONSTVAL { if ($4->substr(0, 1) != "'") - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Cast operation must be applied on sized constants e.g. () , while %s is not a sized constant.", $4->c_str()); AstNode *bits = $2; AstNode *val = const2ast(*$4, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); if (val == NULL) @@ -1361,9 +1821,9 @@ basic_expr: $$ = new AstNode(AST_TO_BITS, bits, val); delete $4; } | - hierarchical_id TOK_CONST { + hierarchical_id TOK_CONSTVAL { if ($2->substr(0, 1) != "'") - frontend_verilog_yyerror("Syntax error."); + frontend_verilog_yyerror("Cast operation must be applied on sized constants, e.g. \'d0, while %s is not a sized constant.", $2->c_str()); AstNode *bits = new AstNode(AST_IDENTIFIER); bits->str = *$1; AstNode *val = const2ast(*$2, case_type_stack.size() == 0 ? 0 : case_type_stack.back(), !lib_mode); @@ -1373,14 +1833,14 @@ basic_expr: delete $1; delete $2; } | - TOK_CONST TOK_CONST { + TOK_CONSTVAL TOK_CONSTVAL { $$ = 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 { + TOK_CONSTVAL { $$ = 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()); @@ -1441,10 +1901,18 @@ basic_expr: $$ = new AstNode(AST_BIT_AND, $1, $4); append_attr($$, $3); } | + basic_expr OP_NAND attr basic_expr { + $$ = new AstNode(AST_BIT_NOT, 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 OP_NOR attr basic_expr { + $$ = new AstNode(AST_BIT_NOT, 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); diff --git a/yosys/frontends/vhdl2verilog/Makefile.inc b/yosys/frontends/vhdl2verilog/Makefile.inc deleted file mode 100644 index 003d89c4a..000000000 --- a/yosys/frontends/vhdl2verilog/Makefile.inc +++ /dev/null @@ -1 +0,0 @@ -OBJS += frontends/vhdl2verilog/vhdl2verilog.o diff --git a/yosys/frontends/vhdl2verilog/vhdl2verilog.cc b/yosys/frontends/vhdl2verilog/vhdl2verilog.cc deleted file mode 100644 index 6f9c0e3f5..000000000 --- a/yosys/frontends/vhdl2verilog/vhdl2verilog.cc +++ /dev/null @@ -1,183 +0,0 @@ -/* - * yosys -- Yosys Open SYnthesis Suite - * - * Copyright (C) 2012 Clifford Wolf - * - * 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 -#include -#include -#include -#include - -#ifndef _WIN32 -# include -# include -#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] ..\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 \n"); - log(" do not import the vhdl2verilog output. instead write it to the\n"); - log(" specified file.\n"); - log("\n"); - log(" -vhdl2verilog_dir \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 \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 \n"); - log(" -unroll_generate\n"); - log(" -nogenericeval\n"); - log(" -nouniquify\n"); - log(" -oldparser\n"); - log(" -suppress \n"); - log(" -quiet\n"); - log(" -nobanner\n"); - log(" -mapfile \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 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/yosys/kernel/calc.cc b/yosys/kernel/calc.cc index a24fa2abf..4a4840771 100644 --- a/yosys/kernel/calc.cc +++ b/yosys/kernel/calc.cc @@ -291,7 +291,7 @@ static RTLIL::Const const_shift_worker(const RTLIL::Const &arg1, const RTLIL::Co BigInteger pos = BigInteger(i) + offset; if (pos < 0) result.bits[i] = RTLIL::State::S0; - else if (pos >= arg1.bits.size()) + else if (pos >= BigInteger(int(arg1.bits.size()))) result.bits[i] = sign_ext ? arg1.bits.back() : RTLIL::State::S0; else result.bits[i] = arg1.bits[pos.toInt()]; @@ -342,7 +342,7 @@ static RTLIL::Const const_shift_shiftx(const RTLIL::Const &arg1, const RTLIL::Co for (int i = 0; i < result_len; i++) { BigInteger pos = BigInteger(i) + offset; - if (pos < 0 || pos >= arg1.bits.size()) + if (pos < 0 || pos >= BigInteger(int(arg1.bits.size()))) result.bits[i] = other_bits; else result.bits[i] = arg1.bits[pos.toInt()]; diff --git a/yosys/kernel/cellaigs.cc b/yosys/kernel/cellaigs.cc index 41f81355d..5fd76afe5 100644 --- a/yosys/kernel/cellaigs.cc +++ b/yosys/kernel/cellaigs.cc @@ -202,6 +202,16 @@ struct AigMaker return or_gate(and_gate(A, B), nor_gate(A, B)); } + int andnot_gate(int A, int B) + { + return and_gate(A, not_gate(B)); + } + + int ornot_gate(int A, int B) + { + return or_gate(A, not_gate(B)); + } + int mux_gate(int A, int B, int S) { return or_gate(and_gate(A, not_gate(S)), and_gate(B, S)); @@ -290,7 +300,7 @@ Aig::Aig(Cell *cell) goto optimize; } - if (cell->type.in("$and", "$_AND_", "$_NAND_", "$or", "$_OR_", "$_NOR_", "$xor", "$xnor", "$_XOR_", "$_XNOR_")) + if (cell->type.in("$and", "$_AND_", "$_NAND_", "$or", "$_OR_", "$_NOR_", "$xor", "$xnor", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) { for (int i = 0; i < GetSize(cell->getPort("\\Y")); i++) { int A = mk.inport("\\A", i); @@ -300,7 +310,9 @@ Aig::Aig(Cell *cell) 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; + cell->type.in("$xnor", "$_XNOR_") ? mk.xnor_gate(A, B) : + cell->type.in("$_ANDNOT_") ? mk.andnot_gate(A, B) : + cell->type.in("$_ORNOT_") ? mk.ornot_gate(A, B) : -1; mk.outport(Y, "\\Y", i); } goto optimize; diff --git a/yosys/kernel/celledges.h b/yosys/kernel/celledges.h index 6aab9ed43..2cc297cb2 100644 --- a/yosys/kernel/celledges.h +++ b/yosys/kernel/celledges.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -38,7 +38,7 @@ struct FwdCellEdgesDatabase : AbstractCellEdgesDatabase dict> 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 { + void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) YS_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); @@ -51,7 +51,7 @@ struct RevCellEdgesDatabase : AbstractCellEdgesDatabase dict> 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 { + void add_edge(RTLIL::Cell *cell, RTLIL::IdString from_port, int from_bit, RTLIL::IdString to_port, int to_bit, int) YS_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); diff --git a/yosys/kernel/celltypes.h b/yosys/kernel/celltypes.h index f0ead1e89..8b8a56111 100644 --- a/yosys/kernel/celltypes.h +++ b/yosys/kernel/celltypes.h @@ -20,7 +20,7 @@ #ifndef CELLTYPES_H #define CELLTYPES_H -#include +#include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN @@ -116,9 +116,14 @@ struct CellTypes setup_type("$assert", {A, EN}, pool(), true); setup_type("$assume", {A, EN}, pool(), true); + setup_type("$live", {A, EN}, pool(), true); + setup_type("$fair", {A, EN}, pool(), true); + setup_type("$cover", {A, EN}, pool(), true); setup_type("$initstate", pool(), {Y}, true); setup_type("$anyconst", pool(), {Y}, true); setup_type("$anyseq", pool(), {Y}, true); + setup_type("$allconst", pool(), {Y}, true); + setup_type("$allseq", pool(), {Y}, true); setup_type("$equiv", {A, B}, {Y}, true); } @@ -152,7 +157,7 @@ struct CellTypes IdString A = "\\A", B = "\\B", C = "\\C", D = "\\D"; IdString E = "\\E", F = "\\F", G = "\\G", H = "\\H"; IdString I = "\\I", J = "\\J", K = "\\K", L = "\\L"; - IdString M = "\\I", N = "\\N", O = "\\O", P = "\\P"; + IdString M = "\\M", N = "\\N", O = "\\O", P = "\\P"; IdString S = "\\S", T = "\\T", U = "\\U", V = "\\V"; IdString Y = "\\Y"; @@ -164,6 +169,8 @@ struct CellTypes setup_type("$_NOR_", {A, B}, {Y}, true); setup_type("$_XOR_", {A, B}, {Y}, true); setup_type("$_XNOR_", {A, B}, {Y}, true); + setup_type("$_ANDNOT_", {A, B}, {Y}, true); + setup_type("$_ORNOT_", {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); @@ -250,7 +257,7 @@ struct CellTypes return v; } - static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len) + static RTLIL::Const eval(RTLIL::IdString type, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len, bool *errp = nullptr) { if (type == "$sshr" && !signed1) type = "$shr"; @@ -312,16 +319,25 @@ struct CellTypes if (type == "$_OR_") return const_or(arg1, arg2, false, false, 1); if (type == "$_NOR_") - return eval_not(const_and(arg1, arg2, false, false, 1)); + return eval_not(const_or(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); + if (type == "$_ANDNOT_") + return const_and(arg1, eval_not(arg2), false, false, 1); + if (type == "$_ORNOT_") + return const_or(arg1, eval_not(arg2), false, false, 1); + + if (errp != nullptr) { + *errp = true; + return State::Sm; + } log_abort(); } - static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2) + static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool *errp = nullptr) { if (cell->type == "$slice") { RTLIL::Const ret; @@ -404,10 +420,10 @@ struct CellTypes bool signed_a = cell->parameters.count("\\A_SIGNED") > 0 && cell->parameters["\\A_SIGNED"].as_bool(); bool signed_b = cell->parameters.count("\\B_SIGNED") > 0 && cell->parameters["\\B_SIGNED"].as_bool(); int result_len = cell->parameters.count("\\Y_WIDTH") > 0 ? cell->parameters["\\Y_WIDTH"].as_int() : -1; - return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len); + return eval(cell->type, arg1, arg2, signed_a, signed_b, result_len, errp); } - static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3) + static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, bool *errp = nullptr) { if (cell->type.in("$mux", "$pmux", "$_MUX_")) { RTLIL::Const ret = arg1; @@ -425,10 +441,10 @@ struct CellTypes return eval_not(const_and(const_or(arg1, arg2, false, false, 1), arg3, false, false, 1)); log_assert(arg3.bits.size() == 0); - return eval(cell, arg1, arg2); + return eval(cell, arg1, arg2, errp); } - static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4) + static RTLIL::Const eval(RTLIL::Cell *cell, const RTLIL::Const &arg1, const RTLIL::Const &arg2, const RTLIL::Const &arg3, const RTLIL::Const &arg4, bool *errp = nullptr) { if (cell->type == "$_AOI4_") return eval_not(const_or(const_and(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1)); @@ -436,7 +452,7 @@ struct CellTypes return eval_not(const_and(const_or(arg1, arg2, false, false, 1), const_and(arg3, arg4, false, false, 1), false, false, 1)); log_assert(arg4.bits.size() == 0); - return eval(cell, arg1, arg2, arg3); + return eval(cell, arg1, arg2, arg3, errp); } }; diff --git a/yosys/kernel/consteval.h b/yosys/kernel/consteval.h index 4d48b45ea..154373a8d 100644 --- a/yosys/kernel/consteval.h +++ b/yosys/kernel/consteval.h @@ -36,8 +36,9 @@ struct ConstEval SigSet sig2driver; std::set busy; std::vector stack; + RTLIL::State defaultval; - ConstEval(RTLIL::Module *module) : module(module), assign_map(module) + ConstEval(RTLIL::Module *module, RTLIL::State defaultval = RTLIL::State::Sm) : module(module), assign_map(module), defaultval(defaultval) { CellTypes ct; ct.setup_internals(); @@ -320,8 +321,13 @@ struct ConstEval if (sig_d.size() > 0 && !eval(sig_d, undef, cell)) return false; - set(sig_y, CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), - sig_c.as_const(), sig_d.as_const())); + bool eval_err = false; + RTLIL::Const eval_ret = CellTypes::eval(cell, sig_a.as_const(), sig_b.as_const(), sig_c.as_const(), sig_d.as_const(), &eval_err); + + if (eval_err) + return false; + + set(sig_y, eval_ret); } return true; @@ -365,6 +371,12 @@ struct ConstEval if (sig.is_fully_const()) return true; + if (defaultval != RTLIL::State::Sm) { + for (auto &bit : sig) + if (bit.wire) bit = defaultval; + return true; + } + for (auto &c : sig.chunks()) if (c.wire != NULL) undef.append(c); diff --git a/yosys/kernel/cost.h b/yosys/kernel/cost.h index 4f12889f4..e795b571b 100644 --- a/yosys/kernel/cost.h +++ b/yosys/kernel/cost.h @@ -20,7 +20,7 @@ #ifndef COST_H #define COST_H -#include +#include "kernel/yosys.h" YOSYS_NAMESPACE_BEGIN @@ -30,19 +30,21 @@ int get_cell_cost(RTLIL::IdString type, const dict *mod_cost_cache = nullptr) { static dict 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 } + { "$_BUF_", 1 }, + { "$_NOT_", 2 }, + { "$_AND_", 4 }, + { "$_NAND_", 4 }, + { "$_OR_", 4 }, + { "$_NOR_", 4 }, + { "$_ANDNOT_", 4 }, + { "$_ORNOT_", 4 }, + { "$_XOR_", 8 }, + { "$_XNOR_", 8 }, + { "$_AOI3_", 6 }, + { "$_OAI3_", 6 }, + { "$_AOI4_", 8 }, + { "$_OAI4_", 8 }, + { "$_MUX_", 4 } }; if (gate_cost.count(type)) diff --git a/yosys/kernel/driver.cc b/yosys/kernel/driver.cc index f8d00c38d..178641101 100644 --- a/yosys/kernel/driver.cc +++ b/yosys/kernel/driver.cc @@ -25,16 +25,26 @@ # include #endif +#ifdef YOSYS_ENABLE_EDITLINE +# include +#endif + #include #include #include #include -#ifdef __linux__ +#if defined (__linux__) || defined(__FreeBSD__) +# include # include # include #endif +#ifdef __FreeBSD__ +# include +# include +#endif + #if !defined(_WIN32) || defined(__MINGW32__) # include #else @@ -75,20 +85,37 @@ USING_YOSYS_NAMESPACE #ifdef EMSCRIPTEN # include # include +# include 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**) +int main(int argc, char **argv) { + EM_ASM( + if (ENVIRONMENT_IS_NODE) + { + FS.mkdir('/hostcwd'); + FS.mount(NODEFS, { root: '.' }, '/hostcwd'); + FS.mkdir('/hostfs'); + FS.mount(NODEFS, { root: '/' }, '/hostfs'); + } + ); + mkdir("/work", 0777); chdir("/work"); log_files.push_back(stdout); log_error_stderr = true; yosys_banner(); yosys_setup(); + + if (argc == 2) + { + // Run the first argument as a script file + run_frontend(argv[1], "script", 0, 0, 0); + } } void run(const char *command) @@ -119,6 +146,35 @@ const char *prompt() #else /* EMSCRIPTEN */ +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) +int yosys_history_offset = 0; +std::string yosys_history_file; +#endif + +void yosys_atexit() +{ +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) + if (!yosys_history_file.empty()) { +#if defined(YOSYS_ENABLE_READLINE) + if (yosys_history_offset > 0) { + history_truncate_file(yosys_history_file.c_str(), 100); + append_history(where_history() - yosys_history_offset, yosys_history_file.c_str()); + } else + write_history(yosys_history_file.c_str()); +#else + write_history(yosys_history_file.c_str()); +#endif + } + + clear_history(); +#if defined(YOSYS_ENABLE_READLINE) + HIST_ENTRY **hist_list = history_list(); + if (hist_list != NULL) + free(hist_list); +#endif +#endif +} + int main(int argc, char **argv) { std::string frontend_command = "auto"; @@ -127,6 +183,7 @@ int main(int argc, char **argv) std::vector plugin_filenames; std::string output_filename = ""; std::string scriptfile = ""; + std::string depsfile = ""; bool scriptfile_tcl = false; bool got_output_filename = false; bool print_banner = true; @@ -136,13 +193,11 @@ int main(int argc, char **argv) bool mode_v = false; bool mode_q = false; -#ifdef YOSYS_ENABLE_READLINE - int history_offset = 0; - std::string history_file; +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) if (getenv("HOME") != NULL) { - history_file = stringf("%s/.yosys_history", getenv("HOME")); - read_history(history_file.c_str()); - history_offset = where_history(); + yosys_history_file = stringf("%s/.yosys_history", getenv("HOME")); + read_history(yosys_history_file.c_str()); + yosys_history_offset = where_history(); } #endif @@ -218,6 +273,20 @@ int main(int argc, char **argv) printf(" yosys_dump_.il is used as filename if none is specified.\n"); printf(" Use 'ALL' as to dump at every header.\n"); printf("\n"); + printf(" -W regex\n"); + printf(" print a warning for all log messages matching the regex.\n"); + printf("\n"); + printf(" -w regex\n"); + printf(" if a warning message matches the regex, it is printed as regular\n"); + printf(" message instead.\n"); + printf("\n"); + printf(" -e regex\n"); + printf(" if a warning message matches the regex, it is printed as error\n"); + printf(" message instead and the tool terminates with a nonzero return code.\n"); + printf("\n"); + printf(" -E \n"); + printf(" write a Makefile dependencies file with in- and output file names\n"); + printf("\n"); printf(" -V\n"); printf(" print version information and exit\n"); printf("\n"); @@ -238,7 +307,7 @@ int main(int argc, char **argv) } int opt; - while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:D:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVSm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:E:")) != -1) { switch (opt) { @@ -320,6 +389,24 @@ int main(int argc, char **argv) scriptfile = optarg; scriptfile_tcl = true; break; + case 'W': + log_warn_regexes.push_back(std::regex(optarg, + std::regex_constants::nosubs | + std::regex_constants::optimize | + std::regex_constants::egrep)); + break; + case 'w': + log_nowarn_regexes.push_back(std::regex(optarg, + std::regex_constants::nosubs | + std::regex_constants::optimize | + std::regex_constants::egrep)); + break; + case 'e': + log_werror_regexes.push_back(std::regex(optarg, + std::regex_constants::nosubs | + std::regex_constants::optimize | + std::regex_constants::egrep)); + break; case 'D': { auto args = split_tokens(optarg, ":"); @@ -342,6 +429,9 @@ int main(int argc, char **argv) } } break; + case 'E': + depsfile = optarg; + break; default: fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); @@ -359,7 +449,20 @@ int main(int argc, char **argv) if (print_stats) log_hasher = new SHA1; +#if defined(__linux__) + // set stack size to >= 128 MB + { + struct rlimit rl; + const rlim_t stack_size = 128L * 1024L * 1024L; + if (getrlimit(RLIMIT_STACK, &rl) == 0 && rl.rlim_cur < stack_size) { + rl.rlim_cur = stack_size; + setrlimit(RLIMIT_STACK, &rl); + } + } +#endif + yosys_setup(); + log_error_atexit = yosys_atexit; for (auto &fn : plugin_filenames) load_plugin(fn, {}); @@ -391,6 +494,24 @@ int main(int argc, char **argv) if (!backend_command.empty()) run_backend(output_filename, backend_command); + if (!depsfile.empty()) + { + FILE *f = fopen(depsfile.c_str(), "wt"); + if (f == nullptr) + log_error("Can't open dependencies file for writing: %s\n", strerror(errno)); + bool first = true; + for (auto fn : yosys_output_files) { + fprintf(f, "%s%s", first ? "" : " ", fn.c_str()); + first = false; + } + fprintf(f, ":"); + for (auto fn : yosys_input_files) { + if (yosys_output_files.count(fn) == 0) + fprintf(f, " %s", fn.c_str()); + } + fprintf(f, "\n"); + } + if (print_stats) { std::string hash = log_hasher->final().substr(0, 10); @@ -404,12 +525,14 @@ int main(int argc, char **argv) if (mode_v && !mode_q) log_files.push_back(stderr); + if (log_warnings_count) + log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count); #ifdef _WIN32 log("End of script. Logfile hash: %s\n", hash.c_str()); #else std::string meminfo; std::string stats_divider = ", "; -# ifdef __linux__ +# if defined(__linux__) std::ifstream statm; statm.open(stringf("/proc/%lld/statm", (long long)getpid())); if (statm.is_open()) { @@ -420,6 +543,19 @@ int main(int argc, char **argv) sz_resident * (getpagesize() / 1024.0 / 1024.0)); stats_divider = "\n"; } +# elif defined(__FreeBSD__) + pid_t pid = getpid(); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid}; + struct kinfo_proc kip; + size_t kip_len = sizeof(kip); + if (sysctl(mib, 4, &kip, &kip_len, NULL, 0) == 0) { + vm_size_t sz_total = kip.ki_size; + segsz_t sz_resident = kip.ki_rssize; + meminfo = stringf(", MEM: %.2f MB total, %.2f MB resident", + (int)sz_total / 1024.0 / 1024.0, + (int)sz_resident * (getpagesize() / 1024.0 / 1024.0)); + stats_divider = "\n"; + } # endif struct rusage ru_buffer; @@ -463,7 +599,7 @@ int main(int argc, char **argv) } } -#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) +#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) if (getenv("YOSYS_COVER_DIR") || getenv("YOSYS_COVER_FILE")) { string filename; @@ -490,25 +626,12 @@ int main(int argc, char **argv) } #endif + yosys_atexit(); + 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); diff --git a/yosys/kernel/hashlib.h b/yosys/kernel/hashlib.h index 3c824b8c3..df534ec1b 100644 --- a/yosys/kernel/hashlib.h +++ b/yosys/kernel/hashlib.h @@ -147,7 +147,7 @@ struct hash_ptr_ops { return a == b; } static inline unsigned int hash(const void *a) { - return (unsigned long)a; + return (uintptr_t)a; } }; @@ -868,6 +868,13 @@ public: return !operator==(other); } + bool hash() const { + unsigned int hashval = mkhash_init; + for (auto &it : entries) + hashval ^= ops.hash(it.udata); + return hashval; + } + void reserve(size_t n) { entries.reserve(n); } size_t size() const { return entries.size(); } bool empty() const { return entries.empty(); } diff --git a/yosys/kernel/log.cc b/yosys/kernel/log.cc index abc401f55..0ee2170a0 100644 --- a/yosys/kernel/log.cc +++ b/yosys/kernel/log.cc @@ -25,7 +25,7 @@ # include #endif -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) # include #endif @@ -41,6 +41,9 @@ YOSYS_NAMESPACE_BEGIN std::vector log_files; std::vector log_streams; std::map> log_hdump; +std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; +std::set log_warnings; +int log_warnings_count = 0; bool log_hdump_all = false; FILE *log_errfile = NULL; SHA1 *log_hasher = NULL; @@ -51,6 +54,7 @@ bool log_cmd_error_throw = false; bool log_quiet_warnings = false; int log_verbose_level; string log_last_error; +void (*log_error_atexit)() = NULL; vector header_count; pool log_id_cache; @@ -136,6 +140,32 @@ void logv(const char *format, va_list ap) for (auto f : log_streams) *f << str; + + static std::string linebuffer; + static bool log_warn_regex_recusion_guard = false; + + if (!log_warn_regex_recusion_guard) + { + log_warn_regex_recusion_guard = true; + + if (log_warn_regexes.empty()) + { + linebuffer.clear(); + } + else + { + linebuffer += str; + + if (!linebuffer.empty() && linebuffer.back() == '\n') { + for (auto &re : log_warn_regexes) + if (std::regex_search(linebuffer, re)) + log_warning("Found log message matching -W regex:\n%s", str.c_str()); + linebuffer.clear(); + } + } + + log_warn_regex_recusion_guard = false; + } } void logv_header(RTLIL::Design *design, const char *format, va_list ap) @@ -173,20 +203,73 @@ void logv_header(RTLIL::Design *design, const char *format, va_list ap) log_files.pop_back(); } -void logv_warning(const char *format, va_list ap) +static void logv_warning_with_prefix(const char *prefix, + const char *format, va_list ap) { - if (log_errfile != NULL && !log_quiet_warnings) - log_files.push_back(log_errfile); + std::string message = vstringf(format, ap); + bool suppressed = false; - log("Warning: "); - logv(format, ap); - log_flush(); + for (auto &re : log_nowarn_regexes) + if (std::regex_search(message, re)) + suppressed = true; - if (log_errfile != NULL && !log_quiet_warnings) - log_files.pop_back(); + if (suppressed) + { + log("Suppressed %s%s", prefix, message.c_str()); + } + else + { + for (auto &re : log_werror_regexes) + if (std::regex_search(message, re)) + log_error("%s", message.c_str()); + + if (log_warnings.count(message)) + { + log("%s%s", prefix, message.c_str()); + log_flush(); + } + else + { + if (log_errfile != NULL && !log_quiet_warnings) + log_files.push_back(log_errfile); + + log("%s%s", prefix, message.c_str()); + log_flush(); + + if (log_errfile != NULL && !log_quiet_warnings) + log_files.pop_back(); + + log_warnings.insert(message); + } + + log_warnings_count++; + } } -void logv_error(const char *format, va_list ap) +void logv_warning(const char *format, va_list ap) +{ + logv_warning_with_prefix("Warning: ", format, ap); +} + +void logv_warning_noprefix(const char *format, va_list ap) +{ + logv_warning_with_prefix("", format, ap); +} + +void log_file_warning(const std::string &filename, int lineno, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + std::string prefix = stringf("%s:%d: Warning: ", + filename.c_str(), lineno); + logv_warning_with_prefix(prefix.c_str(), format, ap); + va_end(ap); +} + +YS_ATTRIBUTE(noreturn) +static void logv_error_with_prefix(const char *prefix, + const char *format, va_list ap) { #ifdef EMSCRIPTEN auto backup_log_files = log_files; @@ -201,9 +284,12 @@ void logv_error(const char *format, va_list ap) f = stderr; log_last_error = vstringf(format, ap); - log("ERROR: %s", log_last_error.c_str()); + log("%s%s", prefix, log_last_error.c_str()); log_flush(); + if (log_error_atexit) + log_error_atexit(); + #ifdef EMSCRIPTEN log_files = backup_log_files; throw 0; @@ -214,6 +300,21 @@ void logv_error(const char *format, va_list ap) #endif } +void logv_error(const char *format, va_list ap) +{ + logv_error_with_prefix("ERROR: ", format, ap); +} + +void log_file_error(const string &filename, int lineno, + const char *format, ...) +{ + va_list ap; + va_start(ap, format); + std::string prefix = stringf("%s:%d: ERROR: ", + filename.c_str(), lineno); + logv_error_with_prefix(prefix.c_str(), format, ap); +} + void log(const char *format, ...) { va_list ap; @@ -238,6 +339,14 @@ void log_warning(const char *format, ...) va_end(ap); } +void log_warning_noprefix(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + logv_warning_noprefix(format, ap); + va_end(ap); +} + void log_error(const char *format, ...) { va_list ap; @@ -262,8 +371,8 @@ void log_cmd_error(const char *format, ...) void log_spacer() { - while (log_newline_count < 2) - log("\n"); + if (log_newline_count < 2) log("\n"); + if (log_newline_count < 2) log("\n"); } void log_push() @@ -280,7 +389,7 @@ void log_pop() log_flush(); } -#if defined(__linux__) && defined(YOSYS_ENABLE_PLUGINS) +#if (defined(__linux__) || defined(__FreeBSD__)) && defined(YOSYS_ENABLE_PLUGINS) void log_backtrace(const char *prefix, int levels) { if (levels <= 0) return; @@ -297,6 +406,9 @@ void log_backtrace(const char *prefix, int levels) if (levels <= 1) return; +#ifndef DEBUG + log("%sframe #2: [build Yosys with ENABLE_DEBUG for deeper backtraces]\n", prefix); +#else 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 { @@ -368,6 +480,7 @@ void log_backtrace(const char *prefix, int levels) } if (levels <= 9) return; +#endif } #else void log_backtrace(const char*, int) { } @@ -461,10 +574,17 @@ void log_cell(RTLIL::Cell *cell, std::string indent) log("%s", buf.str().c_str()); } +void log_wire(RTLIL::Wire *wire, std::string indent) +{ + std::stringstream buf; + ILANG_BACKEND::dump_wire(buf, indent, wire); + log("%s", buf.str().c_str()); +} + // --------------------------------------------------- // This is the magic behind the code coverage counters // --------------------------------------------------- -#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) +#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) dict> extra_coverage_data; @@ -513,4 +633,3 @@ dict> get_coverage_data() #endif YOSYS_NAMESPACE_END - diff --git a/yosys/kernel/log.h b/yosys/kernel/log.h index 53480db31..e1f54a197 100644 --- a/yosys/kernel/log.h +++ b/yosys/kernel/log.h @@ -23,6 +23,7 @@ #define LOG_H #include +#include #ifndef _WIN32 # include @@ -48,6 +49,9 @@ struct log_cmd_error_exception { }; extern std::vector log_files; extern std::vector log_streams; extern std::map> log_hdump; +extern std::vector log_warn_regexes, log_nowarn_regexes, log_werror_regexes; +extern std::set log_warnings; +extern int log_warnings_count; extern bool log_hdump_all; extern FILE *log_errfile; extern SHA1 *log_hasher; @@ -58,16 +62,24 @@ extern bool log_cmd_error_throw; extern bool log_quiet_warnings; extern int log_verbose_level; extern string log_last_error; +extern void (*log_error_atexit)(); 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); +void logv_warning_noprefix(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)); + +// Log with filename to report a problem in a source file. +void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); + +void log_warning_noprefix(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); YS_NORETURN void log_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn); +void log_file_error(const string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4), noreturn); YS_NORETURN void log_cmd_error(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2), noreturn); void log_spacer(); @@ -88,6 +100,7 @@ template static inline const char *log_id(T *obj) { void log_module(RTLIL::Module *module, std::string indent = ""); void log_cell(RTLIL::Cell *cell, std::string indent = ""); +void log_wire(RTLIL::Wire *wire, std::string indent = ""); #ifndef NDEBUG static inline void log_assert_worker(bool cond, const char *expr, const char *file, int line) { @@ -106,7 +119,7 @@ static inline void log_assert_worker(bool cond, const char *expr, const char *fi // This is the magic behind the code coverage counters // --------------------------------------------------- -#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) +#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) #define cover(_id) do { \ static CoverData __d __attribute__((section("yosys_cover_list"), aligned(1), used)) = { __FILE__, __FUNCTION__, _id, __LINE__, 0 }; \ @@ -165,7 +178,7 @@ struct PerformanceTimer } static int64_t query() { -# if _WIN32 +# ifdef _WIN32 return 0; # elif defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) struct timespec ts; @@ -182,7 +195,7 @@ struct PerformanceTimer t += 1000000000ULL * (int64_t) rusage.ru_stime.tv_sec + (int64_t) rusage.ru_stime.tv_usec * 1000ULL; return t; # else -# error Dont know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?). +# error "Don't know how to measure per-process CPU time. Need alternative method (times()/clocks()/gettimeofday()?)." # endif } diff --git a/yosys/kernel/modtools.h b/yosys/kernel/modtools.h index ffcb48d44..409562eb9 100644 --- a/yosys/kernel/modtools.h +++ b/yosys/kernel/modtools.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -158,7 +158,7 @@ struct ModIndex : public RTLIL::Monitor #endif } - virtual void notify_connect(RTLIL::Cell *cell, const RTLIL::IdString &port, const RTLIL::SigSpec &old_sig, RTLIL::SigSpec &sig) YS_OVERRIDE + 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); @@ -169,7 +169,7 @@ struct ModIndex : public RTLIL::Monitor port_add(cell, port, sig); } - virtual void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const RTLIL::SigSig &sigsig) YS_OVERRIDE + void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const RTLIL::SigSig &sigsig) YS_OVERRIDE { log_assert(module == mod); @@ -214,13 +214,13 @@ struct ModIndex : public RTLIL::Monitor } } - virtual void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const std::vector&) YS_OVERRIDE + void notify_connect(RTLIL::Module *mod YS_ATTRIBUTE(unused), const std::vector&) YS_OVERRIDE { log_assert(module == mod); auto_reload_module = true; } - virtual void notify_blackout(RTLIL::Module *mod YS_ATTRIBUTE(unused)) YS_OVERRIDE + void notify_blackout(RTLIL::Module *mod YS_ATTRIBUTE(unused)) YS_OVERRIDE { log_assert(module == mod); auto_reload_module = true; diff --git a/yosys/kernel/register.cc b/yosys/kernel/register.cc index 7a1d0b44b..402a5b3ea 100644 --- a/yosys/kernel/register.cc +++ b/yosys/kernel/register.cc @@ -173,7 +173,7 @@ void Pass::call(RTLIL::Design *design, std::string command) } while (!tok.empty()) { - if (tok == "#") { + if (tok[0] == '#') { int stop; for (stop = 0; stop < GetSize(cmd_buf); stop++) if (cmd_buf[stop] == '\r' || cmd_buf[stop] == '\n') @@ -428,6 +428,7 @@ void Frontend::extra_args(std::istream *&f, std::string &filename, std::vectoropen(filename.c_str()); + yosys_input_files.insert(filename); if (ff->fail()) delete ff; else @@ -543,6 +544,7 @@ void Backend::extra_args(std::ostream *&f, std::string &filename, std::vectoropen(filename.c_str(), std::ofstream::trunc); + yosys_output_files.insert(filename); if (ff->fail()) { delete ff; log_cmd_error("Can't open output file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); @@ -613,7 +615,7 @@ static struct CellHelpMessages { struct HelpPass : public Pass { HelpPass() : Pass("help", "display help messages") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" help ................ list all commands\n"); @@ -682,7 +684,7 @@ struct HelpPass : public Pass { fclose(f); } - virtual void execute(std::vector args, RTLIL::Design*) + void execute(std::vector args, RTLIL::Design*) YS_OVERRIDE { if (args.size() == 1) { log("\n"); @@ -766,7 +768,7 @@ struct HelpPass : public Pass { struct EchoPass : public Pass { EchoPass() : Pass("echo", "turning echoing back of commands on and off") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" echo on\n"); @@ -779,7 +781,7 @@ struct EchoPass : public Pass { log("Do not print all commands to log before executing them. (default)\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design*) + void execute(std::vector args, RTLIL::Design*) YS_OVERRIDE { if (args.size() > 2) cmd_error(args, 2, "Unexpected argument."); @@ -804,10 +806,9 @@ struct MinisatSatSolver : public SatSolver { MinisatSatSolver() : SatSolver("minisat") { yosys_satsolver = this; } - virtual ezSAT *create() YS_OVERRIDE { + ezSAT *create() YS_OVERRIDE { return new ezMiniSAT(); } } MinisatSatSolver; YOSYS_NAMESPACE_END - diff --git a/yosys/kernel/register.h b/yosys/kernel/register.h index 8024c56a0..c74029823 100644 --- a/yosys/kernel/register.h +++ b/yosys/kernel/register.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -88,9 +88,9 @@ struct Frontend : Pass 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 args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; + void run_register() YS_OVERRIDE; + ~Frontend() YS_OVERRIDE; + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; virtual void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) = 0; static std::vector next_args; @@ -104,9 +104,9 @@ 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 args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; + void run_register() YS_OVERRIDE; + ~Backend() YS_OVERRIDE; + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE YS_FINAL; virtual void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) = 0; void extra_args(std::ostream *&f, std::string &filename, std::vector args, size_t argidx); diff --git a/yosys/kernel/rtlil.cc b/yosys/kernel/rtlil.cc index 66bbf0427..8404db5e9 100644 --- a/yosys/kernel/rtlil.cc +++ b/yosys/kernel/rtlil.cc @@ -161,6 +161,50 @@ std::string RTLIL::Const::decode_string() const return string; } +bool RTLIL::Const::is_fully_zero() const +{ + cover("kernel.rtlil.const.is_fully_zero"); + + for (auto bit : bits) + if (bit != RTLIL::State::S0) + return false; + + return true; +} + +bool RTLIL::Const::is_fully_ones() const +{ + cover("kernel.rtlil.const.is_fully_ones"); + + for (auto bit : bits) + if (bit != RTLIL::State::S1) + return false; + + return true; +} + +bool RTLIL::Const::is_fully_def() const +{ + cover("kernel.rtlil.const.is_fully_def"); + + for (auto bit : bits) + if (bit != RTLIL::State::S0 && bit != RTLIL::State::S1) + return false; + + return true; +} + +bool RTLIL::Const::is_fully_undef() const +{ + cover("kernel.rtlil.const.is_fully_undef"); + + for (auto bit : bits) + if (bit != RTLIL::State::Sx && bit != RTLIL::State::Sz) + return false; + + return true; +} + void RTLIL::AttrObject::set_bool_attribute(RTLIL::IdString id) { attributes[id] = RTLIL::Const(1); @@ -201,6 +245,22 @@ pool RTLIL::AttrObject::get_strpool_attribute(RTLIL::IdString id) const return data; } +void RTLIL::AttrObject::set_src_attribute(const std::string &src) +{ + if (src.empty()) + attributes.erase("\\src"); + else + attributes["\\src"] = src; +} + +std::string RTLIL::AttrObject::get_src_attribute() const +{ + std::string src; + if (attributes.count("\\src")) + src = attributes.at("\\src").decode_string(); + return src; +} + bool RTLIL::Selection::selected_module(RTLIL::IdString mod_name) const { if (full_selection) @@ -306,6 +366,8 @@ RTLIL::Design::~Design() delete it->second; for (auto n : verilog_packages) delete n; + for (auto n : verilog_globals) + delete n; } RTLIL::ObjRange RTLIL::Design::modules() @@ -577,8 +639,23 @@ RTLIL::Module::~Module() delete it->second; } -RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict) +void RTLIL::Module::reprocess_module(RTLIL::Design *, dict) { + log_error("Cannot reprocess_module module `%s' !\n", id2cstr(name)); +} + +RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict, bool mayfail) +{ + if (mayfail) + return RTLIL::IdString(); + log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); +} + + +RTLIL::IdString RTLIL::Module::derive(RTLIL::Design*, dict, dict, dict, bool mayfail) +{ + if (mayfail) + return RTLIL::IdString(); log_error("Module `%s' is used with parameters but is not parametric!\n", id2cstr(name)); } @@ -1024,7 +1101,7 @@ namespace { return; } - if (cell->type.in("$assert", "$assume")) { + if (cell->type.in("$assert", "$assume", "$live", "$fair", "$cover")) { port("\\A", 1); port("\\EN", 1); check_expected(); @@ -1037,7 +1114,7 @@ namespace { return; } - if (cell->type.in("$anyconst", "$anyseq")) { + if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq")) { port("\\Y", param("\\WIDTH")); check_expected(); return; @@ -1051,19 +1128,21 @@ namespace { 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 == "$_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 == "$_ANDNOT_") { check_gate("ABY"); return; } + if (cell->type == "$_ORNOT_") { 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; } @@ -1583,18 +1662,19 @@ RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *oth } #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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed, const std::string &src) { \ 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); \ + cell->set_src_attribute(src); \ return cell; \ } \ - RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed) { \ + RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed, const std::string &src) { \ RTLIL::SigSpec sig_y = addWire(NEW_ID, _y_size); \ - add ## _func(name, sig_a, sig_y, is_signed); \ + add ## _func(name, sig_a, sig_y, is_signed, src); \ return sig_y; \ } DEF_METHOD(Not, sig_a.size(), "$not") @@ -1609,7 +1689,7 @@ 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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ cell->parameters["\\A_SIGNED"] = is_signed; \ cell->parameters["\\B_SIGNED"] = is_signed; \ @@ -1619,12 +1699,13 @@ DEF_METHOD(LogicNot, 1, "$logic_not") cell->setPort("\\A", sig_a); \ cell->setPort("\\B", sig_b); \ cell->setPort("\\Y", sig_y); \ + cell->set_src_attribute(src); \ 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; \ + RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed, const std::string &src) { \ + RTLIL::SigSpec sig_y = addWire(NEW_ID, _y_size); \ + add ## _func(name, sig_a, sig_b, sig_y, is_signed, src); \ + 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") @@ -1654,7 +1735,7 @@ 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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ cell->parameters["\\WIDTH"] = sig_a.size(); \ if (_pmux) cell->parameters["\\S_WIDTH"] = sig_s.size(); \ @@ -1662,11 +1743,12 @@ DEF_METHOD(LogicOr, 1, "$logic_or") cell->setPort("\\B", sig_b); \ cell->setPort("\\S", sig_s); \ cell->setPort("\\Y", sig_y); \ + cell->set_src_attribute(src); \ return cell; \ } \ - RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s) { \ + RTLIL::SigSpec RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, const std::string &src) { \ RTLIL::SigSpec sig_y = addWire(NEW_ID, sig_a.size()); \ - add ## _func(name, sig_a, sig_b, sig_s, sig_y); \ + add ## _func(name, sig_a, sig_b, sig_s, sig_y, src); \ return sig_y; \ } DEF_METHOD(Mux, "$mux", 0) @@ -1674,78 +1756,84 @@ 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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ cell->setPort("\\" #_P1, sig1); \ cell->setPort("\\" #_P2, sig2); \ + cell->set_src_attribute(src); \ return cell; \ } \ - RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1) { \ - RTLIL::SigBit sig2 = addWire(NEW_ID); \ - add ## _func(name, sig1, sig2); \ + RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, const std::string &src) { \ + RTLIL::SigBit sig2 = addWire(NEW_ID); \ + add ## _func(name, sig1, sig2, src); \ 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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ cell->setPort("\\" #_P1, sig1); \ cell->setPort("\\" #_P2, sig2); \ cell->setPort("\\" #_P3, sig3); \ + cell->set_src_attribute(src); \ 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); \ + RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, const std::string &src) { \ + RTLIL::SigBit sig3 = addWire(NEW_ID); \ + add ## _func(name, sig1, sig2, sig3, src); \ 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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, RTLIL::SigBit sig4, const std::string &src) { \ RTLIL::Cell *cell = addCell(name, _type); \ cell->setPort("\\" #_P1, sig1); \ cell->setPort("\\" #_P2, sig2); \ cell->setPort("\\" #_P3, sig3); \ cell->setPort("\\" #_P4, sig4); \ + cell->set_src_attribute(src); \ 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); \ + RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, const std::string &src) { \ + RTLIL::SigBit sig4 = addWire(NEW_ID); \ + add ## _func(name, sig1, sig2, sig3, sig4, src); \ 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* RTLIL::Module::add ## _func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, RTLIL::SigBit sig4, RTLIL::SigBit sig5, const std::string &src) { \ 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); \ + cell->set_src_attribute(src); \ 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; \ + RTLIL::SigBit RTLIL::Module::_func(RTLIL::IdString name, RTLIL::SigBit sig1, RTLIL::SigBit sig2, RTLIL::SigBit sig3, RTLIL::SigBit sig4, const std::string &src) { \ + RTLIL::SigBit sig5 = addWire(NEW_ID); \ + add ## _func(name, sig1, sig2, sig3, sig4, sig5, src); \ + 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) +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_3(AndnotGate, "$_ANDNOT_", A, B, Y) +DEF_METHOD_3(OrnotGate, "$_ORNOT_", 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* RTLIL::Module::addPow(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool a_signed, bool b_signed, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$pow"); cell->parameters["\\A_SIGNED"] = a_signed; @@ -1756,10 +1844,11 @@ RTLIL::Cell* RTLIL::Module::addPow(RTLIL::IdString name, RTLIL::SigSpec sig_a, R cell->setPort("\\A", sig_a); cell->setPort("\\B", sig_b); cell->setPort("\\Y", sig_y); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset) +RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$slice"); cell->parameters["\\A_WIDTH"] = sig_a.size(); @@ -1767,10 +1856,11 @@ RTLIL::Cell* RTLIL::Module::addSlice(RTLIL::IdString name, RTLIL::SigSpec sig_a, cell->parameters["\\OFFSET"] = offset; cell->setPort("\\A", sig_a); cell->setPort("\\Y", sig_y); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addConcat(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y) +RTLIL::Cell* RTLIL::Module::addConcat(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$concat"); cell->parameters["\\A_WIDTH"] = sig_a.size(); @@ -1778,55 +1868,88 @@ RTLIL::Cell* RTLIL::Module::addConcat(RTLIL::IdString name, RTLIL::SigSpec sig_a cell->setPort("\\A", sig_a); cell->setPort("\\B", sig_b); cell->setPort("\\Y", sig_y); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut) +RTLIL::Cell* RTLIL::Module::addLut(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut, const std::string &src) { 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); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addTribuf(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y) +RTLIL::Cell* RTLIL::Module::addTribuf(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y, const std::string &src) { 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); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addAssert(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en) +RTLIL::Cell* RTLIL::Module::addAssert(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$assert"); cell->setPort("\\A", sig_a); cell->setPort("\\EN", sig_en); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addAssume(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en) +RTLIL::Cell* RTLIL::Module::addAssume(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$assume"); cell->setPort("\\A", sig_a); cell->setPort("\\EN", sig_en); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addEquiv(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y) +RTLIL::Cell* RTLIL::Module::addLive(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, "$live"); + cell->setPort("\\A", sig_a); + cell->setPort("\\EN", sig_en); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addFair(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, "$fair"); + cell->setPort("\\A", sig_a); + cell->setPort("\\EN", sig_en); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addCover(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src) +{ + RTLIL::Cell *cell = addCell(name, "$cover"); + cell->setPort("\\A", sig_a); + cell->setPort("\\EN", sig_en); + cell->set_src_attribute(src); + return cell; +} + +RTLIL::Cell* RTLIL::Module::addEquiv(RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$equiv"); cell->setPort("\\A", sig_a); cell->setPort("\\B", sig_b); cell->setPort("\\Y", sig_y); + cell->set_src_attribute(src); 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* RTLIL::Module::addSr(RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity, bool clr_polarity, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$sr"); cell->parameters["\\SET_POLARITY"] = set_polarity; @@ -1835,19 +1958,21 @@ RTLIL::Cell* RTLIL::Module::addSr(RTLIL::IdString name, RTLIL::SigSpec sig_set, cell->setPort("\\SET", sig_set); cell->setPort("\\CLR", sig_clr); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addFf(RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q) +RTLIL::Cell* RTLIL::Module::addFf(RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$ff"); cell->parameters["\\WIDTH"] = sig_q.size(); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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* RTLIL::Module::addDff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$dff"); cell->parameters["\\CLK_POLARITY"] = clk_polarity; @@ -1855,10 +1980,11 @@ RTLIL::Cell* RTLIL::Module::addDff(RTLIL::IdString name, RTLIL::SigSpec sig_clk, cell->setPort("\\CLK", sig_clk); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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* 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, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$dffe"); cell->parameters["\\CLK_POLARITY"] = clk_polarity; @@ -1868,11 +1994,12 @@ RTLIL::Cell* RTLIL::Module::addDffe(RTLIL::IdString name, RTLIL::SigSpec sig_clk cell->setPort("\\EN", sig_en); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool set_polarity, bool clr_polarity, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$dffsr"); cell->parameters["\\CLK_POLARITY"] = clk_polarity; @@ -1884,11 +2011,12 @@ RTLIL::Cell* RTLIL::Module::addDffsr(RTLIL::IdString name, RTLIL::SigSpec sig_cl cell->setPort("\\CLR", sig_clr); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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::Const arst_value, bool clk_polarity, bool arst_polarity, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$adff"); cell->parameters["\\CLK_POLARITY"] = clk_polarity; @@ -1899,10 +2027,11 @@ RTLIL::Cell* RTLIL::Module::addAdff(RTLIL::IdString name, RTLIL::SigSpec sig_clk cell->setPort("\\ARST", sig_arst); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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* RTLIL::Module::addDlatch(RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$dlatch"); cell->parameters["\\EN_POLARITY"] = en_polarity; @@ -1910,11 +2039,12 @@ RTLIL::Cell* RTLIL::Module::addDlatch(RTLIL::IdString name, RTLIL::SigSpec sig_e cell->setPort("\\EN", sig_en); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity, bool set_polarity, bool clr_polarity, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$dlatchsr"); cell->parameters["\\EN_POLARITY"] = en_polarity; @@ -1926,38 +2056,42 @@ RTLIL::Cell* RTLIL::Module::addDlatchsr(RTLIL::IdString name, RTLIL::SigSpec sig cell->setPort("\\CLR", sig_clr); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); return cell; } -RTLIL::Cell* RTLIL::Module::addFfGate(RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q) +RTLIL::Cell* RTLIL::Module::addFfGate(RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, const std::string &src) { RTLIL::Cell *cell = addCell(name, "$_FF_"); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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* RTLIL::Module::addDffGate(RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, const std::string &src) { 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); + cell->set_src_attribute(src); 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* 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, const std::string &src) { 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); + cell->set_src_attribute(src); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity, bool set_polarity, bool clr_polarity, const std::string &src) { 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); @@ -1965,31 +2099,34 @@ RTLIL::Cell* RTLIL::Module::addDffsrGate(RTLIL::IdString name, RTLIL::SigSpec si cell->setPort("\\R", sig_clr); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); 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) + bool arst_value, bool clk_polarity, bool arst_polarity, const std::string &src) { 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); + cell->set_src_attribute(src); 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* RTLIL::Module::addDlatchGate(RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity, const std::string &src) { 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); + cell->set_src_attribute(src); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity, bool set_polarity, bool clr_polarity, const std::string &src) { 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); @@ -1997,32 +2134,56 @@ RTLIL::Cell* RTLIL::Module::addDlatchsrGate(RTLIL::IdString name, RTLIL::SigSpec cell->setPort("\\R", sig_clr); cell->setPort("\\D", sig_d); cell->setPort("\\Q", sig_q); + cell->set_src_attribute(src); return cell; } -RTLIL::SigSpec RTLIL::Module::Anyconst(RTLIL::IdString name, int width) +RTLIL::SigSpec RTLIL::Module::Anyconst(RTLIL::IdString name, int width, const std::string &src) { RTLIL::SigSpec sig = addWire(NEW_ID, width); Cell *cell = addCell(name, "$anyconst"); cell->setParam("\\WIDTH", width); cell->setPort("\\Y", sig); + cell->set_src_attribute(src); return sig; } -RTLIL::SigSpec RTLIL::Module::Anyseq(RTLIL::IdString name, int width) +RTLIL::SigSpec RTLIL::Module::Anyseq(RTLIL::IdString name, int width, const std::string &src) { RTLIL::SigSpec sig = addWire(NEW_ID, width); Cell *cell = addCell(name, "$anyseq"); cell->setParam("\\WIDTH", width); cell->setPort("\\Y", sig); + cell->set_src_attribute(src); return sig; } -RTLIL::SigSpec RTLIL::Module::Initstate(RTLIL::IdString name) +RTLIL::SigSpec RTLIL::Module::Allconst(RTLIL::IdString name, int width, const std::string &src) +{ + RTLIL::SigSpec sig = addWire(NEW_ID, width); + Cell *cell = addCell(name, "$allconst"); + cell->setParam("\\WIDTH", width); + cell->setPort("\\Y", sig); + cell->set_src_attribute(src); + return sig; +} + +RTLIL::SigSpec RTLIL::Module::Allseq(RTLIL::IdString name, int width, const std::string &src) +{ + RTLIL::SigSpec sig = addWire(NEW_ID, width); + Cell *cell = addCell(name, "$allseq"); + cell->setParam("\\WIDTH", width); + cell->setPort("\\Y", sig); + cell->set_src_attribute(src); + return sig; +} + +RTLIL::SigSpec RTLIL::Module::Initstate(RTLIL::IdString name, const std::string &src) { RTLIL::SigSpec sig = addWire(NEW_ID); Cell *cell = addCell(name, "$initstate"); cell->setPort("\\Y", sig); + cell->set_src_attribute(src); return sig; } @@ -2048,6 +2209,7 @@ RTLIL::Memory::Memory() hashidx_ = hashidx_count; width = 1; + start_offset = 0; size = 0; } @@ -2764,10 +2926,11 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe other->unpack(); } - for (int i = GetSize(bits_) - 1; i >= 0; i--) { + for (int i = GetSize(bits_) - 1; i >= 0; i--) + { if (bits_[i].wire == NULL) continue; - for (auto &pattern_chunk : pattern.chunks()) { + 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) { @@ -2777,8 +2940,8 @@ void RTLIL::SigSpec::remove2(const RTLIL::SigSpec &pattern, RTLIL::SigSpec *othe other->bits_.erase(other->bits_.begin() + i); other->width_--; } + break; } - } } check(); @@ -3236,6 +3399,21 @@ bool RTLIL::SigSpec::is_fully_zero() const return true; } +bool RTLIL::SigSpec::is_fully_ones() const +{ + cover("kernel.rtlil.sigspec.is_fully_ones"); + + 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::S1) + return false; + } + return true; +} + bool RTLIL::SigSpec::is_fully_def() const { cover("kernel.rtlil.sigspec.is_fully_def"); @@ -3615,6 +3793,11 @@ RTLIL::CaseRule::~CaseRule() delete *it; } +bool RTLIL::CaseRule::empty() const +{ + return actions.empty() && switches.empty(); +} + RTLIL::CaseRule *RTLIL::CaseRule::clone() const { RTLIL::CaseRule *new_caserule = new RTLIL::CaseRule; @@ -3631,6 +3814,11 @@ RTLIL::SwitchRule::~SwitchRule() delete *it; } +bool RTLIL::SwitchRule::empty() const +{ + return cases.empty(); +} + RTLIL::SwitchRule *RTLIL::SwitchRule::clone() const { RTLIL::SwitchRule *new_switchrule = new RTLIL::SwitchRule; diff --git a/yosys/kernel/rtlil.h b/yosys/kernel/rtlil.h index 9430dcb36..f877622aa 100644 --- a/yosys/kernel/rtlil.h +++ b/yosys/kernel/rtlil.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -479,6 +479,11 @@ struct RTLIL::Const inline RTLIL::State &operator[](int index) { return bits.at(index); } inline const RTLIL::State &operator[](int index) const { return bits.at(index); } + bool is_fully_zero() const; + bool is_fully_ones() const; + bool is_fully_def() const; + bool is_fully_undef() const; + inline RTLIL::Const extract(int offset, int len = 1, RTLIL::State padding = RTLIL::State::S0) const { RTLIL::Const ret; ret.bits.reserve(len); @@ -501,9 +506,13 @@ struct RTLIL::AttrObject void set_bool_attribute(RTLIL::IdString id); bool get_bool_attribute(RTLIL::IdString id) const; + void set_strpool_attribute(RTLIL::IdString id, const pool &data); void add_strpool_attribute(RTLIL::IdString id, const pool &data); pool get_strpool_attribute(RTLIL::IdString id) const; + + void set_src_attribute(const std::string &src); + std::string get_src_attribute() const; }; struct RTLIL::SigChunk @@ -696,6 +705,7 @@ public: bool is_fully_const() const; bool is_fully_zero() const; + bool is_fully_ones() const; bool is_fully_def() const; bool is_fully_undef() const; bool has_const() const; @@ -793,7 +803,8 @@ struct RTLIL::Design int refcount_modules_; dict modules_; - std::vector verilog_packages; + std::vector verilog_packages, verilog_globals; + dict> verilog_defines; std::vector selection_stack; dict selection_vars; @@ -895,8 +906,10 @@ public: Module(); virtual ~Module(); - virtual RTLIL::IdString derive(RTLIL::Design *design, dict parameters); + virtual RTLIL::IdString derive(RTLIL::Design *design, dict parameters, bool mayfail = false); + virtual RTLIL::IdString derive(RTLIL::Design *design, dict parameters, dict interfaces, dict modports, bool mayfail = false); virtual size_t count_id(RTLIL::IdString id); + virtual void reprocess_module(RTLIL::Design *design, dict local_interfaces); virtual void sort(); virtual void check(); @@ -910,7 +923,7 @@ public: std::vector ports; void fixup_ports(); - template void rewrite_sigspecs(T functor); + template void rewrite_sigspecs(T &functor); void cloneInto(RTLIL::Module *new_mod) const; virtual RTLIL::Module *clone() const; @@ -955,161 +968,170 @@ public: // 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* addNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addPos (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addNeg (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); - 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* addAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); - 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* addReduceAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addReduceOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addReduceXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addReduceXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addReduceBool (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); - 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* addShl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addShr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addSshl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addSshr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addShift (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addShiftx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); - 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* addLt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addLe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addEq (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addNe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addEqx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addNex (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addGe (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addGt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); - 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* addAdd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addSub (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addMul (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addDiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addMod (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + 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, const std::string &src = ""); - 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* addLogicNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addLogicAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); + RTLIL::Cell* addLogicOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, bool is_signed = false, const std::string &src = ""); - 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* addMux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y, const std::string &src = ""); + RTLIL::Cell* addPmux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, RTLIL::SigSpec sig_y, const std::string &src = ""); - 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* addSlice (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const offset, const std::string &src = ""); + RTLIL::Cell* addConcat (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, const std::string &src = ""); + RTLIL::Cell* addLut (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_y, RTLIL::Const lut, const std::string &src = ""); + RTLIL::Cell* addTribuf (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_y, const std::string &src = ""); + RTLIL::Cell* addAssert (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src = ""); + RTLIL::Cell* addAssume (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src = ""); + RTLIL::Cell* addLive (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src = ""); + RTLIL::Cell* addFair (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src = ""); + RTLIL::Cell* addCover (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_en, const std::string &src = ""); + RTLIL::Cell* addEquiv (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_y, const std::string &src = ""); - 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* addSr (RTLIL::IdString name, RTLIL::SigSpec sig_set, RTLIL::SigSpec sig_clr, RTLIL::SigSpec sig_q, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); + RTLIL::Cell* addFf (RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, const std::string &src = ""); + RTLIL::Cell* addDff (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, const std::string &src = ""); + 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, const std::string &src = ""); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); 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::Const arst_value, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = ""); + RTLIL::Cell* addDlatch (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, const std::string &src = ""); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); - 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* addBufGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addNotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addAndGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addNandGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addOrGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addNorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addXorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addXnorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addAndnotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addOrnotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addMuxGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_s, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addAoi3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_y, const std::string &src = ""); + RTLIL::Cell* addOai3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_y, const std::string &src = ""); + 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, const std::string &src = ""); + 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, const std::string &src = ""); - 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* addFfGate (RTLIL::IdString name, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, const std::string &src = ""); + RTLIL::Cell* addDffGate (RTLIL::IdString name, RTLIL::SigSpec sig_clk, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, const std::string &src = ""); + 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, const std::string &src = ""); 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::SigSpec sig_d, RTLIL::SigSpec sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); 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); + bool arst_value = false, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = ""); + RTLIL::Cell* addDlatchGate (RTLIL::IdString name, RTLIL::SigSpec sig_en, RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, const std::string &src = ""); 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); + RTLIL::SigSpec sig_d, RTLIL::SigSpec sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = ""); // 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 Not (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Pos (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Bu0 (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Neg (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); - 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 And (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Or (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Xor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Xnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); - 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 ReduceAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec ReduceOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec ReduceXor (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec ReduceXnor (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec ReduceBool (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); - 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 Shl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Shr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Sshl (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Sshr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Shift (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Shiftx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); - 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 Lt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Le (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Eq (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Ne (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Eqx (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Nex (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Ge (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Gt (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); - 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 Add (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Sub (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Mul (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Div (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Mod (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec Pow (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool a_signed = false, bool b_signed = false, const std::string &src = ""); - 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 LogicNot (RTLIL::IdString name, RTLIL::SigSpec sig_a, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec LogicAnd (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); + RTLIL::SigSpec LogicOr (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, bool is_signed = false, const std::string &src = ""); - 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::SigSpec Mux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, const std::string &src = ""); + RTLIL::SigSpec Pmux (RTLIL::IdString name, RTLIL::SigSpec sig_a, RTLIL::SigSpec sig_b, RTLIL::SigSpec sig_s, const std::string &src = ""); - 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::SigBit BufGate (RTLIL::IdString name, RTLIL::SigBit sig_a, const std::string &src = ""); + RTLIL::SigBit NotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, const std::string &src = ""); + RTLIL::SigBit AndGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit NandGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit OrGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit NorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit XorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit XnorGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit AndnotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit OrnotGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, const std::string &src = ""); + RTLIL::SigBit MuxGate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_s, const std::string &src = ""); + RTLIL::SigBit Aoi3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, const std::string &src = ""); + RTLIL::SigBit Oai3Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, const std::string &src = ""); + RTLIL::SigBit Aoi4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d, const std::string &src = ""); + RTLIL::SigBit Oai4Gate (RTLIL::IdString name, RTLIL::SigBit sig_a, RTLIL::SigBit sig_b, RTLIL::SigBit sig_c, RTLIL::SigBit sig_d, const std::string &src = ""); - RTLIL::SigSpec Anyconst (RTLIL::IdString name, int width = 1); - RTLIL::SigSpec Anyseq (RTLIL::IdString name, int width = 1); - RTLIL::SigSpec Initstate (RTLIL::IdString name); + RTLIL::SigSpec Anyconst (RTLIL::IdString name, int width = 1, const std::string &src = ""); + RTLIL::SigSpec Anyseq (RTLIL::IdString name, int width = 1, const std::string &src = ""); + RTLIL::SigSpec Allconst (RTLIL::IdString name, int width = 1, const std::string &src = ""); + RTLIL::SigSpec Allseq (RTLIL::IdString name, int width = 1, const std::string &src = ""); + RTLIL::SigSpec Initstate (RTLIL::IdString name, const std::string &src = ""); }; struct RTLIL::Wire : public RTLIL::AttrObject @@ -1193,7 +1215,7 @@ public: module->design->module(type)->get_bool_attribute("\\keep")); } - template void rewrite_sigspecs(T functor); + template void rewrite_sigspecs(T &functor); }; struct RTLIL::CaseRule @@ -1205,7 +1227,9 @@ struct RTLIL::CaseRule ~CaseRule(); void optimize(); - template void rewrite_sigspecs(T functor); + bool empty() const; + + template void rewrite_sigspecs(T &functor); RTLIL::CaseRule *clone() const; }; @@ -1216,7 +1240,9 @@ struct RTLIL::SwitchRule : public RTLIL::AttrObject ~SwitchRule(); - template void rewrite_sigspecs(T functor); + bool empty() const; + + template void rewrite_sigspecs(T &functor); RTLIL::SwitchRule *clone() const; }; @@ -1226,7 +1252,7 @@ struct RTLIL::SyncRule RTLIL::SigSpec signal; std::vector actions; - template void rewrite_sigspecs(T functor); + template void rewrite_sigspecs(T &functor); RTLIL::SyncRule *clone() const; }; @@ -1238,7 +1264,7 @@ struct RTLIL::Process : public RTLIL::AttrObject ~Process(); - template void rewrite_sigspecs(T functor); + template void rewrite_sigspecs(T &functor); RTLIL::Process *clone() const; }; @@ -1287,7 +1313,7 @@ inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) { } template -void RTLIL::Module::rewrite_sigspecs(T functor) +void RTLIL::Module::rewrite_sigspecs(T &functor) { for (auto &it : cells_) it.second->rewrite_sigspecs(functor); @@ -1300,13 +1326,13 @@ void RTLIL::Module::rewrite_sigspecs(T functor) } template -void RTLIL::Cell::rewrite_sigspecs(T functor) { +void RTLIL::Cell::rewrite_sigspecs(T &functor) { for (auto &it : connections_) functor(it.second); } template -void RTLIL::CaseRule::rewrite_sigspecs(T functor) { +void RTLIL::CaseRule::rewrite_sigspecs(T &functor) { for (auto &it : compare) functor(it); for (auto &it : actions) { @@ -1318,7 +1344,7 @@ void RTLIL::CaseRule::rewrite_sigspecs(T functor) { } template -void RTLIL::SwitchRule::rewrite_sigspecs(T functor) +void RTLIL::SwitchRule::rewrite_sigspecs(T &functor) { functor(signal); for (auto it : cases) @@ -1326,7 +1352,7 @@ void RTLIL::SwitchRule::rewrite_sigspecs(T functor) } template -void RTLIL::SyncRule::rewrite_sigspecs(T functor) +void RTLIL::SyncRule::rewrite_sigspecs(T &functor) { functor(signal); for (auto &it : actions) { @@ -1336,7 +1362,7 @@ void RTLIL::SyncRule::rewrite_sigspecs(T functor) } template -void RTLIL::Process::rewrite_sigspecs(T functor) +void RTLIL::Process::rewrite_sigspecs(T &functor) { root_case.rewrite_sigspecs(functor); for (auto it : syncs) diff --git a/yosys/kernel/satgen.h b/yosys/kernel/satgen.h index 690f8e337..210cca3f3 100644 --- a/yosys/kernel/satgen.h +++ b/yosys/kernel/satgen.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -310,7 +310,7 @@ struct SatGen arith_undef_handled = true; } - if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_", "$and", "$or", "$xor", "$xnor", "$add", "$sub")) { std::vector a = importDefSigSpec(cell->getPort("\\A"), timestep); @@ -332,6 +332,10 @@ struct SatGen 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 == "$_ANDNOT_") + ez->assume(ez->vec_eq(ez->vec_and(a, ez->vec_not(b)), yy)); + if (cell->type == "$_ORNOT_") + ez->assume(ez->vec_eq(ez->vec_or(a, ez->vec_not(b)), yy)); if (cell->type == "$add") ez->assume(ez->vec_eq(ez->vec_add(a, b), yy)); if (cell->type == "$sub") @@ -360,6 +364,19 @@ struct SatGen std::vector yX = ez->vec_or(undef_a, undef_b); ez->assume(ez->vec_eq(yX, undef_y)); } + else if (cell->type == "$_ANDNOT_") { + std::vector a0 = ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a)); + std::vector b1 = ez->vec_and(b, ez->vec_not(undef_b)); + std::vector yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a0, b1))); + ez->assume(ez->vec_eq(yX, undef_y)); + } + + else if (cell->type == "$_ORNOT_") { + std::vector a1 = ez->vec_and(a, ez->vec_not(undef_a)); + std::vector b0 = ez->vec_and(ez->vec_not(b), ez->vec_not(undef_b)); + std::vector yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a1, b0))); + ez->assume(ez->vec_eq(yX, undef_y)); + } else log_abort(); @@ -507,11 +524,7 @@ struct SatGen std::vector undef_s = importUndefSigSpec(cell->getPort("\\S"), timestep); std::vector 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; + int maybe_a = ez->CONST_TRUE; std::vector bits_set = std::vector(undef_y.size(), ez->CONST_FALSE); std::vector bits_clr = std::vector(undef_y.size(), ez->CONST_FALSE); @@ -524,18 +537,12 @@ struct SatGen 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)); + maybe_a = ez->AND(maybe_a, ez->NOT(sure_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); + bits_set = ez->vec_ite(maybe_s, 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(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); diff --git a/yosys/kernel/yosys.cc b/yosys/kernel/yosys.cc index 08fee9741..2ed0f4db4 100644 --- a/yosys/kernel/yosys.cc +++ b/yosys/kernel/yosys.cc @@ -25,6 +25,10 @@ # include #endif +#ifdef YOSYS_ENABLE_EDITLINE +# include +#endif + #ifdef YOSYS_ENABLE_PLUGINS # include #endif @@ -42,10 +46,15 @@ # include # include # include +# include # include # include #endif +#ifdef __FreeBSD__ +# include +#endif + #include #include @@ -60,13 +69,15 @@ CellTypes yosys_celltypes; Tcl_Interp *yosys_tcl_interp = NULL; #endif +std::set yosys_input_files, yosys_output_files; + bool memhasher_active = false; uint32_t memhasher_rng = 123456; std::vector memhasher_store; void memhasher_on() { -#ifdef __linux__ +#if defined(__linux__) || defined(__FreeBSD__) memhasher_rng += time(NULL) << 16 ^ getpid(); #endif memhasher_store.resize(0x10000); @@ -106,7 +117,7 @@ void yosys_banner() log(" | |\n"); log(" | yosys -- Yosys Open SYnthesis Suite |\n"); log(" | |\n"); - log(" | Copyright (C) 2012 - 2016 Clifford Wolf |\n"); + log(" | Copyright (C) 2012 - 2018 Clifford Wolf |\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"); @@ -155,7 +166,7 @@ std::string vstringf(const char *fmt, va_list ap) std::string string; char *str = NULL; -#ifdef _WIN32 +#if defined(_WIN32 )|| defined(__CYGWIN__) int sz = 64, rc; while (1) { va_list apc; @@ -592,6 +603,8 @@ static int tcl_yosys_cmd(ClientData, Tcl_Interp *interp, int argc, const char *a std::string tcl_command_name = it.first; if (tcl_command_name == "proc") tcl_command_name = "procs"; + else if (tcl_command_name == "rename") + tcl_command_name = "renames"; 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()); @@ -623,26 +636,37 @@ extern Tcl_Interp *yosys_get_tcl_interp() struct TclPass : public Pass { TclPass() : Pass("tcl", "execute a TCL script file") { } - virtual void help() { + void help() YS_OVERRIDE { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" tcl \n"); + log(" tcl [args]\n"); log("\n"); log("This command executes the tcl commands in the specified file.\n"); log("Use 'yosys cmd' to run the yosys command 'cmd' from tcl.\n"); 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("commands directly as tcl commands to the tcl shell. Yosys commands\n"); + log("'proc' and 'rename' are wrapped to tcl commands 'procs' and 'renames'\n"); + log("in order to avoid a name collision with the built in commands.\n"); + log("\n"); + log("If any arguments are specified, these arguments are provided to the script via\n"); + log("the standard $argc and $argv variables.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *) YS_OVERRIDE { if (args.size() < 2) log_cmd_error("Missing script file.\n"); - if (args.size() > 2) - extra_args(args, 1, design, false); - if (Tcl_EvalFile(yosys_get_tcl_interp(), args[1].c_str()) != TCL_OK) - log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp())); + + std::vector script_args; + for (auto it = args.begin() + 2; it != args.end(); ++it) + script_args.push_back(Tcl_NewStringObj((*it).c_str(), (*it).size())); + + Tcl_Interp *interp = yosys_get_tcl_interp(); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(script_args.size()), 0); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(script_args.size(), script_args.data()), 0); + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(args[1].c_str(), args[1].size()), 0); + if (Tcl_EvalFile(interp, args[1].c_str()) != TCL_OK) + log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); } } TclPass; #endif @@ -659,6 +683,26 @@ std::string proc_self_dirname() buflen--; return std::string(path, buflen); } +#elif defined(__FreeBSD__) +std::string proc_self_dirname() +{ + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t buflen; + char *buffer; + std::string path; + if (sysctl(mib, 4, NULL, &buflen, NULL, 0) != 0) + log_error("sysctl failed: %s\n", strerror(errno)); + buffer = (char*)malloc(buflen); + if (buffer == NULL) + log_error("malloc failed: %s\n", strerror(errno)); + if (sysctl(mib, 4, buffer, &buflen, NULL, 0) != 0) + log_error("sysctl failed: %s\n", strerror(errno)); + while (buflen > 0 && buffer[buflen-1] != '/') + buflen--; + path.assign(buffer, buflen); + free(buffer); + return path; +} #elif defined(__APPLE__) std::string proc_self_dirname() { @@ -700,13 +744,13 @@ std::string proc_self_dirname() return "/"; } #else - #error Dont know how to determine process executable base path! + #error "Don't know how to determine process executable base path!" #endif #ifdef EMSCRIPTEN std::string proc_share_dirname() { - return "/share"; + return "/share/"; } #else std::string proc_share_dirname() @@ -766,7 +810,7 @@ static void handle_label(std::string &command, bool &from_to_active, const std:: while (pos < GetSize(command) && command[pos] != ' ' && command[pos] != '\t' && command[pos] != '\r' && command[pos] != '\n') label += command[pos++]; - if (label.back() == ':' && GetSize(label) > 1) + if (GetSize(label) > 1 && label.back() == ':') { label = label.substr(0, GetSize(label)-1); command = command.substr(pos); @@ -788,14 +832,20 @@ void run_frontend(std::string filename, std::string command, std::string *backen command = "verilog"; else if (filename.size() > 2 && filename.substr(filename.size()-3) == ".sv") command = "verilog -sv"; - else if (filename.size() > 2 && filename.substr(filename.size()-4) == ".vhd") + else if (filename.size() > 3 && filename.substr(filename.size()-4) == ".vhd") command = "vhdl"; else if (filename.size() > 4 && filename.substr(filename.size()-5) == ".blif") command = "blif"; + else if (filename.size() > 5 && filename.substr(filename.size()-6) == ".eblif") + command = "blif"; + else if (filename.size() > 4 && filename.substr(filename.size()-5) == ".json") + command = "json"; 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.size() > 3 && filename.substr(filename.size()-4) == ".tcl") + command = "tcl"; else if (filename == "-") command = "script"; else @@ -823,8 +873,10 @@ void run_frontend(std::string filename, std::string command, std::string *backen FILE *f = stdin; - if (filename != "-") + if (filename != "-") { f = fopen(filename.c_str(), "r"); + yosys_input_files.insert(filename); + } if (f == NULL) log_error("Can't open script file `%s' for reading: %s\n", filename.c_str(), strerror(errno)); @@ -875,7 +927,10 @@ void run_frontend(std::string filename, std::string command, std::string *backen log("\n-- Parsing `%s' using frontend `%s' --\n", filename.c_str(), command.c_str()); } - Frontend::frontend_call(design, NULL, filename, command); + if (command == "tcl") + Pass::call(design, vector({command, filename})); + else + Frontend::frontend_call(design, NULL, filename, command); } void run_frontend(std::string filename, std::string command, RTLIL::Design *design) @@ -903,6 +958,8 @@ void run_backend(std::string filename, std::string command, RTLIL::Design *desig command = "verilog"; else if (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") command = "ilang"; + else if (filename.size() > 4 && filename.substr(filename.size()-4) == ".aig") + command = "aiger"; else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".blif") command = "blif"; else if (filename.size() > 5 && filename.substr(filename.size()-5) == ".edif") @@ -929,7 +986,7 @@ void run_backend(std::string filename, std::string command, RTLIL::Design *desig Backend::backend_call(design, NULL, filename, command); } -#ifdef YOSYS_ENABLE_READLINE +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) static char *readline_cmd_generator(const char *text, int state) { static std::map::iterator it; @@ -1016,14 +1073,14 @@ void shell(RTLIL::Design *design) recursion_counter++; log_cmd_error_throw = true; -#ifdef YOSYS_ENABLE_READLINE - rl_readline_name = "yosys"; +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) + rl_readline_name = (char*)"yosys"; rl_attempted_completion_function = readline_completion; - rl_basic_word_break_characters = " \t\n"; + rl_basic_word_break_characters = (char*)" \t\n"; #endif char *command = NULL; -#ifdef YOSYS_ENABLE_READLINE +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) while ((command = readline(create_prompt(design, recursion_counter))) != NULL) { #else @@ -1037,7 +1094,7 @@ void shell(RTLIL::Design *design) #endif if (command[strspn(command, " \t\r\n")] == 0) continue; -#ifdef YOSYS_ENABLE_READLINE +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) add_history(command); #endif @@ -1067,7 +1124,7 @@ void shell(RTLIL::Design *design) struct ShellPass : public Pass { ShellPass() : Pass("shell", "enter interactive command mode") { } - virtual void help() { + void help() YS_OVERRIDE { log("\n"); log(" shell\n"); log("\n"); @@ -1099,16 +1156,16 @@ struct ShellPass : public Pass { log("Press Ctrl-D or type 'exit' to leave the interactive shell.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { extra_args(args, 1, design, false); shell(design); } } ShellPass; -#ifdef YOSYS_ENABLE_READLINE +#if defined(YOSYS_ENABLE_READLINE) || defined(YOSYS_ENABLE_EDITLINE) struct HistoryPass : public Pass { HistoryPass() : Pass("history", "show last interactive commands") { } - virtual void help() { + void help() YS_OVERRIDE { log("\n"); log(" history\n"); log("\n"); @@ -1117,17 +1174,22 @@ struct HistoryPass : public Pass { log("from executed scripts.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { extra_args(args, 1, design, false); +#ifdef YOSYS_ENABLE_READLINE for(HIST_ENTRY **list = history_list(); *list != NULL; list++) log("%s\n", (*list)->line); +#else + for (int i = where_history(); history_get(i); i++) + log("%s\n", history_get(i)->line); +#endif } } HistoryPass; #endif struct ScriptCmdPass : public Pass { ScriptCmdPass() : Pass("script", "execute commands from script file") { } - virtual void help() { + void help() YS_OVERRIDE { log("\n"); log(" script [:]\n"); log("\n"); @@ -1142,7 +1204,7 @@ struct ScriptCmdPass : public Pass { log("marked with that label (until the next label) is executed.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { if (args.size() < 2) log_cmd_error("Missing script file.\n"); else if (args.size() == 2) diff --git a/yosys/kernel/yosys.h b/yosys/kernel/yosys.h index ae73146b8..c9f973318 100644 --- a/yosys/kernel/yosys.h +++ b/yosys/kernel/yosys.h @@ -1,4 +1,4 @@ -/* +/* -*- c++ -*- * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf @@ -74,6 +74,16 @@ #ifdef YOSYS_ENABLE_TCL # include +# ifdef YOSYS_MXE_HACKS +extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc); +extern Tcl_Interp *Tcl_CreateInterp(void); +extern void Tcl_DeleteInterp(Tcl_Interp *interp); +extern int Tcl_Eval(Tcl_Interp *interp, const char *script); +extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName); +extern void Tcl_Finalize(void); +extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr); +extern const char *Tcl_GetStringResult(Tcl_Interp *interp); +# endif #endif #ifdef _WIN32 @@ -150,6 +160,7 @@ using std::pair; using std::make_tuple; using std::make_pair; +using std::get; using std::min; using std::max; @@ -294,6 +305,9 @@ void run_frontend(std::string filename, std::string command, RTLIL::Design *desi void run_backend(std::string filename, std::string command, RTLIL::Design *design = nullptr); void shell(RTLIL::Design *design); +// journal of all input and output files read (for "yosys -E") +extern std::set yosys_input_files, yosys_output_files; + // from kernel/version_*.o (cc source generated from Makefile) extern const char *yosys_version_str; diff --git a/yosys/libs/ezsat/ezminisat.cc b/yosys/libs/ezsat/ezminisat.cc index e0ee6292d..4be5fd493 100644 --- a/yosys/libs/ezsat/ezminisat.cc +++ b/yosys/libs/ezsat/ezminisat.cc @@ -18,8 +18,12 @@ */ // needed for MiniSAT headers (see Minisat Makefile) -#define __STDC_LIMIT_MACROS +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif #include "ezminisat.h" diff --git a/yosys/libs/ezsat/ezminisat.h b/yosys/libs/ezsat/ezminisat.h index 983e6fd0e..3a34c13c8 100644 --- a/yosys/libs/ezsat/ezminisat.h +++ b/yosys/libs/ezsat/ezminisat.h @@ -28,7 +28,7 @@ #include // minisat is using limit macros and format macros in their headers that -// can be the source of some troubles when used from c++11. thefore we +// can be the source of some troubles when used from c++11. therefore we // don't force ezSAT users to use minisat headers.. namespace Minisat { class Solver; diff --git a/yosys/libs/minisat/00_PATCH_no_fpu_control.patch b/yosys/libs/minisat/00_PATCH_no_fpu_control.patch new file mode 100644 index 000000000..6c754b1ed --- /dev/null +++ b/yosys/libs/minisat/00_PATCH_no_fpu_control.patch @@ -0,0 +1,43 @@ +--- System.cc ++++ System.cc +@@ -97,17 +97,6 @@ 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) + { +--- System.h ++++ System.h +@@ -21,10 +21,6 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA + #ifndef Minisat_System_h + #define Minisat_System_h + +-#if defined(__linux__) +-#include +-#endif +- + #include "IntTypes.h" + + //------------------------------------------------------------------------------------------------- +@@ -36,9 +32,6 @@ 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. + diff --git a/yosys/libs/minisat/00_PATCH_remove_zlib.patch b/yosys/libs/minisat/00_PATCH_remove_zlib.patch index 61a36f7e5..068356b73 100644 --- a/yosys/libs/minisat/00_PATCH_remove_zlib.patch +++ b/yosys/libs/minisat/00_PATCH_remove_zlib.patch @@ -36,3 +36,20 @@ int operator * () const { return (pos >= size) ? EOF : buf[pos]; } void operator ++ () { pos++; assureLookahead(); } +--- Dimacs.h ++++ Dimacs.h +@@ -76,10 +76,10 @@ static void parse_DIMACS_main(B& in, Solver& S, bool strictp = false) { + + // Inserts problem into solver. + // +-template +-static void parse_DIMACS(gzFile input_stream, Solver& S, bool strictp = false) { +- StreamBuffer in(input_stream); +- parse_DIMACS_main(in, S, strictp); } ++//template ++//static void parse_DIMACS(gzFile input_stream, Solver& S, bool strictp = false) { ++// StreamBuffer in(input_stream); ++// parse_DIMACS_main(in, S, strictp); } + + //================================================================================================= + } diff --git a/yosys/libs/minisat/00_PATCH_typofixes.patch b/yosys/libs/minisat/00_PATCH_typofixes.patch new file mode 100644 index 000000000..175f483bf --- /dev/null +++ b/yosys/libs/minisat/00_PATCH_typofixes.patch @@ -0,0 +1,20 @@ +--- Solver.h ++++ Solver.h +@@ -103,7 +103,7 @@ public: + int nFreeVars () const; + void printStats () const; // Print some current statistics to standard output. + +- // Resource contraints: ++ // Resource constraints: + // + void setConfBudget(int64_t x); + void setPropBudget(int64_t x); +@@ -230,7 +230,7 @@ protected: + double learntsize_adjust_confl; + int learntsize_adjust_cnt; + +- // Resource contraints: ++ // Resource constraints: + // + int64_t conflict_budget; // -1 means no budget. + int64_t propagation_budget; // -1 means no budget. diff --git a/yosys/libs/minisat/00_UPDATE.sh b/yosys/libs/minisat/00_UPDATE.sh old mode 100644 new mode 100755 index 96a34ec93..ea26215ab --- a/yosys/libs/minisat/00_UPDATE.sh +++ b/yosys/libs/minisat/00_UPDATE.sh @@ -9,9 +9,11 @@ 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 +sed -i -e '1 i #ifndef __STDC_LIMIT_MACROS\n#define __STDC_LIMIT_MACROS\n#endif' *.cc +sed -i -e '1 i #ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif' *.cc patch -p0 < 00_PATCH_mkLit_default_arg.patch patch -p0 < 00_PATCH_remove_zlib.patch +patch -p0 < 00_PATCH_no_fpu_control.patch +patch -p0 < 00_PATCH_typofixes.patch diff --git a/yosys/libs/minisat/Dimacs.h b/yosys/libs/minisat/Dimacs.h index ccfa1c013..61b9d3ca0 100644 --- a/yosys/libs/minisat/Dimacs.h +++ b/yosys/libs/minisat/Dimacs.h @@ -76,10 +76,10 @@ static void parse_DIMACS_main(B& in, Solver& S, bool strictp = false) { // Inserts problem into solver. // -template -static void parse_DIMACS(gzFile input_stream, Solver& S, bool strictp = false) { - StreamBuffer in(input_stream); - parse_DIMACS_main(in, S, strictp); } +//template +//static void parse_DIMACS(gzFile input_stream, Solver& S, bool strictp = false) { +// StreamBuffer in(input_stream); +// parse_DIMACS_main(in, S, strictp); } //================================================================================================= } diff --git a/yosys/libs/minisat/Options.cc b/yosys/libs/minisat/Options.cc index 1aff3fab2..5c45dd6ac 100644 --- a/yosys/libs/minisat/Options.cc +++ b/yosys/libs/minisat/Options.cc @@ -1,5 +1,9 @@ +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS +#endif /**************************************************************************************[Options.cc] Copyright (c) 2008-2010, Niklas Sorensson diff --git a/yosys/libs/minisat/SimpSolver.cc b/yosys/libs/minisat/SimpSolver.cc index fd5774e0e..7348a905d 100644 --- a/yosys/libs/minisat/SimpSolver.cc +++ b/yosys/libs/minisat/SimpSolver.cc @@ -1,5 +1,9 @@ +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS +#endif /***********************************************************************************[SimpSolver.cc] Copyright (c) 2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson diff --git a/yosys/libs/minisat/Solver.cc b/yosys/libs/minisat/Solver.cc index ab476853a..f6d4fb5ae 100644 --- a/yosys/libs/minisat/Solver.cc +++ b/yosys/libs/minisat/Solver.cc @@ -1,5 +1,9 @@ +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS +#endif /***************************************************************************************[Solver.cc] Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson diff --git a/yosys/libs/minisat/System.cc b/yosys/libs/minisat/System.cc index febe3b40f..1921a1d71 100644 --- a/yosys/libs/minisat/System.cc +++ b/yosys/libs/minisat/System.cc @@ -1,5 +1,9 @@ +#ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS +#endif /***************************************************************************************[System.cc] Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson Copyright (c) 2007-2010, Niklas Sorensson @@ -97,17 +101,6 @@ 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) { diff --git a/yosys/libs/minisat/System.h b/yosys/libs/minisat/System.h index ee92a6e08..cd9d020c7 100644 --- a/yosys/libs/minisat/System.h +++ b/yosys/libs/minisat/System.h @@ -21,10 +21,6 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #ifndef Minisat_System_h #define Minisat_System_h -#if defined(__linux__) -#include -#endif - #include "IntTypes.h" //------------------------------------------------------------------------------------------------- @@ -36,9 +32,6 @@ 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. diff --git a/yosys/libs/subcircuit/README b/yosys/libs/subcircuit/README index b1335681e..ecaa987db 100644 --- a/yosys/libs/subcircuit/README +++ b/yosys/libs/subcircuit/README @@ -109,7 +109,7 @@ look at the demo.cc example program in this directory. Setting up graphs ----------------- -Instanciate the SubCircuit::Graph class and use the methods of this class to +Instantiate the SubCircuit::Graph class and use the methods of this class to set up the circuit. SubCircuit::Graph myGraph; @@ -152,7 +152,7 @@ rotate shift, The method createConstant() can be used to add a constant driver to a signal. The signal value is encoded as one char by bit, allowing for multi-valued -logic matching. The follwoing command sets the lowest bit of cell6.A to a +logic matching. The following command sets the lowest bit of cell6.A to a logic 1: myGraph.createConnection("cell6", "A", 0, '1'); @@ -314,7 +314,7 @@ bool userCompareEdge(needleGraphId, needleFromNodeId, needleFromUserData, needle Perform additional checks on a pair of a pair of adjacent nodes (one adjacent pair from the needle and one adjacent pair from the haystack) - to determine wheter this edge from the needle is compatible with + to determine whether this edge from the needle is compatible with that edge from the haystack. The default implementation always returns true. diff --git a/yosys/libs/subcircuit/subcircuit.h b/yosys/libs/subcircuit/subcircuit.h index 5291c6421..8368efab1 100644 --- a/yosys/libs/subcircuit/subcircuit.h +++ b/yosys/libs/subcircuit/subcircuit.h @@ -131,7 +131,7 @@ namespace SubCircuit public: Solver(); - ~Solver(); + virtual ~Solver(); void setVerbose(); void addGraph(std::string graphId, const Graph &graph); diff --git a/yosys/manual/CHAPTER_CellLib.tex b/yosys/manual/CHAPTER_CellLib.tex index a831fdf33..e22664a82 100644 --- a/yosys/manual/CHAPTER_CellLib.tex +++ b/yosys/manual/CHAPTER_CellLib.tex @@ -211,14 +211,15 @@ Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches \subsection{Memories} \label{sec:memcells} -Memories are either represented using RTLIL::Memory objects and {\tt \$memrd} and {\tt \$memwr} cells -or simply by using {\tt \$mem} cells. +Memories are either represented using RTLIL::Memory objects, {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} +cells, or by {\tt \$mem} cells alone. In the first alternative the RTLIL::Memory objects hold the general metadata for the memory (bit width, size in number of words, etc.) and for each port a {\tt \$memrd} (read port) or {\tt \$memwr} (write port) cell is created. Having individual cells for read and write ports has the advantage that they can be consolidated using resource sharing passes. In some cases this drastically reduces the number of required -ports on the memory cell. +ports on the memory cell. In this alternative, memory initialization data is represented by {\tt \$meminit} cells, +which allow delaying constant folding for initialization addresses and data until after the frontend finishes. The {\tt \$memrd} cells have a clock input \B{CLK}, an enable input \B{EN}, an address input \B{ADDR}, and a data output \B{DATA}. They also have the @@ -253,7 +254,7 @@ enable bit for each data bit), an address input \B{ADDR} and a data input \begin{itemize} \item \B{MEMID} \\ -The name of the RTLIL::Memory object that is associated with this read port. +The name of the RTLIL::Memory object that is associated with this write port. \item \B{ABITS} \\ The number of address bits (width of the \B{ADDR} input port). @@ -262,7 +263,7 @@ The number of address bits (width of the \B{ADDR} input port). The number of data bits (width of the \B{DATA} output port). \item \B{CLK\_ENABLE} \\ -When this parameter is non-zero, the clock is used. Otherwise this read port is asynchronous and +When this parameter is non-zero, the clock is used. Otherwise this write port is asynchronous and the \B{CLK} input is not used. \item \B{CLK\_POLARITY} \\ @@ -273,6 +274,27 @@ edge if this parameter is {\tt 1'b0}. The cell with the higher integer value in this parameter wins a write conflict. \end{itemize} +The {\tt \$meminit} cells have an address input \B{ADDR} and a data input \B{DATA}, with the width +of the \B{DATA} port equal to \B{WIDTH} parameter times \B{WORDS} parameter. Both of the inputs +must resolve to a constant for synthesis to succeed. + +\begin{itemize} +\item \B{MEMID} \\ +The name of the RTLIL::Memory object that is associated with this initialization cell. + +\item \B{ABITS} \\ +The number of address bits (width of the \B{ADDR} input port). + +\item \B{WIDTH} \\ +The number of data bits per memory location. + +\item \B{WORDS} \\ +The number of consecutive memory locations initialized by this cell. + +\item \B{PRIORITY} \\ +The cell with the higher integer value in this parameter wins an initialization conflict. +\end{itemize} + The HDL frontend models a memory using RTLIL::Memory objects and asynchronous {\tt \$memrd} and {\tt \$memwr} cells. The {\tt memory} pass (i.e.~its various sub-passes) migrates {\tt \$dff} cells into the {\tt \$memrd} and {\tt \$memwr} cells making them synchronous, then @@ -295,6 +317,9 @@ The number of address bits. \item \B{WIDTH} \\ The number of data bits per word. +\item \B{INIT} \\ +The initial memory contents. + \item \B{RD\_PORTS} \\ The number of read ports on this memory cell. @@ -345,9 +370,11 @@ This input is \B{WR\_PORTS}*\B{ABITS} bits wide, containing all address signals This input is \B{WR\_PORTS}*\B{WIDTH} bits wide, containing all data signals for the write ports. \end{itemize} -The {\tt techmap} pass can be used to manually map {\tt \$mem} cells to -specialized memory cells on the target architecture, such as block ram resources -on an FPGA. +The {\tt memory\_collect} pass can be used to convert discrete {\tt \$memrd}, {\tt \$memwr}, and {\tt \$meminit} cells +belonging to the same memory to a single {\tt \$mem} cell, whereas the {\tt memory\_unpack} pass performs the inverse operation. +The {\tt memory\_dff} pass can combine asynchronous memory ports that are fed by or feeding registers into synchronous memory ports. +The {\tt memory\_bram} pass can be used to recognize {\tt \$mem} cells that can be implemented with a block RAM resource on an FPGA. +The {\tt memory\_map} pass can be used to implement {\tt \$mem} cells as basic logic: word-wide DFFs and address decoders. \subsection{Finite State Machines} @@ -410,7 +437,7 @@ otherwise. \begin{lstlisting}[mathescape,language=Verilog] always @($ClkEdge$ C, $RstEdge$ R) if (R == $RstLvl$) - Q <= $RstVa$l; + Q <= $RstVal$; else Q <= D; \end{lstlisting} @@ -421,7 +448,8 @@ pass. The combinatorial logic cells can be mapped to physical cells from a Liber using the {\tt abc} pass. \begin{fixme} -Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$equiv}, {\tt \$initstate}, {\tt \$anyconst}, and {\tt \$anyseq} cells. +Add information about {\tt \$assert}, {\tt \$assume}, {\tt \$live}, {\tt \$fair}, {\tt \$cover}, {\tt \$equiv}, +{\tt \$initstate}, {\tt \$anyconst}, {\tt \$anyseq}, {\tt \$allconst}, {\tt \$allseq} cells. \end{fixme} \begin{fixme} @@ -449,6 +477,7 @@ Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLA \end{fixme} \begin{fixme} -Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells. +Add information about {\tt \$\_NAND\_}, {\tt \$\_NOR\_}, {\tt \$\_XNOR\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_ORNOT\_}, +{\tt \$\_AOI3\_}, {\tt \$\_OAI3\_}, {\tt \$\_AOI4\_}, and {\tt \$\_OAI4\_} cells. \end{fixme} diff --git a/yosys/manual/CHAPTER_Overview.tex b/yosys/manual/CHAPTER_Overview.tex index 964875d57..2feb0f1cb 100644 --- a/yosys/manual/CHAPTER_Overview.tex +++ b/yosys/manual/CHAPTER_Overview.tex @@ -428,8 +428,8 @@ memory object has the following properties: All read accesses to the memory are transformed to {\tt \$memrd} cells and all write accesses to {\tt \$memwr} cells by the language frontend. These cells consist of independent read- and write-ports -to the memory. The \B{MEMID} parameter on these cells is used to link them together and to the -RTLIL::Memory object they belong to. +to the memory. Memory initialization is transformed to {\tt \$meminit} cells by the language frontend. +The \B{MEMID} parameter on these cells is used to link them together and to the RTLIL::Memory object they belong to. The rationale behind using separate cells for the individual ports versus creating a large multiport memory cell right in the language frontend is that diff --git a/yosys/manual/CHAPTER_Prog/stubnets.cc b/yosys/manual/CHAPTER_Prog/stubnets.cc index eb77bd404..8123e63db 100644 --- a/yosys/manual/CHAPTER_Prog/stubnets.cc +++ b/yosys/manual/CHAPTER_Prog/stubnets.cc @@ -98,7 +98,7 @@ static void find_stub_nets(RTLIL::Design *design, RTLIL::Module *module, bool re // each pass contains a singleton object that is derived from Pass struct StubnetsPass : public Pass { StubnetsPass() : Pass("stubnets") { } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { // variables to mirror information from passed options bool report_bits = 0; diff --git a/yosys/manual/PRESENTATION_Prog/my_cmd.cc b/yosys/manual/PRESENTATION_Prog/my_cmd.cc index d99bfe1e8..5d9a7e13b 100644 --- a/yosys/manual/PRESENTATION_Prog/my_cmd.cc +++ b/yosys/manual/PRESENTATION_Prog/my_cmd.cc @@ -6,7 +6,7 @@ PRIVATE_NAMESPACE_BEGIN struct MyPass : public Pass { MyPass() : Pass("my_cmd", "just a simple test") { } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log("Arguments to my_cmd:\n"); for (auto &arg : args) @@ -22,7 +22,7 @@ struct MyPass : public Pass { struct Test1Pass : public Pass { Test1Pass() : Pass("test1", "creating the absval module") { } - virtual void execute(std::vector, RTLIL::Design *design) + void execute(std::vector, RTLIL::Design *design) YS_OVERRIDE { if (design->has("\\absval") != 0) log_error("A module with the name absval already exists!\n"); @@ -49,7 +49,7 @@ struct Test1Pass : public Pass { struct Test2Pass : public Pass { Test2Pass() : Pass("test2", "demonstrating sigmap on test module") { } - virtual void execute(std::vector, RTLIL::Design *design) + void execute(std::vector, RTLIL::Design *design) YS_OVERRIDE { if (design->selection_stack.back().empty()) log_cmd_error("This command can't operator on an empty selection!\n"); diff --git a/yosys/manual/clean.sh b/yosys/manual/clean.sh index 11c2e7bf2..addc34ed1 100755 --- a/yosys/manual/clean.sh +++ b/yosys/manual/clean.sh @@ -1,2 +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 +for f in $( find . -name .gitignore ); do sed -Ee "s,^,find ${f%.gitignore} -name ',; s,$,' | xargs rm -f,;" $f; done | bash -v diff --git a/yosys/manual/command-reference-manual.tex b/yosys/manual/command-reference-manual.tex index 8af8ccdd0..bed6326e2 100644 --- a/yosys/manual/command-reference-manual.tex +++ b/yosys/manual/command-reference-manual.tex @@ -23,45 +23,47 @@ library to a target architecture. 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} + strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; + &nf {D}; &put for -liberty with -constr: - strash; dc2; scorr; ifraig; retime -o {D}; strash; dch -f; - map {D}; buffer; upsize {D}; dnsize {D}; stime -p + strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; + &nf {D}; &put; 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 + strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2; + lutpack {S} for -lut/-luts (different LUT sizes): - strash; dc2; scorr; ifraig; retime -o; strash; dch -f; if; mfs + strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2 for -sop: - strash; dc2; scorr; ifraig; retime -o; strash; dch -f; + strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P} otherwise: - strash; dc2; scorr; ifraig; retime -o; strash; dch -f; map + strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; + &nf {D}; &put -fast use different default scripts that are slightly faster (at the cost of output quality): for -liberty without -constr: - retime -o {D}; map {D} + strash; dretime; map {D} for -liberty with -constr: - retime -o {D}; map {D}; buffer; upsize {D}; dnsize {D}; stime -p + strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; + stime -p for -lut/-luts: - retime -o; if + strash; dretime; if for -sop: - retime -o; cover -I {I} -P {P} + strash; dretime; cover -I {I} -P {P} otherwise: - retime -o; map + strash; dretime; map -liberty generate netlists for the specified cell library (using the liberty @@ -81,6 +83,8 @@ library to a target architecture. -D set delay target. the string {D} in the default scripts above is replaced by this option when used, and an empty string otherwise. + this also replaces 'dretime' with 'dretime; retime -o {D}' in the + default scripts above. -I maximum number of SOP inputs. @@ -90,6 +94,10 @@ library to a target architecture. maximum number of SOP products. (replaces {P} in the default scripts above) + -S + maximum number of LUT inputs shared. + (replaces {S} in the default scripts above, default: -S 1) + -lut generate netlist using luts of (max) the specified width. @@ -107,10 +115,21 @@ library to a target architecture. 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. + Map to the specified list of gate types. Supported gates types are: + AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX, AOI3, OAI3, AOI4, OAI4. (The NOT gate is always added to this list automatically.) + The following aliases can be used to reference common sets of gate types: + simple: AND OR XOR MUX + cmos2: NAND NOR + cmos3: NAND NOR AOI3 OAI3 + cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4 + gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT + aig: AND NAND OR NOR ANDNOT ORNOT + + Prefix a gate type with a '-' to remove it from the list. For example + the arguments 'AND,OR,XOR' and 'simple,-MUX' are equivalent. + -dff also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many clock domains are automatically partitioned in clock domains and each @@ -140,8 +159,12 @@ library to a target architecture. 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.) +Note that this is a logic optimization pass within Yosys that is calling ABC +internally. This is not going to "run ABC on your design". It will instead run +ABC on logic snippets extracted from your design. You will not get any useful +output when passing an ABC script that writes a file. Instead write your full +design as BLIF file with write_blif and the load that into ABC externally if +you want to use ABC to convert your design into another format. [1] http://www.eecs.berkeley.edu/~alanmi/abc/ \end{lstlisting} @@ -206,6 +229,22 @@ This command adds asserts to the design that assert that all parallel muxes additional constrained and check the $pmux condition always. \end{lstlisting} +\section{async2sync -- convert async FF inputs to sync circuits} +\label{cmd:async2sync} +\begin{lstlisting}[numbers=left,frame=single] + async2sync [options] [selection] + +This command replaces async FF inputs with sync circuits emulating the same +behavior for when the async signals are actually synchronized to the clock. + +This pass assumes negative hold time for the async FF inputs. For example when +a reset deasserts with the clock edge, then the FF output will still drive the +reset value in the next cycle regardless of the data-in value at the time of +the clock edge. + +Currently only $adff cells are supported by this pass. +\end{lstlisting} + \section{attrmap -- renaming attributes} \label{cmd:attrmap} \begin{lstlisting}[numbers=left,frame=single] @@ -268,6 +307,15 @@ Move or copy attributes on wires to the cells driving them. multiple times. \end{lstlisting} +\section{blackbox -- change type of cells in the design} +\label{cmd:blackbox} +\begin{lstlisting}[numbers=left,frame=single] + blackbox [options] [selection] + +Convert modules into blackbox modules (remove contents and set the blackbox +module attribute). +\end{lstlisting} + \section{cd -- a shortcut for 'select -module '} \label{cmd:cd} \begin{lstlisting}[numbers=left,frame=single] @@ -284,6 +332,12 @@ to 'cd '. cd .. +Remove trailing substrings that start with '.' in current module name until +the name of a module in the current design is generated, then switch to that +module. Otherwise clear the current selection. + + cd + This is just a shortcut for 'select -clear'. \end{lstlisting} @@ -303,10 +357,49 @@ This pass identifies the following problems in the current design: When called with -noinit then this command also checks for wires which have the 'init' attribute set. +When called with -initdrv then this command also checks for wires which have +the 'init' attribute set and aren't driven by a FF cell type. + When called with -assert then the command will produce an error if any problems are found in the current design. \end{lstlisting} +\section{chformal -- change formal constraints of the design} +\label{cmd:chformal} +\begin{lstlisting}[numbers=left,frame=single] + chformal [types] [mode] [options] [selection] + +Make changes to the formal constraints of the design. The [types] options +the type of constraint to operate on. If none of the folling options is given, +the command will operate on all constraint types: + + -assert $assert cells, representing assert(...) constraints + -assume $assume cells, representing assume(...) constraints + -live $live cells, representing assert(s_eventually ...) + -fair $fair cells, representing assume(s_eventually ...) + -cover $cover cells, representing cover() statements + +Exactly one of the following modes must be specified: + + -remove + remove the cells and thus constraints from the design + + -early + bypass FFs that only delay the activation of a constraint + + -delay + delay activation of the constraint by clock cycles + + -skip + ignore activation of the constraint in the first clock cycles + + -assert2assume + -assume2assert + -live2fair + -fair2live + change the roles of cells as indicated. this options can be combined +\end{lstlisting} + \section{chparam -- re-evaluate modules with new parameters} \label{cmd:chparam} \begin{lstlisting}[numbers=left,frame=single] @@ -321,6 +414,20 @@ passed in double quotes ("). List the available parameters of the selected modules. \end{lstlisting} +\section{chtype -- change type of cells in the design} +\label{cmd:chtype} +\begin{lstlisting}[numbers=left,frame=single] + chtype [options] [selection] + +Change the types of cells in the design. + + -set + set the cell type to the given type + + -map + change cells types that match to +\end{lstlisting} + \section{clean -- remove unused cells and wires} \label{cmd:clean} \begin{lstlisting}[numbers=left,frame=single] @@ -376,7 +483,7 @@ 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} +\section{connwrappers -- match width of input-output port pairs} \label{cmd:connwrappers} \begin{lstlisting}[numbers=left,frame=single] connwrappers [options] [selection] @@ -397,6 +504,14 @@ the driving cell. The options -signed, -unsigned, and -port can be specified multiple times. \end{lstlisting} +\section{coolrunner2\_sop -- break \$sop cells into ANDTERM/ORTERM cells} +\label{cmd:coolrunner2_sop} +\begin{lstlisting}[numbers=left,frame=single] + coolrunner2_sop [options] [selection] + +Break $sop cells into ANDTERM/ORTERM cells. +\end{lstlisting} + \section{copy -- copy modules in the design} \label{cmd:copy} \begin{lstlisting}[numbers=left,frame=single] @@ -519,6 +634,19 @@ evaluated in the other design. design -copy-to [-as ] [selection] Copy modules from the current design into the specified one. + + + design -import [-as ] [selection] + +Import the specified design into the current design. The source design must +either have a selected top module or the selection must contain exactly one +module that is then used as top module for this command. + + + design -reset-vlog + +The Verilog front-end remembers defined macros and top-level declarations +between calls to 'read_verilog'. This command resets this memory. \end{lstlisting} \section{dff2dffe -- transform \$dff cells to \$dffe cells} @@ -546,8 +674,18 @@ $_DFF_P_, $_DFF_N_ and $_MUX_. -direct-match like -direct for all DFF cell types matching the expression. this will use $__DFFE_* as matching the - internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is - converted to $_DFFE_[NP]_. + internal gate type $_DFF_*_, and $__DFFSE_* for those matching + $_DFFS_*_, except for $_DFF_[NP]_, which is converted to + $_DFFE_[NP]_. +\end{lstlisting} + +\section{dff2dffs -- process sync set/reset with SR over CE priority} +\label{cmd:dff2dffs} +\begin{lstlisting}[numbers=left,frame=single] + dff2dffs [options] [selection] + +Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before +dff2dffe for SR over CE priority. \end{lstlisting} \section{dffinit -- set INIT param on FF cells} @@ -561,6 +699,11 @@ drives. (This is primarily used in FPGA flows.) -ff operate on the specified cell type. this option can be used multiple times. + + -highlow + use the string values "high" and "low" to represent a single-bit + initial value of 1 or 0. (multi-bit values are not supported in this + mode.) \end{lstlisting} \section{dfflibmap -- technology mapping of flip-flops} @@ -767,6 +910,10 @@ This command tries to prove $equiv cells using a simple direct SAT approach. -undef enable modelling of undef states + -short + create shorter input cones that stop at shared nodes. This yields + simpler SAT problems but sometimes fails to prove equivalence. + -nogroup disabling grouping of $equiv cells by output wire @@ -852,6 +999,10 @@ outputs. when exposing a wire, create an input/output pair and cut the internal signal path at that wire. + -input + when exposing a wire, create an input port and disconnect the internal + driver. + -shared only expose those signals that are shared among the selected modules. this is useful for preparing modules for equivalence checking. @@ -956,6 +1107,64 @@ 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{extract\_counter -- Extract GreenPak4 counter cells} +\label{cmd:extract_counter} +\begin{lstlisting}[numbers=left,frame=single] + extract_counter [options] [selection] + +This pass converts non-resettable or async resettable down counters to +counter cells. Use a target-specific 'techmap' map file to convert those cells +to the actual target cells. + + -maxwidth N + Only extract counters up to N bits wide + + -pout X,Y,... + Only allow parallel output from the counter to the listed cell types + (if not specified, parallel outputs are not restricted) +\end{lstlisting} + +\section{extract\_fa -- find and extract full/half adders} +\label{cmd:extract_fa} +\begin{lstlisting}[numbers=left,frame=single] + extract_fa [options] [selection] + +This pass extracts full/half adders from a gate-level design. + + -fa, -ha + Enable cell types (fa=full adder, ha=half adder) + All types are enabled if none of this options is used + + -d + Set maximum depth for extracted logic cones (default=20) + + -b + Set maximum breadth for extracted logic cones (default=6) + + -v + Verbose output +\end{lstlisting} + +\section{extract\_reduce -- converts gate chains into \$reduce\_* cells} +\label{cmd:extract_reduce} +\begin{lstlisting}[numbers=left,frame=single] + extract_reduce [options] [selection] + +converts gate chains into $reduce_* cells + +This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them +with their corresponding $reduce_* cells. Because this command only operates on +these cell types, it is recommended to map the design to only these cell types +using the `abc -g` command. Note that, in some cases, it may be more effective +to map the design to only $_AND_ cells, run extract_reduce, map the remaining +parts of the design to AND/OR/XOR cells, and run extract_reduce a second time. + + -allow-off-chain + Allows matching of cells that have loads outside the chain. These cells + will be replicated and folded into the $reduce_* cell, but the original + cell will remain, driving its original loads. +\end{lstlisting} + \section{flatten -- flatten design} \label{cmd:flatten} \begin{lstlisting}[numbers=left,frame=single] @@ -1155,21 +1364,12 @@ one-hot encoding and binary encoding is supported. .map \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} +\section{greenpak4\_dffinv -- merge greenpak4 inverters and DFF/latches} \label{cmd:greenpak4_dffinv} \begin{lstlisting}[numbers=left,frame=single] greenpak4_dffinv [options] [selection] -Merge GP_INV cells with GP_DFF* cells. +Merge GP_INV cells with GP_DFF* and GP_DLATCH* cells. \end{lstlisting} \section{help -- display help messages} @@ -1199,6 +1399,10 @@ needed. also check the design hierarchy. this generates an error when an unknown module is used as cell type. + -simcheck + like -check, but also thow an error if blackbox modules are + instantiated, and throw an error if the design has no top module + -purge_lib by default the hierarchy command will not remove library (blackbox) modules. use this option to also remove unused blackbox modules. @@ -1212,6 +1416,11 @@ needed. per default this pass also converts positional arguments in cells to arguments using port names. this option disables this behavior. + -keep_portwidths + per default this pass adjusts the port width on cells that are + module instances when the width does not match the module port. 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 @@ -1416,6 +1625,18 @@ 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{ltp -- print longest topological path} +\label{cmd:ltp} +\begin{lstlisting}[numbers=left,frame=single] + ltp [options] [selection] + +This command prints the longest topological path in the design. (Only considers +paths within a single module, so the design must be flattened.) + + -noff + automatically exclude FF cell types +\end{lstlisting} + \section{lut2mux -- convert \$lut to \$\_MUX\_} \label{cmd:lut2mux} \begin{lstlisting}[numbers=left,frame=single] @@ -1582,6 +1803,15 @@ This pass adds additional circuitry that emulates the Verilog simulation behavior for out-of-bounds memory reads and writes. \end{lstlisting} +\section{memory\_nordff -- extract read port FFs from memories} +\label{cmd:memory_nordff} +\begin{lstlisting}[numbers=left,frame=single] + memory_nordff [options] [selection] + +This pass extracts FFs from memory read ports. This results in a netlist +similar to what one would get from calling memory_dff with -nordff. +\end{lstlisting} + \section{memory\_share -- consolidate memory ports} \label{cmd:memory_share} \begin{lstlisting}[numbers=left,frame=single] @@ -1745,6 +1975,15 @@ This pass only operates on completely selected modules without processes. also remove internal nets if they have a public name \end{lstlisting} +\section{opt\_demorgan -- Optimize reductions with DeMorgan equivalents} +\label{cmd:opt_demorgan} +\begin{lstlisting}[numbers=left,frame=single] + opt_demorgan [selection] + +This pass pushes inverters through $reduce_* cells if this will reduce the +overall gate count of the circuit +\end{lstlisting} + \section{opt\_expr -- perform const folding and simple expression rewriting} \label{cmd:opt_expr} \begin{lstlisting}[numbers=left,frame=single] @@ -1884,15 +2123,16 @@ on partly selected designs. -memx simulate verilog simulation behavior for out-of-bounds memory accesses - using the 'memory_memx' pass. This option implies -nordff. + using the 'memory_memx' pass. -nomem do not run any of the memory_* passes - -nordff - passed to 'memory_dff'. prohibits merging of FFs into memory read ports + -rdff + do not pass -nordff to 'memory_dff'. This enables merging of FFs into + memory read ports. - -nokeepdc + -nokeepdc do not call opt_* with -keepdc -run [:] @@ -2058,6 +2298,38 @@ 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 -- load HDL designs} +\label{cmd:read} +\begin{lstlisting}[numbers=left,frame=single] + read {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv|-formal} .. + +Load the specified Verilog/SystemVerilog files. (Full SystemVerilog support +is only available via Verific.) + +Additional -D[=] options may be added after the option indicating +the language version (and before file names) to set additional verilog defines. + + + read {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} .. + +Load the specified VHDL files. (Requires Verific.) + + + read -define [=].. + +Set global Verilog/SystemVerilog defines. + + + read -undef .. + +Unset global Verilog/SystemVerilog defines. + + + read -incdir + +Add directory to global Verilog/SystemVerilog include directories. +\end{lstlisting} + \section{read\_blif -- read BLIF file} \label{cmd:read_blif} \begin{lstlisting}[numbers=left,frame=single] @@ -2067,6 +2339,10 @@ Load modules from a BLIF file into the current design. -sop Create $sop cells instead of $lut cells + + -wideports + Merge ports that match the pattern 'name[int]' into a single + multi-bit port 'name'. \end{lstlisting} \section{read\_ilang -- read modules from ilang file} @@ -2078,6 +2354,15 @@ 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\_json -- read JSON file} +\label{cmd:read_json} +\begin{lstlisting}[numbers=left,frame=single] + read_json [filename] + +Load modules from a JSON file into the current design See "help write_json" +for a description of the file format. +\end{lstlisting} + \section{read\_liberty -- read cells from liberty file} \label{cmd:read_liberty} \begin{lstlisting}[numbers=left,frame=single] @@ -2088,9 +2373,13 @@ Read cells from liberty file as modules into current design. -lib only create empty blackbox modules - -ignore_redef + -nooverwrite ignore re-definitions of modules. (the default behavior is to - create an error message.) + create an error message if the existing module is not a blackbox + module, and overwrite the existing module if it is a blackbox module.) + + -overwrite + overwrite existing modules with the same name -ignore_miss_func ignore cells with missing function specification of outputs @@ -2099,6 +2388,9 @@ Read cells from liberty file as modules into current design. ignore cells with a missing or invalid direction specification on a pin + -ignore_miss_data_latch + ignore latches with missing data and/or enable pins + -setattr set the specified attribute (to the value 1) on all loaded modules \end{lstlisting} @@ -2131,6 +2423,9 @@ Verilog-2005 is supported. -dump_ast2 dump abstract syntax tree (after simplification) + -no_dump_ptr + do not include hex memory addresses in dump (easier to diff dumps) + -dump_vlog dump ast as Verilog code (after simplification) @@ -2190,9 +2485,13 @@ Verilog-2005 is supported. -icells interpret cell types starting with '$' as internal cell types - -ignore_redef + -nooverwrite ignore re-definitions of modules. (the default behavior is to - create an error message.) + create an error message if the existing module is not a black box + module, and overwrite the existing module otherwise.) + + -overwrite + overwrite existing modules with the same name -defer only read the abstract syntax tree and defer actual compilation @@ -2221,6 +2520,10 @@ 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. +Depending on if read_verilog is run in -formal mode, either the macro +SYNTHESIS or FORMAL is defined automatically. In addition, read_verilog +always defines the macro YOSYS. + See the Yosys README file for a list of non-standard Verilog features supported by the Yosys Verilog front-end. \end{lstlisting} @@ -2251,6 +2554,15 @@ with public names. This ignores all selected ports. Rename top module. \end{lstlisting} +\section{rmports -- remove module ports with no connections} +\label{cmd:rmports} +\begin{lstlisting}[numbers=left,frame=single] + rmports [selection] + +This pass identifies ports in the selected modules which are not used or +driven and removes them. +\end{lstlisting} + \section{sat -- solve a SAT problem in the circuit} \label{cmd:sat} \begin{lstlisting}[numbers=left,frame=single] @@ -2457,11 +2769,9 @@ design. are assumed to be bidirectional 'inout' ports. -set_attr - -set_cell_attr - -set_wire_attr - 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. + set the specified attribute on all cells 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 @@ -2497,7 +2807,7 @@ 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 +used to YS_OVERRIDE the global selection for the command. The syntax of this optional argument is identical to the syntax of the argument described here. @@ -2728,17 +3038,29 @@ The -type option can be used to change the cell type of the selected cells. \begin{lstlisting}[numbers=left,frame=single] setundef [options] [selection] -This command replaced undef (x) constants with defined (0/1) constants. +This command replaces undef (x) constants with defined (0/1) constants. -undriven also set undriven nets to constant values + -expose + also expose undriven nets as inputs (use with -undriven) + -zero replace with bits cleared (0) -one replace with bits set (1) + -undef + replace with undef (x) bits, may be used with -undriven + + -anyseq + replace with $anyseq drivers (for formal) + + -anyconst + replace with $anyconst drivers (for formal) + -random replace with random bits using the specified integer als seed value for the random number generator. @@ -2821,6 +3143,7 @@ to a graphics file (usually SVG or PostScript). -viewer Run the specified command with the graphics file as parameter. + On Windows, this pauses yosys until the viewer exits. -format Generate a graphics file in the specified format. Use 'dot' to just @@ -2882,7 +3205,7 @@ to a graphics file (usually SVG or PostScript). do not add the module name as graph title to the dot file When no is specified, 'dot' is used. When no and is -specified, 'xdot' is used to display the schematic. +specified, 'xdot' is used to display the schematic (POSIX systems only). The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.', unless another prefix is specified using -prefix . @@ -2949,6 +3272,47 @@ will use the same interface as the original $_DFF_*_ cells. The cell parameter map to greenpak4 shift registers. \end{lstlisting} +\section{sim -- simulate the circuit} +\label{cmd:sim} +\begin{lstlisting}[numbers=left,frame=single] + sim [options] [top-level] + +This command simulates the circuit using the given top-level module. + + -vcd + write the simulation results to the given VCD file + + -clock + name of top-level clock input + + -clockn + name of top-level clock input (inverse polarity) + + -reset + name of top-level reset input (active high) + + -resetn + name of top-level inverted reset input (active low) + + -rstlen + number of cycles reset should stay active (default: 1) + + -zinit + zero-initialize all uninitialized regs and memories + + -n + number of cycles to simulate (default: 20) + + -a + include all nets in VCD output, not just those with public names + + -w + writeback mode: use final simulation state as new init state + + -d + enable debug output +\end{lstlisting} + \section{simplemap -- mapping simple coarse-grain cells} \label{cmd:simplemap} \begin{lstlisting}[numbers=left,frame=single] @@ -2963,22 +3327,6 @@ primitives. The following internal cell types are mapped by this pass: $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] @@ -3122,6 +3470,9 @@ on partly selected designs. -nordff passed to 'memory'. prohibits merging of FFs into memory read ports + -noshare + do not run SAT-based resource sharing + -run [:] only run the commands between the labels (see below). an empty from label is synonymous to 'begin', and empty to label is @@ -3164,6 +3515,344 @@ The following commands are executed by this synthesis command: check \end{lstlisting} +\section{synth\_achronix -- synthesis for Acrhonix Speedster22i FPGAs.} +\label{cmd:synth_achronix} +\begin{lstlisting}[numbers=left,frame=single] + synth_achronix [options] + +This command runs synthesis for Achronix Speedster eFPGAs. This work is still experimental. + + -top + use the specified module as top module (default='top') + + -vout + write the design to the specified Verilog netlist file. writing of an + output file is omitted if this parameter is not specified. + + -run : + 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 -sv -lib +/achronix/speedster22i/cells_sim.v + hierarchy -check -top + + flatten: (unless -noflatten) + proc + flatten + tribuf -logic + deminout + + coarse: + synth -run coarse + + fine: + opt -fast -mux_undef -undriven -fine -full + memory_map + opt -undriven -fine + dffsr2dff + dff2dffe -direct-match $_DFF_* + opt -fine + techmap -map +/techmap.v + opt -full + clean -purge + setundef -undriven -zero + abc -markgroups -dff (only if -retime) + + map_luts: + abc -lut 4 + clean + + map_cells: + iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I + techmap -map +/achronix/speedster22i/cells_map.v + clean -purge + + check: + hierarchy -check + stat + check -noinit + + vout: + write_verilog -nodec -attr2comment -defparam -renameprefix syn_ +\end{lstlisting} + +\section{synth\_coolrunner2 -- synthesis for Xilinx Coolrunner-II CPLDs} +\label{cmd:synth_coolrunner2} +\begin{lstlisting}[numbers=left,frame=single] + synth_coolrunner2 [options] + +This command runs synthesis for Coolrunner-II CPLDs. This work is experimental. +It is intended to be used with https://github.com/azonenberg/openfpga as the +place-and-route. + + -top + use the specified module as top module (default='top') + + -json + write the design to the specified JSON file. writing of an output file + is omitted if this parameter is not specified. + + -run : + 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 +/coolrunner2/cells_sim.v + hierarchy -check -top + + flatten: (unless -noflatten) + proc + flatten + tribuf -logic + + coarse: + synth -run coarse + + fine: + opt -fast -full + techmap + techmap -map +/coolrunner2/cells_latch.v + dfflibmap -prepare -liberty +/coolrunner2/xc2_dff.lib + + map_tff: + abc -g AND,XOR + clean + extract -map +/coolrunner2/tff_extract.v + + map_pla: + abc -sop -I 40 -P 56 + clean + + map_cells: + dfflibmap -liberty +/coolrunner2/xc2_dff.lib + dffinit -ff FDCP Q INIT + dffinit -ff FDCP_N Q INIT + dffinit -ff FTCP Q INIT + dffinit -ff FTCP_N Q INIT + dffinit -ff LDCP Q INIT + dffinit -ff LDCP_N Q INIT + coolrunner2_sop + iopadmap -bits -inpad IBUF O:I -outpad IOBUFE I:IO -inoutpad IOBUFE O:IO -toutpad IOBUFE E:I:IO -tinoutpad IOBUFE E:O:I:IO + attrmvcp -attr src -attr LOC t:IOBUFE n:* + attrmvcp -attr src -attr LOC -driven t:IBUF n:* + splitnets + clean + + check: + hierarchy -check + stat + check -noinit + + json: + write_json +\end{lstlisting} + +\section{synth\_easic -- synthesis for eASIC platform} +\label{cmd:synth_easic} +\begin{lstlisting}[numbers=left,frame=single] + synth_easic [options] + +This command runs synthesis for eASIC platform. + + -top + use the specified module as top module + + -vlog + write the design to the specified structural Verilog file. writing of + an output file is omitted if this parameter is not specified. + + -etools + set path to the eTools installation. (default=/opt/eTools) + + -run : + 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_liberty -lib + read_liberty -lib + hierarchy -check -top + + flatten: (unless -noflatten) + proc + flatten + + coarse: + synth -run coarse + + fine: + opt -fast -mux_undef -undriven -fine + memory_map + opt -undriven -fine + techmap + opt -fast + abc -dff (only if -retime) + opt_clean (only if -retime) + + map: + dfflibmap -liberty + abc -liberty + opt_clean + + check: + hierarchy -check + stat + check -noinit + + vlog: + write_verilog -noexpr -attr2comment +\end{lstlisting} + +\section{synth\_ecp5 -- synthesis for ECP5 FPGAs} +\label{cmd:synth_ecp5} +\begin{lstlisting}[numbers=left,frame=single] + synth_ecp5 [options] + +This command runs synthesis for ECP5 FPGAs. + + -top + use the specified module as top module + + -blif + write the design to the specified BLIF file. writing of an output file + is omitted if this parameter is not specified. + + -edif + write the design to the specified EDIF file. writing of an output file + is omitted if this parameter is not specified. + + -json + write the design to the specified JSON file. writing of an output file + is omitted if this parameter is not specified. + + -run : + 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 + + -noccu2 + do not use CCU2 cells in output netlist + + -nodffe + do not use flipflops with CE in output netlist + + -nobram + do not use BRAM cells in output netlist + + -nodram + do not use distributed RAM cells in output netlist + + -nomux + do not use PFU muxes to implement LUTs larger than LUT4s + + -abc2 + run two passes of 'abc' for slightly improved logic density + + -vpr + generate an output netlist (and BLIF file) suitable for VPR + (this feature is experimental and incomplete) + + +The following commands are executed by this synthesis command: + + begin: + read_verilog -lib +/ecp5/cells_sim.v + hierarchy -check -top + + flatten: (unless -noflatten) + proc + flatten + tribuf -logic + deminout + + coarse: + synth -run coarse + + bram: (skip if -nobram) + + dram: (skip if -nodram) + memory_bram -rules +/ecp5/dram.txt + techmap -map +/ecp5/drams_map.v + + fine: + opt -fast -mux_undef -undriven -fine + memory_map + opt -undriven -fine + techmap -map +/techmap.v -map +/ecp5/arith_map.v + abc -dff (only if -retime) + + map_ffs: + dffsr2dff + dff2dffs + opt_clean + dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_* + techmap -D NO_LUT -map +/ecp5/cells_map.v + opt_expr -mux_undef + simplemap + + map_luts: + abc (only if -abc2) + abc -lut 4:7 + clean + + map_cells: + techmap -map +/ecp5/cells_map.v (with -D NO_LUT in vpr mode) + clean + + check: + hierarchy -check + stat + check -noinit + + blif: + opt_clean -purge (vpr mode) + write_blif -attr -cname -conn -param (vpr mode) + write_blif -gates -attr -param (non-vpr mode) + + edif: + write_edif + + json: + write_json +\end{lstlisting} + \section{synth\_gowin -- synthesis for Gowin FPGAs} \label{cmd:synth_gowin} \begin{lstlisting}[numbers=left,frame=single] @@ -3228,7 +3917,7 @@ The following commands are executed by this synthesis command: check -noinit vout: - write_verilog -attr2comment -defparam -renameprefix gen + write_verilog -nodec -attr2comment -defparam -renameprefix gen \end{lstlisting} \section{synth\_greenpak4 -- synthesis for GreenPAK4 FPGAs} @@ -3237,6 +3926,8 @@ The following commands are executed by this synthesis command: synth_greenpak4 [options] This command runs synthesis for GreenPAK4 FPGAs. This work is experimental. +It is intended to be used with https://github.com/azonenberg/openfpga as the +place-and-route. -top use the specified module as top module (default='top') @@ -3276,12 +3967,13 @@ The following commands are executed by this synthesis command: synth -run coarse fine: - greenpak4_counters + extract_counter -pout GP_DCMP,GP_DAC -maxwidth 14 clean opt -fast -mux_undef -undriven -fine memory_map opt -undriven -fine techmap + techmap -map +/greenpak4/cells_latch.v dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib opt -fast abc -dff (only if -retime) @@ -3323,14 +4015,18 @@ The following commands are executed by this synthesis command: This command runs synthesis for iCE40 FPGAs. -top - use the specified module as top module (default='top') + use the specified module as top module -blif write the design to the specified BLIF file. writing of an output file is omitted if this parameter is not specified. -edif - write the design to the specified edif file. writing of an output file + write the design to the specified EDIF file. writing of an output file + is omitted if this parameter is not specified. + + -json + write the design to the specified JSON file. writing of an output file is omitted if this parameter is not specified. -run : @@ -3347,12 +4043,19 @@ This command runs synthesis for iCE40 FPGAs. -nocarry do not use SB_CARRY cells in output netlist + -nodffe + do not use SB_DFFE* 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 + -vpr + generate an output netlist (and BLIF file) suitable for VPR + (this feature is experimental and incomplete) + The following commands are executed by this synthesis command: @@ -3384,7 +4087,7 @@ The following commands are executed by this synthesis command: map_ffs: dffsr2dff dff2dffe -direct-match $_DFF_* - techmap -map +/ice40/cells_map.v + techmap -D NO_LUT -map +/ice40/cells_map.v opt_expr -mux_undef simplemap ice40_ffinit @@ -3399,7 +4102,7 @@ The following commands are executed by this synthesis command: clean map_cells: - techmap -map +/ice40/cells_map.v + techmap -map +/ice40/cells_map.v (with -D NO_LUT in vpr mode) clean check: @@ -3408,10 +4111,116 @@ The following commands are executed by this synthesis command: check -noinit blif: - write_blif -gates -attr -param + opt_clean -purge (vpr mode) + write_blif -attr -cname -conn -param (vpr mode) + write_blif -gates -attr -param (non-vpr mode) edif: write_edif + + json: + write_json +\end{lstlisting} + +\section{synth\_intel -- synthesis for Intel (Altera) FPGAs.} +\label{cmd:synth_intel} +\begin{lstlisting}[numbers=left,frame=single] + synth_intel [options] + +This command runs synthesis for Intel FPGAs. + + -family < max10 | a10gx | cyclone10 | cyclonev | cycloneiv | cycloneive> + generate the synthesis netlist for the specified family. + MAX10 is the default target if not family argument specified. + For Cyclone GX devices, use cycloneiv argument; For Cyclone E, use cycloneive. + Cyclone V and Arria 10 GX devices are experimental, use it with a10gx argument. + + -top + use the specified module as top module (default='top') + + -vqm + write the design to the specified Verilog Quartus Mapping File. Writing of an + output file is omitted if this parameter is not specified. + + -vpr + write BLIF files for VPR flow experiments. The synthesized BLIF output file is not + compatible with the Quartus flow. Writing of an + output file is omitted if this parameter is not specified. + + -run : + 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. + + -noiopads + do not use altsyncram cells in output netlist + + -nobram + do not use altsyncram cells in output netlist + + -noflatten + do not flatten design before synthesis + + -retime + run 'abc' with -dff option + +The following commands are executed by this synthesis command: + + begin: + + family: + read_verilog -sv -lib +/intel/max10/cells_sim.v + read_verilog -sv -lib +/intel/common/m9k_bb.v + read_verilog -sv -lib +/intel/common/altpll_bb.v + hierarchy -check -top + + flatten: (unless -noflatten) + proc + flatten + tribuf -logic + deminout + + coarse: + synth -run coarse + + bram: (skip if -nobram) + memory_bram -rules +/intel/common/brams.txt + techmap -map +/intel/common/brams_map.v + + fine: + opt -fast -mux_undef -undriven -fine -full + memory_map + opt -undriven -fine + dffsr2dff + dff2dffe -direct-match $_DFF_* + opt -fine + techmap -map +/techmap.v + opt -full + clean -purge + setundef -undriven -zero + abc -markgroups -dff (only if -retime) + + map_luts: + abc -lut 4 + clean + + map_cells: + iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I (unless -noiopads) + techmap -map +/intel/max10/cells_map.v + dffinit -highlow -ff dffeas q power_up + clean -purge + + check: + hierarchy -check + stat + check -noinit + + vqm: + write_verilog -attr2comment -defparam -nohex -decimal -renameprefix syn_ + + vpr: + opt_clean -purge + write_blif \end{lstlisting} \section{synth\_xilinx -- synthesis for Xilinx FPGAs} @@ -3430,6 +4239,14 @@ compatible with 7-Series Xilinx devices. write the design to the specified edif file. writing of an output file is omitted if this parameter is not specified. + -blif + write the design to the specified BLIF file. writing of an output file + is omitted if this parameter is not specified. + + -vpr + generate an output netlist (and BLIF file) suitable for VPR + (this feature is experimental and incomplete) + -run : only run the commands between the labels (see below). an empty from label is synonymous to 'begin', and empty to label is @@ -3448,7 +4265,6 @@ The following commands are executed by this synthesis command: 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 flatten: (only if -flatten) @@ -3480,7 +4296,7 @@ The following commands are executed by this synthesis command: clean map_cells: - techmap -map +/xilinx/cells_map.v + techmap -map +/xilinx/cells_map.v (with -D NO_LUT in vpr mode) dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT clean @@ -3491,6 +4307,9 @@ The following commands are executed by this synthesis command: edif: (only if -edif) write_edif + + blif: (only if -blif) + write_blif \end{lstlisting} \section{tcl -- execute a TCL script file} @@ -3502,9 +4321,9 @@ 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'. +commands directly as tcl commands to the tcl shell. Yosys commands +'proc' and 'rename' are wrapped to tcl commands 'procs' and 'renames' +in order to avoid a name collision with the built in commands. \end{lstlisting} \section{techmap -- generic technology mapper} @@ -3548,7 +4367,7 @@ file. -D , -I 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. + '-nooverwrite' 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 @@ -3629,7 +4448,7 @@ 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. +and attributes of the cell that is being replaced. See 'help extract' for a pass that does the opposite thing. @@ -3795,24 +4614,134 @@ This pass transforms $mux cells with 'z' inputs to tristate buffers. to non-tristate logic. this option implies -merge. \end{lstlisting} +\section{uniquify -- create unique copies of modules} +\label{cmd:uniquify} +\begin{lstlisting}[numbers=left,frame=single] + uniquify [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 unique +modules for all selected cells. The created modules are marked with the +'unique' attribute. + +This commands only operates on modules that by themself have the 'unique' +attribute set (the 'top' module is unique implicitly). +\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} .. + verific {-vlog95|-vlog2k|-sv2005|-sv2009|-sv2012|-sv} .. Load the specified Verilog/SystemVerilog files into Verific. +All files specified in one call to this command are one compilation unit. +Files passed to different calls to this command are treated as belonging to +different compilation units. - verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008} .. +Additional -D[=] options may be added after the option indicating +the language version (and before file names) to set additional verilog defines. +The macros SYNTHESIS and VERIFIC are defined implicitly. + + + verific -formal .. + +Like -sv, but define FORMAL instead of SYNTHESIS. + + + verific {-vhdl87|-vhdl93|-vhdl2k|-vhdl2008|-vhdl} .. Load the specified VHDL files into Verific. - verific -import [-gates] {-all | ..} + verific -work {-sv|-vhdl|...} + +Load the specified Verilog/SystemVerilog/VHDL file into the specified library. +(default library when -work is not present: "work") + + + verific -vlog-incdir .. + +Add Verilog include directories. + + + verific -vlog-libdir .. + +Add Verilog library directories. Verific will search in this directories to +find undefined modules. + + + verific -vlog-define [=].. + +Add Verilog defines. + + + verific -vlog-undef .. + +Remove Verilog defines previously set with -vlog-define. + + + verific -set-error .. + verific -set-warning .. + verific -set-info .. + verific -set-ignore .. + +Set message severity. is the string in square brackets when a message +is printed, such as VERI-1209. + + + verific -import [options] .. 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. +reset the internal state of Verific. + +Import options: + + -all + Elaborate all modules, not just the hierarchy below the given top + modules. With this option the list of modules to import is optional. + + -gates + Create a gate-level netlist. + + -flatten + Flatten the design in Verific before importing. + + -extnets + Resolve references to external nets by adding module ports as needed. + + -autocover + Generate automatic cover statements for all asserts + + -v, -vv + Verbose log messages. (-vv is even more verbose than -v.) + +The following additional import options are useful for debugging the Verific +bindings (for Yosys and/or Verific developers): + + -k + Keep going after an unsupported verific primitive is found. The + unsupported primitive is added as blockbox module to the design. + This will also add all SVA related cells to the design parallel to + the checker logic inferred by it. + + -V + Import Verific netlist as-is without translating to Yosys cell types. + + -nosva + Ignore SVA properties, do not infer checker logic. + + -L + Maximum number of ctrl bits for SVA checker FSMs (default=16). + + -n + Keep all Verific names on instances and nets. By default only + user-declared names are preserved. + + -d + Dump the Verific netlist as a verilog file. Visit http://verific.com/ for more information on Verific. \end{lstlisting} @@ -3837,40 +4766,19 @@ 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} +\section{verilog\_defines -- define and undefine verilog defines} +\label{cmd:verilog_defines} \begin{lstlisting}[numbers=left,frame=single] - vhdl2verilog [options] .. + verilog_defines [options] -This command reads VHDL source files using the 'vhdl2verilog' tool and the -Yosys Verilog frontend. +Define and undefine verilog preprocessor macros. - -out - do not import the vhdl2verilog output. instead write it to the - specified file. + -Dname[=definition] + define the preprocessor symbol 'name' and set its optional value + 'definition' - -vhdl2verilog_dir - 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 - The name of the top entity. This option is mandatory. - -The following options are passed as-is to vhdl2verilog: - - -arch - -unroll_generate - -nogenericeval - -nouniquify - -oldparser - -suppress - -quiet - -nobanner - -mapfile - -vhdl2verilog can be obtained from: -http://www.edautils.com/vhdl2verilog.html + -Uname[=definition] + undefine the preprocessor symbol 'name' \end{lstlisting} \section{wreduce -- reduce the word size of operations if possible} @@ -3892,6 +4800,38 @@ Options: flows that use the 'memory_memx' pass. \end{lstlisting} +\section{write\_aiger -- write design to AIGER file} +\label{cmd:write_aiger} +\begin{lstlisting}[numbers=left,frame=single] + write_aiger [options] [filename] + +Write the current design to an AIGER file. The design must be flattened and +must not contain any cell types except $_AND_, $_NOT_, simple FF types, +$assert and $assume cells, and $initstate cells. + +$assert and $assume cells are converted to AIGER bad state properties and +invariant constraints. + + -ascii + write ASCII version of AGIER format + + -zinit + convert FFs to zero-initialized FFs, adding additional inputs for + uninitialized FFs. + + -miter + design outputs are AIGER bad state properties + + -symbols + include a symbol table in the generated AIGER file + + -map + write an extra file with port and latch symbols + + -vmap + like -map, but more verbose +\end{lstlisting} + \section{write\_blif -- write design to BLIF file} \label{cmd:write_blif} \begin{lstlisting}[numbers=left,frame=single] @@ -3949,6 +4889,11 @@ file *.blif when any of this options is used. -cname use the non-standard .cname statement to write cell names + -iname, -iattr + enable -cname and -attr functionality for .names statements + (the .cname and .attr statements will be included in the BLIF + output after the truth table for the .names statement) + -blackbox write blackbox cells with .blackbox statement. @@ -3959,9 +4904,15 @@ file *.blif when any of this options is used. \section{write\_btor -- write design to BTOR file} \label{cmd:write_btor} \begin{lstlisting}[numbers=left,frame=single] - write_btor [filename] + write_btor [options] [filename] -Write the current design to an BTOR file. +Write a BTOR description of the current design. + + -v + Add comments and indentation to BTOR output file + + -s + Output only a single bad property for all asserts \end{lstlisting} \section{write\_edif -- write design to EDIF netlist file} @@ -3979,6 +4930,10 @@ Write the current design to an EDIF netlist file. if the design contains constant nets. use "hilomap" to map to custom constant drivers first) + -pvector {par|bra|ang} + sets the delimiting character for module port rename clauses to + parentheses, square brackets, or angle brackets. + 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 @@ -4003,6 +4958,14 @@ Inside a script the input file can also can a here-document: EOT \end{lstlisting} +\section{write\_firrtl -- write design to a FIRRTL file} +\label{cmd:write_firrtl} +\begin{lstlisting}[numbers=left,frame=single] + write_firrtl [options] [filename] + +Write a FIRRTL netlist of the current design. +\end{lstlisting} + \section{write\_ilang -- write design to ilang file} \label{cmd:write_ilang} \begin{lstlisting}[numbers=left,frame=single] @@ -4123,6 +5086,10 @@ 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. +Numeric parameter and attribute values up to 32 bits are written as decimal +values. Numbers larger than that are written as string holding the binary +representation of the value. + For example the following Verilog code: module test(input x, y); @@ -4242,43 +5209,109 @@ 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\_simplec -- convert design to simple C code} +\label{cmd:write_simplec} +\begin{lstlisting}[numbers=left,frame=single] + write_simplec [options] [filename] + +Write simple C code for simulating the design. The C code writen can be used to +simulate the design in a C environment, but the purpose of this command is to +generate code that works well with C-based formal verification. + + -verbose + this will print the recursive walk used to export the modules. + + -i8, -i16, -i32, -i64 + set the maximum integer bit width to use in the generated code. + +THIS COMMAND IS UNDER CONSTRUCTION +\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 -'' this will declare the sort '_s' (state of the module) and the -functions operating on that state. +'' this will declare the sort '_s' (state of the module) and will +define and declare functions operating on that state. -The '_s' sort represents a module state. Additional '_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 following SMT2 functions are generated for a module with name ''. +Some declarations/definitions are printed with a special comment. A prover +using the SMT2 files can use those comments to collect all relevant metadata +about the design. -The '_t' function evaluates to 'true' when the given pair of states -describes a valid state transition. + ; yosys-smt2-module + (declare-sort |_s| 0) + The sort representing a state of module . -The '_a' function evaluates to 'true' when the given state satisfies -the asserts in the module. + (define-fun |_h| ((state |_s|)) Bool (...)) + This function must be asserted for each state to establish the + design hierarchy. -The '_u' function evaluates to 'true' when the given state satisfies -the assumptions in the module. + ; yosys-smt2-input + ; yosys-smt2-output + ; yosys-smt2-register + ; yosys-smt2-wire + (define-fun |_n | (|_s|) (_ BitVec )) + (define-fun |_n | (|_s|) Bool) + For each port, register, and wire with the 'keep' attribute set an + accessor function is generated. Single-bit wires are returned as Bool, + multi-bit wires as BitVec. -The '_i' function evaluates to 'true' when the given state conforms -to the initial state. Furthermore the '_is' function should be asserted -to be true for initial states in addition to '_i', and should be -asserted to be false for non-initial states. + ; yosys-smt2-cell + (declare-fun |_h | (|_s|) |_s|) + There is a function like that for each hierarchical instance. It + returns the sort that represents the state of the sub-module that + implements the instance. -For hierarchical designs, the '_h' function must be asserted for each -state to establish the design hierarchy. The '_h ' function -evaluates to the state corresponding to the given cell within . + (declare-fun |_is| (|_s|) Bool) + This function must be asserted 'true' for initial states, and 'false' + otherwise. + + (define-fun |_i| ((state |_s|)) Bool (...)) + This function must be asserted 'true' for initial states. For + non-initial states it must be left unconstrained. + + (define-fun |_t| ((state |_s|) (next_state |_s|)) Bool (...)) + This function evaluates to 'true' if the states 'state' and + 'next_state' form a valid state transition. + + (define-fun |_a| ((state |_s|)) Bool (...)) + This function evaluates to 'true' if all assertions hold in the state. + + (define-fun |_u| ((state |_s|)) Bool (...)) + This function evaluates to 'true' if all assumptions hold in the state. + + ; yosys-smt2-assert + (define-fun |_a | ((state |_s|)) Bool (...)) + Each $assert cell is converted into one of this functions. The function + evaluates to 'true' if the assert statement holds in the state. + + ; yosys-smt2-assume + (define-fun |_u | ((state |_s|)) Bool (...)) + Each $assume cell is converted into one of this functions. The function + evaluates to 'true' if the assume statement holds in the state. + + ; yosys-smt2-cover + (define-fun |_c | ((state |_s|)) Bool (...)) + Each $cover cell is converted into one of this functions. The function + evaluates to 'true' if the cover statement is activated in the state. + +Options: -verbose this will print the recursive walk used to export the modules. + -stbv + Use a BitVec sort to represent a state instead of an uninterpreted + sort. As a side-effect this will prevent use of arrays to model + memories. + + -stdt + Use SMT-LIB 2.6 style datatypes to represent a state instead of an + uninterpreted sort. + -nobv disable support for BitVec (FixedSizeBitVectors theory). without this option multi-bit wires are represented using the BitVec sort and @@ -4394,6 +5427,25 @@ Write the current design to an SPICE netlist file. set the specified module as design top module \end{lstlisting} +\section{write\_table -- write design as connectivity table} +\label{cmd:write_table} +\begin{lstlisting}[numbers=left,frame=single] + write_table [options] [filename] + +Write the current design as connectivity table. The output is a tab-separated +ASCII table with the following columns: + + module name + cell name + cell type + cell port + direction + signal + +module inputs and outputs are output using cell type and port '-' and with +'pi' (primary input) or 'po' (primary output) or 'pio' as direction. +\end{lstlisting} + \section{write\_verilog -- write design to Verilog file} \label{cmd:write_verilog} \begin{lstlisting}[numbers=left,frame=single] @@ -4421,13 +5473,21 @@ Write the current design to a Verilog file. -nodec 32-bit constant values are by default dumped as decimal numbers, - not bit pattern. This option decativates this feature and instead + not bit pattern. This option deactivates this feature and instead will write out all constants in binary. + -decimal + dump 32-bit constants in decimal and without size and radix + + -nohex + constant values that are compatible with hex output are usually + dumped as hex values. This option deactivates 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 + deactivates this feature and instead will write string constants as binary numbers. -defparam diff --git a/yosys/misc/create_vcxsrc.sh b/yosys/misc/create_vcxsrc.sh index 215e27c53..924d2722e 100644 --- a/yosys/misc/create_vcxsrc.sh +++ b/yosys/misc/create_vcxsrc.sh @@ -5,11 +5,11 @@ 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 +rm -rf YosysVS-Tpl-v2.zip YosysVS +wget http://www.clifford.at/yosys/nogit/YosysVS-Tpl-v2.zip -unzip YosysVS-Tpl-v1.zip -rm -f YosysVS-Tpl-v1.zip +unzip YosysVS-Tpl-v2.zip +rm -f YosysVS-Tpl-v2.zip mv YosysVS "$vcxsrc" { diff --git a/yosys/misc/yosys.proto b/yosys/misc/yosys.proto new file mode 100644 index 000000000..2870176cb --- /dev/null +++ b/yosys/misc/yosys.proto @@ -0,0 +1,175 @@ +// +// yosys -- Yosys Open SYnthesis Suite +// +// Copyright (C) 2018 Serge Bazanski +// +// 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. +// + +/// Protobuf definition of Yosys RTLIL dump/restore format for RTL designs. + +syntax = "proto3"; + +package yosys.pb; + +// Port direction. +enum Direction { + DIRECTION_INVALID = 0; + DIRECTION_INPUT = 1; + DIRECTION_OUTPUT = 2; + DIRECTION_INOUT = 3; +} + +// A freeform parameter/attribute value. +message Parameter { + oneof value { + int64 int = 1; + string str = 2; + } +} + +// A signal in the design - either a unique identifier for one, or a constant +// driver (low or high). +message Signal { + // A constant signal driver in the design. + enum ConstantDriver { + CONSTANT_DRIVER_INVALID = 0; + CONSTANT_DRIVER_LOW = 1; + CONSTANT_DRIVER_HIGH = 2; + CONSTANT_DRIVER_Z = 3; + CONSTANT_DRIVER_X = 4; + } + oneof type { + // Signal uniquely identified by ID number. + int64 id = 1; + // Constant driver. + ConstantDriver constant = 2; + } +} + +// A vector of signals. +message BitVector { + repeated Signal signal = 1; +} + +// A netlist module. +message Module { + // Freeform attributes. + map attribute = 1; + + // Named ports in this module. + message Port { + Direction direction = 1; + BitVector bits = 2; + } + map port = 2; + + // Named cells in this module. + message Cell { + // Set to true when the name of this cell is automatically created and + // likely not of interest for a regular user. + bool hide_name = 1; + string type = 2; + // Set if this module has an AIG model available. + string model = 3; + // Freeform parameters. + map parameter = 4; + // Freeform attributes. + map attribute = 5; + + /// Ports of the cell. + // Direction of the port, if interface is known. + map port_direction = 6; + // Connection of named port to signal(s). + map connection = 7; + } + map cell = 3; + + // Nets in this module. + message Netname { + // Set to true when the name of this net is automatically created and + // likely not of interest for a regular user. + bool hide_name = 1; + // Signal(s) that make up this net. + BitVector bits = 2; + // Freeform attributes. + map attributes = 3; + } + repeated Netname netname = 4; +} + +// And-Inverter-Graph model. +message Model { + message Node { + // Type of AIG node - or, what its' value is. + enum Type { + TYPE_INVALID = 0; + // The node's value is the value of the specified input port bit. + TYPE_PORT = 1; + // The node's value is the inverted value of the specified input + // port bit. + TYPE_NPORT = 2; + // The node's value is the ANDed value of specified nodes. + TYPE_AND = 3; + // The node's value is the NANDed value of specified nodes. + TYPE_NAND = 4; + // The node's value is a constant 1. + TYPE_TRUE = 5; + // The node's value is a constant 0. + TYPE_FALSE = 6; + }; + Type type = 1; + + message Port { + // Name of port. + string portname = 1; + // Bit index in port. + int64 bitindex = 2; + } + message Gate { + // Node index of left side of operation. + int64 left = 1; + // Node index of right side of operation. + int64 right = 2; + } + oneof node { + // Set for PORT, NPORT + Port port = 2; + // Set for AND, NAND. + Gate gate = 3; + } + + // Set when the node drives given output port(s). + message OutPort { + // Name of port. + string name = 1; + // Bit index in port. + int64 bit_index = 2; + } + repeated OutPort out_port = 4; + } + + // List of AIG nodes - each is explicitely numbered by its' index in this + // array. + repeated Node node = 1; +} + +// A Yosys design netlist dumped from RTLIL. +message Design { + // Human-readable freeform 'remark' string. + string creator = 1; + // List of named modules in design. + map modules = 2; + // List of named AIG models in design (if AIG export enabled). + map models = 3; +} diff --git a/yosys/passes/cmds/Makefile.inc b/yosys/passes/cmds/Makefile.inc index 01ada7739..c8067a8be 100644 --- a/yosys/passes/cmds/Makefile.inc +++ b/yosys/passes/cmds/Makefile.inc @@ -25,4 +25,8 @@ OBJS += passes/cmds/plugin.o OBJS += passes/cmds/check.o OBJS += passes/cmds/qwp.o OBJS += passes/cmds/edgetypes.o - +OBJS += passes/cmds/chformal.o +OBJS += passes/cmds/chtype.o +OBJS += passes/cmds/blackbox.o +OBJS += passes/cmds/ltp.o +OBJS += passes/cmds/bugpoint.o diff --git a/yosys/passes/cmds/add.cc b/yosys/passes/cmds/add.cc index e698926f9..cfccca966 100644 --- a/yosys/passes/cmds/add.cc +++ b/yosys/passes/cmds/add.cc @@ -83,7 +83,7 @@ static void add_wire(RTLIL::Design *design, RTLIL::Module *module, std::string n struct AddPass : public Pass { AddPass() : Pass("add", "add objects to the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -106,7 +106,7 @@ struct AddPass : public Pass { log("selected modules.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string command; std::string arg_name; diff --git a/yosys/passes/cmds/blackbox.cc b/yosys/passes/cmds/blackbox.cc new file mode 100644 index 000000000..6094f8f16 --- /dev/null +++ b/yosys/passes/cmds/blackbox.cc @@ -0,0 +1,81 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 BlackboxPass : public Pass { + BlackboxPass() : Pass("blackbox", "change type of cells in the design") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" blackbox [options] [selection]\n"); + log("\n"); + log("Convert modules into blackbox modules (remove contents and set the blackbox\n"); + log("module attribute).\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-???") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_whole_modules_warn()) + { + pool remove_cells; + pool remove_wires; + + for (auto cell : module->cells()) + remove_cells.insert(cell); + + for (auto wire : module->wires()) + if (wire->port_id == 0) + remove_wires.insert(wire); + + for (auto it = module->memories.begin(); it != module->memories.end(); ++it) + delete it->second; + module->memories.clear(); + + for (auto it = module->processes.begin(); it != module->processes.end(); ++it) + delete it->second; + module->processes.clear(); + + module->new_connections(std::vector()); + + for (auto cell : remove_cells) + module->remove(cell); + + module->remove(remove_wires); + + module->set_bool_attribute("\\blackbox"); + } + } +} BlackboxPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/cmds/bugpoint.cc b/yosys/passes/cmds/bugpoint.cc new file mode 100644 index 000000000..606276e64 --- /dev/null +++ b/yosys/passes/cmds/bugpoint.cc @@ -0,0 +1,369 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "backends/ilang/ilang_backend.h" + +USING_YOSYS_NAMESPACE +using namespace ILANG_BACKEND; +PRIVATE_NAMESPACE_BEGIN + +struct BugpointPass : public Pass { + BugpointPass() : Pass("bugpoint", "minimize testcases") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" bugpoint [options]\n"); + log("\n"); + log("This command minimizes testcases that crash Yosys. It removes an arbitrary part\n"); + log("of the design and recursively invokes Yosys with a given script, repeating these\n"); + log("steps while it can find a smaller design that still causes a crash. Once this\n"); + log("command finishes, it replaces the current design with the smallest testcase it\n"); + log("was able to produce.\n"); + log("\n"); + log("It is possible to specify the kinds of design part that will be removed. If none\n"); + log("are specified, all parts of design will be removed.\n"); + log("\n"); + log(" -yosys \n"); + log(" use this Yosys binary. if not specified, `yosys` is used.\n"); + log("\n"); + log(" -script \n"); + log(" use this script to crash Yosys. required.\n"); + log("\n"); + log(" -grep \n"); + log(" only consider crashes that place this string in the log file.\n"); + log("\n"); + log(" -fast\n"); + log(" run `clean -purge` after each minimization step. converges faster, but\n"); + log(" produces larger testcases, and may fail to produce any testcase at all if\n"); + log(" the crash is related to dangling wires.\n"); + log("\n"); + log(" -clean\n"); + log(" run `clean -purge` before checking testcase and after finishing. produces\n"); + log(" smaller and more useful testcases, but may fail to produce any testcase\n"); + log(" at all if the crash is related to dangling wires.\n"); + log("\n"); + log(" -modules\n"); + log(" try to remove modules.\n"); + log("\n"); + log(" -ports\n"); + log(" try to remove module ports.\n"); + log("\n"); + log(" -cells\n"); + log(" try to remove cells.\n"); + log("\n"); + log(" -connections\n"); + log(" try to reconnect ports to 'x.\n"); + log("\n"); + } + + bool run_yosys(RTLIL::Design *design, string yosys_cmd, string script) + { + design->sort(); + + std::ofstream f("bugpoint-case.il"); + ILANG_BACKEND::dump_design(f, design, /*only_selected=*/false, /*flag_m=*/true, /*flag_n=*/false); + f.close(); + + string yosys_cmdline = stringf("%s -qq -L bugpoint-case.log -s %s bugpoint-case.il", yosys_cmd.c_str(), script.c_str()); + return run_command(yosys_cmdline) == 0; + } + + bool check_logfile(string grep) + { + if (grep.empty()) + return true; + + std::ifstream f("bugpoint-case.log"); + while (!f.eof()) + { + string line; + getline(f, line); + if (line.find(grep) != std::string::npos) + return true; + } + return false; + } + + RTLIL::Design *clean_design(RTLIL::Design *design, bool do_clean = true, bool do_delete = false) + { + if (!do_clean) + return design; + + RTLIL::Design *design_copy = new RTLIL::Design; + for (auto &it : design->modules_) + design_copy->add(it.second->clone()); + Pass::call(design_copy, "clean -purge"); + + if (do_delete) + delete design; + return design_copy; + } + + RTLIL::Design *simplify_something(RTLIL::Design *design, int &seed, bool stage2, bool modules, bool ports, bool cells, bool connections) + { + RTLIL::Design *design_copy = new RTLIL::Design; + for (auto &it : design->modules_) + design_copy->add(it.second->clone()); + + int index = 0; + if (modules) + { + for (auto &it : design_copy->modules_) + { + if (it.second->get_bool_attribute("\\blackbox")) + continue; + + if (index++ == seed) + { + log("Trying to remove module %s.\n", it.first.c_str()); + design_copy->remove(it.second); + return design_copy; + } + } + } + if (ports) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_bool_attribute("\\blackbox")) + continue; + + for (auto wire : mod->wires()) + { + if (!stage2 && wire->get_bool_attribute("$bugpoint")) + continue; + + if (wire->port_input || wire->port_output) + { + if (index++ == seed) + { + log("Trying to remove module port %s.\n", log_signal(wire)); + wire->port_input = wire->port_output = false; + mod->fixup_ports(); + return design_copy; + } + } + } + } + } + if (cells) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_bool_attribute("\\blackbox")) + continue; + + for (auto &it : mod->cells_) + { + if (index++ == seed) + { + log("Trying to remove cell %s.%s.\n", mod->name.c_str(), it.first.c_str()); + mod->remove(it.second); + return design_copy; + } + } + } + } + if (connections) + { + for (auto mod : design_copy->modules()) + { + if (mod->get_bool_attribute("\\blackbox")) + continue; + + for (auto cell : mod->cells()) + { + for (auto it : cell->connections_) + { + RTLIL::SigSpec port = cell->getPort(it.first); + bool is_undef = port.is_fully_undef(); + bool is_port = port.is_wire() && (port.as_wire()->port_input || port.as_wire()->port_output); + + if(is_undef || (!stage2 && is_port)) + continue; + + if (index++ == seed) + { + log("Trying to remove cell port %s.%s.%s.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str()); + RTLIL::SigSpec port_x(State::Sx, port.size()); + cell->unsetPort(it.first); + cell->setPort(it.first, port_x); + return design_copy; + } + + if (!stage2 && (cell->input(it.first) || cell->output(it.first)) && index++ == seed) + { + log("Trying to expose cell port %s.%s.%s as module port.\n", mod->name.c_str(), cell->name.c_str(), it.first.c_str()); + RTLIL::Wire *wire = mod->addWire(NEW_ID, port.size()); + wire->set_bool_attribute("$bugpoint"); + wire->port_input = cell->input(it.first); + wire->port_output = cell->output(it.first); + cell->unsetPort(it.first); + cell->setPort(it.first, wire); + mod->fixup_ports(); + return design_copy; + } + } + } + } + } + return NULL; + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + string yosys_cmd = "yosys", script, grep; + bool fast = false, clean = false; + bool modules = false, ports = false, cells = false, connections = false, has_part = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-yosys" && argidx + 1 < args.size()) { + yosys_cmd = args[++argidx]; + continue; + } + if (args[argidx] == "-script" && argidx + 1 < args.size()) { + script = args[++argidx]; + continue; + } + if (args[argidx] == "-grep" && argidx + 1 < args.size()) { + grep = args[++argidx]; + continue; + } + if (args[argidx] == "-fast") { + fast = true; + continue; + } + if (args[argidx] == "-clean") { + clean = true; + continue; + } + if (args[argidx] == "-modules") { + modules = true; + has_part = true; + continue; + } + if (args[argidx] == "-ports") { + ports = true; + has_part = true; + continue; + } + if (args[argidx] == "-cells") { + cells = true; + has_part = true; + continue; + } + if (args[argidx] == "-connections") { + connections = true; + has_part = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!has_part) + { + modules = true; + ports = true; + cells = true; + connections = true; + } + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + RTLIL::Design *crashing_design = clean_design(design, clean); + if (run_yosys(crashing_design, yosys_cmd, script)) + log_cmd_error("The provided script file and Yosys binary do not crash on this design!\n"); + if (!check_logfile(grep)) + log_cmd_error("The provided grep string is not found in the log file!\n"); + + int seed = 0, crashing_seed = seed; + bool found_something = false, stage2 = false; + while (true) + { + if (RTLIL::Design *simplified = simplify_something(crashing_design, seed, stage2, modules, ports, cells, connections)) + { + simplified = clean_design(simplified, fast, /*do_delete=*/true); + + bool crashes; + if (clean) + { + RTLIL::Design *testcase = clean_design(simplified); + crashes = !run_yosys(testcase, yosys_cmd, script); + delete testcase; + } + else + { + crashes = !run_yosys(simplified, yosys_cmd, script); + } + + if (crashes && check_logfile(grep)) + { + log("Testcase crashes.\n"); + if (crashing_design != design) + delete crashing_design; + crashing_design = simplified; + crashing_seed = seed; + found_something = true; + } + else + { + log("Testcase does not crash.\n"); + delete simplified; + seed++; + } + } + else + { + seed = 0; + if (found_something) + found_something = false; + else + { + if (!stage2) + { + log("Demoting introduced module ports.\n"); + stage2 = true; + } + else + { + log("Simplifications exhausted.\n"); + break; + } + } + } + } + + if (crashing_design != design) + { + Pass::call(design, "design -reset"); + crashing_design = clean_design(crashing_design, clean, /*do_delete=*/true); + for (auto &it : crashing_design->modules_) + design->add(it.second->clone()); + delete crashing_design; + } + } +} BugpointPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/cmds/check.cc b/yosys/passes/cmds/check.cc index b3622cb19..64697c134 100644 --- a/yosys/passes/cmds/check.cc +++ b/yosys/passes/cmds/check.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct CheckPass : public Pass { CheckPass() : Pass("check", "check for obvious problems in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -44,14 +44,18 @@ struct CheckPass : public Pass { 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 -initdrv then this command also checks for wires which have\n"); + log("the 'init' attribute set and aren't driven by a FF cell type.\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 args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { int counter = 0; bool noinit = false; + bool initdrv = false; bool assert_mode = false; size_t argidx; @@ -60,6 +64,10 @@ struct CheckPass : public Pass { noinit = true; continue; } + if (args[argidx] == "-initdrv") { + initdrv = true; + continue; + } if (args[argidx] == "-assert") { assert_mode = true; continue; @@ -70,6 +78,49 @@ struct CheckPass : public Pass { log_header(design, "Executing CHECK pass (checking for obvious problems).\n"); + pool fftypes; + fftypes.insert("$sr"); + fftypes.insert("$ff"); + fftypes.insert("$dff"); + fftypes.insert("$dffe"); + fftypes.insert("$dffsr"); + fftypes.insert("$adff"); + fftypes.insert("$dlatch"); + fftypes.insert("$dlatchsr"); + fftypes.insert("$_DFFE_NN_"); + fftypes.insert("$_DFFE_NP_"); + fftypes.insert("$_DFFE_PN_"); + fftypes.insert("$_DFFE_PP_"); + fftypes.insert("$_DFFSR_NNN_"); + fftypes.insert("$_DFFSR_NNP_"); + fftypes.insert("$_DFFSR_NPN_"); + fftypes.insert("$_DFFSR_NPP_"); + fftypes.insert("$_DFFSR_PNN_"); + fftypes.insert("$_DFFSR_PNP_"); + fftypes.insert("$_DFFSR_PPN_"); + fftypes.insert("$_DFFSR_PPP_"); + fftypes.insert("$_DFF_NN0_"); + fftypes.insert("$_DFF_NN1_"); + fftypes.insert("$_DFF_NP0_"); + fftypes.insert("$_DFF_NP1_"); + fftypes.insert("$_DFF_N_"); + fftypes.insert("$_DFF_PN0_"); + fftypes.insert("$_DFF_PN1_"); + fftypes.insert("$_DFF_PP0_"); + fftypes.insert("$_DFF_PP1_"); + fftypes.insert("$_DFF_P_"); + fftypes.insert("$_DLATCHSR_NNN_"); + fftypes.insert("$_DLATCHSR_NNP_"); + fftypes.insert("$_DLATCHSR_NPN_"); + fftypes.insert("$_DLATCHSR_NPP_"); + fftypes.insert("$_DLATCHSR_PNN_"); + fftypes.insert("$_DLATCHSR_PNP_"); + fftypes.insert("$_DLATCHSR_PPN_"); + fftypes.insert("$_DLATCHSR_PPP_"); + fftypes.insert("$_DLATCH_N_"); + fftypes.insert("$_DLATCH_P_"); + fftypes.insert("$_FF_"); + for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) @@ -109,6 +160,8 @@ struct CheckPass : public Pass { if (bit.wire) wire_drivers_count[bit]++; } + pool init_bits; + for (auto wire : module->wires()) { if (wire->port_input) { SigSpec sig = sigmap(wire); @@ -121,9 +174,15 @@ struct CheckPass : public Pass { 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++; + if (wire->attributes.count("\\init")) { + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initval) && i < GetSize(wire); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + init_bits.insert(sigmap(SigBit(wire, i))); + if (noinit) { + log_warning("Wire %s.%s has an unprocessed 'init' attribute.\n", log_id(module), log_id(wire)); + counter++; + } } } @@ -150,6 +209,26 @@ struct CheckPass : public Pass { log_warning("%s", message.c_str()); counter++; } + + if (initdrv) + { + for (auto cell : module->cells()) + { + if (fftypes.count(cell->type) == 0) + continue; + + for (auto bit : sigmap(cell->getPort("\\Q"))) + init_bits.erase(bit); + } + + SigSpec init_sig(init_bits); + init_sig.sort_and_unify(); + + for (auto chunk : init_sig.chunks()) { + log_warning("Wire %s.%s has 'init' attribute and is not driven by an FF cell.\n", log_id(module), log_signal(chunk)); + counter++; + } + } } log("found and reported %d problems.\n", counter); diff --git a/yosys/passes/cmds/chformal.cc b/yosys/passes/cmds/chformal.cc new file mode 100644 index 000000000..7e32da65f --- /dev/null +++ b/yosys/passes/cmds/chformal.cc @@ -0,0 +1,282 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 ChformalPass : public Pass { + ChformalPass() : Pass("chformal", "change formal constraints of the design") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chformal [types] [mode] [options] [selection]\n"); + log("\n"); + log("Make changes to the formal constraints of the design. The [types] options\n"); + log("the type of constraint to operate on. If none of the following options are given,\n"); + log("the command will operate on all constraint types:\n"); + log("\n"); + log(" -assert $assert cells, representing assert(...) constraints\n"); + log(" -assume $assume cells, representing assume(...) constraints\n"); + log(" -live $live cells, representing assert(s_eventually ...)\n"); + log(" -fair $fair cells, representing assume(s_eventually ...)\n"); + log(" -cover $cover cells, representing cover() statements\n"); + log("\n"); + log("Exactly one of the following modes must be specified:\n"); + log("\n"); + log(" -remove\n"); + log(" remove the cells and thus constraints from the design\n"); + log("\n"); + log(" -early\n"); + log(" bypass FFs that only delay the activation of a constraint\n"); + log("\n"); + log(" -delay \n"); + log(" delay activation of the constraint by clock cycles\n"); + log("\n"); + log(" -skip \n"); + log(" ignore activation of the constraint in the first clock cycles\n"); + log("\n"); + log(" -assert2assume\n"); + log(" -assume2assert\n"); + log(" -live2fair\n"); + log(" -fair2live\n"); + log(" change the roles of cells as indicated. these options can be combined\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + bool assert2assume = false; + bool assume2assert = false; + bool live2fair = false; + bool fair2live = false; + + pool constr_types; + char mode = 0; + int mode_arg = 0; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-assert") { + constr_types.insert("$assert"); + continue; + } + if (args[argidx] == "-assume") { + constr_types.insert("$assume"); + continue; + } + if (args[argidx] == "-live") { + constr_types.insert("$live"); + continue; + } + if (args[argidx] == "-fair") { + constr_types.insert("$fair"); + continue; + } + if (args[argidx] == "-cover") { + constr_types.insert("$cover"); + continue; + } + if (mode == 0 && args[argidx] == "-remove") { + mode = 'r'; + continue; + } + if (mode == 0 && args[argidx] == "-early") { + mode = 'e'; + continue; + } + if (mode == 0 && args[argidx] == "-delay" && argidx+1 < args.size()) { + mode = 'd'; + mode_arg = atoi(args[++argidx].c_str()); + continue; + } + if (mode == 0 && args[argidx] == "-skip" && argidx+1 < args.size()) { + mode = 's'; + mode_arg = atoi(args[++argidx].c_str()); + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-assert2assume") { + assert2assume = true; + mode = 'c'; + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-assume2assert") { + assume2assert = true; + mode = 'c'; + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-live2fair") { + live2fair = true; + mode = 'c'; + continue; + } + if ((mode == 0 || mode == 'c') && args[argidx] == "-fair2live") { + fair2live = true; + mode = 'c'; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (constr_types.empty()) { + constr_types.insert("$assert"); + constr_types.insert("$assume"); + constr_types.insert("$live"); + constr_types.insert("$fair"); + constr_types.insert("$cover"); + } + + if (mode == 0) + log_cmd_error("Mode option is missing.\n"); + + for (auto module : design->selected_modules()) + { + vector constr_cells; + + for (auto cell : module->selected_cells()) + if (constr_types.count(cell->type)) + constr_cells.push_back(cell); + + if (mode == 'r') + { + for (auto cell : constr_cells) + module->remove(cell); + } + else + if (mode == 'e') + { + SigMap sigmap(module); + dict>> ffmap; + pool init_zero, init_one; + + 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 (initval[i] == State::S0) + init_zero.insert(initsig[i]); + if (initval[i] == State::S1) + init_one.insert(initsig[i]); + } + } + + for (auto cell : module->selected_cells()) + { + if (cell->type == "$ff") { + SigSpec D = sigmap(cell->getPort("\\D")); + SigSpec Q = sigmap(cell->getPort("\\Q")); + for (int i = 0; i < GetSize(D); i++) + ffmap[Q[i]] = make_pair(D[i], make_pair(State::Sm, false)); + } + if (cell->type == "$dff") { + SigSpec D = sigmap(cell->getPort("\\D")); + SigSpec Q = sigmap(cell->getPort("\\Q")); + SigSpec C = sigmap(cell->getPort("\\CLK")); + bool clockpol = cell->getParam("\\CLK_POLARITY").as_bool(); + for (int i = 0; i < GetSize(D); i++) + ffmap[Q[i]] = make_pair(D[i], make_pair(C, clockpol)); + } + } + + for (auto cell : constr_cells) + while (true) + { + SigSpec A = sigmap(cell->getPort("\\A")); + SigSpec EN = sigmap(cell->getPort("\\EN")); + + if (ffmap.count(A) == 0 || ffmap.count(EN) == 0) + break; + + if (!init_zero.count(EN)) { + if (cell->type == "$cover") break; + if (cell->type.in("$assert", "$assume") && !init_one.count(A)) break; + } + + const auto &A_map = ffmap.at(A); + const auto &EN_map = ffmap.at(EN); + + if (A_map.second != EN_map.second) + break; + + cell->setPort("\\A", A_map.first); + cell->setPort("\\EN", EN_map.first); + } + } + else + if (mode == 'd') + { + for (auto cell : constr_cells) + for (int i = 0; i < mode_arg; i++) + { + SigSpec orig_a = cell->getPort("\\A"); + SigSpec orig_en = cell->getPort("\\EN"); + + Wire *new_a = module->addWire(NEW_ID); + Wire *new_en = module->addWire(NEW_ID); + new_en->attributes["\\init"] = State::S0; + + module->addFf(NEW_ID, orig_a, new_a); + module->addFf(NEW_ID, orig_en, new_en); + + cell->setPort("\\A", new_a); + cell->setPort("\\EN", new_en); + } + } + else + if (mode == 's') + { + SigSpec en = State::S1; + + for (int i = 0; i < mode_arg; i++) { + Wire *w = module->addWire(NEW_ID); + w->attributes["\\init"] = State::S0; + module->addFf(NEW_ID, en, w); + en = w; + } + + for (auto cell : constr_cells) + cell->setPort("\\EN", module->LogicAnd(NEW_ID, en, cell->getPort("\\EN"))); + } + else + if (mode == 'c') + { + for (auto cell : constr_cells) + if (assert2assume && cell->type == "$assert") + cell->type = "$assume"; + else if (assume2assert && cell->type == "$assume") + cell->type = "$assert"; + else if (live2fair && cell->type == "$live") + cell->type = "$fair"; + else if (fair2live && cell->type == "$fair") + cell->type = "$live"; + } + } + } +} ChformalPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/cmds/chtype.cc b/yosys/passes/cmds/chtype.cc new file mode 100644 index 000000000..979aeadd4 --- /dev/null +++ b/yosys/passes/cmds/chtype.cc @@ -0,0 +1,83 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 ChtypePass : public Pass { + ChtypePass() : Pass("chtype", "change type of cells in the design") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" chtype [options] [selection]\n"); + log("\n"); + log("Change the types of cells in the design.\n"); + log("\n"); + log(" -set \n"); + log(" set the cell type to the given type\n"); + log("\n"); + log(" -map \n"); + log(" change cells types that match to \n"); + log("\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + IdString set_type; + dict map_types; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (set_type == IdString() && args[argidx] == "-set" && argidx+1 < args.size()) { + set_type = RTLIL::escape_id(args[++argidx]); + continue; + } + if (args[argidx] == "-map" && argidx+2 < args.size()) { + IdString old_type = RTLIL::escape_id(args[++argidx]); + IdString new_type = RTLIL::escape_id(args[++argidx]); + map_types[old_type] = new_type; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (map_types.count(cell->type)) { + cell->type = map_types.at(cell->type); + continue; + } + + if (set_type != IdString()) { + cell->type = set_type; + continue; + } + } + } + } +} ChtypePass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/cmds/connect.cc b/yosys/passes/cmds/connect.cc index 52611cf44..f93bada27 100644 --- a/yosys/passes/cmds/connect.cc +++ b/yosys/passes/cmds/connect.cc @@ -43,7 +43,7 @@ static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap & struct ConnectPass : public Pass { ConnectPass() : Pass("connect", "create or remove connections") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -75,7 +75,7 @@ struct ConnectPass : public Pass { log("This command does not operate on module with processes.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { RTLIL::Module *module = NULL; for (auto &it : design->modules_) { @@ -137,7 +137,7 @@ struct ConnectPass : public Pass { if (!set_lhs.empty()) { if (!unset_expr.empty() || !port_cell.empty()) - log_cmd_error("Cant use -set together with -unset and/or -port.\n"); + log_cmd_error("Can't use -set together with -unset and/or -port.\n"); RTLIL::SigSpec sig_lhs, sig_rhs; if (!RTLIL::SigSpec::parse_sel(sig_lhs, design, module, set_lhs)) @@ -157,7 +157,7 @@ struct ConnectPass : public Pass { if (!unset_expr.empty()) { if (!port_cell.empty() || flag_nounset) - log_cmd_error("Cant use -unset together with -port and/or -nounset.\n"); + log_cmd_error("Can't use -unset together with -port and/or -nounset.\n"); RTLIL::SigSpec sig; if (!RTLIL::SigSpec::parse_sel(sig, design, module, unset_expr)) @@ -170,7 +170,7 @@ struct ConnectPass : public Pass { if (!port_cell.empty()) { if (flag_nounset) - log_cmd_error("Cant use -port together with -nounset.\n"); + log_cmd_error("Can't use -port together with -nounset.\n"); if (module->cells_.count(RTLIL::escape_id(port_cell)) == 0) log_cmd_error("Can't find cell %s.\n", port_cell.c_str()); diff --git a/yosys/passes/cmds/connwrappers.cc b/yosys/passes/cmds/connwrappers.cc index c9ab226d5..5a15cbbaf 100644 --- a/yosys/passes/cmds/connwrappers.cc +++ b/yosys/passes/cmds/connwrappers.cc @@ -149,8 +149,8 @@ struct ConnwrappersWorker }; struct ConnwrappersPass : public Pass { - ConnwrappersPass() : Pass("connwrappers", "replace undef values with defined constants") { } - virtual void help() + ConnwrappersPass() : Pass("connwrappers", "match width of input-output port pairs") { } + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -172,7 +172,7 @@ struct ConnwrappersPass : public Pass { log("The options -signed, -unsigned, and -port can be specified multiple times.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { ConnwrappersWorker worker; diff --git a/yosys/passes/cmds/copy.cc b/yosys/passes/cmds/copy.cc index fb863512b..acd2dba52 100644 --- a/yosys/passes/cmds/copy.cc +++ b/yosys/passes/cmds/copy.cc @@ -26,7 +26,7 @@ PRIVATE_NAMESPACE_BEGIN struct CopyPass : public Pass { CopyPass() : Pass("copy", "copy modules in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -36,7 +36,7 @@ struct CopyPass : public Pass { log("by this command.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { if (args.size() != 3) log_cmd_error("Invalid number of arguments!\n"); diff --git a/yosys/passes/cmds/cover.cc b/yosys/passes/cmds/cover.cc index 1475475c3..0ec747671 100644 --- a/yosys/passes/cmds/cover.cc +++ b/yosys/passes/cmds/cover.cc @@ -35,7 +35,7 @@ PRIVATE_NAMESPACE_BEGIN struct CoverPass : public Pass { CoverPass() : Pass("cover", "print code coverage counters") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -83,7 +83,7 @@ struct CoverPass : public Pass { log("Coverage counters are only available in Yosys for Linux.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::vector out_files; std::vector patterns; @@ -128,7 +128,7 @@ struct CoverPass : public Pass { log("\n"); } -#if defined(YOSYS_ENABLE_COVER) && defined(__linux__) +#if defined(YOSYS_ENABLE_COVER) && (defined(__linux__) || defined(__FreeBSD__)) for (auto &it : get_coverage_data()) { if (!patterns.empty()) { for (auto &p : patterns) diff --git a/yosys/passes/cmds/delete.cc b/yosys/passes/cmds/delete.cc index 6d51d30e7..f8d91ea48 100644 --- a/yosys/passes/cmds/delete.cc +++ b/yosys/passes/cmds/delete.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN struct DeletePass : public Pass { DeletePass() : Pass("delete", "delete objects in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -40,7 +40,7 @@ struct DeletePass : public Pass { log("selected wires, thus 'deleting' module ports.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_input = false; bool flag_output = false; diff --git a/yosys/passes/cmds/design.cc b/yosys/passes/cmds/design.cc index e900e7b9c..172addcc1 100644 --- a/yosys/passes/cmds/design.cc +++ b/yosys/passes/cmds/design.cc @@ -17,10 +17,8 @@ * */ -#include "kernel/register.h" -#include "kernel/celltypes.h" -#include "kernel/rtlil.h" -#include "kernel/log.h" +#include "kernel/yosys.h" +#include "frontends/ast/ast.h" YOSYS_NAMESPACE_BEGIN @@ -29,7 +27,7 @@ std::vector pushed_designs; struct DesignPass : public Pass { DesignPass() : Pass("design", "save, restore and reset current design") { } - virtual ~DesignPass() { + ~DesignPass() YS_OVERRIDE { for (auto &it : saved_designs) delete it.second; saved_designs.clear(); @@ -37,7 +35,7 @@ struct DesignPass : public Pass { delete it; pushed_designs.clear(); } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -82,13 +80,28 @@ struct DesignPass : public Pass { log("\n"); log("Copy modules from the current design into the specified one.\n"); log("\n"); + log("\n"); + log(" design -import [-as ] [selection]\n"); + log("\n"); + log("Import the specified design into the current design. The source design must\n"); + log("either have a selected top module or the selection must contain exactly one\n"); + log("module that is then used as top module for this command.\n"); + log("\n"); + log("\n"); + log(" design -reset-vlog\n"); + log("\n"); + log("The Verilog front-end remembers defined macros and top-level declarations\n"); + log("between calls to 'read_verilog'. This command resets this memory.\n"); + log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool got_mode = false; bool reset_mode = false; + bool reset_vlog_mode = false; bool push_mode = false; bool pop_mode = false; + bool import_mode = false; RTLIL::Design *copy_from_design = NULL, *copy_to_design = NULL; std::string save_name, load_name, as_name; std::vector copy_src_modules; @@ -102,6 +115,11 @@ struct DesignPass : public Pass { reset_mode = true; continue; } + if (!got_mode && args[argidx] == "-reset-vlog") { + got_mode = true; + reset_vlog_mode = true; + continue; + } if (!got_mode && args[argidx] == "-push") { got_mode = true; push_mode = true; @@ -146,8 +164,17 @@ struct DesignPass : public Pass { copy_from_design = design; continue; } - if (copy_from_design != NULL && args[argidx] == "-as" && argidx+1 < args.size()) { + if (!got_mode && args[argidx] == "-import" && argidx+1 < args.size()) { got_mode = true; + import_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; + as_name = args[argidx]; + continue; + } + if (copy_from_design != NULL && args[argidx] == "-as" && argidx+1 < args.size()) { as_name = args[++argidx]; continue; } @@ -156,10 +183,10 @@ struct DesignPass : public Pass { if (copy_from_design != NULL) { - if (copy_from_design != design && argidx == args.size()) + if (copy_from_design != design && argidx == args.size() && !import_mode) cmd_error(args, argidx, "Missing selection."); - RTLIL::Selection sel = design->selection_stack.back(); + RTLIL::Selection sel; if (argidx != args.size()) { handle_extra_select_args(this, args, argidx, args.size(), copy_from_design); sel = copy_from_design->selection_stack.back(); @@ -175,6 +202,17 @@ struct DesignPass : public Pass { if (sel.selected_module(it.first)) log_cmd_error("Module %s is only partly selected.\n", RTLIL::id2cstr(it.first)); } + + if (import_mode) { + for (auto module : copy_src_modules) + { + if (module->get_bool_attribute("\\top")) { + copy_src_modules.clear(); + copy_src_modules.push_back(module); + break; + } + } + } } extra_args(args, argidx, design, false); @@ -185,6 +223,68 @@ struct DesignPass : public Pass { if (pop_mode && pushed_designs.empty()) log_cmd_error("No pushed designs.\n"); + if (import_mode) + { + std::string prefix = RTLIL::escape_id(as_name); + + pool queue; + dict done; + + if (copy_to_design->modules_.count(prefix)) + delete copy_to_design->modules_.at(prefix); + + if (GetSize(copy_src_modules) != 1) + log_cmd_error("No top module found in source design.\n"); + + for (auto mod : copy_src_modules) + { + log("Importing %s as %s.\n", log_id(mod), log_id(prefix)); + + copy_to_design->modules_[prefix] = mod->clone(); + copy_to_design->modules_[prefix]->name = prefix; + copy_to_design->modules_[prefix]->design = copy_to_design; + copy_to_design->modules_[prefix]->attributes.erase("\\top"); + + queue.insert(copy_to_design->modules_[prefix]); + done[mod->name] = prefix; + } + + while (!queue.empty()) + { + pool old_queue; + old_queue.swap(queue); + + for (auto mod : old_queue) + for (auto cell : mod->cells()) + { + Module *fmod = copy_from_design->module(cell->type); + + if (fmod == nullptr) + continue; + + if (done.count(cell->type) == 0) + { + std::string trg_name = prefix + "." + (cell->type.c_str() + (*cell->type.c_str() == '\\')); + + log("Importing %s as %s.\n", log_id(fmod), log_id(trg_name)); + + if (copy_to_design->modules_.count(trg_name)) + delete copy_to_design->modules_.at(trg_name); + + copy_to_design->modules_[trg_name] = fmod->clone(); + copy_to_design->modules_[trg_name]->name = trg_name; + copy_to_design->modules_[trg_name]->design = copy_to_design; + copy_to_design->modules_[trg_name]->attributes.erase("\\top"); + + queue.insert(copy_to_design->modules_[trg_name]); + done[cell->type] = trg_name; + } + + cell->type = done.at(cell->type); + } + } + } + else if (copy_to_design != NULL) { if (!as_name.empty() && copy_src_modules.size() > 1) @@ -196,6 +296,7 @@ struct DesignPass : public Pass { 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; @@ -235,19 +336,34 @@ struct DesignPass : public Pass { design->selection_stack.push_back(RTLIL::Selection()); } + if (reset_mode || reset_vlog_mode || !load_name.empty() || push_mode || pop_mode) + { + for (auto node : design->verilog_packages) + delete node; + design->verilog_packages.clear(); + + for (auto node : design->verilog_globals) + delete node; + design->verilog_globals.clear(); + + design->verilog_defines.clear(); + } + 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; + + if (pop_mode) { + delete saved_design; + pushed_designs.pop_back(); + } } } } DesignPass; diff --git a/yosys/passes/cmds/edgetypes.cc b/yosys/passes/cmds/edgetypes.cc index 7b75a009f..58ed6457d 100644 --- a/yosys/passes/cmds/edgetypes.cc +++ b/yosys/passes/cmds/edgetypes.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct EdgetypePass : public Pass { EdgetypePass() : Pass("edgetypes", "list all types of edges in selection") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -35,7 +35,7 @@ struct EdgetypePass : public Pass { log("is a 4-tuple of source and sink cell type and port name.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { diff --git a/yosys/passes/cmds/logcmd.cc b/yosys/passes/cmds/logcmd.cc index 85386f3d2..522e1089d 100644 --- a/yosys/passes/cmds/logcmd.cc +++ b/yosys/passes/cmds/logcmd.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct LogPass : public Pass { LogPass() : Pass("log", "print text and log files") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -52,7 +52,7 @@ struct LogPass : public Pass { log(" do not append a newline\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design*) + void execute(std::vector args, RTLIL::Design*) YS_OVERRIDE { size_t argidx; bool to_stdout = false; diff --git a/yosys/passes/cmds/ltp.cc b/yosys/passes/cmds/ltp.cc new file mode 100644 index 000000000..05701710b --- /dev/null +++ b/yosys/passes/cmds/ltp.cc @@ -0,0 +1,185 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct LtpWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap sigmap; + + dict> bits; + dict> bit2bits; + dict> bit2ff; + + int maxlvl; + SigBit maxbit; + pool busy; + + LtpWorker(RTLIL::Module *module, bool noff) : design(module->design), module(module), sigmap(module) + { + CellTypes ff_celltypes; + + if (noff) { + ff_celltypes.setup_internals_mem(); + ff_celltypes.setup_stdcells_mem(); + } + + for (auto wire : module->selected_wires()) + for (auto bit : sigmap(wire)) + bits[bit] = tuple(-1, State::Sx, nullptr); + + for (auto cell : module->selected_cells()) + { + pool src_bits, dst_bits; + + for (auto &conn : cell->connections()) + for (auto bit : sigmap(conn.second)) { + if (cell->input(conn.first)) + src_bits.insert(bit); + if (cell->output(conn.first)) + dst_bits.insert(bit); + } + + if (noff && ff_celltypes.cell_known(cell->type)) { + for (auto s : src_bits) + for (auto d : dst_bits) { + bit2ff[s] = tuple(d, cell); + break; + } + continue; + } + + for (auto s : src_bits) + for (auto d : dst_bits) + bit2bits[s][d] = cell; + } + + maxlvl = -1; + maxbit = State::Sx; + } + + void runner(SigBit bit, int level, SigBit from, Cell *via) + { + auto &bitinfo = bits.at(bit); + + if (get<0>(bitinfo) >= level) + return; + + if (busy.count(bit) > 0) { + log_warning("Detected loop at %s in %s\n", log_signal(bit), log_id(module)); + return; + } + + busy.insert(bit); + get<0>(bitinfo) = level; + get<1>(bitinfo) = from; + get<2>(bitinfo) = via; + + if (level > maxlvl) { + maxlvl = level; + maxbit = bit; + } + + if (bit2bits.count(bit)) { + for (auto &it : bit2bits.at(bit)) + runner(it.first, level+1, bit, it.second); + } + + busy.erase(bit); + } + + void printpath(SigBit bit) + { + auto &bitinfo = bits.at(bit); + if (get<2>(bitinfo)) { + printpath(get<1>(bitinfo)); + log("%5d: %s (via %s)\n", get<0>(bitinfo), log_signal(bit), log_id(get<2>(bitinfo))); + } else { + log("%5d: %s\n", get<0>(bitinfo), log_signal(bit)); + } + } + + void run() + { + for (auto &it : bits) + if (get<0>(it.second) < 0) + runner(it.first, 0, State::Sx, nullptr); + + log("\n"); + log("Longest topological path in %s (length=%d):\n", log_id(module), maxlvl); + + if (maxlvl >= 0) + printpath(maxbit); + + if (bit2ff.count(maxbit)) + log("%5s: %s (via %s)\n", "ff", log_signal(get<0>(bit2ff.at(maxbit))), log_id(get<1>(bit2ff.at(maxbit)))); + } +}; + +struct LtpPass : public Pass { + LtpPass() : Pass("ltp", "print longest topological path") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ltp [options] [selection]\n"); + log("\n"); + log("This command prints the longest topological path in the design. (Only considers\n"); + log("paths within a single module, so the design must be flattened.)\n"); + log("\n"); + log(" -noff\n"); + log(" automatically exclude FF cell types\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + bool noff = false; + + log_header(design, "Executing LTP pass (find longest path).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-noff") { + noff = true; + continue; + } + break; + } + + extra_args(args, argidx, design); + + for (Module *module : design->selected_modules()) + { + if (module->has_processes_warn()) + continue; + + LtpWorker worker(module, noff); + worker.run(); + } + } +} LtpPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/cmds/plugin.cc b/yosys/passes/cmds/plugin.cc index 828c671de..aa6d5b6cc 100644 --- a/yosys/passes/cmds/plugin.cc +++ b/yosys/passes/cmds/plugin.cc @@ -58,7 +58,7 @@ void load_plugin(std::string, std::vector) struct PluginPass : public Pass { PluginPass() : Pass("plugin", "load and list loaded plugins") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -76,7 +76,7 @@ struct PluginPass : public Pass { log(" List loaded plugins\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string plugin_filename; std::vector plugin_aliases; diff --git a/yosys/passes/cmds/qwp.cc b/yosys/passes/cmds/qwp.cc index 1b800b6df..1c64a7b77 100644 --- a/yosys/passes/cmds/qwp.cc +++ b/yosys/passes/cmds/qwp.cc @@ -778,7 +778,7 @@ struct QwpWorker struct QwpPass : public Pass { QwpPass() : Pass("qwp", "quadratic wirelength placer") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -808,7 +808,7 @@ struct QwpPass : public Pass { log("dense matrix operations. It is only a toy-placer for small circuits.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { QwpConfig config; xorshift32_state = 123456789; @@ -835,6 +835,7 @@ struct QwpPass : public Pass { } if (args[argidx] == "-dump" && argidx+1 < args.size()) { config.dump_file.open(args[++argidx], std::ofstream::trunc); + yosys_output_files.insert(args[argidx]); continue; } break; diff --git a/yosys/passes/cmds/rename.cc b/yosys/passes/cmds/rename.cc index 6a002869b..698ce7235 100644 --- a/yosys/passes/cmds/rename.cc +++ b/yosys/passes/cmds/rename.cc @@ -52,9 +52,54 @@ static void rename_in_module(RTLIL::Module *module, std::string from_name, std:: log_cmd_error("Object `%s' not found!\n", from_name.c_str()); } +static std::string derive_name_from_src(const std::string &src, int counter) +{ + std::string src_base = src.substr(0, src.find('|')); + if (src_base.empty()) + return stringf("$%d", counter); + else + return stringf("\\%s$%d", src_base.c_str(), counter); +} + +static IdString derive_name_from_wire(const RTLIL::Cell &cell) +{ + // Find output + const SigSpec *output = nullptr; + int num_outputs = 0; + for (auto &connection : cell.connections()) { + if (cell.output(connection.first)) { + output = &connection.second; + num_outputs++; + } + } + + if (num_outputs != 1) // Skip cells thad drive multiple outputs + return cell.name; + + std::string name = ""; + for (auto &chunk : output->chunks()) { + // Skip cells that drive privately named wires + if (!chunk.wire || chunk.wire->name.str()[0] == '$') + return cell.name; + + if (name != "") + name += "$"; + + name += chunk.wire->name.str(); + if (chunk.wire->width != chunk.width) { + name += "["; + if (chunk.width != 1) + name += std::to_string(chunk.offset + chunk.width) + ":"; + name += std::to_string(chunk.offset) + "]"; + } + } + + return name + cell.type.str(); +} + struct RenamePass : public Pass { RenamePass() : Pass("rename", "rename object in the design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -63,6 +108,14 @@ struct RenamePass : public Pass { log("Rename the specified object. Note that selection patterns are not supported\n"); log("by this command.\n"); log("\n"); + log(" rename -src [selection]\n"); + log("\n"); + log("Assign names auto-generated from the src attribute to all selected wires and\n"); + log("cells with private names.\n"); + log("\n"); + log(" rename -wire [selection]\n"); + log("Assign auto-generated names based on the wires they drive to all selected\n"); + log("cells with private names. Ignores cells driving privatly named wires.\n"); log("\n"); log(" rename -enumerate [-pattern ] [selection]\n"); log("\n"); @@ -81,9 +134,11 @@ struct RenamePass : public Pass { log("Rename top module.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string pattern_prefix = "_", pattern_suffix = "_"; + bool flag_src = false; + bool flag_wire = false; bool flag_enumerate = false; bool flag_hide = false; bool flag_top = false; @@ -93,6 +148,16 @@ struct RenamePass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; + if (arg == "-src" && !got_mode) { + flag_src = true; + got_mode = true; + continue; + } + if (arg == "-wire" && !got_mode) { + flag_wire = true; + got_mode = true; + continue; + } if (arg == "-enumerate" && !got_mode) { flag_enumerate = true; got_mode = true; @@ -117,6 +182,57 @@ struct RenamePass : public Pass { break; } + if (flag_src) + { + extra_args(args, argidx, design); + + for (auto &mod : design->modules_) + { + int counter = 0; + + RTLIL::Module *module = mod.second; + if (!design->selected(module)) + continue; + + dict new_wires; + for (auto &it : module->wires_) { + if (it.first[0] == '$' && design->selected(module, it.second)) + it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++); + new_wires[it.second->name] = it.second; + } + module->wires_.swap(new_wires); + module->fixup_ports(); + + dict new_cells; + for (auto &it : module->cells_) { + if (it.first[0] == '$' && design->selected(module, it.second)) + it.second->name = derive_name_from_src(it.second->get_src_attribute(), counter++); + new_cells[it.second->name] = it.second; + } + module->cells_.swap(new_cells); + } + } + else + if (flag_wire) + { + extra_args(args, argidx, design); + + for (auto &mod : design->modules_) + { + RTLIL::Module *module = mod.second; + if (!design->selected(module)) + continue; + + dict new_cells; + for (auto &it : module->cells_) { + if (it.first[0] == '$' && design->selected(module, it.second)) + it.second->name = derive_name_from_wire(*it.second); + new_cells[it.second->name] = it.second; + } + module->cells_.swap(new_cells); + } + } + else if (flag_enumerate) { extra_args(args, argidx, design); diff --git a/yosys/passes/cmds/scatter.cc b/yosys/passes/cmds/scatter.cc index f083e1f67..7123ba9fb 100644 --- a/yosys/passes/cmds/scatter.cc +++ b/yosys/passes/cmds/scatter.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct ScatterPass : public Pass { ScatterPass() : Pass("scatter", "add additional intermediate nets") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -41,7 +41,7 @@ struct ScatterPass : public Pass { log("Use the opt_clean command to get rid of the additional nets.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { CellTypes ct(design); extra_args(args, 1, design); diff --git a/yosys/passes/cmds/scc.cc b/yosys/passes/cmds/scc.cc index bb6d74474..99f4fbae8 100644 --- a/yosys/passes/cmds/scc.cc +++ b/yosys/passes/cmds/scc.cc @@ -163,16 +163,8 @@ struct SccWorker } 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(); + cellToNextCell[cell] = sigToNextCells.find(cellToNextSig[cell]); if (!nofeedbackMode && cellToNextCell[cell].count(cell)) { log("Found an SCC:"); @@ -183,6 +175,16 @@ struct SccWorker sccList.push_back(scc); log("\n"); } + } + + labelCounter = 0; + cellLabels.clear(); + + while (!workQueue.empty()) + { + RTLIL::Cell *cell = *workQueue.begin(); + log_assert(cellStack.size() == 0); + cellDepth.clear(); run(cell, 0, maxDepth); } @@ -216,7 +218,7 @@ struct SccWorker struct SccPass : public Pass { SccPass() : Pass("scc", "detect strongly connected components (logic loops)") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -244,20 +246,18 @@ struct SccPass : public Pass { log(" are assumed to be bidirectional 'inout' ports.\n"); log("\n"); log(" -set_attr \n"); - log(" -set_cell_attr \n"); - log(" -set_wire_attr \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(" set the specified attribute on all cells that are part of a logic\n"); + log(" loop. the special token {} in the value is replaced with a unique\n"); + log(" 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 args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - std::map setCellAttr, setWireAttr; + std::map setAttr; bool allCellTypes = false; bool selectMode = false; bool nofeedbackMode = false; @@ -285,18 +285,7 @@ struct SccPass : public Pass { 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]; + setAttr[args[argidx+1]] = args[argidx+2]; argidx += 2; continue; } @@ -309,9 +298,6 @@ struct SccPass : public Pass { 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; @@ -319,7 +305,33 @@ struct SccPass : public Pass { if (design->selected(mod_it.second)) { SccWorker worker(design, mod_it.second, nofeedbackMode, allCellTypes, maxDepth); - scc_counter += GetSize(worker.sccList); + + if (!setAttr.empty()) + { + for (const auto &cells : worker.sccList) + { + for (auto attr : setAttr) + { + IdString attr_name(RTLIL::escape_id(attr.first)); + string attr_valstr = attr.second; + string index = stringf("%d", scc_counter); + + for (size_t pos = 0; (pos = attr_valstr.find("{}", pos)) != string::npos; pos += index.size()) + attr_valstr.replace(pos, 2, index); + + Const attr_value(attr_valstr); + + for (auto cell : cells) + cell->attributes[attr_name] = attr_value; + } + + scc_counter++; + } + } + else + { + scc_counter += GetSize(worker.sccList); + } if (selectMode) worker.select(newSelection); diff --git a/yosys/passes/cmds/select.cc b/yosys/passes/cmds/select.cc index d2e1a2e2b..b5e8ef1af 100644 --- a/yosys/passes/cmds/select.cc +++ b/yosys/passes/cmds/select.cc @@ -760,6 +760,9 @@ static void select_stmt(RTLIL::Design *design, std::string arg) if (!design->selected_active_module.empty()) { arg_mod = design->selected_active_module; arg_memb = arg; + } else + if (GetSize(arg) >= 2 && arg[0] >= 'a' && arg[0] <= 'z' && arg[1] == ':') { + arg_mod = "*", arg_memb = arg; } else { size_t pos = arg.find('/'); if (pos == std::string::npos) { @@ -893,6 +896,29 @@ static void select_stmt(RTLIL::Design *design, std::string arg) select_filter_active_mod(design, work_stack.back()); } +static std::string describe_selection_for_assert(RTLIL::Design *design, RTLIL::Selection *sel) +{ + std::string desc = "Selection contains:\n"; + for (auto mod_it : design->modules_) + { + if (sel->selected_module(mod_it.first)) { + for (auto &it : mod_it.second->wires_) + if (sel->selected_member(mod_it.first, it.first)) + desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); + for (auto &it : mod_it.second->memories) + if (sel->selected_member(mod_it.first, it.first)) + desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); + for (auto &it : mod_it.second->cells_) + if (sel->selected_member(mod_it.first, it.first)) + desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); + for (auto &it : mod_it.second->processes) + if (sel->selected_member(mod_it.first, it.first)) + desc += stringf("%s/%s\n", id2cstr(mod_it.first), id2cstr(it.first)); + } + } + return desc; +} + PRIVATE_NAMESPACE_END YOSYS_NAMESPACE_BEGIN @@ -947,7 +973,7 @@ PRIVATE_NAMESPACE_BEGIN struct SelectPass : public Pass { SelectPass() : Pass("select", "modify and view the list of selected objects") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1164,7 +1190,7 @@ struct SelectPass : public Pass { log(" select */t:SWITCH %%x:+[GATE] */t:SWITCH %%d\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool add_mode = false; bool del_mode = false; @@ -1263,6 +1289,7 @@ struct SelectPass : public Pass { log_cmd_error("Option -read can not be combined with a selection expression.\n"); std::ifstream f(read_file); + yosys_input_files.insert(read_file); if (f.fail()) log_error("Can't open '%s' for reading: %s\n", read_file.c_str(), strerror(errno)); @@ -1331,6 +1358,7 @@ struct SelectPass : public Pass { FILE *f = NULL; if (!write_file.empty()) { f = fopen(write_file.c_str(), "w"); + yosys_output_files.insert(write_file); if (f == NULL) log_error("Can't open '%s' for writing: %s\n", write_file.c_str(), strerror(errno)); } @@ -1389,7 +1417,12 @@ struct SelectPass : public Pass { log_cmd_error("No selection to check.\n"); work_stack.back().optimize(design); if (!work_stack.back().empty()) - log_error("Assertion failed: selection is not empty:%s\n", sel_str.c_str()); + { + RTLIL::Selection *sel = &work_stack.back(); + sel->optimize(design); + std::string desc = describe_selection_for_assert(design, sel); + log_error("Assertion failed: selection is not empty:%s\n%s", sel_str.c_str(), desc.c_str()); + } return; } @@ -1399,7 +1432,12 @@ struct SelectPass : public Pass { log_cmd_error("No selection to check.\n"); work_stack.back().optimize(design); if (work_stack.back().empty()) - log_error("Assertion failed: selection is empty:%s\n", sel_str.c_str()); + { + RTLIL::Selection *sel = &work_stack.back(); + sel->optimize(design); + std::string desc = describe_selection_for_assert(design, sel); + log_error("Assertion failed: selection is empty:%s\n%s", sel_str.c_str(), desc.c_str()); + } return; } @@ -1426,14 +1464,23 @@ struct SelectPass : public Pass { total_count++; } if (assert_count >= 0 && assert_count != total_count) - log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n", - total_count, assert_count, sel_str.c_str()); + { + std::string desc = describe_selection_for_assert(design, sel); + log_error("Assertion failed: selection contains %d elements instead of the asserted %d:%s\n%s", + total_count, assert_count, sel_str.c_str(), desc.c_str()); + } if (assert_max >= 0 && assert_max < total_count) - log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n", - total_count, assert_max, sel_str.c_str()); + { + std::string desc = describe_selection_for_assert(design, sel); + log_error("Assertion failed: selection contains %d elements, more than the maximum number %d:%s\n%s", + total_count, assert_max, sel_str.c_str(), desc.c_str()); + } if (assert_min >= 0 && assert_min > total_count) - log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n", - total_count, assert_min, sel_str.c_str()); + { + std::string desc = describe_selection_for_assert(design, sel); + log_error("Assertion failed: selection contains %d elements, less than the minimum number %d:%s\n%s", + total_count, assert_min, sel_str.c_str(), desc.c_str()); + } return; } @@ -1465,7 +1512,7 @@ struct SelectPass : public Pass { struct CdPass : public Pass { CdPass() : Pass("cd", "a shortcut for 'select -module '") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1482,20 +1529,56 @@ struct CdPass : public Pass { log("\n"); log(" cd ..\n"); log("\n"); + log("Remove trailing substrings that start with '.' in current module name until\n"); + log("the name of a module in the current design is generated, then switch to that\n"); + log("module. Otherwise clear the current selection.\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 args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - if (args.size() != 2) + if (args.size() != 1 && args.size() != 2) log_cmd_error("Invalid number of arguments.\n"); - if (args[1] == "..") { + if (args.size() == 1 || args[1] == "/") { design->selection_stack.back() = RTLIL::Selection(true); design->selected_active_module = std::string(); return; } + if (args[1] == "..") + { + string modname = design->selected_active_module; + + design->selection_stack.back() = RTLIL::Selection(true); + design->selected_active_module = std::string(); + + while (1) + { + size_t pos = modname.rfind('.'); + + if (pos == string::npos) + break; + + modname = modname.substr(0, pos); + Module *mod = design->module(modname); + + if (mod == nullptr) + continue; + + 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; + } + + return; + } + std::string modname = RTLIL::escape_id(args[1]); if (design->modules_.count(modname) == 0 && !design->selected_active_module.empty()) { @@ -1537,7 +1620,7 @@ static void log_matches(const char *title, Module *module, T list) struct LsPass : public Pass { LsPass() : Pass("ls", "list modules or objects in modules") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1548,7 +1631,7 @@ struct LsPass : public Pass { log("When an active module is selected, this prints a list of objects in the module.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { size_t argidx = 1; extra_args(args, argidx, design); diff --git a/yosys/passes/cmds/setattr.cc b/yosys/passes/cmds/setattr.cc index 689e3148b..d38a6b3da 100644 --- a/yosys/passes/cmds/setattr.cc +++ b/yosys/passes/cmds/setattr.cc @@ -56,7 +56,7 @@ static void do_setunset(dict &attrs, const std::v struct SetattrPass : public Pass { SetattrPass() : Pass("setattr", "set/unset attributes on objects") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -69,7 +69,7 @@ struct SetattrPass : public Pass { log("instead of objects within modules.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::vector setunset_list; bool flag_mod = false; @@ -130,7 +130,7 @@ struct SetattrPass : public Pass { struct SetparamPass : public Pass { SetparamPass() : Pass("setparam", "set/unset parameters on objects") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -142,7 +142,7 @@ struct SetparamPass : public Pass { log("The -type option can be used to change the cell type of the selected cells.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { vector setunset_list; string new_cell_type; @@ -188,7 +188,7 @@ struct SetparamPass : public Pass { struct ChparamPass : public Pass { ChparamPass() : Pass("chparam", "re-evaluate modules with new parameters") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -203,7 +203,7 @@ struct ChparamPass : public Pass { log("List the available parameters of the selected modules.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::vector setunset_list; dict new_parameters; diff --git a/yosys/passes/cmds/setundef.cc b/yosys/passes/cmds/setundef.cc index 26b2eb87d..56ef2d125 100644 --- a/yosys/passes/cmds/setundef.cc +++ b/yosys/passes/cmds/setundef.cc @@ -23,33 +23,82 @@ #include "kernel/rtlil.h" #include "kernel/log.h" +#define MODE_ZERO 0 +#define MODE_ONE 1 +#define MODE_UNDEF 2 +#define MODE_RANDOM 3 +#define MODE_ANYSEQ 4 +#define MODE_ANYCONST 5 + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +static RTLIL::Wire * add_wire(RTLIL::Module *module, std::string name, int width, bool flag_input, bool flag_output) +{ + RTLIL::Wire *wire = NULL; + name = RTLIL::escape_id(name); + + if (module->count_id(name) != 0) + { + log("Module %s already has such an object %s.\n", module->name.c_str(), name.c_str()); + name += "$"; + return add_wire(module, name, width, flag_input, flag_output); + } + 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()); + } + + return wire; +} + struct SetundefWorker { int next_bit_mode; uint32_t next_bit_state; + vector siglist; RTLIL::State next_bit() { - if (next_bit_mode == 0) + if (next_bit_mode == MODE_ZERO) return RTLIL::State::S0; - if (next_bit_mode == 1) + if (next_bit_mode == MODE_ONE) 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); + if (next_bit_mode == MODE_UNDEF) + return RTLIL::State::Sx; - return ((next_bit_state >> (next_bit_state & 15)) & 16) ? RTLIL::State::S0 : RTLIL::State::S1; + if (next_bit_mode == MODE_RANDOM) + { + // 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; + } + + log_abort(); } void operator()(RTLIL::SigSpec &sig) { + if (next_bit_mode == MODE_ANYSEQ || next_bit_mode == MODE_ANYCONST) { + siglist.push_back(&sig); + return; + } + for (auto &bit : sig) if (bit.wire == NULL && bit.data > RTLIL::State::S1) bit = next_bit(); @@ -58,38 +107,53 @@ struct SetundefWorker struct SetundefPass : public Pass { SetundefPass() : Pass("setundef", "replace undef values with defined constants") { } - virtual void help() + void help() YS_OVERRIDE { // |---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("This command replaces 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(" -expose\n"); + log(" also expose undriven nets as inputs (use with -undriven)\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(" -undef\n"); + log(" replace with undef (x) bits, may be used with -undriven\n"); + log("\n"); + log(" -anyseq\n"); + log(" replace with $anyseq drivers (for formal)\n"); + log("\n"); + log(" -anyconst\n"); + log(" replace with $anyconst drivers (for formal)\n"); + log("\n"); log(" -random \n"); - log(" replace with random bits using the specified integer als seed\n"); + log(" replace with random bits using the specified integer as seed\n"); log(" value for the random number generator.\n"); log("\n"); log(" -init\n"); log(" also create/update init values for flip-flops\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool got_value = false; bool undriven_mode = false; + bool expose_mode = false; bool init_mode = false; SetundefWorker worker; + log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -97,14 +161,38 @@ struct SetundefPass : public Pass { undriven_mode = true; continue; } + if (args[argidx] == "-expose") { + expose_mode = true; + continue; + } if (args[argidx] == "-zero") { got_value = true; - worker.next_bit_mode = 0; + worker.next_bit_mode = MODE_ZERO; + worker.next_bit_state = 0; continue; } if (args[argidx] == "-one") { got_value = true; - worker.next_bit_mode = 1; + worker.next_bit_mode = MODE_ONE; + worker.next_bit_state = 0; + continue; + } + if (args[argidx] == "-anyseq") { + got_value = true; + worker.next_bit_mode = MODE_ANYSEQ; + worker.next_bit_state = 0; + continue; + } + if (args[argidx] == "-anyconst") { + got_value = true; + worker.next_bit_mode = MODE_ANYCONST; + worker.next_bit_state = 0; + continue; + } + if (args[argidx] == "-undef") { + got_value = true; + worker.next_bit_mode = MODE_UNDEF; + worker.next_bit_state = 0; continue; } if (args[argidx] == "-init") { @@ -113,7 +201,7 @@ struct SetundefPass : public Pass { } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; - worker.next_bit_mode = 2; + worker.next_bit_mode = MODE_RANDOM; worker.next_bit_state = atoi(args[++argidx].c_str()) + 1; for (int i = 0; i < 10; i++) worker.next_bit(); @@ -123,8 +211,20 @@ struct SetundefPass : public Pass { } extra_args(args, argidx, design); + if (!got_value && expose_mode) { + log("Using default as -undef with -expose.\n"); + got_value = true; + worker.next_bit_mode = MODE_UNDEF; + worker.next_bit_state = 0; + } + + if (expose_mode && !undriven_mode) + log_cmd_error("Option -expose must be used with option -undriven.\n"); if (!got_value) - log_cmd_error("One of the options -zero, -one, or -random must be specified.\n"); + log_cmd_error("One of the options -zero, -one, -anyseq, -anyconst, or -random must be specified.\n"); + + if (init_mode && (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST)) + log_cmd_error("The options -init and -anyseq / -anyconst are exclusive.\n"); for (auto module : design->selected_modules()) { @@ -133,25 +233,103 @@ struct SetundefPass : public Pass { 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; + if (expose_mode) + { + SigMap sigmap(module); + dict wire_drivers; + pool used_wires; + SigPool undriven_signals; - for (auto &it : module->wires_) - if (!it.second->port_input) + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { + SigSpec sig = sigmap(conn.second); + if (cell->input(conn.first)) + for (auto bit : sig) + if (bit.wire) + used_wires.insert(bit); + if (cell->output(conn.first)) + for (int i = 0; i < GetSize(sig); i++) + if (sig[i].wire) + wire_drivers[sig[i]] = true; + } + + 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]] = true; + } + if (wire->port_output) { + SigSpec sig = sigmap(wire); + for (auto bit : sig) + if (bit.wire) + used_wires.insert(bit); + } + } + + pool undriven_wires; + for (auto bit : used_wires) + if (!wire_drivers.count(bit)) + undriven_wires.insert(bit.wire); + + for (auto &it : undriven_wires) + undriven_signals.add(sigmap(it)); + + for (auto &it : undriven_wires) + if (it->port_input) + undriven_signals.del(sigmap(it)); + + 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::Wire * wire; + if (c.wire->width == c.width) { + wire = c.wire; + wire->port_input = true; + } else { + string name = c.wire->name.str() + "$[" + std::to_string(c.width + c.offset) + ":" + std::to_string(c.offset) + "]"; + wire = add_wire(module, name, c.width, true, false); + module->connect(RTLIL::SigSig(c, wire)); + } + log("Exposing undriven wire %s as input.\n", wire->name.c_str()); + } + module->fixup_ports(); + } + else + { + SigMap sigmap(module); + SigPool undriven_signals; + + for (auto &it : module->wires_) 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)); + for (auto &it : module->wires_) + if (it.second->port_input) + undriven_signals.del(sigmap(it.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)); + 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; + if (worker.next_bit_mode == MODE_ANYSEQ) + bits = module->Anyseq(NEW_ID, c.width); + else if (worker.next_bit_mode == MODE_ANYCONST) + bits = module->Anyconst(NEW_ID, c.width); + else + for (int i = 0; i < c.width; i++) + bits.append(worker.next_bit()); + module->connect(RTLIL::SigSig(c, bits)); + } } } @@ -236,6 +414,35 @@ struct SetundefPass : public Pass { } module->rewrite_sigspecs(worker); + + if (worker.next_bit_mode == MODE_ANYSEQ || worker.next_bit_mode == MODE_ANYCONST) + { + vector siglist; + siglist.swap(worker.siglist); + + for (auto sigptr : siglist) + { + SigSpec &sig = *sigptr; + int cursor = 0; + + while (cursor < GetSize(sig)) + { + int width = 0; + while (cursor+width < GetSize(sig) && sig[cursor+width] == State::Sx) + width++; + + if (width > 0) { + if (worker.next_bit_mode == MODE_ANYSEQ) + sig.replace(cursor, module->Anyseq(NEW_ID, width)); + else + sig.replace(cursor, module->Anyconst(NEW_ID, width)); + cursor += width; + } else { + cursor++; + } + } + } + } } } } SetundefPass; diff --git a/yosys/passes/cmds/show.cc b/yosys/passes/cmds/show.cc index 3a3939a81..58acd302d 100644 --- a/yosys/passes/cmds/show.cc +++ b/yosys/passes/cmds/show.cc @@ -30,6 +30,10 @@ # include #endif +#ifdef YOSYS_ENABLE_EDITLINE +# include +#endif + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -569,7 +573,7 @@ struct ShowWorker struct ShowPass : public Pass { ShowPass() : Pass("show", "generate schematics using graphviz") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -580,6 +584,7 @@ struct ShowPass : public Pass { log("\n"); log(" -viewer \n"); log(" Run the specified command with the graphics file as parameter.\n"); + log(" On Windows, this pauses yosys until the viewer exits.\n"); log("\n"); log(" -format \n"); log(" Generate a graphics file in the specified format. Use 'dot' to just\n"); @@ -618,7 +623,7 @@ struct ShowPass : public Pass { log(" assigned to each unique value of this attribute.\n"); log("\n"); log(" -width\n"); - log(" annotate busses with a label indicating the width of the bus.\n"); + log(" annotate buses with a label indicating the width of the bus.\n"); log("\n"); log(" -signed\n"); log(" mark ports (A, B) that are declared as signed (using the [AB]_SIGNED\n"); @@ -641,7 +646,7 @@ struct ShowPass : public Pass { log(" do not add the module name as graph title to the dot file\n"); log("\n"); log("When no is specified, 'dot' is used. When no and is\n"); - log("specified, 'xdot' is used to display the schematic.\n"); + log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n"); log("\n"); log("The generated output files are '~/.yosys_show.dot' and '~/.yosys_show.',\n"); log("unless another prefix is specified using -prefix .\n"); @@ -651,7 +656,7 @@ struct ShowPass : public Pass { log("the 'show' command is executed.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Generating Graphviz representation of design.\n"); log_push(); @@ -677,6 +682,7 @@ struct ShowPass : public Pass { bool flag_enum = false; bool flag_abbreviate = true; bool flag_notitle = false; + bool custom_prefix = false; RTLIL::IdString colorattr; size_t argidx; @@ -693,6 +699,7 @@ struct ShowPass : public Pass { } if (arg == "-prefix" && argidx+1 < args.size()) { prefix = args[++argidx]; + custom_prefix = true; continue; } if (arg == "-color" && argidx+2 < args.size()) { @@ -778,6 +785,7 @@ struct ShowPass : public Pass { for (auto filename : libfiles) { std::ifstream f; f.open(filename.c_str()); + yosys_input_files.insert(filename); if (f.fail()) log_error("Can't open lib file `%s'.\n", filename.c_str()); RTLIL::Design *lib = new RTLIL::Design; @@ -793,6 +801,8 @@ struct ShowPass : public Pass { log("Writing dot description to `%s'.\n", dot_file.c_str()); FILE *f = fopen(dot_file.c_str(), "w"); + if (custom_prefix) + yosys_output_files.insert(dot_file); if (f == NULL) { for (auto lib : libs) delete lib; @@ -808,14 +818,30 @@ struct ShowPass : public Pass { 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()); + #ifdef _WIN32 + // system()/cmd.exe does not understand single quotes on Windows. + #define DOT_CMD "dot -T%s \"%s\" > \"%s.new\" && move \"%s.new\" \"%s\"" + #else + #define DOT_CMD "dot -T%s '%s' > '%s.new' && mv '%s.new' '%s'" + #endif + std::string cmd = stringf(DOT_CMD, format.c_str(), dot_file.c_str(), out_file.c_str(), out_file.c_str(), out_file.c_str()); + #undef DOT_CMD 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()); + #ifdef _WIN32 + // system()/cmd.exe does not understand single quotes nor + // background tasks on Windows. So we have to pause yosys + // until the viewer exits. + #define VIEW_CMD "%s \"%s\"" + #else + #define VIEW_CMD "%s '%s' &" + #endif + std::string cmd = stringf(VIEW_CMD, viewer_exe.c_str(), out_file.c_str()); + #undef VIEW_CMD log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); diff --git a/yosys/passes/cmds/splice.cc b/yosys/passes/cmds/splice.cc index 7418ec4d2..bafafca4e 100644 --- a/yosys/passes/cmds/splice.cc +++ b/yosys/passes/cmds/splice.cc @@ -247,7 +247,7 @@ struct SpliceWorker struct SplicePass : public Pass { SplicePass() : Pass("splice", "create explicit splicing cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -288,7 +288,7 @@ struct SplicePass : public Pass { log("by selected wires are rewired.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool sel_by_cell = false; bool sel_by_wire = false; diff --git a/yosys/passes/cmds/splitnets.cc b/yosys/passes/cmds/splitnets.cc index 14eeb066f..f5a1f17b3 100644 --- a/yosys/passes/cmds/splitnets.cc +++ b/yosys/passes/cmds/splitnets.cc @@ -37,14 +37,20 @@ struct SplitnetsWorker new_wire_name += format.substr(0, 1); if (width > 1) { - new_wire_name += stringf("%d", offset+width-1); + if (wire->upto) + new_wire_name += stringf("%d", wire->start_offset+wire->width-(offset+width)-1); + else + new_wire_name += stringf("%d", wire->start_offset+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 (wire->upto) + new_wire_name += stringf("%d", wire->start_offset+wire->width-offset-1); + else + new_wire_name += stringf("%d", wire->start_offset+offset); if (format.size() > 1) new_wire_name += format.substr(1, 1); @@ -81,7 +87,7 @@ struct SplitnetsWorker struct SplitnetsPass : public Pass { SplitnetsPass() : Pass("splitnets", "split up multi-bit nets") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -103,7 +109,7 @@ struct SplitnetsPass : public Pass { log(" and split nets so that no driver drives only part of a net.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_ports = false; bool flag_driver = false; diff --git a/yosys/passes/cmds/stat.cc b/yosys/passes/cmds/stat.cc index 362a0edfc..54f4ea817 100644 --- a/yosys/passes/cmds/stat.cc +++ b/yosys/passes/cmds/stat.cc @@ -112,7 +112,7 @@ struct statdata_t "$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")) { + "$add", "$sub", "$mul", "$div", "$mod", "$pow", "$alu")) { 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; @@ -142,7 +142,7 @@ struct statdata_t } } - void log_data() + void log_data(RTLIL::IdString mod_name, bool top_mod) { log(" Number of wires: %6d\n", num_wires); log(" Number of wire bits: %6d\n", num_wire_bits); @@ -163,7 +163,7 @@ struct statdata_t if (area != 0) { log("\n"); - log(" Chip area for this module: %f\n", area); + log(" Chip area for %smodule '%s': %f\n", (top_mod) ? "top " : "", mod_name.c_str(), area); } } }; @@ -190,6 +190,7 @@ void read_liberty_cellarea(dict &cell_area, string liberty_fil { std::ifstream f; f.open(liberty_file.c_str()); + yosys_input_files.insert(liberty_file); if (f.fail()) log_cmd_error("Can't open liberty file `%s': %s\n", liberty_file.c_str(), strerror(errno)); LibertyParser libparser(f); @@ -208,7 +209,7 @@ void read_liberty_cellarea(dict &cell_area, string liberty_fil struct StatPass : public Pass { StatPass() : Pass("stat", "print some statistics") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -230,7 +231,7 @@ struct StatPass : public Pass { log(" e.g. $add_8 for an 8 bit wide $add cell.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Printing statistics.\n"); @@ -274,7 +275,7 @@ struct StatPass : public Pass { log("\n"); log("=== %s%s ===\n", RTLIL::id2cstr(mod->name), design->selected_whole_module(mod->name) ? "" : " (partially selected)"); log("\n"); - data.log_data(); + data.log_data(mod->name, false); } if (top_mod != NULL && GetSize(mod_stat) > 1) @@ -287,7 +288,7 @@ struct StatPass : public Pass { statdata_t data = hierarchy_worker(mod_stat, top_mod->name, 0); log("\n"); - data.log_data(); + data.log_data(top_mod->name, true); } log("\n"); diff --git a/yosys/passes/cmds/tee.cc b/yosys/passes/cmds/tee.cc index 3db2dbf0e..ee96ace86 100644 --- a/yosys/passes/cmds/tee.cc +++ b/yosys/passes/cmds/tee.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct TeePass : public Pass { TeePass() : Pass("tee", "redirect command output to file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -37,7 +37,7 @@ struct TeePass : public Pass { log("specified logfile(s).\n"); log("\n"); log(" -q\n"); - log(" Do not print output to the normal destination (console and/or log file)\n"); + log(" Do not print output to the normal destination (console and/or log file).\n"); log("\n"); log(" -o logfile\n"); log(" Write output to this file, truncate if exists.\n"); @@ -46,10 +46,10 @@ struct TeePass : public Pass { log(" Write output to this file, append if exists.\n"); log("\n"); log(" +INT, -INT\n"); - log(" Add/subract INT from the -v setting for this command.\n"); + log(" Add/subtract INT from the -v setting for this command.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::vector backup_log_files, files_to_close; int backup_log_verbose_level = log_verbose_level; @@ -65,6 +65,7 @@ struct TeePass : public Pass { 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); + yosys_input_files.insert(args[argidx]); if (f == NULL) { for (auto cf : files_to_close) fclose(cf); diff --git a/yosys/passes/cmds/torder.cc b/yosys/passes/cmds/torder.cc index 56223610d..3c0eac8de 100644 --- a/yosys/passes/cmds/torder.cc +++ b/yosys/passes/cmds/torder.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct TorderPass : public Pass { TorderPass() : Pass("torder", "print cells in topological order") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -43,7 +43,7 @@ struct TorderPass : public Pass { log(" are not used in topological sorting. this option deactivates that.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool noautostop = false; dict> stop_db; diff --git a/yosys/passes/cmds/trace.cc b/yosys/passes/cmds/trace.cc index 1a5f873ff..f5305cde9 100644 --- a/yosys/passes/cmds/trace.cc +++ b/yosys/passes/cmds/trace.cc @@ -25,34 +25,34 @@ PRIVATE_NAMESPACE_BEGIN struct TraceMonitor : public RTLIL::Monitor { - virtual void notify_module_add(RTLIL::Module *module) YS_OVERRIDE + 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 + 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 + 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 + 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 &sigsig_vec) YS_OVERRIDE + void notify_connect(RTLIL::Module *module, const std::vector &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 + void notify_blackout(RTLIL::Module *module) YS_OVERRIDE { log("#TRACE# Blackout in module %s:\n", log_id(module)); } @@ -60,7 +60,7 @@ struct TraceMonitor : public RTLIL::Monitor struct TracePass : public Pass { TracePass() : Pass("trace", "redirect command output to file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -70,7 +70,7 @@ struct TracePass : public Pass { log("the design in real time.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -95,4 +95,3 @@ struct TracePass : public Pass { } TracePass; PRIVATE_NAMESPACE_END - diff --git a/yosys/passes/cmds/write_file.cc b/yosys/passes/cmds/write_file.cc index b78265933..9613b462b 100644 --- a/yosys/passes/cmds/write_file.cc +++ b/yosys/passes/cmds/write_file.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct WriteFileFrontend : public Frontend { WriteFileFrontend() : Frontend("=write_file", "write a text to a file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -44,7 +44,7 @@ struct WriteFileFrontend : public Frontend { log(" EOT\n"); log("\n"); } - virtual void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design*) + void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design*) YS_OVERRIDE { bool append_mode = false; std::string output_filename; @@ -67,6 +67,7 @@ struct WriteFileFrontend : public Frontend { extra_args(f, filename, args, argidx); FILE *of = fopen(output_filename.c_str(), append_mode ? "a" : "w"); + yosys_output_files.insert(output_filename); char buffer[64 * 1024]; int bytes; diff --git a/yosys/passes/equiv/Makefile.inc b/yosys/passes/equiv/Makefile.inc index dd7b3be02..27ea54b22 100644 --- a/yosys/passes/equiv/Makefile.inc +++ b/yosys/passes/equiv/Makefile.inc @@ -9,4 +9,4 @@ OBJS += passes/equiv/equiv_induct.o OBJS += passes/equiv/equiv_struct.o OBJS += passes/equiv/equiv_purge.o OBJS += passes/equiv/equiv_mark.o - +OBJS += passes/equiv/equiv_opt.o diff --git a/yosys/passes/equiv/equiv_add.cc b/yosys/passes/equiv/equiv_add.cc index 0494a724f..71599f46e 100644 --- a/yosys/passes/equiv/equiv_add.cc +++ b/yosys/passes/equiv/equiv_add.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct EquivAddPass : public Pass { EquivAddPass() : Pass("equiv_add", "add a $equiv cell") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -39,7 +39,7 @@ struct EquivAddPass : public Pass { log("This command adds $equiv cells for the ports of the specified cells.\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { bool try_mode = false; diff --git a/yosys/passes/equiv/equiv_induct.cc b/yosys/passes/equiv/equiv_induct.cc index c958c3de4..bcc68d6d2 100644 --- a/yosys/passes/equiv/equiv_induct.cc +++ b/yosys/passes/equiv/equiv_induct.cc @@ -162,7 +162,7 @@ struct EquivInductWorker struct EquivInductPass : public Pass { EquivInductPass() : Pass("equiv_induct", "proving $equiv cells using temporal induction") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -192,7 +192,7 @@ struct EquivInductPass : public Pass { log("after reset.\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { int success_counter = 0; bool model_undef = false; diff --git a/yosys/passes/equiv/equiv_make.cc b/yosys/passes/equiv/equiv_make.cc index 40ca42621..dbd8682e6 100644 --- a/yosys/passes/equiv/equiv_make.cc +++ b/yosys/passes/equiv/equiv_make.cc @@ -40,6 +40,16 @@ struct EquivMakeWorker pool undriven_bits; SigMap assign_map; + dict> bit2driven; // map: bit <--> and its driven cells + + CellTypes comb_ct; + + EquivMakeWorker() + { + comb_ct.setup_internals(); + comb_ct.setup_stdcells(); + } + void read_blacklists() { for (auto fn : blacklists) @@ -260,11 +270,11 @@ struct EquivMakeWorker 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); + log(" Skipping signal bit %s [%d]: undriven on gold side.\n", id2cstr(gold_wire->name), i); continue; } if (undriven_bits.count(assign_map(SigBit(gate_wire, i)))) { - log(" Skipping signal bit %d: undriven on gate side.\n", i); + log(" Skipping signal bit %s [%d]: undriven on gate side.\n", id2cstr(gate_wire->name), i); continue; } equiv_mod->addEquiv(NEW_ID, SigSpec(gold_wire, i), SigSpec(gate_wire, i), SigSpec(wire, i)); @@ -278,16 +288,31 @@ struct EquivMakeWorker } } + init_bit2driven(); + + pool visited_cells; for (auto c : cells_list) for (auto &conn : c->connections()) if (!ct.cell_output(c->type, conn.first)) { SigSpec old_sig = assign_map(conn.second); SigSpec new_sig = rd_signal_map(old_sig); - if (old_sig != new_sig) { - log("Changing input %s of cell %s (%s): %s -> %s\n", - log_id(conn.first), log_id(c), log_id(c->type), - log_signal(old_sig), log_signal(new_sig)); - c->setPort(conn.first, new_sig); + + if(old_sig != new_sig) { + SigSpec tmp_sig = old_sig; + for (int i = 0; i < GetSize(old_sig); i++) { + SigBit old_bit = old_sig[i], new_bit = new_sig[i]; + + visited_cells.clear(); + if (check_signal_in_fanout(visited_cells, old_bit, new_bit)) + continue; + + log("Changing input %s of cell %s (%s): %s -> %s\n", + log_id(conn.first), log_id(c), log_id(c->type), + log_signal(old_bit), log_signal(new_bit)); + + tmp_sig[i] = new_bit; + } + c->setPort(conn.first, tmp_sig); } } @@ -378,6 +403,57 @@ struct EquivMakeWorker } } + void init_bit2driven() + { + for (auto cell : equiv_mod->cells()) { + if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_")) + continue; + for (auto &conn : cell->connections()) + { + if (yosys_celltypes.cell_input(cell->type, conn.first)) + for (auto bit : assign_map(conn.second)) + { + bit2driven[bit].insert(cell); + } + } + } + } + + bool check_signal_in_fanout(pool & visited_cells, SigBit source_bit, SigBit target_bit) + { + if (source_bit == target_bit) + return true; + + if (bit2driven.count(source_bit) == 0) + return false; + + auto driven_cells = bit2driven.at(source_bit); + for (auto driven_cell: driven_cells) + { + bool is_comb = comb_ct.cell_known(driven_cell->type); + if (!is_comb) + continue; + + if (visited_cells.count(driven_cell) > 0) + continue; + visited_cells.insert(driven_cell); + + for (auto &conn: driven_cell->connections()) + { + if (yosys_celltypes.cell_input(driven_cell->type, conn.first)) + continue; + + for (auto bit: conn.second) { + bool is_in_fanout = check_signal_in_fanout(visited_cells, bit, target_bit); + if (is_in_fanout == true) + return true; + } + } + } + + return false; + } + void run() { copy_to_equiv(); @@ -390,7 +466,7 @@ struct EquivMakeWorker struct EquivMakePass : public Pass { EquivMakePass() : Pass("equiv_make", "prepare a circuit for equivalence checking") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -415,7 +491,7 @@ struct EquivMakePass : public Pass { log("checking problem. Use 'miter -equiv' if you want to create a miter circuit.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { EquivMakeWorker worker; worker.ct.setup(design); diff --git a/yosys/passes/equiv/equiv_mark.cc b/yosys/passes/equiv/equiv_mark.cc index 22c501763..135eaf145 100644 --- a/yosys/passes/equiv/equiv_mark.cc +++ b/yosys/passes/equiv/equiv_mark.cc @@ -204,7 +204,7 @@ struct EquivMarkWorker struct EquivMarkPass : public Pass { EquivMarkPass() : Pass("equiv_mark", "mark equivalence checking regions") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -216,7 +216,7 @@ struct EquivMarkPass : public Pass { log("wires and cells.\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { log_header(design, "Executing EQUIV_MARK pass.\n"); diff --git a/yosys/passes/equiv/equiv_miter.cc b/yosys/passes/equiv/equiv_miter.cc index eb2e5a171..e06f9515b 100644 --- a/yosys/passes/equiv/equiv_miter.cc +++ b/yosys/passes/equiv/equiv_miter.cc @@ -261,7 +261,7 @@ struct EquivMiterWorker struct EquivMiterPass : public Pass { EquivMiterPass() : Pass("equiv_miter", "extract miter from equiv circuit") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -282,7 +282,7 @@ struct EquivMiterPass : public Pass { log(" Create compare logic that handles undefs correctly\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { EquivMiterWorker worker; worker.ct.setup(design); diff --git a/yosys/passes/equiv/equiv_opt.cc b/yosys/passes/equiv/equiv_opt.cc new file mode 100644 index 000000000..86550a69b --- /dev/null +++ b/yosys/passes/equiv/equiv_opt.cc @@ -0,0 +1,157 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct EquivOptPass:public ScriptPass +{ + EquivOptPass() : ScriptPass("equiv_opt", "prove equivalence for optimized circuit") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" equiv_opt [options] [command]\n"); + log("\n"); + log("This command checks circuit equivalence before and after an optimization pass.\n"); + log("\n"); + log(" -run :\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to the start of the command list, and empty to\n"); + log(" label is synonymous to the end of the command list.\n"); + log("\n"); + log(" -map \n"); + log(" expand the modules in this file before proving equivalence. this is\n"); + log(" useful for handling architecture-specific primitives.\n"); + log("\n"); + log(" -assert\n"); + log(" produce an error if the circuits are not equivalent\n"); + log("\n"); + log("The following commands are executed by this verification command:\n"); + help_script(); + log("\n"); + } + + std::string command, techmap_opts; + bool assert; + + void clear_flags() YS_OVERRIDE + { + command = ""; + techmap_opts = ""; + assert = false; + } + + void execute(std::vector < std::string > args, RTLIL::Design * design) YS_OVERRIDE + { + string run_from, run_to; + clear_flags(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-run" && argidx + 1 < args.size()) { + size_t pos = args[argidx + 1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos + 1); + continue; + } + if (args[argidx] == "-map" && argidx + 1 < args.size()) { + techmap_opts += " -map " + args[++argidx]; + continue; + } + if (args[argidx] == "-assert") { + assert = true; + continue; + } + break; + } + + for (; argidx < args.size(); argidx++) { + if (command.empty()) { + if (args[argidx].substr(0, 1) == "-") + cmd_error(args, argidx, "Unknown option."); + } else { + command += " "; + } + command += args[argidx]; + } + + if (command.empty()) + log_cmd_error("No optimization pass specified!\n"); + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + log_header(design, "Executing EQUIV_OPT pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("run_pass")) { + run("hierarchy -auto-top"); + run("design -save preopt"); + if (help_mode) + run("[command]"); + else + run(command); + run("design -stash postopt"); + } + + if (check_label("prepare")) { + run("design -copy-from preopt -as gold A:top"); + run("design -copy-from postopt -as gate A:top"); + } + + if ((!techmap_opts.empty() || help_mode) && check_label("techmap", "(only with -map)")) { + string opts; + if (help_mode) + opts = " -map ..."; + else + opts = techmap_opts; + run("techmap -D EQUIV -autoproc" + opts); + } + + if (check_label("prove")) { + run("equiv_make gold gate equiv"); + run("equiv_induct equiv"); + if (help_mode) + run("equiv_status [-assert] equiv"); + else if (assert) + run("equiv_status -assert equiv"); + else + run("equiv_status equiv"); + } + + if (check_label("restore")) { + run("design -load preopt"); + } + } +} EquivOptPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/equiv/equiv_purge.cc b/yosys/passes/equiv/equiv_purge.cc index 163b1009b..18b3e7d36 100644 --- a/yosys/passes/equiv/equiv_purge.cc +++ b/yosys/passes/equiv/equiv_purge.cc @@ -80,7 +80,7 @@ struct EquivPurgeWorker Wire *wire = module->addWire(name, GetSize(sig)); wire->port_input = true; module->connect(sig, wire); - log(" Module input: %s\n", log_signal(wire)); + log(" Module input: %s (%s)\n", log_signal(wire), log_signal(sig)); return module->addWire(NEW_ID, GetSize(sig)); } } @@ -142,7 +142,7 @@ struct EquivPurgeWorker for (auto bit : queue) visited.insert(bit); - + for (auto bit : queue) { auto &cells = up_bit2cells[bit]; @@ -176,7 +176,7 @@ struct EquivPurgeWorker struct EquivPurgePass : public Pass { EquivPurgePass() : Pass("equiv_purge", "purge equivalence checking module") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -187,7 +187,7 @@ struct EquivPurgePass : public Pass { log("ports as needed.\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { log_header(design, "Executing EQUIV_PURGE pass.\n"); diff --git a/yosys/passes/equiv/equiv_remove.cc b/yosys/passes/equiv/equiv_remove.cc index 770497a51..c5c28c7d9 100644 --- a/yosys/passes/equiv/equiv_remove.cc +++ b/yosys/passes/equiv/equiv_remove.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN struct EquivRemovePass : public Pass { EquivRemovePass() : Pass("equiv_remove", "remove $equiv cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -40,7 +40,7 @@ struct EquivRemovePass : public Pass { log(" keep gate circuit\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { bool mode_gold = false; bool mode_gate = false; diff --git a/yosys/passes/equiv/equiv_simple.cc b/yosys/passes/equiv/equiv_simple.cc index 49963ed68..c2fab26f2 100644 --- a/yosys/passes/equiv/equiv_simple.cc +++ b/yosys/passes/equiv/equiv_simple.cc @@ -35,13 +35,14 @@ struct EquivSimpleWorker ezSatPtr ez; SatGen satgen; int max_seq; + bool short_cones; bool verbose; pool> imported_cells_cache; - EquivSimpleWorker(const vector &equiv_cells, SigMap &sigmap, dict &bit2driver, int max_seq, bool verbose, bool model_undef) : + EquivSimpleWorker(const vector &equiv_cells, SigMap &sigmap, dict &bit2driver, int max_seq, bool short_cones, 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) + sigmap(sigmap), bit2driver(bit2driver), satgen(ez.get(), &sigmap), max_seq(max_seq), short_cones(short_cones), verbose(verbose) { satgen.model_undef = model_undef; } @@ -59,7 +60,7 @@ struct EquivSimpleWorker 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 (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_")) { if (!conn.first.in("\\CLK", "\\C")) next_seed.insert(bit); } else @@ -142,22 +143,44 @@ struct EquivSimpleWorker pool short_bits_cone_a, short_bits_cone_b; pool 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); + if (short_cones) + { + 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); + 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); + } + else + { + short_cells_cone_a = full_cells_cone_a; + short_bits_cone_a = full_bits_cone_a; + next_seed_a.swap(seed_a); + + short_cells_cone_b = full_cells_cone_b; + short_bits_cone_b = full_bits_cone_b; + next_seed_b.swap(seed_b); + } pool 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)); + #if 0 + for (auto cell : short_cells_cone_a) + log(" A-side cell: %s\n", log_id(cell)); + + for (auto cell : short_cells_cone_b) + log(" B-side cell: %s\n", log_id(cell)); + #endif + } for (auto cell : problem_cells) { auto key = pair(cell, step+1); @@ -250,7 +273,7 @@ struct EquivSimpleWorker struct EquivSimplePass : public Pass { EquivSimplePass() : Pass("equiv_simple", "try proving simple $equiv instances") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -264,6 +287,10 @@ struct EquivSimplePass : public Pass { log(" -undef\n"); log(" enable modelling of undef states\n"); log("\n"); + log(" -short\n"); + log(" create shorter input cones that stop at shared nodes. This yields\n"); + log(" simpler SAT problems but sometimes fails to prove equivalence.\n"); + log("\n"); log(" -nogroup\n"); log(" disabling grouping of $equiv cells by output wire\n"); log("\n"); @@ -271,9 +298,9 @@ struct EquivSimplePass : public Pass { log(" the max. number of time steps to be considered (default = 1)\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { - bool verbose = false, model_undef = false, nogroup = false; + bool verbose = false, short_cones = false, model_undef = false, nogroup = false; int success_counter = 0; int max_seq = 1; @@ -285,6 +312,10 @@ struct EquivSimplePass : public Pass { verbose = true; continue; } + if (args[argidx] == "-short") { + short_cones = true; + continue; + } if (args[argidx] == "-undef") { model_undef = true; continue; @@ -329,7 +360,7 @@ struct EquivSimplePass : public Pass { 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_")) + if (!ct.cell_known(cell->type) && !cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_", "$ff", "$_FF_")) continue; for (auto &conn : cell->connections()) if (yosys_celltypes.cell_output(cell->type, conn.first)) @@ -346,7 +377,7 @@ struct EquivSimplePass : public Pass { for (auto it2 : it.second) cells.push_back(it2.second); - EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, verbose, model_undef); + EquivSimpleWorker worker(cells, sigmap, bit2driver, max_seq, short_cones, verbose, model_undef); success_counter += worker.run(); } } diff --git a/yosys/passes/equiv/equiv_status.cc b/yosys/passes/equiv/equiv_status.cc index 7b9230b35..b4a93ccf5 100644 --- a/yosys/passes/equiv/equiv_status.cc +++ b/yosys/passes/equiv/equiv_status.cc @@ -24,7 +24,7 @@ PRIVATE_NAMESPACE_BEGIN struct EquivStatusPass : public Pass { EquivStatusPass() : Pass("equiv_status", "print status of equivalent checking module") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -36,7 +36,7 @@ struct EquivStatusPass : public Pass { log(" produce an error if any unproven $equiv cell is found\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { bool assert_mode = false; int unproven_count = 0; diff --git a/yosys/passes/equiv/equiv_struct.cc b/yosys/passes/equiv/equiv_struct.cc index c4ced6a71..a7973fd04 100644 --- a/yosys/passes/equiv/equiv_struct.cc +++ b/yosys/passes/equiv/equiv_struct.cc @@ -283,7 +283,7 @@ struct EquivStructWorker struct EquivStructPass : public Pass { EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -314,7 +314,7 @@ struct EquivStructPass : public Pass { log(" maximum number of iterations to run before aborting\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { pool fwonly_cells({ "$equiv" }); bool mode_icells = false; diff --git a/yosys/passes/fsm/fsm.cc b/yosys/passes/fsm/fsm.cc index 997558b85..c5cb338ab 100644 --- a/yosys/passes/fsm/fsm.cc +++ b/yosys/passes/fsm/fsm.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct FsmPass : public Pass { FsmPass() : Pass("fsm", "extract and optimize finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -68,7 +68,7 @@ struct FsmPass : public Pass { log(" passed through to fsm_recode pass\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_nomap = false; bool flag_norecode = false; diff --git a/yosys/passes/fsm/fsm_detect.cc b/yosys/passes/fsm/fsm_detect.cc index 6a560f166..5ae991b28 100644 --- a/yosys/passes/fsm/fsm_detect.cc +++ b/yosys/passes/fsm/fsm_detect.cc @@ -180,7 +180,7 @@ static void detect_fsm(RTLIL::Wire *wire) 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)); + Const val(set_output ? State::S1 : State::S0, GetSize(sig)); ce.set(sig, val); } } @@ -196,13 +196,13 @@ static void detect_fsm(RTLIL::Wire *wire) vector warnings; if (is_module_port) - warnings.push_back("Forcing fsm recoding on module port might result in larger circuit.\n"); + warnings.push_back("Forcing FSM recoding on module port might result in larger circuit.\n"); if (!looks_like_good_state_reg) - warnings.push_back("Users of state reg look like fsm recoding might result in larger circuit.\n"); + warnings.push_back("Users of state reg look like FSM recoding might result in larger circuit.\n"); if (has_init_attr) - warnings.push_back("Init value on fsm state registers are ignored. Possible simulation-synthesis mismatch!"); + warnings.push_back("Initialization value on FSM state register is ignored. Possible simulation-synthesis mismatch!\n"); if (!looks_like_state_reg) warnings.push_back("Doesn't look like a proper FSM. Possible simulation-synthesis mismatch!\n"); @@ -215,7 +215,7 @@ static void detect_fsm(RTLIL::Wire *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)); + log("FSM state register %s.%s already has fsm_encoding attribute.\n", log_id(wire->module), log_id(wire)); } } else @@ -236,7 +236,7 @@ static void detect_fsm(RTLIL::Wire *wire) log(" Users of register don't seem to benefit from recoding.\n"); if (has_init_attr) - log(" Register has an initialization value."); + log(" Register has an initialization value.\n"); if (is_self_resetting) log(" Circuit seems to be self-resetting.\n"); @@ -245,7 +245,7 @@ static void detect_fsm(RTLIL::Wire *wire) struct FsmDetectPass : public Pass { FsmDetectPass() : Pass("fsm_detect", "finding FSMs in design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -261,7 +261,7 @@ struct FsmDetectPass : public Pass { log("'fsm_encoding' attribute to \"none\".\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_DETECT pass (finding FSMs in design).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/fsm/fsm_expand.cc b/yosys/passes/fsm/fsm_expand.cc index e7b9dcf90..c34d0c15c 100644 --- a/yosys/passes/fsm/fsm_expand.cc +++ b/yosys/passes/fsm/fsm_expand.cc @@ -54,13 +54,27 @@ struct FsmExpand if (cell->getPort("\\A").size() < 2) return true; + int in_bits = 0; RTLIL::SigSpec new_signals; - if (cell->hasPort("\\A")) + + if (cell->hasPort("\\A")) { + in_bits += GetSize(cell->getPort("\\A")); new_signals.append(assign_map(cell->getPort("\\A"))); - if (cell->hasPort("\\B")) + } + + if (cell->hasPort("\\B")) { + in_bits += GetSize(cell->getPort("\\B")); new_signals.append(assign_map(cell->getPort("\\B"))); - if (cell->hasPort("\\S")) + } + + if (cell->hasPort("\\S")) { + in_bits += GetSize(cell->getPort("\\S")); new_signals.append(assign_map(cell->getPort("\\S"))); + } + + if (in_bits > 8) + return false; + if (cell->hasPort("\\Y")) new_signals.append(assign_map(cell->getPort("\\Y"))); @@ -173,6 +187,16 @@ struct FsmExpand new_ctrl_out.append(output_sig); fsm_cell->setPort("\\CTRL_OUT", new_ctrl_out); + if (GetSize(input_sig) > 10) + log_warning("Cell %s.%s (%s) has %d input bits, merging into FSM %s.%s might be problematic.\n", + log_id(cell->module), log_id(cell), log_id(cell->type), + GetSize(input_sig), log_id(fsm_cell->module), log_id(fsm_cell)); + + if (GetSize(fsm_data.transition_table) > 10000) + log_warning("Transition table for FSM %s.%s already has %d rows, merging more cells " + "into this FSM might be problematic.\n", log_id(fsm_cell->module), log_id(fsm_cell), + GetSize(fsm_data.transition_table)); + std::vector new_transition_table; for (auto &tr : fsm_data.transition_table) { for (int i = 0; i < (1 << input_sig.size()); i++) { @@ -241,7 +265,7 @@ struct FsmExpand struct FsmExpandPass : public Pass { FsmExpandPass() : Pass("fsm_expand", "expand FSM cells by merging logic into it") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -255,7 +279,7 @@ struct FsmExpandPass : public Pass { log("word-wide cells. Call with -full to consider all cells for merging.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool full_mode = false; diff --git a/yosys/passes/fsm/fsm_export.cc b/yosys/passes/fsm/fsm_export.cc index 1cbfcfae8..8eb1872f0 100644 --- a/yosys/passes/fsm/fsm_export.cc +++ b/yosys/passes/fsm/fsm_export.cc @@ -120,7 +120,7 @@ void write_kiss2(struct RTLIL::Module *module, struct RTLIL::Cell *cell, std::st */ struct FsmExportPass : public Pass { FsmExportPass() : Pass("fsm_export", "exporting FSMs to KISS2 files") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -143,7 +143,7 @@ struct FsmExportPass : public Pass { log(" use binary state encoding as state names instead of s0, s1, ...\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { dict::iterator attr_it; std::string arg; diff --git a/yosys/passes/fsm/fsm_extract.cc b/yosys/passes/fsm/fsm_extract.cc index 8a4ee3f26..6095eaf30 100644 --- a/yosys/passes/fsm/fsm_extract.cc +++ b/yosys/passes/fsm/fsm_extract.cc @@ -178,7 +178,7 @@ undef_bit_in_next_state: log_state_in = fsm_data.state_table.at(state_in); if (states.count(ce.values_map(ce.assign_map(dff_in)).as_const()) == 0) { - log(" transition: %10s %s -> INVALID_STATE(%s) %s %s\n", + log(" transition: %10s %s -> INVALID_STATE(%s) %s %s\n", log_signal(log_state_in), log_signal(tr.ctrl_in), log_signal(ce.values_map(ce.assign_map(dff_in))), log_signal(tr.ctrl_out), undef_bit_in_next_state_mode ? " SHORTENED" : ""); @@ -194,7 +194,7 @@ undef_bit_in_next_state: log_signal(log_state_in), log_signal(tr.ctrl_in), log_signal(fsm_data.state_table[tr.state_out]), log_signal(tr.ctrl_out)); } else { - log(" transition: %10s %s -> %10s %s \n", + 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)); } @@ -401,7 +401,7 @@ static void extract_fsm(RTLIL::Wire *wire) struct FsmExtractPass : public Pass { FsmExtractPass() : Pass("fsm_extract", "extracting FSMs in design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -417,7 +417,7 @@ struct FsmExtractPass : public Pass { log("'opt_clean' pass to eliminate this signal.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_EXTRACT pass (extracting FSM from design).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/fsm/fsm_info.cc b/yosys/passes/fsm/fsm_info.cc index 2cc1a7d53..0548259ee 100644 --- a/yosys/passes/fsm/fsm_info.cc +++ b/yosys/passes/fsm/fsm_info.cc @@ -30,7 +30,7 @@ PRIVATE_NAMESPACE_BEGIN struct FsmInfoPass : public Pass { FsmInfoPass() : Pass("fsm_info", "print information on finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -41,7 +41,7 @@ struct FsmInfoPass : public Pass { log("pass so that this information is included in the synthesis log file.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_INFO pass (dumping all available information on FSM cells).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/fsm/fsm_map.cc b/yosys/passes/fsm/fsm_map.cc index c42303752..90c958912 100644 --- a/yosys/passes/fsm/fsm_map.cc +++ b/yosys/passes/fsm/fsm_map.cc @@ -274,9 +274,6 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) { 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]; @@ -325,7 +322,7 @@ static void map_fsm(RTLIL::Cell *fsm_cell, RTLIL::Module *module) struct FsmMapPass : public Pass { FsmMapPass() : Pass("fsm_map", "mapping FSMs to basic logic") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -334,7 +331,7 @@ struct FsmMapPass : public Pass { log("This pass translates FSM cells to flip-flops and logic.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_MAP pass (mapping FSMs to basic logic).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/fsm/fsm_opt.cc b/yosys/passes/fsm/fsm_opt.cc index 5b1da44fc..3a6ac2746 100644 --- a/yosys/passes/fsm/fsm_opt.cc +++ b/yosys/passes/fsm/fsm_opt.cc @@ -323,7 +323,7 @@ PRIVATE_NAMESPACE_BEGIN struct FsmOptPass : public Pass { FsmOptPass() : Pass("fsm_opt", "optimize finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -334,7 +334,7 @@ struct FsmOptPass : public Pass { log("combination with the 'opt_clean' pass (see also 'help fsm').\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FSM_OPT pass (simple optimizations of FSMs).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/fsm/fsm_recode.cc b/yosys/passes/fsm/fsm_recode.cc index e1bde728f..fa1ff48cc 100644 --- a/yosys/passes/fsm/fsm_recode.cc +++ b/yosys/passes/fsm/fsm_recode.cc @@ -126,7 +126,7 @@ static void fsm_recode(RTLIL::Cell *cell, RTLIL::Module *module, FILE *fm_set_fs struct FsmRecodePass : public Pass { FsmRecodePass() : Pass("fsm_recode", "recoding finite state machines") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -151,7 +151,7 @@ struct FsmRecodePass : public Pass { log(" .map \n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { FILE *fm_set_fsm_file = NULL; FILE *encfile = NULL; diff --git a/yosys/passes/hierarchy/Makefile.inc b/yosys/passes/hierarchy/Makefile.inc index 1fb669c11..b3f139b72 100644 --- a/yosys/passes/hierarchy/Makefile.inc +++ b/yosys/passes/hierarchy/Makefile.inc @@ -1,5 +1,5 @@ OBJS += passes/hierarchy/hierarchy.o -OBJS += passes/hierarchy/singleton.o +OBJS += passes/hierarchy/uniquify.o OBJS += passes/hierarchy/submod.o diff --git a/yosys/passes/hierarchy/hierarchy.cc b/yosys/passes/hierarchy/hierarchy.cc index e21a7a4e3..0e28dbca2 100644 --- a/yosys/passes/hierarchy/hierarchy.cc +++ b/yosys/passes/hierarchy/hierarchy.cc @@ -2,6 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 Ruben Undheim * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,6 +19,7 @@ */ #include "kernel/yosys.h" +#include "frontends/verific/verific.h" #include #include #include @@ -138,15 +140,42 @@ void generate(RTLIL::Design *design, const std::vector &celltypes, } } -bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, std::vector &libdirs) +bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check, bool flag_simcheck, std::vector &libdirs) { bool did_something = false; std::map> array_cells; std::string filename; + bool has_interface_ports = false; + + // If any of the ports are actually interface ports, we will always need to + // reprocess the module: + if(!module->get_bool_attribute("\\interfaces_replaced_in_module")) { + for (auto &wire : module->wires_) { + if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) + has_interface_ports = true; + } + } + + // Always keep track of all derived interfaces available in the current module in 'interfaces_in_module': + dict interfaces_in_module; for (auto &cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; + if(cell->get_bool_attribute("\\is_interface")) { + RTLIL::Module *intf_module = design->modules_[cell->type]; + interfaces_in_module[cell->name] = intf_module; + } + } + + for (auto &cell_it : module->cells_) + { + RTLIL::Cell *cell = cell_it.second; + bool has_interfaces_not_found = false; + + std::vector connections_to_remove; + std::vector connections_to_add_name; + std::vector connections_to_add_signal; if (cell->type.substr(0, 7) == "$array:") { int pos_idx = cell->type.str().find_first_of(':'); @@ -157,6 +186,8 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check array_cells[cell] = std::pair(idx, num); cell->type = cell->type.str().substr(pos_type + 1); } + dict interfaces_to_add_to_submodule; + dict modports_used_in_submodule; if (design->modules_.count(cell->type) == 0) { @@ -173,24 +204,24 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check for (auto &dir : libdirs) { - filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".v"; - if (check_file_exists(filename)) { - std::vector args; - args.push_back(filename); - Frontend::frontend_call(design, NULL, filename, "verilog"); - goto loaded_module; - } + static const vector> extensions_list = + { + {".v", "verilog"}, + {".sv", "verilog -sv"}, + {".il", "ilang"} + }; - filename = dir + "/" + RTLIL::unescape_id(cell->type) + ".il"; - if (check_file_exists(filename)) { - std::vector args; - args.push_back(filename); - Frontend::frontend_call(design, NULL, filename, "ilang"); - goto loaded_module; + for (auto &ext : extensions_list) + { + filename = dir + "/" + RTLIL::unescape_id(cell->type) + ext.first; + if (check_file_exists(filename)) { + Frontend::frontend_call(design, NULL, filename, ext.second); + goto loaded_module; + } } } - if (flag_check && cell->type[0] != '$') + if ((flag_check || flag_simcheck) && 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; @@ -199,11 +230,85 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check if (design->modules_.count(cell->type) == 0) log_error("File `%s' from libdir does not declare module `%s'.\n", filename.c_str(), cell->type.c_str()); did_something = true; - } else - if (flag_check) + } else { + + RTLIL::Module *mod = design->module(cell->type); + + // Go over all connections and see if any of them are SV interfaces. If they are, then add the replacements to + // some lists, so that the ports for sub-modules can be replaced further down: + for (auto &conn : cell->connections()) { + if(mod->wires_.count(conn.first) != 0 && mod->wire(conn.first)->get_bool_attribute("\\is_interface")) { // Check if the connection is present as an interface in the sub-module's port list + //const pool &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_type"); + //for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness) + //} + + // Find if the sub-module has set a modport for the current interface connection: + const pool &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute("\\interface_modport"); + std::string interface_modport = ""; + for (auto &d : interface_modport_pool) { + interface_modport = "\\" + d; + } + if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute("\\is_interface")) { // Check if the connected wire is a potential interface in the parent module + std::string interface_name_str = conn.second.bits()[0].wire->name.str(); + interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name + interface_name_str = "\\" + interface_name_str; + RTLIL::IdString interface_name = interface_name_str; + bool not_found_interface = false; + if(module->get_bool_attribute("\\interfaces_replaced_in_module")) { // If 'interfaces' in the cell have not be been handled yet, there is no need to derive the sub-module either + // Check if the interface instance is present in module: + // Interface instances may either have the plain name or the name appended with '_inst_from_top_dummy'. + // Check for both of them here + int nexactmatch = interfaces_in_module.count(interface_name) > 0; + std::string interface_name_str2 = interface_name_str + "_inst_from_top_dummy"; + RTLIL::IdString interface_name2 = interface_name_str2; + int nmatch2 = interfaces_in_module.count(interface_name2) > 0; + if (nexactmatch > 0 || nmatch2 > 0) { + if (nexactmatch != 0) // Choose the one with the plain name if it exists + interface_name2 = interface_name; + RTLIL::Module *mod_replace_ports = interfaces_in_module.at(interface_name2); + for (auto &mod_wire : mod_replace_ports->wires_) { // Go over all wires in interface, and add replacements to lists. + std::string signal_name1 = conn.first.str() + "." + log_id(mod_wire.first); + std::string signal_name2 = interface_name.str() + "." + log_id(mod_wire.first); + connections_to_add_name.push_back(RTLIL::IdString(signal_name1)); + if(module->wires_.count(signal_name2) == 0) { + log_error("Could not find signal '%s' in '%s'\n", signal_name2.c_str(), log_id(module->name)); + } + else { + RTLIL::Wire *wire_in_parent = module->wire(signal_name2); + connections_to_add_signal.push_back(wire_in_parent); + } + } + connections_to_remove.push_back(conn.first); + interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2); + + // Add modports to a dict which will be passed to AstModule::derive + if (interface_modport != "") { + modports_used_in_submodule[conn.first] = interface_modport; + } + } + else not_found_interface = true; + } + else not_found_interface = true; + // If the interface instance has not already been derived in the module, we cannot complete at this stage. Set "has_interfaces_not_found" + // which will delay the expansion of this cell: + if (not_found_interface) { + // If we have already gone over all cells in this module, and the interface has still not been found - flag it as an error: + if(!(module->get_bool_attribute("\\cells_not_processed"))) { + log_warning("Could not find interface instance for `%s' in `%s'\n", log_id(interface_name), log_id(module)); + } + else { + // Only set has_interfaces_not_found if it would be possible to find them, since otherwiser we will end up in an infinite loop: + has_interfaces_not_found = true; + } + } + } + } + } + // + + if (flag_check || flag_simcheck) { - RTLIL::Module *mod = design->module(cell->type); - for (auto &conn : cell->connections()) + for (auto &conn : cell->connections()) { if (conn.first[0] == '$' && '0' <= conn.first[1] && conn.first[1] <= '9') { int id = atoi(conn.first.c_str()+1); if (id <= 0 || id > GetSize(mod->ports)) @@ -212,23 +317,78 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check } else if (mod->wire(conn.first) == nullptr || mod->wire(conn.first)->port_id == 0) log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a port named '%s'.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(conn.first)); + } for (auto ¶m : cell->parameters) - if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$') + if (mod->avail_parameters.count(param.first) == 0 && param.first[0] != '$' && strchr(param.first.c_str(), '.') == NULL) log_error("Module `%s' referenced in module `%s' in cell `%s' does not have a parameter named '%s'.\n", log_id(cell->type), log_id(module), log_id(cell), log_id(param.first)); + + } + } + RTLIL::Module *mod = design->modules_[cell->type]; + + if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { + if (flag_simcheck) + log_error("Module `%s' referenced in module `%s' in cell `%s' is a blackbox module.\n", + cell->type.c_str(), module->name.c_str(), cell->name.c_str()); + continue; } - if (cell->parameters.size() == 0) + // If interface instances not yet found, skip cell for now, and say we did something, so that we will return back here: + if(has_interfaces_not_found) { + did_something = true; // waiting for interfaces to be handled continue; + } - if (design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) + // Do the actual replacements of the SV interface port connection with the individual signal connections: + for(unsigned int i=0;iconnections_[connections_to_add_name[i]] = connections_to_add_signal[i]; + } + // Remove the connection for the interface itself: + for(unsigned int i=0;iconnections_.erase(connections_to_remove[i]); + } + + // If there are no overridden parameters AND not interfaces, then we can use the existing module instance as the type + // for the cell: + if (cell->parameters.size() == 0 && (interfaces_to_add_to_submodule.size() == 0 || !(cell->get_bool_attribute("\\module_not_derived")))) { + // If the cell being processed is an the interface instance itself, go down to "handle_interface_instance:", + // so that the signals of the interface are added to the parent module. + if (mod->get_bool_attribute("\\is_interface")) { + goto handle_interface_instance; + } continue; + } - RTLIL::Module *mod = design->modules_[cell->type]; - cell->type = mod->derive(design, cell->parameters); + cell->type = mod->derive(design, cell->parameters, interfaces_to_add_to_submodule, modports_used_in_submodule); cell->parameters.clear(); did_something = true; + + handle_interface_instance: + + // We add all the signals of the interface explicitly to the parent module. This is always needed when we encounter + // an interface instance: + if (mod->get_bool_attribute("\\is_interface") && cell->get_bool_attribute("\\module_not_derived")) { + cell->set_bool_attribute("\\is_interface"); + RTLIL::Module *derived_module = design->modules_[cell->type]; + interfaces_in_module[cell->name] = derived_module; + did_something = true; + } + // We clear 'module_not_derived' such that we will not rederive the cell again (needed when there are interfaces connected to the cell) + cell->attributes.erase("\\module_not_derived"); } + // Clear the attribute 'cells_not_processed' such that it can be known that we + // have been through all cells at least once, and that we can know whether + // to flag an error because of interface instances not found: + module->attributes.erase("\\cells_not_processed"); + + + // If any interface instances or interface ports were found in the module, we need to rederive it completely: + if ((interfaces_in_module.size() > 0 || has_interface_ports) && !module->get_bool_attribute("\\interfaces_replaced_in_module")) { + module->reprocess_module(design, interfaces_in_module); + return did_something; + } + for (auto &it : array_cells) { @@ -254,7 +414,7 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check 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) + if (conn_size == port_size || conn_size == 0) 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)); @@ -298,6 +458,20 @@ void hierarchy_clean(RTLIL::Design *design, RTLIL::Module *top, bool purge_lib) for (auto &it : design->modules_) if (used.count(it.second) == 0) del_modules.push_back(it.second); + else { + // Now all interface ports must have been exploded, and it is hence + // safe to delete all of the remaining dummy interface ports: + pool del_wires; + for(auto &wire : it.second->wires_) { + if ((wire.second->port_input || wire.second->port_output) && wire.second->get_bool_attribute("\\is_interface")) { + del_wires.insert(wire.second); + } + } + if (del_wires.size() > 0) { + it.second->remove(del_wires); + it.second->fixup_ports(); + } + } int del_counter = 0; for (auto mod : del_modules) { @@ -317,7 +491,7 @@ bool set_keep_assert(std::map &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")) + if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in("$assert", "$assume", "$live", "$fair", "$cover")) return cache[mod] = true; } return cache[mod]; @@ -336,9 +510,23 @@ int find_top_mod_score(Design *design, Module *module, dict &db) return db.at(module); } +RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod) +{ + if(top_mod != NULL && top_mod->get_bool_attribute("\\initial_top")) + return top_mod; + else { + for (auto mod : design->modules()) { + if (mod->get_bool_attribute("\\top")) { + return mod; + } + } + } + return NULL; +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -354,6 +542,10 @@ struct HierarchyPass : public Pass { 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(" -simcheck\n"); + log(" like -check, but also throw an error if blackbox modules are\n"); + log(" instantiated, and throw an error if the design has no top module\n"); + log("\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"); @@ -367,6 +559,11 @@ struct HierarchyPass : public Pass { 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(" -keep_portwidths\n"); + log(" per default this pass adjusts the port width on cells that are\n"); + log(" module instances when the width does not match the module port. this\n"); + log(" 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"); @@ -400,18 +597,21 @@ struct HierarchyPass : public Pass { log("in the current design.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing HIERARCHY pass (managing design hierarchy).\n"); bool flag_check = false; + bool flag_simcheck = false; bool purge_lib = false; RTLIL::Module *top_mod = NULL; + std::string load_top_mod; std::vector libdirs; bool auto_top_mode = false; bool generate_mode = false; bool keep_positionals = false; + bool keep_portwidths = false; bool nokeep_asserts = false; std::vector generate_cells; std::vector generate_ports; @@ -419,7 +619,7 @@ struct HierarchyPass : public Pass { size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-generate" && !flag_check && !top_mod) { + if (args[argidx] == "-generate" && !flag_check && !flag_simcheck && !top_mod) { generate_mode = true; log("Entering generate mode.\n"); while (++argidx < args.size()) { @@ -462,6 +662,10 @@ struct HierarchyPass : public Pass { flag_check = true; continue; } + if (args[argidx] == "-simcheck") { + flag_simcheck = true; + continue; + } if (args[argidx] == "-purge_lib") { purge_lib = true; continue; @@ -470,6 +674,10 @@ struct HierarchyPass : public Pass { keep_positionals = true; continue; } + if (args[argidx] == "-keep_portwidths") { + keep_portwidths = true; + continue; + } if (args[argidx] == "-nokeep_asserts") { nokeep_asserts = true; continue; @@ -488,7 +696,7 @@ struct HierarchyPass : public Pass { 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()); + load_top_mod = args[argidx]; continue; } if (args[argidx] == "-auto-top") { @@ -499,6 +707,22 @@ struct HierarchyPass : public Pass { } extra_args(args, argidx, design, false); + if (!load_top_mod.empty()) { +#ifdef YOSYS_ENABLE_VERIFIC + if (verific_import_pending) { + verific_import(design, load_top_mod); + top_mod = design->module(RTLIL::escape_id(load_top_mod)); + } +#endif + if (top_mod == NULL) + log_cmd_error("Module `%s' not found!\n", load_top_mod.c_str()); + } else { +#ifdef YOSYS_ENABLE_VERIFIC + if (verific_import_pending) + verific_import(design); +#endif + } + if (generate_mode) { generate(design, generate_cells, generate_ports); return; @@ -524,6 +748,17 @@ struct HierarchyPass : public Pass { log("Automatically selected %s as design top module.\n", log_id(top_mod)); } + if (flag_simcheck && top_mod == nullptr) + log_error("Design has no top module.\n"); + + if (top_mod != NULL) { + for (auto &mod_it : design->modules_) + if (mod_it.second == top_mod) + mod_it.second->attributes["\\initial_top"] = RTLIL::Const(1); + else + mod_it.second->attributes.erase("\\initial_top"); + } + bool did_something = true; while (did_something) { @@ -539,22 +774,46 @@ struct HierarchyPass : public Pass { } for (auto module : used_modules) { - if (expand_module(design, module, flag_check, libdirs)) + if (expand_module(design, module, flag_check, flag_simcheck, libdirs)) did_something = true; } + + + // The top module might have changed if interface instances have been detected in it: + RTLIL::Module *tmp_top_mod = check_if_top_has_changed(design, top_mod); + if (tmp_top_mod != NULL) { + if (tmp_top_mod != top_mod){ + top_mod = tmp_top_mod; + did_something = true; + } + } + + // Delete modules marked as 'to_delete': + std::vector modules_to_delete; + for(auto &mod_it : design->modules_) { + if (mod_it.second->get_bool_attribute("\\to_delete")) { + modules_to_delete.push_back(mod_it.second); + } + } + for(size_t i=0; iremove(modules_to_delete[i]); + } } + if (top_mod != NULL) { log_header(design, "Analyzing design hierarchy..\n"); hierarchy_clean(design, top_mod, purge_lib); } if (top_mod != NULL) { - for (auto &mod_it : design->modules_) + for (auto &mod_it : design->modules_) { if (mod_it.second == top_mod) mod_it.second->attributes["\\top"] = RTLIL::Const(1); else mod_it.second->attributes.erase("\\top"); + mod_it.second->attributes.erase("\\initial_top"); + } } if (!nokeep_asserts) { @@ -614,6 +873,72 @@ struct HierarchyPass : public Pass { } } + std::set blackbox_derivatives; + std::vector design_modules = design->modules(); + + for (auto module : design_modules) + for (auto cell : module->cells()) + { + Module *m = design->module(cell->type); + + if (m == nullptr) + continue; + + if (m->get_bool_attribute("\\blackbox") && !cell->parameters.empty()) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } + + for (auto &conn : cell->connections()) + { + Wire *w = m->wire(conn.first); + + if (w == nullptr || w->port_id == 0) + continue; + + if (GetSize(conn.second) == 0) + continue; + + SigSpec sig = conn.second; + + if (!keep_portwidths && GetSize(w) != GetSize(conn.second)) + { + if (GetSize(w) < GetSize(conn.second)) + { + int n = GetSize(conn.second) - GetSize(w); + if (!w->port_input && w->port_output) + module->connect(sig.extract(GetSize(w), n), Const(0, n)); + sig.remove(GetSize(w), n); + } + else + { + int n = GetSize(w) - GetSize(conn.second); + if (w->port_input && !w->port_output) + sig.append(Const(0, n)); + else + sig.append(module->addWire(NEW_ID, n)); + } + + if (!conn.second.is_fully_const() || !w->port_input || w->port_output) + log_warning("Resizing cell port %s.%s.%s from %d bits to %d bits.\n", log_id(module), log_id(cell), + log_id(conn.first), GetSize(conn.second), GetSize(sig)); + cell->setPort(conn.first, sig); + } + + if (w->port_output && !w->port_input && sig.has_const()) + log_error("Output port %s.%s.%s (%s) is connected to constants: %s\n", + log_id(module), log_id(cell), log_id(conn.first), log_id(cell->type), log_signal(sig)); + } + } + + for (auto module : blackbox_derivatives) + design->remove(module); + log_pop(); } } HierarchyPass; diff --git a/yosys/passes/hierarchy/submod.cc b/yosys/passes/hierarchy/submod.cc index 9f312f82d..ec242aa1f 100644 --- a/yosys/passes/hierarchy/submod.cc +++ b/yosys/passes/hierarchy/submod.cc @@ -169,6 +169,7 @@ struct SubmodWorker } new_mod->fixup_ports(); + ct.setup_module(new_mod); for (RTLIL::Cell *cell : submod.cells) { RTLIL::Cell *new_cell = new_mod->addCell(cell->name, cell); @@ -268,7 +269,7 @@ struct SubmodWorker struct SubmodPass : public Pass { SubmodPass() : Pass("submod", "moving part of a module to a new submodule") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -296,7 +297,7 @@ struct SubmodPass : public Pass { log("with -copy to not modify the source module.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing SUBMOD pass (moving cells to submodules as requested).\n"); log_push(); diff --git a/yosys/passes/hierarchy/singleton.cc b/yosys/passes/hierarchy/uniquify.cc similarity index 68% rename from yosys/passes/hierarchy/singleton.cc rename to yosys/passes/hierarchy/uniquify.cc index 03c365fb5..c88ecd82e 100644 --- a/yosys/passes/hierarchy/singleton.cc +++ b/yosys/passes/hierarchy/uniquify.cc @@ -22,28 +22,28 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct SingletonPass : public Pass { - SingletonPass() : Pass("singleton", "create singleton modules") { } - virtual void help() +struct UniquifyPass : public Pass { + UniquifyPass() : Pass("uniquify", "create unique copies of modules") { } + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" singleton [selection]\n"); + log(" uniquify [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("optimizations and other operations on the design. This pass creates unique\n"); log("modules for all selected cells. The created modules are marked with the\n"); - log("'singleton' attribute.\n"); + log("'unique' 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("This commands only operates on modules that by themself have the 'unique'\n"); + log("attribute set (the 'top' module is unique implicitly).\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - log_header(design, "Executing SINGLETON pass (creating singleton modules).\n"); + log_header(design, "Executing UNIQUIFY pass (creating unique copies of modules).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -56,7 +56,7 @@ struct SingletonPass : public Pass { extra_args(args, argidx, design); bool did_something = true; - int singleton_cnt = 0; + int count = 0; while (did_something) { @@ -64,12 +64,13 @@ struct SingletonPass : public Pass { for (auto module : design->selected_modules()) { - if (!module->get_bool_attribute("\\singleton") && !module->get_bool_attribute("\\top")) + if (!module->get_bool_attribute("\\unique") && !module->get_bool_attribute("\\top")) continue; for (auto cell : module->selected_cells()) { - auto tmod = design->module(cell->type); + Module *tmod = design->module(cell->type); + IdString newname = module->name.str() + "." + log_id(cell->name); if (tmod == nullptr) continue; @@ -77,25 +78,25 @@ struct SingletonPass : public Pass { if (tmod->get_bool_attribute("\\blackbox")) continue; - if (tmod->get_bool_attribute("\\singleton")) + if (tmod->get_bool_attribute("\\unique") && newname == tmod->name) continue; - cell->type = module->name.str() + "." + log_id(cell->name); - log("Creating singleton '%s'.\n", log_id(cell->type)); + log("Creating module %s from %s.\n", log_id(newname), log_id(tmod)); auto smod = tmod->clone(); - smod->name = cell->type; - smod->set_bool_attribute("\\singleton"); + smod->name = newname; + cell->type = newname; + smod->set_bool_attribute("\\unique"); design->add(smod); did_something = true; - singleton_cnt++; + count++; } } } - log("Created %d singleton modules.\n", singleton_cnt); + log("Created %d unique modules.\n", count); } -} SingletonPass; +} UniquifyPass; PRIVATE_NAMESPACE_END diff --git a/yosys/passes/memory/Makefile.inc b/yosys/passes/memory/Makefile.inc index ad359c01b..e468c3a07 100644 --- a/yosys/passes/memory/Makefile.inc +++ b/yosys/passes/memory/Makefile.inc @@ -7,4 +7,5 @@ OBJS += passes/memory/memory_unpack.o OBJS += passes/memory/memory_bram.o OBJS += passes/memory/memory_map.o OBJS += passes/memory/memory_memx.o +OBJS += passes/memory/memory_nordff.o diff --git a/yosys/passes/memory/memory.cc b/yosys/passes/memory/memory.cc index 947d598be..712bc2537 100644 --- a/yosys/passes/memory/memory.cc +++ b/yosys/passes/memory/memory.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct MemoryPass : public Pass { MemoryPass() : Pass("memory", "translate memories to basic cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -48,7 +48,7 @@ struct MemoryPass : public Pass { log("or multiport memory blocks if called with the -nomap option.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_nomap = false; bool flag_nordff = false; diff --git a/yosys/passes/memory/memory_bram.cc b/yosys/passes/memory/memory_bram.cc index a7f9cf382..cf4095d06 100644 --- a/yosys/passes/memory/memory_bram.cc +++ b/yosys/passes/memory/memory_bram.cc @@ -472,8 +472,12 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, std::vector new_wr_en(GetSize(old_wr_en)); std::vector new_wr_data(GetSize(old_wr_data)); std::vector new_rd_data(GetSize(old_rd_data)); + std::vector> new_initdata; std::vector shuffle_map; + if (cell_init) + new_initdata.resize(mem_size); + for (auto &it : en_order) { auto &bits = bits_wr_en.at(it); @@ -489,6 +493,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, } for (int j = 0; j < rd_ports; j++) new_rd_data[j].append(old_rd_data[j][bits[i]]); + if (cell_init) { + for (int j = 0; j < mem_size; j++) + new_initdata[j].push_back(initdata[j][bits[i]]); + } shuffle_map.push_back(bits[i]); } @@ -499,6 +507,10 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, } for (int j = 0; j < rd_ports; j++) new_rd_data[j].append(State::Sx); + if (cell_init) { + for (int j = 0; j < mem_size; j++) + new_initdata[j].push_back(State::Sx); + } shuffle_map.push_back(-1); } } @@ -522,6 +534,11 @@ bool replace_cell(Cell *cell, const rules_t &rules, const rules_t::bram_t &bram, for (int i = 0; i < rd_ports; i++) rd_data.replace(i*mem_width, new_rd_data[i]); + + if (cell_init) { + for (int i = 0; i < mem_size; i++) + initdata[i] = Const(new_initdata[i]); + } } // assign write ports @@ -623,6 +640,7 @@ grow_read_ports:; pi.sig_addr = SigSpec(); pi.sig_data = SigSpec(); pi.sig_en = SigSpec(); + pi.make_outreg = false; } new_portinfos.push_back(pi); if (pi.dupidx == dup_count-1) { @@ -1120,7 +1138,7 @@ void handle_cell(Cell *cell, const rules_t &rules) struct MemoryBramPass : public Pass { MemoryBramPass() : Pass("memory_bram", "map memories to block rams") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1210,7 +1228,7 @@ struct MemoryBramPass : public Pass { log("the data bits to accommodate the enable pattern of port A.\n"); log("\n"); } - virtual void execute(vector args, Design *design) + void execute(vector args, Design *design) YS_OVERRIDE { rules_t rules; diff --git a/yosys/passes/memory/memory_collect.cc b/yosys/passes/memory/memory_collect.cc index ab66e3fb8..369fcc84e 100644 --- a/yosys/passes/memory/memory_collect.cc +++ b/yosys/passes/memory/memory_collect.cc @@ -184,9 +184,6 @@ Cell *handle_memory(Module *module, RTLIL::Memory *memory) mem->parameters["\\OFFSET"] = Const(memory->start_offset); mem->parameters["\\SIZE"] = Const(memory->size); mem->parameters["\\ABITS"] = Const(addr_bits); - - while (GetSize(init_data) > 1 && init_data.bits.back() == State::Sx && init_data.bits[GetSize(init_data)-2] == State::Sx) - init_data.bits.pop_back(); mem->parameters["\\INIT"] = init_data; log_assert(sig_wr_clk.size() == wr_ports); @@ -246,7 +243,7 @@ static void handle_module(Design *design, Module *module) struct MemoryCollectPass : public Pass { MemoryCollectPass() : Pass("memory_collect", "creating multi-port memory cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -256,7 +253,7 @@ struct MemoryCollectPass : public Pass { log("memory cells.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_COLLECT pass (generating $mem cells).\n"); extra_args(args, 1, design); for (auto &mod_it : design->modules_) diff --git a/yosys/passes/memory/memory_dff.cc b/yosys/passes/memory/memory_dff.cc index 40691d160..220d29295 100644 --- a/yosys/passes/memory/memory_dff.cc +++ b/yosys/passes/memory/memory_dff.cc @@ -33,8 +33,20 @@ struct MemoryDffWorker dict sigbit_users_count; dict mux_cells_a, mux_cells_b; pool forward_merged_dffs, candidate_dffs; + pool init_bits; - MemoryDffWorker(Module *module) : module(module), sigmap(module) { } + MemoryDffWorker(Module *module) : module(module), sigmap(module) + { + for (auto wire : module->wires()) { + if (wire->attributes.count("\\init") == 0) + continue; + SigSpec sig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + init_bits.insert(sig[i]); + } + } bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false) { @@ -45,6 +57,9 @@ struct MemoryDffWorker if (bit.wire == NULL) continue; + if (!after && init_bits.count(sigmap(bit))) + return false; + for (auto cell : dff_cells) { if (after && forward_merged_dffs.count(cell)) @@ -72,6 +87,9 @@ struct MemoryDffWorker if (d.size() != 1) continue; + if (after && init_bits.count(d)) + return false; + bit = d; clk = this_clk; clk_polarity = this_clk_polarity; @@ -265,7 +283,7 @@ struct MemoryDffWorker struct MemoryDffPass : public Pass { MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -279,7 +297,7 @@ struct MemoryDffPass : public Pass { log(" do not merge registers on read ports\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_wr_only = false; diff --git a/yosys/passes/memory/memory_map.cc b/yosys/passes/memory/memory_map.cc index bffeec857..a0b808e56 100644 --- a/yosys/passes/memory/memory_map.cc +++ b/yosys/passes/memory/memory_map.cc @@ -352,7 +352,7 @@ struct MemoryMapWorker struct MemoryMapPass : public Pass { MemoryMapPass() : Pass("memory_map", "translate multiport memories to basic cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -362,7 +362,7 @@ struct MemoryMapPass : public Pass { log("pass to word-wide DFFs and address decoders.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { 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()) diff --git a/yosys/passes/memory/memory_memx.cc b/yosys/passes/memory/memory_memx.cc index 2b02e2490..958370164 100644 --- a/yosys/passes/memory/memory_memx.cc +++ b/yosys/passes/memory/memory_memx.cc @@ -28,7 +28,7 @@ PRIVATE_NAMESPACE_BEGIN struct MemoryMemxPass : public Pass { MemoryMemxPass() : Pass("memory_memx", "emulate vlog sim behavior for mem ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -38,7 +38,7 @@ struct MemoryMemxPass : public Pass { log("behavior for out-of-bounds memory reads and writes.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_MEMX pass (converting $mem cells to logic and flip-flops).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/memory/memory_nordff.cc b/yosys/passes/memory/memory_nordff.cc new file mode 100644 index 000000000..ba0361c0f --- /dev/null +++ b/yosys/passes/memory/memory_nordff.cc @@ -0,0 +1,121 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 MemoryNordffPass : public Pass { + MemoryNordffPass() : Pass("memory_nordff", "extract read port FFs from memories") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" memory_nordff [options] [selection]\n"); + log("\n"); + log("This pass extracts FFs from memory read ports. This results in a netlist\n"); + log("similar to what one would get from calling memory_dff with -nordff.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing MEMORY_NORDFF pass (extracting $dff cells from $mem).\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 module : design->selected_modules()) + for (auto cell : vector(module->selected_cells())) + { + if (cell->type != "$mem") + continue; + + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + + SigSpec rd_addr = cell->getPort("\\RD_ADDR"); + SigSpec rd_data = cell->getPort("\\RD_DATA"); + SigSpec rd_clk = cell->getPort("\\RD_CLK"); + SigSpec rd_en = cell->getPort("\\RD_EN"); + Const rd_clk_enable = cell->getParam("\\RD_CLK_ENABLE"); + Const rd_clk_polarity = cell->getParam("\\RD_CLK_POLARITY"); + + for (int i = 0; i < rd_ports; i++) + { + bool clk_enable = rd_clk_enable[i] == State::S1; + + if (clk_enable) + { + bool clk_polarity = cell->getParam("\\RD_CLK_POLARITY")[i] == State::S1; + bool transparent = cell->getParam("\\RD_TRANSPARENT")[i] == State::S1; + + SigSpec clk = cell->getPort("\\RD_CLK")[i] ; + SigSpec en = cell->getPort("\\RD_EN")[i]; + Cell *c; + + if (transparent) + { + SigSpec sig_q = module->addWire(NEW_ID, abits); + SigSpec sig_d = rd_addr.extract(abits * i, abits); + rd_addr.replace(abits * i, sig_q); + if (en != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, en); + c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity); + } + else + { + SigSpec sig_d = module->addWire(NEW_ID, width); + SigSpec sig_q = rd_data.extract(width * i, width); + rd_data.replace(width *i, sig_d); + if (en != State::S1) + sig_d = module->Mux(NEW_ID, sig_q, sig_d, en); + c = module->addDff(NEW_ID, clk, sig_d, sig_q, clk_polarity); + } + + log("Extracted %s FF from read port %d of %s.%s: %s\n", transparent ? "addr" : "data", + i, log_id(module), log_id(cell), log_id(c)); + } + + rd_en[i] = State::S1; + rd_clk[i] = State::S0; + rd_clk_enable[i] = State::S0; + rd_clk_polarity[i] = State::S1; + } + + cell->setPort("\\RD_ADDR", rd_addr); + cell->setPort("\\RD_DATA", rd_data); + cell->setPort("\\RD_CLK", rd_clk); + cell->setPort("\\RD_EN", rd_en); + cell->setParam("\\RD_CLK_ENABLE", rd_clk_enable); + cell->setParam("\\RD_CLK_POLARITY", rd_clk_polarity); + } + } +} MemoryNordffPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/memory/memory_share.cc b/yosys/passes/memory/memory_share.cc index ca09ac52c..172afe0cb 100644 --- a/yosys/passes/memory/memory_share.cc +++ b/yosys/passes/memory/memory_share.cc @@ -726,7 +726,7 @@ struct MemoryShareWorker struct MemorySharePass : public Pass { MemorySharePass() : Pass("memory_share", "consolidate memory ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -752,7 +752,7 @@ struct MemorySharePass : public Pass { log("optimizations) such as \"share\" and \"opt_merge\".\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MEMORY_SHARE pass (consolidating $memrd/$memwr cells).\n"); extra_args(args, 1, design); for (auto module : design->selected_modules()) diff --git a/yosys/passes/memory/memory_unpack.cc b/yosys/passes/memory/memory_unpack.cc index a0fc31b5e..49ec66792 100644 --- a/yosys/passes/memory/memory_unpack.cc +++ b/yosys/passes/memory/memory_unpack.cc @@ -127,7 +127,7 @@ void handle_module(RTLIL::Design *design, RTLIL::Module *module) struct MemoryUnpackPass : public Pass { MemoryUnpackPass() : Pass("memory_unpack", "unpack multi-port memory cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -137,7 +137,7 @@ struct MemoryUnpackPass : public Pass { log("$memwr cells. It is the counterpart to the memory_collect pass.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) { + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { 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_) diff --git a/yosys/passes/opt/Makefile.inc b/yosys/passes/opt/Makefile.inc index a8b1537bb..c3e0a2a40 100644 --- a/yosys/passes/opt/Makefile.inc +++ b/yosys/passes/opt/Makefile.inc @@ -10,5 +10,8 @@ OBJS += passes/opt/opt_expr.o ifneq ($(SMALL),1) OBJS += passes/opt/share.o OBJS += passes/opt/wreduce.o +OBJS += passes/opt/opt_demorgan.o +OBJS += passes/opt/rmports.o +OBJS += passes/opt/opt_lut.o endif diff --git a/yosys/passes/opt/opt.cc b/yosys/passes/opt/opt.cc index 021c1a03f..a4aca2fee 100644 --- a/yosys/passes/opt/opt.cc +++ b/yosys/passes/opt/opt.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct OptPass : public Pass { OptPass() : Pass("opt", "perform simple optimizations") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -63,7 +63,7 @@ struct OptPass : public Pass { log("\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string opt_clean_args; std::string opt_expr_args; diff --git a/yosys/passes/opt/opt_clean.cc b/yosys/passes/opt/opt_clean.cc index 6600ffa25..c3b13acaf 100644 --- a/yosys/passes/opt/opt_clean.cc +++ b/yosys/passes/opt/opt_clean.cc @@ -64,7 +64,7 @@ struct keep_cache_t bool query(Cell *cell) { - if (cell->type.in("$memwr", "$meminit", "$assert", "$assume")) + if (cell->type.in("$memwr", "$meminit", "$assert", "$assume", "$live", "$fair", "$cover")) return true; if (cell->has_keep_attr()) @@ -91,9 +91,16 @@ void rmunused_module_cells(Module *module, bool verbose) 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)) + for (auto raw_bit : it2.second) { + if (raw_bit.wire == nullptr) + continue; + auto bit = sigmap(raw_bit); + if (bit.wire == nullptr) + log_warning("Driver-driver conflict for %s between cell %s.%s and constant %s in %s: Resolved using constant.\n", + log_signal(raw_bit), log_id(cell), log_id(it2.first), log_signal(bit), log_id(module)); if (bit.wire != nullptr) wire2driver[bit].insert(cell); + } } if (keep_cache.query(cell)) queue.insert(cell); @@ -320,19 +327,89 @@ void rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos 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); + del_wires_count++; } module->remove(del_wires); - count_rm_wires += del_wires.size();; + count_rm_wires += del_wires.size(); - if (del_wires_count > 0) + if (verbose && 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) +bool rmunused_module_init(RTLIL::Module *module, bool purge_mode, bool verbose) +{ + bool did_something = false; + CellTypes fftypes; + fftypes.setup_internals_mem(); + + SigMap sigmap(module); + dict qbits; + + for (auto cell : module->cells()) + if (fftypes.cell_known(cell->type) && cell->hasPort("\\Q")) + { + SigSpec sig = cell->getPort("\\Q"); + + for (int i = 0; i < GetSize(sig); i++) + { + SigBit bit = sig[i]; + + if (bit.wire == nullptr || bit.wire->attributes.count("\\init") == 0) + continue; + + Const init = bit.wire->attributes.at("\\init"); + + if (i >= GetSize(init) || init[i] == State::Sx || init[i] == State::Sz) + continue; + + sigmap.add(bit); + qbits[bit] = init[i]; + } + } + + for (auto wire : module->wires()) + { + if (!purge_mode && wire->name[0] == '\\') + continue; + + if (wire->attributes.count("\\init") == 0) + continue; + + Const init = wire->attributes.at("\\init"); + + for (int i = 0; i < GetSize(wire) && i < GetSize(init); i++) + { + if (init[i] == State::Sx || init[i] == State::Sz) + continue; + + SigBit wire_bit = SigBit(wire, i); + SigBit mapped_wire_bit = sigmap(wire_bit); + + if (wire_bit == mapped_wire_bit) + goto next_wire; + + if (qbits.count(sigmap(SigBit(wire, i))) == 0) + goto next_wire; + + if (qbits.at(sigmap(SigBit(wire, i))) != init[i]) + goto next_wire; + } + + if (verbose) + log(" removing redundant init attribute on %s.\n", log_id(wire)); + + wire->attributes.erase("\\init"); + did_something = true; + next_wire:; + } + + return did_something; +} + +void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose, bool rminit) { if (verbose) log("Finding unused cells or wires in module %s..\n", module->name.c_str()); @@ -358,11 +435,14 @@ void rmunused_module(RTLIL::Module *module, bool purge_mode, bool verbose) rmunused_module_cells(module, verbose); rmunused_module_signals(module, purge_mode, verbose); + + if (rminit && rmunused_module_init(module, purge_mode, verbose)) + rmunused_module_signals(module, purge_mode, verbose); } struct OptCleanPass : public Pass { OptCleanPass() : Pass("opt_clean", "remove unused cells and wires") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -379,7 +459,7 @@ struct OptCleanPass : public Pass { log(" also remove internal nets if they have a public name\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool purge_mode = false; @@ -406,9 +486,12 @@ struct OptCleanPass : public Pass { for (auto module : design->selected_whole_modules_warn()) { if (module->has_processes_warn()) continue; - rmunused_module(module, purge_mode, true); + rmunused_module(module, purge_mode, true, true); } + 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(); @@ -422,7 +505,7 @@ struct OptCleanPass : public Pass { struct CleanPass : public Pass { CleanPass() : Pass("clean", "remove unused cells and wires") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -437,7 +520,7 @@ struct CleanPass : public Pass { log("in -purge mode between the commands.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool purge_mode = false; @@ -465,7 +548,7 @@ struct CleanPass : public Pass { for (auto module : design->selected_whole_modules()) { if (module->has_processes()) continue; - rmunused_module(module, purge_mode, false); + rmunused_module(module, purge_mode, false, false); } if (count_rm_cells > 0 || count_rm_wires > 0) diff --git a/yosys/passes/opt/opt_demorgan.cc b/yosys/passes/opt/opt_demorgan.cc new file mode 100644 index 000000000..1699a6454 --- /dev/null +++ b/yosys/passes/opt/opt_demorgan.cc @@ -0,0 +1,202 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 Clifford Wolf + * + * 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 + +void demorgan_worker( + ModIndex& index, + Cell *cell, + unsigned int& cells_changed) +{ + SigMap& sigmap = index.sigmap; + auto m = cell->module; + + //TODO: Add support for reduce_xor + //DeMorgan of XOR is either XOR (if even number of inputs) or XNOR (if odd number) + + if( (cell->type != "$reduce_and") && (cell->type != "$reduce_or") ) + return; + + auto insig = sigmap(cell->getPort("\\A")); + log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), GetSize(insig)); + int num_inverted = 0; + for(int i=0; i ports = index.query_ports(b); + bool inverted = false; + for(auto x : ports) + { + if(x.port == "\\Y" && x.cell->type == "$_NOT_") + { + inverted = true; + break; + } + } + + if(inverted) + num_inverted ++; + } + + //Stop if less than half of the inputs are inverted + if(num_inverted*2 < GetSize(insig)) + { + log(" %d / %d inputs are inverted, not pushing\n", num_inverted, GetSize(insig)); + return; + } + + //More than half of the inputs are inverted! Push through + cells_changed ++; + log(" %d / %d inputs are inverted, pushing inverter through reduction\n", num_inverted, GetSize(insig)); + + //For each input, either add or remove the inverter as needed + //TODO: this duplicates the loop up above, can we refactor it? + for(int i=0; i ports = index.query_ports(b); + RTLIL::Cell* srcinv = NULL; + for(auto x : ports) + { + if(x.port == "\\Y" && x.cell->type == "$_NOT_") + { + srcinv = x.cell; + break; + } + } + + //We are NOT inverted! Add an inverter + if(!srcinv) + { + auto inverted_b = m->addWire(NEW_ID); + m->addNot(NEW_ID, RTLIL::SigSpec(b), RTLIL::SigSpec(inverted_b)); + insig[i] = inverted_b; + } + + //We ARE inverted - bypass it + //Don't automatically delete the inverter since other stuff might still use it + else + insig[i] = srcinv->getPort("\\A"); + } + + //Cosmetic fixup: If our input is just a scrambled version of one bus, rearrange it + //Reductions are all commutative, so there's no point in having them in a weird order + bool same_signal = true; + RTLIL::Wire* srcwire = insig[0].wire; + dict seen_bits; + for(int i=0; iwidth) ) + { + log("Rearranging bits\n"); + RTLIL::SigSpec newsig; + for(int i=0; isetPort("\\A", insig); + + //Change the cell type + if(cell->type == "$reduce_and") + cell->type = "$reduce_or"; + else if(cell->type == "$reduce_or") + cell->type = "$reduce_and"; + //don't change XOR + + //Add an inverter to the output + auto inverted_output = cell->getPort("\\Y"); + auto uninverted_output = m->addWire(NEW_ID); + m->addNot(NEW_ID, RTLIL::SigSpec(uninverted_output), inverted_output); + cell->setPort("\\Y", uninverted_output); +} + +struct OptDemorganPass : public Pass { + OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_demorgan [selection]\n"); + log("\n"); + log("This pass pushes inverters through $reduce_* cells if this will reduce the\n"); + log("overall gate count of the circuit\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n"); + + int argidx = 0; + extra_args(args, argidx, design); + + unsigned int cells_changed = 0; + for (auto module : design->selected_modules()) + { + ModIndex index(module); + for (auto cell : module->selected_cells()) + demorgan_worker(index, cell, cells_changed); + } + + if(cells_changed) + log("Pushed inverters through %u reductions\n", cells_changed); + } +} OptDemorganPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/opt/opt_expr.cc b/yosys/passes/opt/opt_expr.cc index b62eae285..26a3ca7bc 100644 --- a/yosys/passes/opt/opt_expr.cc +++ b/yosys/passes/opt/opt_expr.cc @@ -259,6 +259,45 @@ bool is_one_or_minus_one(const Const &value, bool is_signed, bool &is_negative) return last_bit_one; } +int get_highest_hot_index(RTLIL::SigSpec signal) +{ + for (int i = GetSize(signal) - 1; i >= 0; i--) + { + if (signal[i] == RTLIL::State::S0) + continue; + + if (signal[i] == RTLIL::State::S1) + return i; + + break; + } + + return -1; +} + +// if the signal has only one bit set, return the index of that bit. +// otherwise return -1 +int get_onehot_bit_index(RTLIL::SigSpec signal) +{ + int bit_index = -1; + + for (int i = 0; i < GetSize(signal); i++) + { + if (signal[i] == RTLIL::State::S0) + continue; + + if (signal[i] != RTLIL::State::S1) + return -1; + + if (bit_index != -1) + return -1; + + bit_index = i; + } + + return bit_index; +} + 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)) @@ -348,19 +387,20 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons 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) + if (cell->type.in("$and", "$logic_and") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool()) 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) + if (cell->type.in("$or", "$logic_or") && GetSize(cell->getPort("\\A")) == 1 && GetSize(cell->getPort("\\B")) == 1 && !cell->getParam("\\A_SIGNED").as_bool()) detect_const_or = true; if (detect_const_and || detect_const_or) { pool input_bits = assign_map(cell->getPort("\\A")).to_sigbit_pool(); - bool found_zero = false, found_one = false, found_inv = false; + bool found_zero = false, found_one = false, found_undef = false, found_inv = false, many_conconst = false; + SigBit non_const_input = State::Sm; if (cell->hasPort("\\B")) { vector more_bits = assign_map(cell->getPort("\\B")).to_sigbit_vector(); @@ -368,12 +408,20 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } 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 (bit.wire) { + if (invert_map.count(bit) && input_bits.count(invert_map.at(bit))) + found_inv = true; + if (non_const_input != State::Sm) + many_conconst = true; + non_const_input = many_conconst ? State::Sm : bit; + } else { + if (bit == State::S0) + found_zero = true; + else if (bit == State::S1) + found_one = true; + else + found_undef = true; + } } if (detect_const_and && (found_zero || found_inv)) { @@ -387,6 +435,12 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons replace_cell(assign_map, module, cell, "const_or", "\\Y", RTLIL::State::S1); goto next_cell; } + + if (non_const_input != State::Sm && !found_undef) { + cover("opt.opt_expr.and_or_buffer"); + replace_cell(assign_map, module, cell, "and_or_buffer", "\\Y", non_const_input); + goto next_cell; + } } if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor", "$neg") && @@ -411,6 +465,53 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons if (group_cell_inputs(module, cell, true, assign_map)) goto next_cell; + if (cell->type == "$logic_not" || cell->type == "$logic_and" || cell->type == "$logic_or" || + cell->type == "$reduce_or" || cell->type == "$reduce_and" || cell->type == "$reduce_bool") + { + SigBit neutral_bit = cell->type == "$reduce_and" ? State::S1 : State::S0; + + RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); + RTLIL::SigSpec new_sig_a; + + for (auto bit : sig_a) + if (bit != neutral_bit) new_sig_a.append(bit); + + if (GetSize(new_sig_a) == 0) + new_sig_a.append(neutral_bit); + + if (GetSize(new_sig_a) < GetSize(sig_a)) { + cover_list("opt.opt_expr.fine.neutral_A", "$logic_not", "$logic_and", "$logic_or", "$reduce_or", "$reduce_and", "$reduce_bool", cell->type.str()); + log("Replacing port A of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_a), log_signal(new_sig_a)); + cell->setPort("\\A", new_sig_a); + cell->parameters.at("\\A_WIDTH") = GetSize(new_sig_a); + did_something = true; + } + } + + if (cell->type == "$logic_and" || cell->type == "$logic_or") + { + SigBit neutral_bit = State::S0; + + RTLIL::SigSpec sig_b = assign_map(cell->getPort("\\B")); + RTLIL::SigSpec new_sig_b; + + for (auto bit : sig_b) + if (bit != neutral_bit) new_sig_b.append(bit); + + if (GetSize(new_sig_b) == 0) + new_sig_b.append(neutral_bit); + + if (GetSize(new_sig_b) < GetSize(sig_b)) { + cover_list("opt.opt_expr.fine.neutral_B", "$logic_and", "$logic_or", cell->type.str()); + log("Replacing port B of %s cell `%s' in module `%s' with shorter expression: %s -> %s\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str(), log_signal(sig_b), log_signal(new_sig_b)); + cell->setPort("\\B", new_sig_b); + cell->parameters.at("\\B_WIDTH") = GetSize(new_sig_b); + did_something = true; + } + } + if (cell->type == "$reduce_and") { RTLIL::SigSpec sig_a = assign_map(cell->getPort("\\A")); @@ -633,6 +734,23 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + if (cell->type == "$_TBUF_" || cell->type == "$tribuf") { + RTLIL::SigSpec input = cell->getPort(cell->type == "$_TBUF_" ? "\\E" : "\\EN"); + RTLIL::SigSpec a = cell->getPort("\\A"); + assign_map.apply(input); + assign_map.apply(a); + if (input == State::S1) + ACTION_DO("\\Y", cell->getPort("\\A")); + if (input == State::S0 && !a.is_fully_undef()) { + cover("opt.opt_expr.action_" S__LINE__); + log("Replacing data input of %s cell `%s' in module `%s' with constant undef.\n", + cell->type.c_str(), cell->name.c_str(), module->name.c_str()); + cell->setPort("\\A", SigSpec(State::Sx, GetSize(a))); + did_something = true; + goto next_cell; + } + } + if (cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex") { RTLIL::SigSpec a = cell->getPort("\\A"); @@ -1167,6 +1285,218 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } } + // remove redundant pairs of bits in ==, ===, !=, and !== + // replace cell with const driver if inputs can't be equal + if (do_fine && cell->type.in("$eq", "$ne", "$eqx", "$nex")) + { + pool> redundant_cache; + mfp contradiction_cache; + + contradiction_cache.promote(State::S0); + contradiction_cache.promote(State::S1); + + int a_width = cell->getParam("\\A_WIDTH").as_int(); + int b_width = cell->getParam("\\B_WIDTH").as_int(); + + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + int width = is_signed ? std::min(a_width, b_width) : std::max(a_width, b_width); + + SigSpec sig_a = cell->getPort("\\A"); + SigSpec sig_b = cell->getPort("\\B"); + + int redundant_bits = 0; + + for (int i = width-1; i >= 0; i--) + { + SigBit bit_a = i < a_width ? assign_map(sig_a[i]) : State::S0; + SigBit bit_b = i < b_width ? assign_map(sig_b[i]) : State::S0; + + if (bit_a != State::Sx && bit_a != State::Sz && + bit_b != State::Sx && bit_b != State::Sz) + contradiction_cache.merge(bit_a, bit_b); + + if (bit_b < bit_a) + std::swap(bit_a, bit_b); + + pair key(bit_a, bit_b); + + if (redundant_cache.count(key)) { + if (i < a_width) sig_a.remove(i); + if (i < b_width) sig_b.remove(i); + redundant_bits++; + continue; + } + + redundant_cache.insert(key); + } + + if (contradiction_cache.find(State::S0) == contradiction_cache.find(State::S1)) + { + SigSpec y_sig = cell->getPort("\\Y"); + Const y_value(cell->type.in("$eq", "$eqx") ? 0 : 1, GetSize(y_sig)); + + log("Replacing cell `%s' in module `%s' with constant driver %s.\n", + log_id(cell), log_id(module), log_signal(y_value)); + + module->connect(y_sig, y_value); + module->remove(cell); + + did_something = true; + goto next_cell; + } + + if (redundant_bits) + { + log("Removed %d redundant input bits from %s cell `%s' in module `%s'.\n", + redundant_bits, log_id(cell->type), log_id(cell), log_id(module)); + + cell->setPort("\\A", sig_a); + cell->setPort("\\B", sig_b); + cell->setParam("\\A_WIDTH", GetSize(sig_a)); + cell->setParam("\\B_WIDTH", GetSize(sig_b)); + + did_something = true; + goto next_cell; + } + } + + // simplify comparisons + if (do_fine && (cell->type == "$lt" || cell->type == "$ge" || cell->type == "$gt" || cell->type == "$le")) + { + IdString cmp_type = cell->type; + SigSpec var_sig = cell->getPort("\\A"); + SigSpec const_sig = cell->getPort("\\B"); + int var_width = cell->parameters["\\A_WIDTH"].as_int(); + int const_width = cell->parameters["\\B_WIDTH"].as_int(); + bool is_signed = cell->getParam("\\A_SIGNED").as_bool(); + + if (!const_sig.is_fully_const()) + { + std::swap(var_sig, const_sig); + std::swap(var_width, const_width); + if (cmp_type == "$gt") + cmp_type = "$lt"; + else if (cmp_type == "$lt") + cmp_type = "$gt"; + else if (cmp_type == "$ge") + cmp_type = "$le"; + else if (cmp_type == "$le") + cmp_type = "$ge"; + } + + if (const_sig.is_fully_def() && const_sig.is_fully_const()) + { + std::string condition, replacement; + SigSpec replace_sig(State::S0, GetSize(cell->getPort("\\Y"))); + bool replace = false; + bool remove = false; + + if (!is_signed) + { /* unsigned */ + if (const_sig.is_fully_zero() && cmp_type == "$lt") { + condition = "unsigned X<0"; + replacement = "constant 0"; + replace_sig[0] = State::S0; + replace = true; + } + if (const_sig.is_fully_zero() && cmp_type == "$ge") { + condition = "unsigned X>=0"; + replacement = "constant 1"; + replace_sig[0] = State::S1; + replace = true; + } + if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$gt") { + condition = "unsigned X>~0"; + replacement = "constant 0"; + replace_sig[0] = State::S0; + replace = true; + } + if (const_width == var_width && const_sig.is_fully_ones() && cmp_type == "$le") { + condition = "unsigned X<=~0"; + replacement = "constant 1"; + replace_sig[0] = State::S1; + replace = true; + } + + int const_bit_hot = get_onehot_bit_index(const_sig); + if (const_bit_hot >= 0 && const_bit_hot < var_width) + { + RTLIL::SigSpec var_high_sig(RTLIL::State::S0, var_width - const_bit_hot); + for (int i = const_bit_hot; i < var_width; i++) { + var_high_sig[i - const_bit_hot] = var_sig[i]; + } + + if (cmp_type == "$lt") + { + condition = stringf("unsigned X<%s", log_signal(const_sig)); + replacement = stringf("!X[%d:%d]", var_width - 1, const_bit_hot); + module->addLogicNot(NEW_ID, var_high_sig, cell->getPort("\\Y")); + remove = true; + } + if (cmp_type == "$ge") + { + condition = stringf("unsigned X>=%s", log_signal(const_sig)); + replacement = stringf("|X[%d:%d]", var_width - 1, const_bit_hot); + module->addReduceOr(NEW_ID, var_high_sig, cell->getPort("\\Y")); + remove = true; + } + } + + int const_bit_set = get_highest_hot_index(const_sig); + if(const_bit_set >= var_width) + { + string cmp_name; + if (cmp_type == "$lt" || cmp_type == "$le") + { + if (cmp_type == "$lt") cmp_name = "<"; + if (cmp_type == "$le") cmp_name = "<="; + condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig)); + replacement = "constant 1"; + replace_sig[0] = State::S1; + replace = true; + } + if (cmp_type == "$gt" || cmp_type == "$ge") + { + if (cmp_type == "$gt") cmp_name = ">"; + if (cmp_type == "$ge") cmp_name = ">="; + condition = stringf("unsigned X[%d:0]%s%s", var_width - 1, cmp_name.c_str(), log_signal(const_sig)); + replacement = "constant 0"; + replace_sig[0] = State::S0; + replace = true; + } + } + } + else + { /* signed */ + if (const_sig.is_fully_zero() && cmp_type == "$lt") + { + condition = "signed X<0"; + replacement = stringf("X[%d]", var_width - 1); + replace_sig[0] = var_sig[var_width - 1]; + replace = true; + } + if (const_sig.is_fully_zero() && cmp_type == "$ge") + { + condition = "signed X>=0"; + replacement = stringf("X[%d]", var_width - 1); + module->addNot(NEW_ID, var_sig[var_width - 1], cell->getPort("\\Y")); + remove = true; + } + } + + if (replace || remove) + { + log("Replacing %s cell `%s' (implementing %s) with %s.\n", + log_id(cell->type), log_id(cell), condition.c_str(), replacement.c_str()); + if (replace) + module->connect(cell->getPort("\\Y"), replace_sig); + module->remove(cell); + did_something = true; + goto next_cell; + } + } + } + next_cell:; #undef ACTION_DO #undef ACTION_DO_Y @@ -1177,14 +1507,14 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons struct OptExprPass : public Pass { OptExprPass() : Pass("opt_expr", "perform const folding and simple expression rewriting") { } - virtual void help() + void help() YS_OVERRIDE { // |---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("It also performs some simple expression rewriting.\n"); log("\n"); log(" -mux_undef\n"); log(" remove 'undef' inputs from $mux, $pmux and $_MUX_ cells\n"); @@ -1211,7 +1541,7 @@ struct OptExprPass : public Pass { log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool mux_undef = false; bool mux_bool = false; diff --git a/yosys/passes/opt/opt_lut.cc b/yosys/passes/opt/opt_lut.cc new file mode 100644 index 000000000..26855fd70 --- /dev/null +++ b/yosys/passes/opt/opt_lut.cc @@ -0,0 +1,607 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptLutWorker +{ + dict> &dlogic; + RTLIL::Module *module; + ModIndex index; + SigMap sigmap; + + pool luts; + dict luts_arity; + dict> luts_dlogics; + dict> luts_dlogic_inputs; + + int eliminated_count = 0, combined_count = 0; + + bool evaluate_lut(RTLIL::Cell *lut, dict inputs) + { + SigSpec lut_input = sigmap(lut->getPort("\\A")); + int lut_width = lut->getParam("\\WIDTH").as_int(); + Const lut_table = lut->getParam("\\LUT"); + int lut_index = 0; + + for (int i = 0; i < lut_width; i++) + { + SigBit input = sigmap(lut_input[i]); + if (inputs.count(input)) + { + lut_index |= inputs[input] << i; + } + else + { + lut_index |= SigSpec(lut_input[i]).as_bool() << i; + } + } + + return lut_table.extract(lut_index).as_bool(); + } + + void show_stats_by_arity() + { + dict arity_counts; + dict dlogic_counts; + int max_arity = 0; + + for (auto lut_arity : luts_arity) + { + max_arity = max(max_arity, lut_arity.second); + arity_counts[lut_arity.second]++; + } + + for (auto &lut_dlogics : luts_dlogics) + { + for (auto &lut_dlogic : lut_dlogics.second) + { + dlogic_counts[lut_dlogic->type]++; + } + } + + log("Number of LUTs: %8zu\n", luts.size()); + for (int arity = 1; arity <= max_arity; arity++) + { + if (arity_counts[arity]) + log(" %d-LUT %16d\n", arity, arity_counts[arity]); + } + for (auto &dlogic_count : dlogic_counts) + { + log(" with %-12s %4d\n", dlogic_count.first.c_str(), dlogic_count.second); + } + } + + OptLutWorker(dict> &dlogic, RTLIL::Module *module, int limit) : + dlogic(dlogic), module(module), index(module), sigmap(module) + { + log("Discovering LUTs.\n"); + for (auto cell : module->selected_cells()) + { + if (cell->type == "$lut") + { + int lut_width = cell->getParam("\\WIDTH").as_int(); + SigSpec lut_input = cell->getPort("\\A"); + int lut_arity = 0; + + log("Found $lut\\WIDTH=%d cell %s.%s.\n", lut_width, log_id(module), log_id(cell)); + luts.insert(cell); + + // First, find all dedicated logic we're connected to. This results in an overapproximation + // of such connections. + pool lut_all_dlogics; + for (int i = 0; i < lut_width; i++) + { + SigBit bit = lut_input[i]; + for (auto &port : index.query_ports(bit)) + { + if (dlogic.count(port.cell->type)) + { + auto &dlogic_map = dlogic[port.cell->type]; + if (dlogic_map.count(i)) + { + if (port.port == dlogic_map[i]) + { + lut_all_dlogics.insert(port.cell); + } + } + } + } + } + + // Second, make sure that the connection to dedicated logic is legal. If it is not legal, + // it means one of the two things: + // * The connection is spurious. I.e. this is dedicated logic that will be packed + // with some other LUT, and it just happens to be connected to this LUT as well. + // * The connection is illegal. + // In either of these cases, we don't need to concern ourselves with preserving the connection + // between this LUT and this dedicated logic cell. + pool lut_legal_dlogics; + pool lut_dlogic_inputs; + for (auto lut_dlogic : lut_all_dlogics) + { + auto &dlogic_map = dlogic[lut_dlogic->type]; + bool legal = true; + for (auto &dlogic_conn : dlogic_map) + { + if (lut_width <= dlogic_conn.first) + { + log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log(" LUT input A[%d] not present.\n", dlogic_conn.first); + legal = false; + break; + } + if (sigmap(lut_input[dlogic_conn.first]) != sigmap(lut_dlogic->getPort(dlogic_conn.second))) + { + log(" LUT has illegal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + log(" LUT input A[%d] (wire %s) not connected to %s port %s (wire %s).\n", dlogic_conn.first, log_signal(lut_input[dlogic_conn.first]), lut_dlogic->type.c_str(), dlogic_conn.second.c_str(), log_signal(lut_dlogic->getPort(dlogic_conn.second))); + legal = false; + break; + } + } + + if (legal) + { + log(" LUT has legal connection to %s cell %s.%s.\n", lut_dlogic->type.c_str(), log_id(module), log_id(lut_dlogic)); + lut_legal_dlogics.insert(lut_dlogic); + for (auto &dlogic_conn : dlogic_map) + lut_dlogic_inputs.insert(dlogic_conn.first); + } + } + + // Third, determine LUT arity. An n-wide LUT that has k constant inputs and m inputs shared with dedicated + // logic implements an (n-k-m)-ary function. + for (int i = 0; i < lut_width; i++) + { + SigBit bit = lut_input[i]; + if (bit.wire || lut_dlogic_inputs.count(i)) + lut_arity++; + } + + log(" Cell implements a %d-LUT.\n", lut_arity); + luts_arity[cell] = lut_arity; + luts_dlogics[cell] = lut_legal_dlogics; + luts_dlogic_inputs[cell] = lut_dlogic_inputs; + } + } + show_stats_by_arity(); + + log("\n"); + log("Eliminating LUTs.\n"); + pool worklist = luts; + while (worklist.size()) + { + if (limit == 0) + { + log("Limit reached.\n"); + break; + } + + auto lut = worklist.pop(); + SigSpec lut_input = sigmap(lut->getPort("\\A")); + pool &lut_dlogic_inputs = luts_dlogic_inputs[lut]; + + vector lut_inputs; + for (auto &bit : lut_input) + { + if (bit.wire) + lut_inputs.push_back(sigmap(bit)); + } + + bool const0_match = true; + bool const1_match = true; + vector input_matches; + for (size_t i = 0; i < lut_inputs.size(); i++) + input_matches.push_back(true); + + for (int eval = 0; eval < 1 << lut_inputs.size(); eval++) + { + dict eval_inputs; + for (size_t i = 0; i < lut_inputs.size(); i++) + eval_inputs[lut_inputs[i]] = (eval >> i) & 1; + bool value = evaluate_lut(lut, eval_inputs); + if (value != 0) + const0_match = false; + if (value != 1) + const1_match = false; + for (size_t i = 0; i < lut_inputs.size(); i++) + { + if (value != eval_inputs[lut_inputs[i]]) + input_matches[i] = false; + } + } + + int input_match = -1; + for (size_t i = 0; i < lut_inputs.size(); i++) + if (input_matches[i]) + input_match = i; + + if (const0_match || const1_match || input_match != -1) + { + log("Found redundant cell %s.%s.\n", log_id(module), log_id(lut)); + + SigBit value; + if (const0_match) + { + log(" Cell evaluates constant 0.\n"); + value = State::S0; + } + if (const1_match) + { + log(" Cell evaluates constant 1.\n"); + value = State::S1; + } + if (input_match != -1) { + log(" Cell evaluates signal %s.\n", log_signal(lut_inputs[input_match])); + value = lut_inputs[input_match]; + } + + if (lut_dlogic_inputs.size()) + { + log(" Not eliminating cell (connected to dedicated logic).\n"); + } + else + { + SigSpec lut_output = lut->getPort("\\Y"); + for (auto &port : index.query_ports(lut_output)) + { + if (port.cell != lut && luts.count(port.cell)) + worklist.insert(port.cell); + } + + module->connect(lut_output, value); + sigmap.add(lut_output, value); + + module->remove(lut); + luts.erase(lut); + luts_arity.erase(lut); + luts_dlogics.erase(lut); + luts_dlogic_inputs.erase(lut); + + eliminated_count++; + if (limit > 0) + limit--; + } + } + } + show_stats_by_arity(); + + log("\n"); + log("Combining LUTs.\n"); + worklist = luts; + while (worklist.size()) + { + if (limit == 0) + { + log("Limit reached.\n"); + break; + } + + auto lutA = worklist.pop(); + SigSpec lutA_input = sigmap(lutA->getPort("\\A")); + SigSpec lutA_output = sigmap(lutA->getPort("\\Y")[0]); + int lutA_width = lutA->getParam("\\WIDTH").as_int(); + int lutA_arity = luts_arity[lutA]; + pool &lutA_dlogic_inputs = luts_dlogic_inputs[lutA]; + + auto lutA_output_ports = index.query_ports(lutA->getPort("\\Y")); + if (lutA_output_ports.size() != 2) + continue; + + for (auto &port : lutA_output_ports) + { + if (port.cell == lutA) + continue; + + if (luts.count(port.cell)) + { + auto lutB = port.cell; + SigSpec lutB_input = sigmap(lutB->getPort("\\A")); + SigSpec lutB_output = sigmap(lutB->getPort("\\Y")[0]); + int lutB_width = lutB->getParam("\\WIDTH").as_int(); + int lutB_arity = luts_arity[lutB]; + pool &lutB_dlogic_inputs = luts_dlogic_inputs[lutB]; + + log("Found %s.%s (cell A) feeding %s.%s (cell B).\n", log_id(module), log_id(lutA), log_id(module), log_id(lutB)); + + if (index.query_is_output(lutA->getPort("\\Y"))) + { + log(" Not combining LUTs (cascade connection feeds module output).\n"); + continue; + } + + pool lutA_inputs; + pool lutB_inputs; + for (auto &bit : lutA_input) + { + if (bit.wire) + lutA_inputs.insert(sigmap(bit)); + } + for (auto &bit : lutB_input) + { + if (bit.wire) + lutB_inputs.insert(sigmap(bit)); + } + + pool common_inputs; + for (auto &bit : lutA_inputs) + { + if (lutB_inputs.count(bit)) + common_inputs.insert(bit); + } + + int lutM_arity = lutA_arity + lutB_arity - 1 - common_inputs.size(); + if (lutA_dlogic_inputs.size()) + log(" Cell A is a %d-LUT with %zu dedicated connections. ", lutA_arity, lutA_dlogic_inputs.size()); + else + log(" Cell A is a %d-LUT. ", lutA_arity); + if (lutB_dlogic_inputs.size()) + log("Cell B is a %d-LUT with %zu dedicated connections.\n", lutB_arity, lutB_dlogic_inputs.size()); + else + log("Cell B is a %d-LUT.\n", lutB_arity); + log(" Cells share %zu input(s) and can be merged into one %d-LUT.\n", common_inputs.size(), lutM_arity); + + const int COMBINE_A = 1, COMBINE_B = 2, COMBINE_EITHER = COMBINE_A | COMBINE_B; + int combine_mask = 0; + if (lutM_arity > lutA_width) + { + log(" Not combining LUTs into cell A (combined LUT wider than cell A).\n"); + } + else if (lutB_dlogic_inputs.size() > 0) + { + log(" Not combining LUTs into cell A (cell B is connected to dedicated logic).\n"); + } + else if (lutB->get_bool_attribute("\\lut_keep")) + { + log(" Not combining LUTs into cell A (cell B has attribute \\lut_keep).\n"); + } + else + { + combine_mask |= COMBINE_A; + } + if (lutM_arity > lutB_width) + { + log(" Not combining LUTs into cell B (combined LUT wider than cell B).\n"); + } + else if (lutA_dlogic_inputs.size() > 0) + { + log(" Not combining LUTs into cell B (cell A is connected to dedicated logic).\n"); + } + else if (lutA->get_bool_attribute("\\lut_keep")) + { + log(" Not combining LUTs into cell B (cell A has attribute \\lut_keep).\n"); + } + else + { + combine_mask |= COMBINE_B; + } + + int combine = combine_mask; + if (combine == COMBINE_EITHER) + { + log(" Can combine into either cell.\n"); + if (lutA_arity == 1) + { + log(" Cell A is a buffer or inverter, combining into cell B.\n"); + combine = COMBINE_B; + } + else if (lutB_arity == 1) + { + log(" Cell B is a buffer or inverter, combining into cell A.\n"); + combine = COMBINE_A; + } + else + { + log(" Arbitrarily combining into cell A.\n"); + combine = COMBINE_A; + } + } + + RTLIL::Cell *lutM, *lutR; + pool lutM_inputs, lutR_inputs; + pool lutM_dlogic_inputs; + if (combine == COMBINE_A) + { + log(" Combining LUTs into cell A.\n"); + lutM = lutA; + lutM_inputs = lutA_inputs; + lutM_dlogic_inputs = lutA_dlogic_inputs; + lutR = lutB; + lutR_inputs = lutB_inputs; + } + else if (combine == COMBINE_B) + { + log(" Combining LUTs into cell B.\n"); + lutM = lutB; + lutM_inputs = lutB_inputs; + lutM_dlogic_inputs = lutB_dlogic_inputs; + lutR = lutA; + lutR_inputs = lutA_inputs; + } + else + { + log(" Cannot combine LUTs.\n"); + continue; + } + + pool lutR_unique; + for (auto &bit : lutR_inputs) + { + if (!common_inputs.count(bit) && bit != lutA_output) + lutR_unique.insert(bit); + } + + int lutM_width = lutM->getParam("\\WIDTH").as_int(); + SigSpec lutM_input = sigmap(lutM->getPort("\\A")); + std::vector lutM_new_inputs; + for (int i = 0; i < lutM_width; i++) + { + bool input_unused = false; + if (sigmap(lutM_input[i]) == lutA_output) + input_unused = true; + if (!lutM_input[i].wire && !lutM_dlogic_inputs.count(i)) + input_unused = true; + + if (input_unused && lutR_unique.size()) + { + SigBit new_input = lutR_unique.pop(); + log(" Connecting input %d as %s.\n", i, log_signal(new_input)); + lutM_new_inputs.push_back(new_input); + } + else if (sigmap(lutM_input[i]) == lutA_output) + { + log(" Disconnecting cascade input %d.\n", i); + lutM_new_inputs.push_back(SigBit()); + } + else + { + log(" Leaving input %d as %s.\n", i, log_signal(lutM_input[i])); + lutM_new_inputs.push_back(lutM_input[i]); + } + } + log_assert(lutR_unique.size() == 0); + + RTLIL::Const lutM_new_table(State::Sx, 1 << lutM_width); + for (int eval = 0; eval < 1 << lutM_width; eval++) + { + dict eval_inputs; + for (size_t i = 0; i < lutM_new_inputs.size(); i++) + { + eval_inputs[lutM_new_inputs[i]] = (eval >> i) & 1; + } + eval_inputs[lutA_output] = evaluate_lut(lutA, eval_inputs); + lutM_new_table[eval] = (RTLIL::State) evaluate_lut(lutB, eval_inputs); + } + + log(" Cell A truth table: %s.\n", lutA->getParam("\\LUT").as_string().c_str()); + log(" Cell B truth table: %s.\n", lutB->getParam("\\LUT").as_string().c_str()); + log(" Merged truth table: %s.\n", lutM_new_table.as_string().c_str()); + + lutM->setParam("\\LUT", lutM_new_table); + lutM->setPort("\\A", lutM_new_inputs); + lutM->setPort("\\Y", lutB_output); + + luts_arity[lutM] = lutM_arity; + luts.erase(lutR); + luts_arity.erase(lutR); + lutR->module->remove(lutR); + + worklist.insert(lutM); + worklist.erase(lutR); + + combined_count++; + if (limit > 0) + limit--; + } + } + } + show_stats_by_arity(); + } +}; + +static void split(std::vector &tokens, const std::string &text, char sep) +{ + size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != std::string::npos) { + tokens.push_back(text.substr(start, end - start)); + start = end + 1; + } + tokens.push_back(text.substr(start)); +} + +struct OptLutPass : public Pass { + OptLutPass() : Pass("opt_lut", "optimize LUT cells") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_lut [options] [selection]\n"); + log("\n"); + log("This pass combines cascaded $lut cells with unused inputs.\n"); + log("\n"); + log(" -dlogic :=[:=...]\n"); + log(" preserve connections to dedicated logic cell that has ports\n"); + log(" connected to LUT inputs . this includes\n"); + log(" the case where both LUT and dedicated logic input are connected to\n"); + log(" the same constant.\n"); + log("\n"); + log(" -limit N\n"); + log(" only perform the first N combines, then stop. useful for debugging.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_LUT pass (optimize LUTs).\n"); + + dict> dlogic; + int limit = -1; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-dlogic" && argidx+1 < args.size()) + { + std::vector tokens; + split(tokens, args[++argidx], ':'); + if (tokens.size() < 2) + log_cmd_error("The -dlogic option requires at least one connection.\n"); + IdString type = "\\" + tokens[0]; + for (auto it = tokens.begin() + 1; it != tokens.end(); ++it) { + std::vector conn_tokens; + split(conn_tokens, *it, '='); + if (conn_tokens.size() != 2) + log_cmd_error("Invalid format of -dlogic signal mapping.\n"); + IdString logic_port = "\\" + conn_tokens[0]; + int lut_input = atoi(conn_tokens[1].c_str()); + dlogic[type][lut_input] = logic_port; + } + continue; + } + if (args[argidx] == "-limit" && argidx + 1 < args.size()) + { + limit = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + int eliminated_count = 0, combined_count = 0; + for (auto module : design->selected_modules()) + { + OptLutWorker worker(dlogic, module, limit - eliminated_count - combined_count); + eliminated_count += worker.eliminated_count; + combined_count += worker.combined_count; + } + if (eliminated_count) + design->scratchpad_set_bool("opt.did_something", true); + if (combined_count) + design->scratchpad_set_bool("opt.did_something", true); + log("\n"); + log("Eliminated %d LUTs.\n", eliminated_count); + log("Combined %d LUTs.\n", combined_count); + } +} OptLutPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/opt/opt_merge.cc b/yosys/passes/opt/opt_merge.cc index 97989d271..eedf88904 100644 --- a/yosys/passes/opt/opt_merge.cc +++ b/yosys/passes/opt/opt_merge.cc @@ -275,13 +275,24 @@ struct OptMergeWorker ct.cell_types.erase("$pmux"); } + ct.cell_types.erase("$tribuf"); + ct.cell_types.erase("$_TBUF_"); + ct.cell_types.erase("$anyseq"); + ct.cell_types.erase("$anyconst"); + ct.cell_types.erase("$allseq"); + ct.cell_types.erase("$allconst"); + 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")); + if (it.second->attributes.count("\\init") != 0) { + Const initval = it.second->attributes.at("\\init"); + for (int i = 0; i < GetSize(initval) && i < GetSize(it.second); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + dff_init_map.add(SigBit(it.second, i), initval[i]); + } bool did_something = true; while (did_something) @@ -330,7 +341,7 @@ struct OptMergeWorker struct OptMergePass : public Pass { OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -346,7 +357,7 @@ struct OptMergePass : public Pass { log(" Operate on all cell types, not just built-in types.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); diff --git a/yosys/passes/opt/opt_muxtree.cc b/yosys/passes/opt/opt_muxtree.cc index f5ddc2af9..375697dc8 100644 --- a/yosys/passes/opt/opt_muxtree.cc +++ b/yosys/passes/opt/opt_muxtree.cc @@ -36,6 +36,7 @@ struct OptMuxtreeWorker RTLIL::Module *module; SigMap assign_map; int removed_count; + int glob_abort_cnt = 100000; struct bitinfo_t { bool seen_non_mux; @@ -293,6 +294,9 @@ struct OptMuxtreeWorker void eval_mux_port(knowledge_t &knowledge, int mux_idx, int port_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { + if (glob_abort_cnt == 0) + return; + muxinfo_t &muxinfo = mux2info[mux_idx]; if (do_enable_ports) @@ -315,7 +319,7 @@ struct OptMuxtreeWorker knowledge.visited_muxes[m] = true; parent_muxes.push_back(m); } - for (int m : parent_muxes) + for (int m : parent_muxes) { if (root_enable_muxes.at(m)) continue; else if (root_muxes.at(m)) { @@ -327,6 +331,9 @@ struct OptMuxtreeWorker eval_mux(knowledge, m, false, do_enable_ports, abort_count - 1); } else eval_mux(knowledge, m, do_replace_known, do_enable_ports, abort_count); + if (glob_abort_cnt == 0) + return; + } for (int m : parent_muxes) knowledge.visited_muxes[m] = false; @@ -390,6 +397,12 @@ struct OptMuxtreeWorker void eval_mux(knowledge_t &knowledge, int mux_idx, bool do_replace_known, bool do_enable_ports, int abort_count) { + if (glob_abort_cnt == 0) { + log(" Giving up (too many iterations)\n"); + return; + } + glob_abort_cnt--; + muxinfo_t &muxinfo = mux2info[mux_idx]; // set input ports to constants if we find known active or inactive signals @@ -433,6 +446,9 @@ struct OptMuxtreeWorker if (knowledge.known_inactive.at(portinfo.ctrl_sig)) continue; eval_mux_port(knowledge, mux_idx, port_idx, do_replace_known, do_enable_ports, abort_count); + + if (glob_abort_cnt == 0) + return; } } @@ -449,7 +465,7 @@ struct OptMuxtreeWorker struct OptMuxtreePass : public Pass { OptMuxtreePass() : Pass("opt_muxtree", "eliminate dead trees in multiplexer trees") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -462,7 +478,7 @@ struct OptMuxtreePass : public Pass { log("This pass only operates on completely selected modules without processes.\n"); log("\n"); } - virtual void execute(vector args, RTLIL::Design *design) + void execute(vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing OPT_MUXTREE pass (detect dead branches in mux trees).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/opt/opt_reduce.cc b/yosys/passes/opt/opt_reduce.cc index eb9d02ad5..d99f1ca6a 100644 --- a/yosys/passes/opt/opt_reduce.cc +++ b/yosys/passes/opt/opt_reduce.cc @@ -329,7 +329,7 @@ struct OptReduceWorker struct OptReducePass : public Pass { OptReducePass() : Pass("opt_reduce", "simplify large MUXes and AND/OR gates") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -350,7 +350,7 @@ struct OptReducePass : public Pass { log(" alias for -fine\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool do_fine = false; diff --git a/yosys/passes/opt/opt_rmdff.cc b/yosys/passes/opt/opt_rmdff.cc index 922f086f1..5880254c1 100644 --- a/yosys/passes/opt/opt_rmdff.cc +++ b/yosys/passes/opt/opt_rmdff.cc @@ -39,11 +39,201 @@ void remove_init_attr(SigSpec sig) wbit.wire->attributes.at("\\init")[wbit.offset] = State::Sx; } +bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell) +{ + SigSpec sig_set, sig_clr; + State pol_set, pol_clr; + + if (cell->hasPort("\\S")) + sig_set = cell->getPort("\\S"); + + if (cell->hasPort("\\R")) + sig_clr = cell->getPort("\\R"); + + if (cell->hasPort("\\SET")) + sig_set = cell->getPort("\\SET"); + + if (cell->hasPort("\\CLR")) + sig_clr = cell->getPort("\\CLR"); + + log_assert(GetSize(sig_set) == GetSize(sig_clr)); + + if (cell->type.substr(0,8) == "$_DFFSR_") { + pol_set = cell->type[9] == 'P' ? State::S1 : State::S0; + pol_clr = cell->type[10] == 'P' ? State::S1 : State::S0; + } else + if (cell->type.substr(0,11) == "$_DLATCHSR_") { + pol_set = cell->type[12] == 'P' ? State::S1 : State::S0; + pol_clr = cell->type[13] == 'P' ? State::S1 : State::S0; + } else + if (cell->type == "$dffsr" || cell->type == "$dlatchsr") { + pol_set = cell->parameters["\\SET_POLARITY"].as_bool() ? State::S1 : State::S0; + pol_clr = cell->parameters["\\CLR_POLARITY"].as_bool() ? State::S1 : State::S0; + } else + log_abort(); + + State npol_set = pol_set == State::S0 ? State::S1 : State::S0; + State npol_clr = pol_clr == State::S0 ? State::S1 : State::S0; + + SigSpec sig_d = cell->getPort("\\D"); + SigSpec sig_q = cell->getPort("\\Q"); + + bool did_something = false; + bool proper_sr = false; + bool used_pol_set = false; + bool used_pol_clr = false; + bool hasreset = false; + Const reset_val; + SigSpec sig_reset; + + for (int i = 0; i < GetSize(sig_set); i++) + { + SigBit s = sig_set[i], c = sig_clr[i]; + + if (s != npol_set || c != npol_clr) + hasreset = true; + + if (s == pol_set || c == pol_clr) + { + log("Constantly %s Q bit %s for SR cell %s (%s) from module %s.\n", + s == pol_set ? "set" : "cleared", log_signal(sig_q[i]), + log_id(cell), log_id(cell->type), log_id(mod)); + + remove_init_attr(sig_q[i]); + mod->connect(sig_q[i], s == pol_set ? State::S1 : State::S0); + sig_set.remove(i); + sig_clr.remove(i); + sig_d.remove(i); + sig_q.remove(i--); + did_something = true; + continue; + } + if (sig_reset.empty() && s.wire != nullptr) sig_reset = s; + if (sig_reset.empty() && c.wire != nullptr) sig_reset = c; + + if (s.wire != nullptr && s != sig_reset) proper_sr = true; + if (c.wire != nullptr && c != sig_reset) proper_sr = true; + + if ((s.wire == nullptr) != (c.wire == nullptr)) { + if (s.wire != nullptr) used_pol_set = true; + if (c.wire != nullptr) used_pol_clr = true; + reset_val.bits.push_back(c.wire == nullptr ? State::S1 : State::S0); + } else + proper_sr = true; + } + + if (!hasreset) + proper_sr = false; + + if (GetSize(sig_set) == 0) + { + log("Removing %s (%s) from module %s.\n", log_id(cell), log_id(cell->type), log_id(mod)); + mod->remove(cell); + return true; + } + + if (cell->type == "$dffsr" || cell->type == "$dlatchsr") + { + cell->setParam("\\WIDTH", GetSize(sig_d)); + cell->setPort("\\SET", sig_set); + cell->setPort("\\CLR", sig_clr); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + } + else + { + cell->setPort("\\S", sig_set); + cell->setPort("\\R", sig_clr); + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + } + + if (proper_sr) + return did_something; + + if (used_pol_set && used_pol_clr && pol_set != pol_clr) + return did_something; + + if (cell->type == "$dlatchsr") + return did_something; + + State unified_pol = used_pol_set ? pol_set : pol_clr; + + if (cell->type == "$dffsr") + { + if (hasreset) + { + log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$adff", log_id(mod)); + + cell->type = "$adff"; + cell->setParam("\\ARST_POLARITY", unified_pol); + cell->setParam("\\ARST_VALUE", reset_val); + cell->setPort("\\ARST", sig_reset); + + cell->unsetParam("\\SET_POLARITY"); + cell->unsetParam("\\CLR_POLARITY"); + cell->unsetPort("\\SET"); + cell->unsetPort("\\CLR"); + + return true; + } + else + { + log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), "$dff", log_id(mod)); + + cell->type = "$dff"; + cell->unsetParam("\\SET_POLARITY"); + cell->unsetParam("\\CLR_POLARITY"); + cell->unsetPort("\\SET"); + cell->unsetPort("\\CLR"); + + return true; + } + } + else + { + IdString new_type; + + if (cell->type.substr(0,8) == "$_DFFSR_") + new_type = stringf("$_DFF_%c_", cell->type[8]); + else if (cell->type.substr(0,11) == "$_DLATCHSR_") + new_type = stringf("$_DLATCH_%c_", cell->type[11]); + else + log_abort(); + + log("Converting %s (%s) to %s in module %s.\n", log_id(cell), log_id(cell->type), log_id(new_type), log_id(mod)); + + cell->type = new_type; + cell->unsetPort("\\S"); + cell->unsetPort("\\R"); + + return did_something; + } +} + bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) { - SigSpec sig_e = dlatch->getPort("\\EN"); + SigSpec sig_e; + State on_state, off_state; - if (sig_e == State::S0) + if (dlatch->type == "$dlatch") { + sig_e = assign_map(dlatch->getPort("\\EN")); + on_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S1 : State::S0; + off_state = dlatch->getParam("\\EN_POLARITY").as_bool() ? State::S0 : State::S1; + } else + if (dlatch->type == "$_DLATCH_P_") { + sig_e = assign_map(dlatch->getPort("\\E")); + on_state = State::S1; + off_state = State::S0; + } else + if (dlatch->type == "$_DLATCH_N_") { + sig_e = assign_map(dlatch->getPort("\\E")); + on_state = State::S0; + off_state = State::S1; + } else + log_abort(); + + if (sig_e == off_state) { RTLIL::Const val_init; for (auto bit : dff_init_map(dlatch->getPort("\\Q"))) @@ -52,7 +242,7 @@ bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) goto delete_dlatch; } - if (sig_e == State::S1) + if (sig_e == on_state) { mod->connect(dlatch->getPort("\\Q"), dlatch->getPort("\\D")); goto delete_dlatch; @@ -168,7 +358,7 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) goto delete_dff; } - if (sig_d == sig_q && (!sig_r.size() || !has_init || val_init == val_rv)) { + if (sig_d == sig_q && (sig_r.empty() || !has_init || val_init == val_rv)) { if (sig_r.size()) mod->connect(sig_q, val_rv); if (has_init) @@ -176,6 +366,28 @@ bool handle_dff(RTLIL::Module *mod, RTLIL::Cell *dff) goto delete_dff; } + if (!sig_r.empty() && sig_r.is_fully_const()) + { + if (sig_r == val_rp || sig_r.is_fully_undef()) { + mod->connect(sig_q, val_rv); + goto delete_dff; + } + + log("Removing unused reset from %s (%s) from module %s.\n", log_id(dff), log_id(dff->type), log_id(mod)); + + if (dff->type == "$adff") { + dff->type = "$dff"; + dff->unsetPort("\\ARST"); + dff->unsetParam("\\ARST_POLARITY"); + dff->unsetParam("\\ARST_VALUE"); + return true; + } + + log_assert(dff->type.substr(0,6) == "$_DFF_"); + dff->type = stringf("$_DFF_%c_", + dff->type[6]); + dff->unsetPort("\\R"); + } + return false; delete_dff: @@ -187,7 +399,7 @@ delete_dff: struct OptRmdffPass : public Pass { OptRmdffPass() : Pass("opt_rmdff", "remove DFFs with constant inputs") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -197,7 +409,7 @@ struct OptRmdffPass : public Pass { log("a constant driver.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { int total_count = 0, total_initdrv = 0; log_header(design, "Executing OPT_RMDFF pass (remove dff with constant values).\n"); @@ -221,12 +433,16 @@ struct OptRmdffPass : public Pass { assign_map.set(module); dff_init_map.set(module); + mux_drivers.clear(); + init_attributes.clear(); 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(initval) && i < GetSize(wire); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + dff_init_map.add(SigBit(wire, i), initval[i]); for (int i = 0; i < GetSize(wire); i++) { SigBit wire_bit(wire, i), mapped_bit = assign_map(wire_bit); if (mapped_bit.wire) { @@ -245,6 +461,7 @@ struct OptRmdffPass : public Pass { mux_drivers.clear(); std::vector dff_list; + std::vector dffsr_list; std::vector dlatch_list; for (auto cell : module->cells()) { @@ -262,16 +479,28 @@ struct OptRmdffPass : public Pass { if (!design->selected(module, cell)) continue; + if (cell->type.in("$_DFFSR_NNN_", "$_DFFSR_NNP_", "$_DFFSR_NPN_", "$_DFFSR_NPP_", + "$_DFFSR_PNN_", "$_DFFSR_PNP_", "$_DFFSR_PPN_", "$_DFFSR_PPP_", "$dffsr", + "$_DLATCHSR_NNN_", "$_DLATCHSR_NNP_", "$_DLATCHSR_NPN_", "$_DLATCHSR_NPP_", + "$_DLATCHSR_PNN_", "$_DLATCHSR_PNP_", "$_DLATCHSR_PPN_", "$_DLATCHSR_PPP_", "$dlatchsr")) + dffsr_list.push_back(cell->name); + 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") + if (cell->type.in("$dlatch", "$_DLATCH_P_", "$_DLATCH_N_")) dlatch_list.push_back(cell->name); } + for (auto &id : dffsr_list) { + if (module->cell(id) != nullptr && + handle_dffsr(module, module->cells_[id])) + total_count++; + } + for (auto &id : dff_list) { if (module->cell(id) != nullptr && handle_dff(module, module->cells_[id])) @@ -310,6 +539,7 @@ struct OptRmdffPass : public Pass { assign_map.clear(); mux_drivers.clear(); + init_attributes.clear(); if (total_count || total_initdrv) design->scratchpad_set_bool("opt.did_something", true); diff --git a/yosys/passes/opt/rmports.cc b/yosys/passes/opt/rmports.cc new file mode 100644 index 000000000..fc1596ebf --- /dev/null +++ b/yosys/passes/opt/rmports.cc @@ -0,0 +1,187 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct RmportsPassPass : public Pass { + RmportsPassPass() : Pass("rmports", "remove module ports with no connections") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" rmports [selection]\n"); + log("\n"); + log("This pass identifies ports in the selected modules which are not used or\n"); + log("driven and removes them.\n"); + log("\n"); + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing RMPORTS pass (remove ports with no connections).\n"); + + size_t argidx = 1; + extra_args(args, argidx, design); + + // The set of ports we removed + dict> removed_ports; + + // Find all of the unused ports, and remove them from that module + auto modules = design->selected_modules(); + for(auto mod : modules) + ScanModule(mod, removed_ports); + + // Remove the unused ports from all instances of those modules + for(auto mod : modules) + CleanupModule(mod, removed_ports); + } + + void CleanupModule(Module *module, dict> &removed_ports) + { + log("Removing now-unused cell ports in module %s\n", module->name.c_str()); + + auto cells = module->cells(); + for(auto cell : cells) + { + if(removed_ports.find(cell->type) == removed_ports.end()) + { + // log(" Not touching instance \"%s\" because we didn't remove any ports from module \"%s\"\n", + // cell->name.c_str(), cell->type.c_str()); + continue; + } + + auto ports_to_remove = removed_ports[cell->type]; + for(auto p : ports_to_remove) + { + log(" Removing port \"%s\" from instance \"%s\"\n", + p.c_str(), cell->type.c_str()); + cell->unsetPort(p); + } + } + } + + void ScanModule(Module* module, dict> &removed_ports) + { + log("Finding unconnected ports in module %s\n", module->name.c_str()); + + pool used_ports; + + // See what wires are used. + // Start by checking connections between named wires + auto &conns = module->connections(); + for(auto sigsig : conns) + { + auto s1 = sigsig.first; + auto s2 = sigsig.second; + + int len1 = s1.size(); + int len2 = s2.size(); + int len = len1; + if(len2 < len1) + len = len2; + + for(int i=0; iname.c_str(), w2->name.c_str()); + + if( (w1->port_input || w1->port_output) && (used_ports.find(w1->name) == used_ports.end()) ) + used_ports.insert(w1->name); + + if( (w2->port_input || w2->port_output) && (used_ports.find(w2->name) == used_ports.end()) ) + used_ports.insert(w2->name); + } + } + + // Then check connections to cells + auto cells = module->cells(); + for(auto cell : cells) + { + auto &cconns = cell->connections(); + for(auto conn : cconns) + { + for(int i=0; iname.c_str()); + if( (sig->port_input || sig->port_output) && (used_ports.find(sig->name) == used_ports.end()) ) + used_ports.insert(sig->name); + } + } + } + + // Now that we know what IS used, get rid of anything that isn't in that list + pool unused_ports; + for(auto port : module->ports) + { + if(used_ports.find(port) != used_ports.end()) + continue; + unused_ports.insert(port); + } + + // Print the ports out as we go through them + for(auto port : unused_ports) + { + log(" removing unused port %s\n", port.c_str()); + removed_ports[module->name].insert(port); + + // Remove from ports list + for(size_t i=0; iports.size(); i++) + { + if(module->ports[i] == port) + { + module->ports.erase(module->ports.begin() + i); + break; + } + } + + // Mark the wire as no longer a port + auto wire = module->wire(port); + wire->port_input = false; + wire->port_output = false; + wire->port_id = 0; + } + log("Removed %zu unused ports.\n", unused_ports.size()); + + // Re-number all of the wires that DO have ports still on them + for(size_t i=0; iports.size(); i++) + { + auto port = module->ports[i]; + auto wire = module->wire(port); + wire->port_id = i+1; + } + } + +} RmportsPassPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/opt/share.cc b/yosys/passes/opt/share.cc index 22914eaa7..c85c27427 100644 --- a/yosys/passes/opt/share.cc +++ b/yosys/passes/opt/share.cc @@ -710,8 +710,12 @@ struct ShareWorker RTLIL::Cell *supercell = module->addCell(NEW_ID, c1); RTLIL::SigSpec addr1 = c1->getPort("\\ADDR"); RTLIL::SigSpec addr2 = c2->getPort("\\ADDR"); - if (addr1 != addr2) - supercell->setPort("\\ADDR", module->Mux(NEW_ID, addr2, addr1, act)); + if (GetSize(addr1) < GetSize(addr2)) + addr1.extend_u0(GetSize(addr2)); + else + addr2.extend_u0(GetSize(addr1)); + supercell->setPort("\\ADDR", addr1 != addr2 ? module->Mux(NEW_ID, addr2, addr1, act) : addr1); + supercell->parameters["\\ABITS"] = RTLIL::Const(GetSize(addr1)); supercell_aux.insert(module->addPos(NEW_ID, supercell->getPort("\\DATA"), c2->getPort("\\DATA"))); supercell_aux.insert(supercell); return supercell; @@ -1421,7 +1425,7 @@ struct ShareWorker struct SharePass : public Pass { SharePass() : Pass("share", "perform sat-based resource sharing") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1453,7 +1457,7 @@ struct SharePass : public Pass { log(" Only perform the first N merges, then stop. This is useful for debugging.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { ShareWorkerConfig config; diff --git a/yosys/passes/opt/wreduce.cc b/yosys/passes/opt/wreduce.cc index 07503fbb3..8063b86a6 100644 --- a/yosys/passes/opt/wreduce.cc +++ b/yosys/passes/opt/wreduce.cc @@ -235,8 +235,11 @@ struct WreduceWorker } else { while (GetSize(sig) > 0) { - auto info = mi.query(sig[GetSize(sig)-1]); + auto bit = sig[GetSize(sig)-1]; + if (keep_bits.count(bit)) + break; + auto info = mi.query(bit); if (info->is_output || GetSize(info->ports) > 1) break; @@ -353,7 +356,7 @@ struct WreduceWorker struct WreducePass : public Pass { WreducePass() : Pass("wreduce", "reduce the word size of operations if possible") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -373,7 +376,7 @@ struct WreducePass : public Pass { log(" flows that use the 'memory_memx' pass.\n"); log("\n"); } - virtual void execute(std::vector args, Design *design) + void execute(std::vector args, Design *design) YS_OVERRIDE { WreduceConfig config; bool opt_memx = false; diff --git a/yosys/passes/proc/proc.cc b/yosys/passes/proc/proc.cc index d5366f266..ef7cb0f71 100644 --- a/yosys/passes/proc/proc.cc +++ b/yosys/passes/proc/proc.cc @@ -27,7 +27,7 @@ PRIVATE_NAMESPACE_BEGIN struct ProcPass : public Pass { ProcPass() : Pass("proc", "translate processes to netlists") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -57,7 +57,7 @@ struct ProcPass : public Pass { log(" executed in -ifx mode.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string global_arst; bool ifxmode = false; diff --git a/yosys/passes/proc/proc_arst.cc b/yosys/passes/proc/proc_arst.cc index 216b00ddd..b69eba3f9 100644 --- a/yosys/passes/proc/proc_arst.cc +++ b/yosys/passes/proc/proc_arst.cc @@ -203,7 +203,7 @@ restart_proc_arst: struct ProcArstPass : public Pass { ProcArstPass() : Pass("proc_arst", "detect asynchronous resets") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -221,7 +221,7 @@ struct ProcArstPass : public Pass { log(" in the 'init' attribute on the net.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string global_arst; bool global_arst_neg = false; diff --git a/yosys/passes/proc/proc_clean.cc b/yosys/passes/proc/proc_clean.cc index 7dbabc211..3919e4b9c 100644 --- a/yosys/passes/proc/proc_clean.cc +++ b/yosys/passes/proc/proc_clean.cc @@ -77,18 +77,36 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did } else { - bool all_cases_are_empty = true; - for (auto cs : sw->cases) { - if (cs->actions.size() != 0 || cs->switches.size() != 0) - all_cases_are_empty = false; + bool all_fully_def = true; + for (auto cs : sw->cases) + { if (max_depth != 0) proc_clean_case(cs, did_something, count, max_depth-1); + for (auto cmp : cs->compare) + if (!cmp.is_fully_def()) + all_fully_def = false; } - if (all_cases_are_empty) { - did_something = true; - for (auto cs : sw->cases) - delete cs; - sw->cases.clear(); + if (all_fully_def) + { + for (auto cs = sw->cases.begin(); cs != sw->cases.end();) + { + if ((*cs)->empty()) + { + did_something = true; + delete *cs; + cs = sw->cases.erase(cs); + } + else ++cs; + } + } + else + { + while (!sw->cases.empty() && sw->cases.back()->empty()) + { + did_something = true; + delete sw->cases.back(); + sw->cases.pop_back(); + } } } } @@ -106,7 +124,7 @@ void proc_clean_case(RTLIL::CaseRule *cs, bool &did_something, int &count, int m } for (size_t i = 0; i < cs->switches.size(); i++) { RTLIL::SwitchRule *sw = cs->switches[i]; - if (sw->cases.size() == 0) { + if (sw->empty()) { cs->switches.erase(cs->switches.begin() + (i--)); did_something = true; delete sw; @@ -143,7 +161,7 @@ void proc_clean(RTLIL::Module *mod, RTLIL::Process *proc, int &total_count) struct ProcCleanPass : public Pass { ProcCleanPass() : Pass("proc_clean", "remove empty parts of processes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -153,7 +171,7 @@ struct ProcCleanPass : public Pass { log("if it contains only empty structures.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { int total_count = 0; log_header(design, "Executing PROC_CLEAN pass (remove empty switches from decision trees).\n"); diff --git a/yosys/passes/proc/proc_dff.cc b/yosys/passes/proc/proc_dff.cc index 98653dc6b..519d35cd6 100644 --- a/yosys/passes/proc/proc_dff.cc +++ b/yosys/passes/proc/proc_dff.cc @@ -322,6 +322,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) } } + SigSpec sig_q = sig; ce.assign_map.apply(insig); ce.assign_map.apply(rstval); ce.assign_map.apply(sig); @@ -350,13 +351,13 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) 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, + gen_dffsr(mod, insig, rstval, sig_q, 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, + gen_dff(mod, insig, rstval.as_const(), sig_q, sync_edge && sync_edge->type == RTLIL::SyncType::STp, sync_level && sync_level->type == RTLIL::SyncType::ST1, sync_edge ? sync_edge->signal : SigSpec(), @@ -369,7 +370,7 @@ void proc_dff(RTLIL::Module *mod, RTLIL::Process *proc, ConstEval &ce) struct ProcDffPass : public Pass { ProcDffPass() : Pass("proc_dff", "extract flip-flops from processes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -379,7 +380,7 @@ struct ProcDffPass : public Pass { log("d-type flip-flop cells.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_DFF pass (convert process syncs to FFs).\n"); diff --git a/yosys/passes/proc/proc_dlatch.cc b/yosys/passes/proc/proc_dlatch.cc index 6621afd33..d9d5dfbed 100644 --- a/yosys/passes/proc/proc_dlatch.cc +++ b/yosys/passes/proc/proc_dlatch.cc @@ -217,7 +217,7 @@ struct proc_dlatch_db_t return make_inner(children); } - SigBit make_hold(int n) + SigBit make_hold(int n, string &src) { if (n == true_node) return State::S1; @@ -235,20 +235,20 @@ struct proc_dlatch_db_t 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)); + and_bits.append(module->Not(NEW_ID, rule.signal, false, src)); else - and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match)); + and_bits.append(module->Eq(NEW_ID, rule.signal, rule.match, false, src)); } 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)); + or_bits.append(make_hold(k, src)); + and_bits.append(module->ReduceOr(NEW_ID, or_bits, false, src)); } if (GetSize(and_bits) == 2) - and_bits = module->And(NEW_ID, and_bits[0], and_bits[1]); + and_bits = module->And(NEW_ID, and_bits[0], and_bits[1], false, src); log_assert(GetSize(and_bits) == 1); rules_sig[n] = and_bits[0]; @@ -340,6 +340,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) RTLIL::SigSig latches_bits, nolatches_bits; dict latches_out_in; dict latches_hold; + std::string src = proc->get_src_attribute(); for (auto sr : proc->syncs) { @@ -405,7 +406,8 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) 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); + Cell *cell = db.module->addDlatch(NEW_ID, db.module->Not(NEW_ID, db.make_hold(n, src)), rhs, lhs); + cell->set_src_attribute(src); db.generated_dlatches.insert(cell); log("Latch inferred for signal `%s.%s' from process `%s.%s': %s\n", @@ -420,7 +422,7 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc) struct ProcDlatchPass : public Pass { ProcDlatchPass() : Pass("proc_dlatch", "extract latches from processes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -430,7 +432,7 @@ struct ProcDlatchPass : public Pass { log("d-type latches.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_DLATCH pass (convert process syncs to latches).\n"); diff --git a/yosys/passes/proc/proc_init.cc b/yosys/passes/proc/proc_init.cc index 0c8fb83dc..e2dc07e53 100644 --- a/yosys/passes/proc/proc_init.cc +++ b/yosys/passes/proc/proc_init.cc @@ -102,7 +102,7 @@ void proc_init(RTLIL::Module *mod, RTLIL::Process *proc) struct ProcInitPass : public Pass { ProcInitPass() : Pass("proc_init", "convert initial block to init attributes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -113,7 +113,7 @@ struct ProcInitPass : public Pass { log("respective wire.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_INIT pass (extract init attributes).\n"); diff --git a/yosys/passes/proc/proc_mux.cc b/yosys/passes/proc/proc_mux.cc index 57e131ca5..1329c1fef 100644 --- a/yosys/passes/proc/proc_mux.cc +++ b/yosys/passes/proc/proc_mux.cc @@ -382,7 +382,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) struct ProcMuxPass : public Pass { ProcMuxPass() : Pass("proc_mux", "convert decision trees to multiplexers") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -396,7 +396,7 @@ struct ProcMuxPass : public Pass { log(" 'case' expressions and 'if' conditions.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool ifxmode = false; log_header(design, "Executing PROC_MUX pass (convert decision trees to multiplexers).\n"); diff --git a/yosys/passes/proc/proc_rmdead.cc b/yosys/passes/proc/proc_rmdead.cc index 5672fb475..7c334e661 100644 --- a/yosys/passes/proc/proc_rmdead.cc +++ b/yosys/passes/proc/proc_rmdead.cc @@ -65,7 +65,7 @@ void proc_rmdead(RTLIL::SwitchRule *sw, int &counter) struct ProcRmdeadPass : public Pass { ProcRmdeadPass() : Pass("proc_rmdead", "eliminate dead trees in decision trees") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -74,7 +74,7 @@ struct ProcRmdeadPass : public Pass { log("This pass identifies unreachable branches in decision trees and removes them.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PROC_RMDEAD pass (remove dead branches from decision trees).\n"); diff --git a/yosys/passes/sat/Makefile.inc b/yosys/passes/sat/Makefile.inc index 6785b750a..8ab0280c0 100644 --- a/yosys/passes/sat/Makefile.inc +++ b/yosys/passes/sat/Makefile.inc @@ -2,8 +2,10 @@ OBJS += passes/sat/sat.o OBJS += passes/sat/freduce.o OBJS += passes/sat/eval.o +OBJS += passes/sat/sim.o OBJS += passes/sat/miter.o OBJS += passes/sat/expose.o OBJS += passes/sat/assertpmux.o OBJS += passes/sat/clk2fflogic.o +OBJS += passes/sat/async2sync.o diff --git a/yosys/passes/sat/assertpmux.cc b/yosys/passes/sat/assertpmux.cc index 63a907671..509cb0ba9 100644 --- a/yosys/passes/sat/assertpmux.cc +++ b/yosys/passes/sat/assertpmux.cc @@ -181,7 +181,7 @@ struct AssertpmuxWorker struct AssertpmuxPass : public Pass { AssertpmuxPass() : Pass("assertpmux", "convert internal signals to module ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -199,7 +199,7 @@ struct AssertpmuxPass : public Pass { log(" additional constrained and check the $pmux condition always.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_noinit = false; bool flag_always = false; diff --git a/yosys/passes/sat/async2sync.cc b/yosys/passes/sat/async2sync.cc new file mode 100644 index 000000000..c92db7118 --- /dev/null +++ b/yosys/passes/sat/async2sync.cc @@ -0,0 +1,147 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 Async2syncPass : public Pass { + Async2syncPass() : Pass("async2sync", "convert async FF inputs to sync circuits") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" async2sync [options] [selection]\n"); + log("\n"); + log("This command replaces async FF inputs with sync circuits emulating the same\n"); + log("behavior for when the async signals are actually synchronized to the clock.\n"); + log("\n"); + log("This pass assumes negative hold time for the async FF inputs. For example when\n"); + log("a reset deasserts with the clock edge, then the FF output will still drive the\n"); + log("reset value in the next cycle regardless of the data-in value at the time of\n"); + log("the clock edge.\n"); + log("\n"); + log("Currently only $adff cells are supported by this pass.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + // bool flag_noinit = false; + + log_header(design, "Executing ASYNC2SYNC pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-noinit") { + // flag_noinit = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + dict initbits; + pool 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(module->selected_cells())) + { + if (cell->type.in("$adff")) + { + // bool clk_pol = cell->parameters["\\CLK_POLARITY"].as_bool(); + bool arst_pol = cell->parameters["\\ARST_POLARITY"].as_bool(); + Const arst_val = cell->parameters["\\ARST_VALUE"]; + + SigSpec sig_clk = cell->getPort("\\CLK"); + SigSpec sig_arst = cell->getPort("\\ARST"); + SigSpec sig_d = cell->getPort("\\D"); + SigSpec sig_q = cell->getPort("\\Q"); + + log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n", + log_id(module), log_id(cell), log_id(cell->type), + log_signal(sig_arst), log_signal(sig_d), log_signal(sig_q)); + + Const init_val; + for (int i = 0; i < GetSize(sig_q); i++) { + SigBit bit = sigmap(sig_q[i]); + init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx); + del_initbits.insert(bit); + } + + Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d)); + Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q)); + new_q->attributes["\\init"] = init_val; + + if (arst_pol) { + module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d); + module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q); + } else { + module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d); + module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q); + } + + cell->setPort("\\D", new_d); + cell->setPort("\\Q", new_q); + cell->unsetPort("\\ARST"); + cell->unsetParam("\\ARST_POLARITY"); + cell->unsetParam("\\ARST_VALUE"); + cell->type = "$dff"; + 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; + } + } + } +} Async2syncPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/sat/clk2fflogic.cc b/yosys/passes/sat/clk2fflogic.cc index ef6d5dd72..49ec795d3 100644 --- a/yosys/passes/sat/clk2fflogic.cc +++ b/yosys/passes/sat/clk2fflogic.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct Clk2fflogicPass : public Pass { Clk2fflogicPass() : Pass("clk2fflogic", "convert clocked FFs to generic $ff cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -36,7 +36,7 @@ struct Clk2fflogicPass : public Pass { log("multiple clocks.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { // bool flag_noinit = false; @@ -72,7 +72,88 @@ struct Clk2fflogicPass : public Pass { for (auto cell : vector(module->selected_cells())) { - if (cell->type.in("$dlatch")) + if (cell->type.in("$mem")) + { + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int rd_ports = cell->getParam("\\RD_PORTS").as_int(); + int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + + for (int i = 0; i < rd_ports; i++) { + if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool()) + log_error("Read port %d of memory %s.%s is clocked. This is not supported by \"clk2fflogic\"! " + "Call \"memory\" with -nordff to avoid this error.\n", i, log_id(cell), log_id(module)); + } + + Const wr_clk_en_param = cell->getParam("\\WR_CLK_ENABLE"); + Const wr_clk_pol_param = cell->getParam("\\WR_CLK_POLARITY"); + + SigSpec wr_clk_port = cell->getPort("\\WR_CLK"); + SigSpec wr_en_port = cell->getPort("\\WR_EN"); + SigSpec wr_addr_port = cell->getPort("\\WR_ADDR"); + SigSpec wr_data_port = cell->getPort("\\WR_DATA"); + + for (int wport = 0; wport < wr_ports; wport++) + { + bool clken = wr_clk_en_param[wport] == State::S1; + bool clkpol = wr_clk_pol_param[wport] == State::S1; + + if (!clken) + continue; + + SigBit clk = wr_clk_port[wport]; + SigSpec en = wr_en_port.extract(wport*width, width); + SigSpec addr = wr_addr_port.extract(wport*abits, abits); + SigSpec data = wr_data_port.extract(wport*width, width); + + log("Modifying write port %d on memory %s.%s: CLK=%s, A=%s, D=%s\n", + wport, log_id(module), log_id(cell), log_signal(clk), + log_signal(addr), log_signal(data)); + + Wire *past_clk = module->addWire(NEW_ID); + past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0; + module->addFf(NEW_ID, clk, past_clk); + + 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); + + SigSpec en_q = module->addWire(NEW_ID, GetSize(en)); + module->addFf(NEW_ID, en, en_q); + + SigSpec addr_q = module->addWire(NEW_ID, GetSize(addr)); + module->addFf(NEW_ID, addr, addr_q); + + SigSpec data_q = module->addWire(NEW_ID, GetSize(data)); + module->addFf(NEW_ID, data, data_q); + + wr_clk_port[wport] = State::S0; + wr_en_port.replace(wport*width, module->Mux(NEW_ID, Const(0, GetSize(en_q)), en_q, clock_edge)); + wr_addr_port.replace(wport*abits, addr_q); + wr_data_port.replace(wport*width, data_q); + + wr_clk_en_param[wport] = State::S0; + wr_clk_pol_param[wport] = State::S0; + } + + cell->setParam("\\WR_CLK_ENABLE", wr_clk_en_param); + cell->setParam("\\WR_CLK_POLARITY", wr_clk_pol_param); + + cell->setPort("\\WR_CLK", wr_clk_port); + cell->setPort("\\WR_EN", wr_en_port); + cell->setPort("\\WR_ADDR", wr_addr_port); + cell->setPort("\\WR_DATA", wr_data_port); + } + + if (cell->type.in("$dlatch", "$dlatchsr")) { bool enpol = cell->parameters["\\EN_POLARITY"].as_bool(); @@ -87,10 +168,31 @@ struct Clk2fflogicPass : public Pass { 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); + if (cell->type == "$dlatch") + { + 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); + } else - module->addMux(NEW_ID, sig_d, past_q, sig_en, sig_q); + { + SigSpec t; + if (enpol) + t = module->Mux(NEW_ID, past_q, sig_d, sig_en); + else + t = module->Mux(NEW_ID, sig_d, past_q, sig_en); + + SigSpec s = cell->getPort("\\SET"); + if (!cell->parameters["\\SET_POLARITY"].as_bool()) + s = module->Not(NEW_ID, s); + t = module->Or(NEW_ID, t, s); + + SigSpec c = cell->getPort("\\CLR"); + if (cell->parameters["\\CLR_POLARITY"].as_bool()) + c = module->Not(NEW_ID, c); + module->addAnd(NEW_ID, t, c, sig_q); + } Const initval; bool assign_initval = false; diff --git a/yosys/passes/sat/eval.cc b/yosys/passes/sat/eval.cc index 09f69cc5c..008cd2dfa 100644 --- a/yosys/passes/sat/eval.cc +++ b/yosys/passes/sat/eval.cc @@ -360,7 +360,7 @@ struct VlogHammerReporter struct EvalPass : public Pass { EvalPass() : Pass("eval", "evaluate the circuit given an input") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -383,7 +383,7 @@ struct EvalPass : public Pass { log(" then all output ports of the current module are used.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::vector> sets; std::vector shows, tables; diff --git a/yosys/passes/sat/expose.cc b/yosys/passes/sat/expose.cc index 9427547f3..809345486 100644 --- a/yosys/passes/sat/expose.cc +++ b/yosys/passes/sat/expose.cc @@ -220,7 +220,7 @@ RTLIL::Wire *add_new_wire(RTLIL::Module *module, RTLIL::IdString name, int width struct ExposePass : public Pass { ExposePass() : Pass("expose", "convert internal signals to module ports") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -236,6 +236,10 @@ struct ExposePass : public Pass { 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(" -input\n"); + log(" when exposing a wire, create an input port and disconnect the internal\n"); + log(" driver.\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"); @@ -253,12 +257,13 @@ struct ExposePass : public Pass { log(" designator for the exposed signal.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool flag_shared = false; bool flag_evert = false; bool flag_dff = false; bool flag_cut = false; + bool flag_input = false; bool flag_evert_dff = false; std::string sep = "."; @@ -279,10 +284,14 @@ struct ExposePass : public Pass { flag_dff = true; continue; } - if (args[argidx] == "-cut") { + if (args[argidx] == "-cut" && !flag_input) { flag_cut = true; continue; } + if (args[argidx] == "-input" && !flag_cut) { + flag_input = true; + continue; + } if (args[argidx] == "-evert-dff") { flag_evert_dff = true; continue; @@ -464,16 +473,42 @@ struct ExposePass : public Pass { 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_input) + { + if (!it.second->port_input) { + it.second->port_input = true; + log("New module port: %s/%s\n", RTLIL::id2cstr(module->name), RTLIL::id2cstr(it.second->name)); + RTLIL::Wire *w = module->addWire(NEW_ID, GetSize(it.second)); + out_to_in_map.add(it.second, w); + } + } + else + { + 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_input) + { + for (auto &it : module->cells_) { + if (!ct.cell_known(it.second->type)) + continue; + for (auto &conn : it.second->connections_) + if (ct.cell_output(it.second->type, conn.first)) + conn.second = out_to_in_map(sigmap(conn.second)); } - 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); - } + for (auto &conn : module->connections_) + conn.first = out_to_in_map(sigmap(conn.first)); } if (flag_cut) diff --git a/yosys/passes/sat/freduce.cc b/yosys/passes/sat/freduce.cc index 77263f6a2..f29631639 100644 --- a/yosys/passes/sat/freduce.cc +++ b/yosys/passes/sat/freduce.cc @@ -687,7 +687,8 @@ struct FreduceWorker } std::map bitusage; - module->rewrite_sigspecs(CountBitUsage(sigmap, bitusage)); + CountBitUsage bitusage_worker(sigmap, bitusage); + module->rewrite_sigspecs(bitusage_worker); if (!dump_prefix.empty()) dump(); @@ -759,7 +760,7 @@ struct FreduceWorker struct FreducePass : public Pass { FreducePass() : Pass("freduce", "perform functional reduction") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -790,7 +791,7 @@ struct FreducePass : public Pass { log("circuit that is analyzed.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { reduce_counter = 0; reduce_stop_at = 0; diff --git a/yosys/passes/sat/miter.cc b/yosys/passes/sat/miter.cc index 9e150b60c..d37f1b126 100644 --- a/yosys/passes/sat/miter.cc +++ b/yosys/passes/sat/miter.cc @@ -358,7 +358,7 @@ void create_miter_assert(struct Pass *that, std::vector args, RTLIL struct MiterPass : public Pass { MiterPass() : Pass("miter", "automatically create a miter circuit") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -402,7 +402,7 @@ struct MiterPass : public Pass { log(" call 'flatten; opt_expr -keepdc -undriven;;' on the miter circuit.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { if (args.size() > 1 && args[1] == "-equiv") { create_miter_equiv(this, args, design); diff --git a/yosys/passes/sat/sat.cc b/yosys/passes/sat/sat.cc index a6ac7afd4..695a03e15 100644 --- a/yosys/passes/sat/sat.cc +++ b/yosys/passes/sat/sat.cc @@ -691,7 +691,6 @@ struct SatHelper // VCD has some limits on internal (non-display) identifier names, so make legal ones std::map 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) { @@ -891,7 +890,7 @@ void print_qed() struct SatPass : public Pass { SatPass() : Pass("sat", "solve a SAT problem in the circuit") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1058,7 +1057,7 @@ struct SatPass : public Pass { log(" Like -falsify but do not return an error for timeouts.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::vector> sets, sets_init, prove, prove_x; std::map>> sets_at; diff --git a/yosys/passes/sat/sim.cc b/yosys/passes/sat/sim.cc new file mode 100644 index 000000000..53e248adf --- /dev/null +++ b/yosys/passes/sat/sim.cc @@ -0,0 +1,862 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 SimShared +{ + bool debug = false; + bool hide_internal = true; + bool writeback = false; + bool zinit = false; + int rstlen = 1; +}; + +void zinit(State &v) +{ + if (v != State::S1) + v = State::S0; +} + +void zinit(Const &v) +{ + for (auto &bit : v.bits) + zinit(bit); +} + +struct SimInstance +{ + SimShared *shared; + + Module *module; + Cell *instance; + + SimInstance *parent; + dict children; + + SigMap sigmap; + dict state_nets; + dict> upd_cells; + dict> upd_outports; + + pool dirty_bits; + pool dirty_cells; + pool dirty_children; + + struct ff_state_t + { + State past_clock; + Const past_d; + }; + + struct mem_state_t + { + Const past_wr_clk; + Const past_wr_en; + Const past_wr_addr; + Const past_wr_data; + Const data; + }; + + dict ff_database; + dict mem_database; + pool formal_database; + + dict> vcd_database; + + SimInstance(SimShared *shared, Module *module, Cell *instance = nullptr, SimInstance *parent = nullptr) : + shared(shared), module(module), instance(instance), parent(parent), sigmap(module) + { + if (parent) { + log_assert(parent->children.count(instance) == 0); + parent->children[instance] = this; + } + + for (auto wire : module->wires()) + { + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(sig); i++) { + if (state_nets.count(sig[i]) == 0) + state_nets[sig[i]] = State::Sx; + if (wire->port_output) { + upd_outports[sig[i]].insert(wire); + dirty_bits.insert(sig[i]); + } + } + + if (wire->attributes.count("\\init")) { + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) { + state_nets[sig[i]] = initval[i]; + dirty_bits.insert(sig[i]); + } + } + } + + for (auto cell : module->cells()) + { + Module *mod = module->design->module(cell->type); + + if (mod != nullptr) { + dirty_children.insert(new SimInstance(shared, mod, cell, this)); + } + + for (auto &port : cell->connections()) { + if (cell->input(port.first)) + for (auto bit : sigmap(port.second)) + upd_cells[bit].insert(cell); + } + + if (cell->type.in("$dff")) { + ff_state_t ff; + ff.past_clock = State::Sx; + ff.past_d = Const(State::Sx, cell->getParam("\\WIDTH").as_int()); + ff_database[cell] = ff; + } + + if (cell->type == "$mem") + { + mem_state_t mem; + + mem.past_wr_clk = Const(State::Sx, GetSize(cell->getPort("\\WR_CLK"))); + mem.past_wr_en = Const(State::Sx, GetSize(cell->getPort("\\WR_EN"))); + mem.past_wr_addr = Const(State::Sx, GetSize(cell->getPort("\\WR_ADDR"))); + mem.past_wr_data = Const(State::Sx, GetSize(cell->getPort("\\WR_DATA"))); + + mem.data = cell->getParam("\\INIT"); + int sz = cell->getParam("\\SIZE").as_int() * cell->getParam("\\WIDTH").as_int(); + + if (GetSize(mem.data) > sz) + mem.data.bits.resize(sz); + + while (GetSize(mem.data) < sz) + mem.data.bits.push_back(State::Sx); + + mem_database[cell] = mem; + } + + if (cell->type.in("$assert", "$cover", "$assume")) { + formal_database.insert(cell); + } + } + + if (shared->zinit) + { + for (auto &it : ff_database) + { + Cell *cell = it.first; + ff_state_t &ff = it.second; + zinit(ff.past_d); + + SigSpec qsig = cell->getPort("\\Q"); + Const qdata = get_state(qsig); + zinit(qdata); + set_state(qsig, qdata); + } + + for (auto &it : mem_database) { + mem_state_t &mem = it.second; + zinit(mem.past_wr_en); + zinit(mem.data); + } + } + } + + ~SimInstance() + { + for (auto child : children) + delete child.second; + } + + IdString name() const + { + if (instance != nullptr) + return instance->name; + return module->name; + } + + std::string hiername() const + { + if (instance != nullptr) + return parent->hiername() + "." + log_id(instance->name); + + return log_id(module->name); + } + + Const get_state(SigSpec sig) + { + Const value; + + for (auto bit : sigmap(sig)) + if (bit.wire == nullptr) + value.bits.push_back(bit.data); + else if (state_nets.count(bit)) + value.bits.push_back(state_nets.at(bit)); + else + value.bits.push_back(State::Sz); + + if (shared->debug) + log("[%s] get %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value)); + return value; + } + + bool set_state(SigSpec sig, Const value) + { + bool did_something = false; + + sig = sigmap(sig); + log_assert(GetSize(sig) == GetSize(value)); + + for (int i = 0; i < GetSize(sig); i++) + if (state_nets.at(sig[i]) != value[i]) { + state_nets.at(sig[i]) = value[i]; + dirty_bits.insert(sig[i]); + did_something = true; + } + + if (shared->debug) + log("[%s] set %s: %s\n", hiername().c_str(), log_signal(sig), log_signal(value)); + return did_something; + } + + void update_cell(Cell *cell) + { + if (ff_database.count(cell)) + return; + + if (formal_database.count(cell)) + return; + + if (mem_database.count(cell)) + { + mem_state_t &mem = mem_database.at(cell); + + int num_rd_ports = cell->getParam("\\RD_PORTS").as_int(); + + int size = cell->getParam("\\SIZE").as_int(); + int offset = cell->getParam("\\OFFSET").as_int(); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + + if (cell->getParam("\\RD_CLK_ENABLE").as_bool()) + log_error("Memory %s.%s has clocked read ports. Run 'memory' with -nordff.\n", log_id(module), log_id(cell)); + + SigSpec rd_addr_sig = cell->getPort("\\RD_ADDR"); + SigSpec rd_data_sig = cell->getPort("\\RD_DATA"); + + for (int port_idx = 0; port_idx < num_rd_ports; port_idx++) + { + Const addr = get_state(rd_addr_sig.extract(port_idx*abits, abits)); + Const data = Const(State::Sx, width); + + if (addr.is_fully_def()) { + int index = addr.as_int() - offset; + if (index >= 0 && index < size) + data = mem.data.extract(index*width, width); + } + + set_state(rd_data_sig.extract(port_idx*width, width), data); + } + + return; + } + + if (children.count(cell)) + { + auto child = children.at(cell); + for (auto &conn: cell->connections()) + if (cell->input(conn.first)) { + Const value = get_state(conn.second); + child->set_state(child->module->wire(conn.first), value); + } + dirty_children.insert(child); + return; + } + + if (yosys_celltypes.cell_evaluable(cell->type)) + { + RTLIL::SigSpec sig_a, sig_b, sig_c, sig_d, sig_s, sig_y; + bool has_a, has_b, has_c, has_d, has_s, has_y; + + has_a = cell->hasPort("\\A"); + has_b = cell->hasPort("\\B"); + has_c = cell->hasPort("\\C"); + has_d = cell->hasPort("\\D"); + has_s = cell->hasPort("\\S"); + has_y = cell->hasPort("\\Y"); + + if (has_a) sig_a = cell->getPort("\\A"); + if (has_b) sig_b = cell->getPort("\\B"); + if (has_c) sig_c = cell->getPort("\\C"); + if (has_d) sig_d = cell->getPort("\\D"); + if (has_s) sig_s = cell->getPort("\\S"); + if (has_y) sig_y = cell->getPort("\\Y"); + + if (shared->debug) + log("[%s] eval %s (%s)\n", hiername().c_str(), log_id(cell), log_id(cell->type)); + + // Simple (A -> Y) and (A,B -> Y) cells + if (has_a && !has_c && !has_d && !has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b))); + return; + } + + // (A,B,C -> Y) cells + if (has_a && has_b && has_c && !has_d && !has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_c))); + return; + } + + // (A,B,S -> Y) cells + if (has_a && has_b && !has_c && !has_d && has_s && has_y) { + set_state(sig_y, CellTypes::eval(cell, get_state(sig_a), get_state(sig_b), get_state(sig_s))); + return; + } + + log_warning("Unsupported evaluable cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); + return; + } + + log_error("Unsupported cell type: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); + } + + void update_ph1() + { + pool queue_cells; + pool queue_outports; + + queue_cells.swap(dirty_cells); + + while (1) + { + for (auto bit : dirty_bits) + { + if (upd_cells.count(bit)) + for (auto cell : upd_cells.at(bit)) + queue_cells.insert(cell); + + if (upd_outports.count(bit) && parent != nullptr) + for (auto wire : upd_outports.at(bit)) + queue_outports.insert(wire); + } + + dirty_bits.clear(); + + if (!queue_cells.empty()) + { + for (auto cell : queue_cells) + update_cell(cell); + + queue_cells.clear(); + continue; + } + + for (auto wire : queue_outports) + if (instance->hasPort(wire->name)) { + Const value = get_state(wire); + parent->set_state(instance->getPort(wire->name), value); + } + + queue_outports.clear(); + + for (auto child : dirty_children) + child->update_ph1(); + + dirty_children.clear(); + + if (dirty_bits.empty()) + break; + } + } + + bool update_ph2() + { + bool did_something = false; + + for (auto &it : ff_database) + { + Cell *cell = it.first; + ff_state_t &ff = it.second; + + if (cell->type.in("$dff")) + { + bool clkpol = cell->getParam("\\CLK_POLARITY").as_bool(); + State current_clock = get_state(cell->getPort("\\CLK"))[0]; + + if (clkpol ? (ff.past_clock == State::S1 || current_clock != State::S1) : + (ff.past_clock == State::S0 || current_clock != State::S0)) + continue; + + if (set_state(cell->getPort("\\Q"), ff.past_d)) + did_something = true; + } + } + + for (auto &it : mem_database) + { + Cell *cell = it.first; + mem_state_t &mem = it.second; + + int num_wr_ports = cell->getParam("\\WR_PORTS").as_int(); + + int size = cell->getParam("\\SIZE").as_int(); + int offset = cell->getParam("\\OFFSET").as_int(); + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + + Const wr_clk_enable = cell->getParam("\\WR_CLK_ENABLE"); + Const wr_clk_polarity = cell->getParam("\\WR_CLK_POLARITY"); + Const current_wr_clk = get_state(cell->getPort("\\WR_CLK")); + + for (int port_idx = 0; port_idx < num_wr_ports; port_idx++) + { + Const addr, data, enable; + + if (wr_clk_enable[port_idx] == State::S0) + { + addr = get_state(cell->getPort("\\WR_ADDR").extract(port_idx*abits, abits)); + data = get_state(cell->getPort("\\WR_DATA").extract(port_idx*width, width)); + enable = get_state(cell->getPort("\\WR_EN").extract(port_idx*width, width)); + } + else + { + if (wr_clk_polarity[port_idx] == State::S1 ? + (mem.past_wr_clk[port_idx] == State::S1 || current_wr_clk[port_idx] != State::S1) : + (mem.past_wr_clk[port_idx] == State::S0 || current_wr_clk[port_idx] != State::S0)) + continue; + + addr = mem.past_wr_addr.extract(port_idx*abits, abits); + data = mem.past_wr_data.extract(port_idx*width, width); + enable = mem.past_wr_en.extract(port_idx*width, width); + } + + if (addr.is_fully_def()) + { + int index = addr.as_int() - offset; + if (index >= 0 && index < size) + for (int i = 0; i < width; i++) + if (enable[i] == State::S1 && mem.data.bits.at(index*width+i) != data[i]) { + mem.data.bits.at(index*width+i) = data[i]; + dirty_cells.insert(cell); + did_something = true; + } + } + } + } + + for (auto it : children) + if (it.second->update_ph2()) { + dirty_children.insert(it.second); + did_something = true; + } + + return did_something; + } + + void update_ph3() + { + for (auto &it : ff_database) + { + Cell *cell = it.first; + ff_state_t &ff = it.second; + + if (cell->type.in("$dff")) { + ff.past_clock = get_state(cell->getPort("\\CLK"))[0]; + ff.past_d = get_state(cell->getPort("\\D")); + } + } + + for (auto &it : mem_database) + { + Cell *cell = it.first; + mem_state_t &mem = it.second; + + mem.past_wr_clk = get_state(cell->getPort("\\WR_CLK")); + mem.past_wr_en = get_state(cell->getPort("\\WR_EN")); + mem.past_wr_addr = get_state(cell->getPort("\\WR_ADDR")); + mem.past_wr_data = get_state(cell->getPort("\\WR_DATA")); + } + + for (auto cell : formal_database) + { + string label = log_id(cell); + if (cell->attributes.count("\\src")) + label = cell->attributes.at("\\src").decode_string(); + + State a = get_state(cell->getPort("\\A"))[0]; + State en = get_state(cell->getPort("\\EN"))[0]; + + if (cell->type == "$cover" && en == State::S1 && a != State::S1) + log("Cover %s.%s (%s) reached.\n", hiername().c_str(), log_id(cell), label.c_str()); + + if (cell->type == "$assume" && en == State::S1 && a != State::S1) + log("Assumption %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str()); + + if (cell->type == "$assert" && en == State::S1 && a != State::S1) + log_warning("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str()); + } + + for (auto it : children) + it.second->update_ph3(); + } + + void writeback(pool &wbmods) + { + if (wbmods.count(module)) + log_error("Instance %s of module %s is not unique: Writeback not possible. (Fix by running 'uniquify'.)\n", hiername().c_str(), log_id(module)); + + wbmods.insert(module); + + for (auto wire : module->wires()) + wire->attributes.erase("\\init"); + + for (auto &it : ff_database) + { + Cell *cell = it.first; + SigSpec sig_q = cell->getPort("\\Q"); + Const initval = get_state(sig_q); + + for (int i = 0; i < GetSize(sig_q); i++) + { + Wire *w = sig_q[i].wire; + + if (w->attributes.count("\\init") == 0) + w->attributes["\\init"] = Const(State::Sx, GetSize(w)); + + w->attributes["\\init"][sig_q[i].offset] = initval[i]; + } + } + + for (auto &it : mem_database) + { + Cell *cell = it.first; + mem_state_t &mem = it.second; + Const initval = mem.data; + + while (GetSize(initval) >= 2) { + if (initval[GetSize(initval)-1] != State::Sx) break; + if (initval[GetSize(initval)-2] != State::Sx) break; + initval.bits.pop_back(); + } + + cell->setParam("\\INIT", initval); + } + + for (auto it : children) + it.second->writeback(wbmods); + } + + void write_vcd_header(std::ofstream &f, int &id) + { + f << stringf("$scope module %s $end\n", log_id(name())); + + for (auto wire : module->wires()) + { + if (shared->hide_internal && wire->name[0] == '$') + continue; + + f << stringf("$var wire %d n%d %s%s $end\n", GetSize(wire), id, wire->name[0] == '$' ? "\\" : "", log_id(wire)); + vcd_database[wire] = make_pair(id++, Const()); + } + + for (auto child : children) + child.second->write_vcd_header(f, id); + + f << stringf("$upscope $end\n"); + } + + void write_vcd_step(std::ofstream &f) + { + for (auto &it : vcd_database) + { + Wire *wire = it.first; + Const value = get_state(wire); + int id = it.second.first; + + if (it.second.second == value) + continue; + + it.second.second = value; + + f << "b"; + for (int i = GetSize(value)-1; i >= 0; i--) { + switch (value[i]) { + case State::S0: f << "0"; break; + case State::S1: f << "1"; break; + case State::Sx: f << "x"; break; + default: f << "z"; + } + } + + f << stringf(" n%d\n", id); + } + + for (auto child : children) + child.second->write_vcd_step(f); + } +}; + +struct SimWorker : SimShared +{ + SimInstance *top = nullptr; + std::ofstream vcdfile; + pool clock, clockn, reset, resetn; + + ~SimWorker() + { + delete top; + } + + void write_vcd_header() + { + if (!vcdfile.is_open()) + return; + + int id = 1; + top->write_vcd_header(vcdfile, id); + + vcdfile << stringf("$enddefinitions $end\n"); + } + + void write_vcd_step(int t) + { + if (!vcdfile.is_open()) + return; + + vcdfile << stringf("#%d\n", t); + top->write_vcd_step(vcdfile); + } + + void update() + { + while (1) + { + if (debug) + log("\n-- ph1 --\n"); + + top->update_ph1(); + + if (debug) + log("\n-- ph2 --\n"); + + if (!top->update_ph2()) + break; + } + + if (debug) + log("\n-- ph3 --\n"); + + top->update_ph3(); + } + + void set_inports(pool ports, State value) + { + for (auto portname : ports) + { + Wire *w = top->module->wire(portname); + + if (w == nullptr) + log_error("Can't find port %s on module %s.\n", log_id(portname), log_id(top->module)); + + top->set_state(w, value); + } + } + + void run(Module *topmod, int numcycles) + { + log_assert(top == nullptr); + top = new SimInstance(this, topmod); + + if (debug) + log("\n===== 0 =====\n"); + else + log("Simulating cycle 0.\n"); + + set_inports(reset, State::S1); + set_inports(resetn, State::S0); + + set_inports(clock, State::Sx); + set_inports(clockn, State::Sx); + + update(); + + write_vcd_header(); + write_vcd_step(0); + + for (int cycle = 0; cycle < numcycles; cycle++) + { + if (debug) + log("\n===== %d =====\n", 10*cycle + 5); + + set_inports(clock, State::S0); + set_inports(clockn, State::S1); + + update(); + write_vcd_step(10*cycle + 5); + + if (debug) + log("\n===== %d =====\n", 10*cycle + 10); + else + log("Simulating cycle %d.\n", cycle+1); + + set_inports(clock, State::S1); + set_inports(clockn, State::S0); + + if (cycle+1 == rstlen) { + set_inports(reset, State::S0); + set_inports(resetn, State::S1); + } + + update(); + write_vcd_step(10*cycle + 10); + } + + write_vcd_step(10*numcycles + 2); + + if (writeback) { + pool wbmods; + top->writeback(wbmods); + } + } +}; + +struct SimPass : public Pass { + SimPass() : Pass("sim", "simulate the circuit") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" sim [options] [top-level]\n"); + log("\n"); + log("This command simulates the circuit using the given top-level module.\n"); + log("\n"); + log(" -vcd \n"); + log(" write the simulation results to the given VCD file\n"); + log("\n"); + log(" -clock \n"); + log(" name of top-level clock input\n"); + log("\n"); + log(" -clockn \n"); + log(" name of top-level clock input (inverse polarity)\n"); + log("\n"); + log(" -reset \n"); + log(" name of top-level reset input (active high)\n"); + log("\n"); + log(" -resetn \n"); + log(" name of top-level inverted reset input (active low)\n"); + log("\n"); + log(" -rstlen \n"); + log(" number of cycles reset should stay active (default: 1)\n"); + log("\n"); + log(" -zinit\n"); + log(" zero-initialize all uninitialized regs and memories\n"); + log("\n"); + log(" -n \n"); + log(" number of cycles to simulate (default: 20)\n"); + log("\n"); + log(" -a\n"); + log(" include all nets in VCD output, not just those with public names\n"); + log("\n"); + log(" -w\n"); + log(" writeback mode: use final simulation state as new init state\n"); + log("\n"); + log(" -d\n"); + log(" enable debug output\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + SimWorker worker; + int numcycles = 20; + + log_header(design, "Executing SIM pass (simulate the circuit).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-vcd" && argidx+1 < args.size()) { + worker.vcdfile.open(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-n" && argidx+1 < args.size()) { + numcycles = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-rstlen" && argidx+1 < args.size()) { + worker.rstlen = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-clock" && argidx+1 < args.size()) { + worker.clock.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-clockn" && argidx+1 < args.size()) { + worker.clockn.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-reset" && argidx+1 < args.size()) { + worker.reset.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-resetn" && argidx+1 < args.size()) { + worker.resetn.insert(RTLIL::escape_id(args[++argidx])); + continue; + } + if (args[argidx] == "-a") { + worker.hide_internal = false; + continue; + } + if (args[argidx] == "-d") { + worker.debug = true; + continue; + } + if (args[argidx] == "-w") { + worker.writeback = true; + continue; + } + if (args[argidx] == "-zinit") { + worker.zinit = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + Module *top_mod = nullptr; + + if (design->full_selection()) { + top_mod = design->top_module(); + } else { + auto mods = design->selected_whole_modules(); + if (GetSize(mods) != 1) + log_cmd_error("Only one top module must be selected.\n"); + top_mod = mods.front(); + } + + worker.run(top_mod, numcycles); + } +} SimPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/techmap/Makefile.inc b/yosys/passes/techmap/Makefile.inc index 311a1af95..cf9e198ad 100644 --- a/yosys/passes/techmap/Makefile.inc +++ b/yosys/passes/techmap/Makefile.inc @@ -16,6 +16,9 @@ ifneq ($(SMALL),1) OBJS += passes/techmap/iopadmap.o OBJS += passes/techmap/hilomap.o OBJS += passes/techmap/extract.o +OBJS += passes/techmap/extract_fa.o +OBJS += passes/techmap/extract_counter.o +OBJS += passes/techmap/extract_reduce.o OBJS += passes/techmap/alumacc.o OBJS += passes/techmap/dff2dffe.o OBJS += passes/techmap/dffinit.o @@ -32,6 +35,8 @@ OBJS += passes/techmap/insbuf.o OBJS += passes/techmap/attrmvcp.o OBJS += passes/techmap/attrmap.o OBJS += passes/techmap/zinit.o +OBJS += passes/techmap/dff2dffs.o +OBJS += passes/techmap/flowmap.o endif GENFILES += passes/techmap/techmap.inc @@ -54,4 +59,3 @@ yosys-filterlib$(EXE): passes/techmap/filterlib.o $(Q) mkdir -p $(dir $@) $(P) $(LD) -o yosys-filterlib$(EXE) $(LDFLAGS) $^ $(LDLIBS) endif - diff --git a/yosys/passes/techmap/abc.cc b/yosys/passes/techmap/abc.cc index cc79296c5..d2d15a4a9 100644 --- a/yosys/passes/techmap/abc.cc +++ b/yosys/passes/techmap/abc.cc @@ -29,17 +29,17 @@ // Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025 // http://en.wikipedia.org/wiki/Topological_sorting -#define ABC_COMMAND_LIB "strash; 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_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" +#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2" +#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" -#define ABC_FAST_COMMAND_LIB "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" +#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}" +#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "strash; dretime; if" +#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "strash; dretime; map" #include "kernel/register.h" #include "kernel/sigtools.h" @@ -60,6 +60,10 @@ #include "frontends/blif/blifparse.h" +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -74,6 +78,8 @@ enum class gate_type_t { G_NOR, G_XOR, G_XNOR, + G_ANDNOT, + G_ORNOT, G_MUX, G_AOI3, G_OAI3, @@ -90,6 +96,7 @@ struct gate_t int in1, in2, in3, in4; bool is_port; RTLIL::SigBit bit; + RTLIL::State init; }; bool map_mux4; @@ -102,10 +109,13 @@ SigMap assign_map; RTLIL::Module *module; std::vector signal_list; std::map signal_map; +std::map signal_init; pool enabled_gates; +bool recover_init; bool clk_polarity, en_polarity; RTLIL::SigSpec clk_sig, en_sig; +dict pi_map, po_map; 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) { @@ -121,6 +131,10 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1, gate.in4 = -1; gate.is_port = false; gate.bit = bit; + if (signal_init.count(bit)) + gate.init = signal_init.at(bit); + else + gate.init = State::Sx; signal_list.push_back(gate); signal_map[bit] = gate.id; } @@ -207,7 +221,7 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) return; } - if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) + if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) { RTLIL::SigSpec sig_a = cell->getPort("\\A"); RTLIL::SigSpec sig_b = cell->getPort("\\B"); @@ -232,6 +246,10 @@ void extract_cell(RTLIL::Cell *cell, bool keepff) 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 if (cell->type == "$_ANDNOT_") + map_signal(sig_y, G(ANDNOT), mapped_a, mapped_b); + else if (cell->type == "$_ORNOT_") + map_signal(sig_y, G(ORNOT), mapped_a, mapped_b); else log_abort(); @@ -532,11 +550,13 @@ std::string replace_tempdir(std::string text, std::string tempdir_name, bool sho } 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) + "/" + text.substr(pos + GetSize(selfdir_name)); + if (selfdir_name != "/") { + while (1) { + size_t pos = text.find(selfdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "/" + text.substr(pos + GetSize(selfdir_name)); + } } return text; @@ -588,6 +608,14 @@ struct abc_output_filter void next_line(const std::string &line) { + int pi, po; + if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { + log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", + pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", + po, po_map.count(po) ? po_map.at(po).c_str() : "???"); + return; + } + for (char ch : line) next_char(ch); } @@ -595,7 +623,7 @@ struct abc_output_filter void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, std::string liberty_file, std::string constr_file, bool cleanup, vector 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, + bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode, const std::vector &cells, bool show_tempdir, bool sop_mode) { module = current_module; @@ -603,11 +631,12 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin signal_map.clear(); signal_list.clear(); + pi_map.clear(); + po_map.clear(); + recover_init = false; if (clk_str != "$") { - assign_map.set(module); - clk_polarity = true; clk_sig = RTLIL::SigSpec(); @@ -615,6 +644,30 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin en_sig = RTLIL::SigSpec(); } + 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_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; if (!cleanup) tempdir_name[0] = tempdir_name[4] = '_'; @@ -652,7 +705,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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"; + abc_script += "; lutpack {S}"; } 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) @@ -660,6 +713,10 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin else abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + if (script_file.empty() && !delay_target.empty()) + for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) + abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); + for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); @@ -669,6 +726,9 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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); + for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos)) + abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3); + abc_script += stringf("; write_blif %s/output.blif", tempdir_name.c_str()); abc_script = add_echos_to_abc_cmd(abc_script); @@ -680,30 +740,6 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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) @@ -749,7 +785,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!si.is_port || si.type != G(NONE)) continue; fprintf(f, " n%d", si.id); - count_input++; + pi_map[count_input++] = log_signal(si.bit); } if (count_input == 0) fprintf(f, " dummy_input\n"); @@ -761,7 +797,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin if (!si.is_port || si.type == G(NONE)) continue; fprintf(f, " n%d", si.id); - count_output++; + po_map[count_output++] = log_signal(si.bit); } fprintf(f, "\n"); @@ -806,6 +842,13 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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(ANDNOT)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "10 1\n"); + } else if (si.type == G(ORNOT)) { + fprintf(f, ".names n%d n%d n%d\n", si.in1, si.in2, si.id); + fprintf(f, "1- 1\n"); + fprintf(f, "-0 1\n"); } else if (si.type == G(MUX)) { fprintf(f, ".names n%d n%d n%d n%d\n", si.in1, si.in2, si.in3, si.id); fprintf(f, "1-0 1\n"); @@ -829,7 +872,11 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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); + if (si.init == State::S0 || si.init == State::S1) { + fprintf(f, ".latch n%d n%d %d\n", si.in1, si.id, si.init == State::S1 ? 1 : 0); + recover_init = true; + } else + fprintf(f, ".latch n%d n%d 2\n", si.in1, si.id); } else if (si.type != G(NONE)) log_abort(); if (si.type != G(NONE)) @@ -851,38 +898,42 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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("ANDNOT")) + fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ANDNOT_")); + if (enabled_gates.empty() || enabled_gates.count("ORNOT")) + fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ORNOT_")); 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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_")); + 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()) { @@ -898,8 +949,24 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin 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()); +#ifndef YOSYS_LINK_ABC abc_output_filter filt(tempdir_name, show_tempdir); int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); +#else + // These needs to be mutable, supposedly due to getopt + char *abc_argv[5]; + string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); + abc_argv[0] = strdup(exe_file.c_str()); + abc_argv[1] = strdup("-s"); + abc_argv[2] = strdup("-f"); + abc_argv[3] = strdup(tmp_script_name.c_str()); + abc_argv[4] = 0; + int ret = Abc_RealMain(4, abc_argv); + free(abc_argv[0]); + free(abc_argv[1]); + free(abc_argv[2]); + free(abc_argv[3]); +#endif if (ret != 0) log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); @@ -954,7 +1021,8 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin design->select(module, cell); continue; } - if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || c->type == "\\XNOR") { + if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" || + c->type == "\\XNOR" || c->type == "\\ANDNOT" || c->type == "\\ORNOT") { 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)])); @@ -1130,6 +1198,15 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin module->connect(conn); } + if (recover_init) + for (auto wire : mapped_mod->wires()) { + if (wire->attributes.count("\\init")) { + Wire *w = module->wires_[remap_name(wire->name)]; + log_assert(w->attributes.count("\\init") == 0); + w->attributes["\\init"] = wire->attributes.at("\\init"); + } + } + 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; @@ -1171,7 +1248,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin struct AbcPass : public Pass { AbcPass() : Pass("abc", "use ABC for technology mapping") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1205,7 +1282,7 @@ struct AbcPass : public Pass { 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("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack {S}").c_str()); log("\n"); log(" for -lut/-luts (different LUT sizes):\n"); log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); @@ -1253,6 +1330,8 @@ struct AbcPass : public Pass { log(" -D \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(" this also replaces 'dretime' with 'dretime; retime -o {D}' in the\n"); + log(" default scripts above.\n"); log("\n"); log(" -I \n"); log(" maximum number of SOP inputs.\n"); @@ -1262,6 +1341,10 @@ struct AbcPass : public Pass { log(" maximum number of SOP products.\n"); log(" (replaces {P} in the default scripts above)\n"); log("\n"); + log(" -S \n"); + log(" maximum number of LUT inputs shared.\n"); + log(" (replaces {S} in the default scripts above, default: -S 1)\n"); + log("\n"); log(" -lut \n"); log(" generate netlist using luts of (max) the specified width.\n"); log("\n"); @@ -1283,10 +1366,21 @@ struct AbcPass : public Pass { // 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(" Map to the specified list of gate types. Supported gates types are:\n"); + log(" AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX, AOI3, OAI3, AOI4, OAI4.\n"); log(" (The NOT gate is always added to this list automatically.)\n"); log("\n"); + log(" The following aliases can be used to reference common sets of gate types:\n"); + log(" simple: AND OR XOR MUX\n"); + log(" cmos2: NAND NOR\n"); + log(" cmos3: NAND NOR AOI3 OAI3\n"); + log(" cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4\n"); + log(" gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT\n"); + log(" aig: AND NAND OR NOR ANDNOT ORNOT\n"); + log("\n"); + log(" Prefix a gate type with a '-' to remove it from the list. For example\n"); + log(" the arguments 'AND,OR,XOR' and 'simple,-MUX' are equivalent.\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"); @@ -1316,24 +1410,35 @@ struct AbcPass : public Pass { 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("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); + log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); + log("ABC on logic snippets extracted from your design. You will not get any useful\n"); + log("output when passing an ABC script that writes a file. Instead write your full\n"); + log("design as BLIF file with write_blif and the load that into ABC externally if\n"); + log("you want to use ABC to convert your design into another format.\n"); log("\n"); log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); + assign_map.clear(); + signal_list.clear(); + signal_map.clear(); + signal_init.clear(); + pi_map.clear(); + po_map.clear(); + #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; + std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1"; bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool show_tempdir = false, sop_mode = false; vector lut_costs; @@ -1365,17 +1470,20 @@ struct AbcPass : public Pass { } if (arg == "-script" && argidx+1 < args.size()) { script_file = args[++argidx]; + rewrite_filename(script_file); 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]; + rewrite_filename(liberty_file); if (!liberty_file.empty() && !is_absolute_path(liberty_file)) liberty_file = std::string(pwd) + "/" + liberty_file; continue; } if (arg == "-constr" && argidx+1 < args.size()) { + rewrite_filename(constr_file); constr_file = args[++argidx]; if (!constr_file.empty() && !is_absolute_path(constr_file)) constr_file = std::string(pwd) + "/" + constr_file; @@ -1393,6 +1501,10 @@ struct AbcPass : public Pass { sop_products = "-P " + args[++argidx]; continue; } + if (arg == "-S" && argidx+1 < args.size()) { + lutin_shared = "-S " + args[++argidx]; + continue; + } if (arg == "-lut" && argidx+1 < args.size()) { string arg = args[++argidx]; size_t pos = arg.find_first_of(':'); @@ -1445,20 +1557,83 @@ struct AbcPass : public Pass { } if (arg == "-g" && argidx+1 < args.size()) { for (auto g : split_tokens(args[++argidx], ",")) { + vector gate_list; + bool remove_gates = false; + if (GetSize(g) > 0 && g[0] == '-') { + remove_gates = true; + g = g.substr(1); + } 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 == "ANDNOT") goto ok_gate; + if (g == "ORNOT") 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; + if (g == "simple") { + gate_list.push_back("AND"); + gate_list.push_back("OR"); + gate_list.push_back("XOR"); + gate_list.push_back("MUX"); + goto ok_alias; + } + if (g == "cmos2") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + goto ok_alias; + } + if (g == "cmos3") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + gate_list.push_back("AOI3"); + gate_list.push_back("OAI3"); + goto ok_alias; + } + if (g == "cmos4") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + gate_list.push_back("AOI3"); + gate_list.push_back("OAI3"); + gate_list.push_back("AOI4"); + gate_list.push_back("OAI4"); + goto ok_alias; + } + if (g == "gates") { + gate_list.push_back("AND"); + gate_list.push_back("NAND"); + gate_list.push_back("OR"); + gate_list.push_back("NOR"); + gate_list.push_back("XOR"); + gate_list.push_back("XNOR"); + gate_list.push_back("ANDNOT"); + gate_list.push_back("ORNOT"); + goto ok_alias; + } + if (g == "aig") { + gate_list.push_back("AND"); + gate_list.push_back("NAND"); + gate_list.push_back("OR"); + gate_list.push_back("NOR"); + gate_list.push_back("ANDNOT"); + gate_list.push_back("ORNOT"); + goto ok_alias; + } cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); ok_gate: - enabled_gates.insert(g); + gate_list.push_back(g); + ok_alias: + for (auto gate : gate_list) { + if (remove_gates) + enabled_gates.erase(gate); + else + enabled_gates.insert(gate); + } } continue; } @@ -1501,163 +1676,190 @@ struct AbcPass : public Pass { log_cmd_error("Got -constr but no -liberty!\n"); for (auto mod : design->selected_modules()) - if (mod->processes.size() > 0) + { + if (mod->processes.size() > 0) { log("Skipping module %s as it contains processes.\n", log_id(mod)); - else if (!dff_mode || !clk_str.empty()) + continue; + } + + assign_map.set(mod); + signal_init.clear(); + + for (Wire *wire : mod->wires()) + if (wire->attributes.count("\\init")) { + SigSpec initsig = assign_map(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + switch (initval[i]) { + case State::S0: + signal_init[initsig[i]] = State::S0; + break; + case State::S1: + signal_init[initsig[i]] = State::S0; + break; + default: + break; + } + } + + 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 + delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); + continue; + } + + CellTypes ct(design); + + std::vector all_cells = mod->selected_cells(); + std::set unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set expand_queue, next_expand_queue; + std::set expand_queue_up, next_expand_queue_up; + std::set expand_queue_down, next_expand_queue_down; + + typedef tuple clkdomain_t; + std::map> assigned_cells; + std::map assigned_cells_reverse; + + std::map> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + + for (auto cell : all_cells) { - assign_map.set(mod); - CellTypes ct(design); + clkdomain_t key; - std::vector all_cells = mod->selected_cells(); - std::set unassigned_cells(all_cells.begin(), all_cells.end()); - - std::set expand_queue, next_expand_queue; - std::set expand_queue_up, next_expand_queue_up; - std::set expand_queue_down, next_expand_queue_down; - - typedef tuple clkdomain_t; - std::map> assigned_cells; - std::map assigned_cells_reverse; - - std::map> cell_to_bit, cell_to_bit_up, cell_to_bit_down; - std::map> 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); - } + 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 (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") { - 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); - } + key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); } - - while (!expand_queue.empty()) + else + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") { - RTLIL::Cell *cell = *expand_queue.begin(); + 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.erase(cell); + expand_queue_up.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); + 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); + } } - 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; + 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); + } } - 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); + 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, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode); + assign_map.set(mod); + } + } + assign_map.clear(); signal_list.clear(); signal_map.clear(); + signal_init.clear(); + pi_map.clear(); + po_map.clear(); log_pop(); } diff --git a/yosys/passes/techmap/aigmap.cc b/yosys/passes/techmap/aigmap.cc index b9ac7aded..35df2ff79 100644 --- a/yosys/passes/techmap/aigmap.cc +++ b/yosys/passes/techmap/aigmap.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct AigmapPass : public Pass { AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" aigmap [options] [selection]\n"); @@ -37,7 +37,7 @@ struct AigmapPass : public Pass { log(" Enable creation of $_NAND_ cells\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool nand_mode = false; diff --git a/yosys/passes/techmap/alumacc.cc b/yosys/passes/techmap/alumacc.cc index 9f6dd02d0..dc7d416b0 100644 --- a/yosys/passes/techmap/alumacc.cc +++ b/yosys/passes/techmap/alumacc.cc @@ -55,19 +55,19 @@ struct AlumaccWorker 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())); + cached_gt = alu_cell->module->Not(NEW_ID, alu_cell->module->Or(NEW_ID, get_lt(), get_eq()), false, alu_cell->get_src_attribute()); return cached_gt; } RTLIL::SigSpec get_eq() { if (GetSize(cached_eq) == 0) - cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X")); + cached_eq = alu_cell->module->ReduceAnd(NEW_ID, alu_cell->getPort("\\X"), false, alu_cell->get_src_attribute()); return cached_eq; } RTLIL::SigSpec get_ne() { if (GetSize(cached_ne) == 0) - cached_ne = alu_cell->module->Not(NEW_ID, get_eq()); + cached_ne = alu_cell->module->Not(NEW_ID, get_eq(), false, alu_cell->get_src_attribute()); return cached_ne; } @@ -75,7 +75,7 @@ struct AlumaccWorker 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]); + cached_cf = alu_cell->module->Not(NEW_ID, cached_cf[GetSize(cached_cf)-1], false, alu_cell->get_src_attribute()); } return cached_cf; } @@ -352,10 +352,13 @@ struct AlumaccWorker { 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)); + cell->set_src_attribute(n->cell->get_src_attribute()); + n->macc.optimize(GetSize(n->y)); n->macc.to_cell(cell); cell->setPort("\\Y", n->y); @@ -452,6 +455,7 @@ struct AlumaccWorker void replace_alu() { + std::string src(""); for (auto &it1 : sig_alu) for (auto n : it1.second) { @@ -475,6 +479,9 @@ struct AlumaccWorker log("%s%s", i ? ", ": "", log_id(n->cells[i])); log(": %s\n", log_id(n->alu_cell)); + if (n->cells.size() > 0) + n->alu_cell->set_src_attribute(n->cells[0]->get_src_attribute()); + 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); @@ -532,7 +539,7 @@ struct AlumaccWorker struct AlumaccPass : public Pass { AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -542,7 +549,7 @@ struct AlumaccPass : public Pass { log("and $macc cells.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n"); diff --git a/yosys/passes/techmap/attrmap.cc b/yosys/passes/techmap/attrmap.cc index dec81d216..0b5576b06 100644 --- a/yosys/passes/techmap/attrmap.cc +++ b/yosys/passes/techmap/attrmap.cc @@ -81,7 +81,7 @@ struct AttrmapAction { struct AttrmapTocase : AttrmapAction { string name; - virtual bool apply(IdString &id, Const&) { + bool apply(IdString &id, Const&) YS_OVERRIDE { if (match_name(name, id, true)) id = RTLIL::escape_id(name); return true; @@ -90,7 +90,7 @@ struct AttrmapTocase : AttrmapAction { struct AttrmapRename : AttrmapAction { string old_name, new_name; - virtual bool apply(IdString &id, Const&) { + bool apply(IdString &id, Const&) YS_OVERRIDE { if (match_name(old_name, id)) id = RTLIL::escape_id(new_name); return true; @@ -101,7 +101,7 @@ struct AttrmapMap : AttrmapAction { bool imap; string old_name, new_name; string old_value, new_value; - virtual bool apply(IdString &id, Const &val) { + bool apply(IdString &id, Const &val) YS_OVERRIDE { if (match_name(old_name, id) && match_value(old_value, val, true)) { id = RTLIL::escape_id(new_name); val = make_value(new_value); @@ -112,7 +112,7 @@ struct AttrmapMap : AttrmapAction { struct AttrmapRemove : AttrmapAction { string name, value; - virtual bool apply(IdString &id, Const &val) { + bool apply(IdString &id, Const &val) YS_OVERRIDE { return !(match_name(name, id) && match_value(value, val)); } }; @@ -144,7 +144,7 @@ void attrmap_apply(string objname, vector> &actio struct AttrmapPass : public Pass { AttrmapPass() : Pass("attrmap", "renaming attributes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -179,7 +179,7 @@ struct AttrmapPass : public Pass { log(" -imap keep=\"false\" keep=0 -remove keep=0\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n"); diff --git a/yosys/passes/techmap/attrmvcp.cc b/yosys/passes/techmap/attrmvcp.cc index 1537def00..e59aa6518 100644 --- a/yosys/passes/techmap/attrmvcp.cc +++ b/yosys/passes/techmap/attrmvcp.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct AttrmvcpPass : public Pass { AttrmvcpPass() : Pass("attrmvcp", "move or copy attributes from wires to driving cells") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" attrmvcp [options] [selection]\n"); @@ -53,7 +53,7 @@ struct AttrmvcpPass : public Pass { log(" multiple times.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ATTRMVCP pass (move or copy attributes).\n"); diff --git a/yosys/passes/techmap/deminout.cc b/yosys/passes/techmap/deminout.cc index ed4e45762..47d0ff416 100644 --- a/yosys/passes/techmap/deminout.cc +++ b/yosys/passes/techmap/deminout.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct DeminoutPass : public Pass { DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" deminout [options] [selection]\n"); @@ -33,7 +33,7 @@ struct DeminoutPass : public Pass { log("\"Demote\" inout ports to input or output ports, if possible.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n"); @@ -57,7 +57,7 @@ struct DeminoutPass : public Pass { for (auto module : design->selected_modules()) { SigMap sigmap(module); - pool bits_written, bits_used, bits_inout; + pool bits_written, bits_used, bits_inout, bits_tribuf; dict bits_numports; for (auto wire : module->wires()) @@ -82,6 +82,25 @@ struct DeminoutPass : public Pass { if (cellport_in) for (auto bit : sigmap(conn.second)) bits_used.insert(bit); + + if (conn.first == "\\Y" && cell->type.in("$mux", "$pmux", "$_MUX_", "$_TBUF_", "$tribuf")) + { + bool tribuf = (cell->type == "$_TBUF_" || cell->type == "$tribuf"); + + if (!tribuf) { + for (auto &c : cell->connections()) { + if (!c.first.in("\\A", "\\B")) + continue; + for (auto b : sigmap(c.second)) + if (b == State::Sz) + tribuf = true; + } + } + + if (tribuf) + for (auto bit : sigmap(conn.second)) + bits_tribuf.insert(bit); + } } for (auto wire : module->selected_wires()) @@ -94,11 +113,17 @@ struct DeminoutPass : public Pass { { if (bits_numports[bit] > 1 || bits_inout.count(bit)) new_input = true, new_output = true; - - if (bits_written.count(bit)) + if (bit == State::S0 || bit == State::S1) new_output = true; - else if (bits_used.count(bit)) - new_input = true; + if (bits_written.count(bit)) { + new_output = true; + if (bits_tribuf.count(bit)) + goto tribuf_bit; + } else { + tribuf_bit: + if (bits_used.count(bit)) + new_input = true; + } } if (new_input != new_output) { diff --git a/yosys/passes/techmap/dff2dffe.cc b/yosys/passes/techmap/dff2dffe.cc index 1b8920bb7..7e1040963 100644 --- a/yosys/passes/techmap/dff2dffe.cc +++ b/yosys/passes/techmap/dff2dffe.cc @@ -253,7 +253,7 @@ struct Dff2dffeWorker struct Dff2dffePass : public Pass { Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -267,6 +267,10 @@ struct Dff2dffePass : public Pass { log(" operate in the opposite direction: replace $dffe cells with combinations\n"); log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n"); log("\n"); + log(" -unmap-mince N\n"); + log(" Same as -unmap but only unmap $dffe where the clock enable port\n"); + log(" signal is used by less $dffe than the specified number\n"); + log("\n"); log(" -direct \n"); log(" map directly to external gate type. can\n"); log(" be any internal gate-level FF cell (except $_DFFE_??_). the\n"); @@ -279,15 +283,17 @@ struct Dff2dffePass : public Pass { log(" -direct-match \n"); log(" like -direct for all DFF cell types matching the expression.\n"); log(" this will use $__DFFE_* as matching the\n"); - log(" internal gate type $_DFF_*_, except for $_DFF_[NP]_, which is\n"); - log(" converted to $_DFFE_[NP]_.\n"); + log(" internal gate type $_DFF_*_, and $__DFFSE_* for those matching\n"); + log(" $_DFFS_*_, except for $_DFF_[NP]_, which is converted to \n"); + log(" $_DFFE_[NP]_.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); bool unmap_mode = false; + int min_ce_use = -1; dict direct_dict; size_t argidx; @@ -296,6 +302,11 @@ struct Dff2dffePass : public Pass { unmap_mode = true; continue; } + if (args[argidx] == "-unmap-mince" && argidx + 1 < args.size()) { + unmap_mode = true; + min_ce_use = std::stoi(args[++argidx]); + continue; + } if (args[argidx] == "-direct" && argidx + 2 < args.size()) { string direct_from = RTLIL::escape_id(args[++argidx]); string direct_to = RTLIL::escape_id(args[++argidx]); @@ -315,6 +326,15 @@ struct Dff2dffePass : public Pass { 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 (patmatch(pattern, "$__DFFS_NN0_")) found_match = true, direct_dict["$__DFFS_NN0_"] = "$__DFFSE_NN0"; + if (patmatch(pattern, "$__DFFS_NN1_")) found_match = true, direct_dict["$__DFFS_NN1_"] = "$__DFFSE_NN1"; + if (patmatch(pattern, "$__DFFS_NP0_")) found_match = true, direct_dict["$__DFFS_NP0_"] = "$__DFFSE_NP0"; + if (patmatch(pattern, "$__DFFS_NP1_")) found_match = true, direct_dict["$__DFFS_NP1_"] = "$__DFFSE_NP1"; + if (patmatch(pattern, "$__DFFS_PN0_")) found_match = true, direct_dict["$__DFFS_PN0_"] = "$__DFFSE_PN0"; + if (patmatch(pattern, "$__DFFS_PN1_")) found_match = true, direct_dict["$__DFFS_PN1_"] = "$__DFFSE_PN1"; + if (patmatch(pattern, "$__DFFS_PP0_")) found_match = true, direct_dict["$__DFFS_PP0_"] = "$__DFFSE_PP0"; + if (patmatch(pattern, "$__DFFS_PP1_")) found_match = true, direct_dict["$__DFFS_PP1_"] = "$__DFFSE_PP1"; if (!found_match) log_cmd_error("No cell types matched pattern '%s'.\n", pattern); continue; @@ -333,8 +353,21 @@ struct Dff2dffePass : public Pass { if (!mod->has_processes_warn()) { if (unmap_mode) { + SigMap sigmap(mod); for (auto cell : mod->selected_cells()) { if (cell->type == "$dffe") { + if (min_ce_use >= 0) { + int ce_use = 0; + for (auto cell_other : mod->selected_cells()) { + if (cell_other->type != cell->type) + continue; + if (sigmap(cell->getPort("\\EN")) == sigmap(cell_other->getPort("\\EN"))) + ce_use++; + } + if (ce_use >= min_ce_use) + continue; + } + RTLIL::SigSpec tmp = mod->addWire(NEW_ID, GetSize(cell->getPort("\\D"))); mod->addDff(NEW_ID, cell->getPort("\\CLK"), tmp, cell->getPort("\\Q"), cell->getParam("\\CLK_POLARITY").as_bool()); if (cell->getParam("\\EN_POLARITY").as_bool()) @@ -345,6 +378,18 @@ struct Dff2dffePass : public Pass { continue; } if (cell->type.substr(0, 7) == "$_DFFE_") { + if (min_ce_use >= 0) { + int ce_use = 0; + for (auto cell_other : mod->selected_cells()) { + if (cell_other->type != cell->type) + continue; + if (sigmap(cell->getPort("\\E")) == sigmap(cell_other->getPort("\\E"))) + ce_use++; + } + if (ce_use >= min_ce_use) + continue; + } + bool clk_pol = cell->type.substr(7, 1) == "P"; bool en_pol = cell->type.substr(8, 1) == "P"; RTLIL::SigSpec tmp = mod->addWire(NEW_ID); diff --git a/yosys/passes/techmap/dff2dffs.cc b/yosys/passes/techmap/dff2dffs.cc new file mode 100644 index 000000000..39a4f6ade --- /dev/null +++ b/yosys/passes/techmap/dff2dffs.cc @@ -0,0 +1,142 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 Dff2dffsPass : public Pass { + Dff2dffsPass() : Pass("dff2dffs", "process sync set/reset with SR over CE priority") { } + void help() YS_OVERRIDE + { + log("\n"); + log(" dff2dffs [options] [selection]\n"); + log("\n"); + log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n"); + log("dff2dffe for SR over CE priority.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing dff2dffs 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 dff_types; + dff_types.insert("$_DFF_N_"); + dff_types.insert("$_DFF_P_"); + + for (auto module : design->selected_modules()) + { + log("Merging set/reset $_MUX_ cells into DFFs in %s.\n", log_id(module)); + + SigMap sigmap(module); + dict sr_muxes; + vector ff_cells; + + for (auto cell : module->selected_cells()) + { + if (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; + bool invert_sr; + sr_sig = bit_s; + if (bit_a.wire == nullptr) { + bit_d = bit_b; + sr_val = bit_a; + invert_sr = true; + } else { + log_assert(bit_b.wire == nullptr); + bit_d = bit_a; + sr_val = bit_b; + invert_sr = false; + } + + if (sr_val == State::S1) { + if (cell->type == "$_DFF_N_") { + if (invert_sr) cell->type = "$__DFFS_NN1_"; + else cell->type = "$__DFFS_NP1_"; + } else { + log_assert(cell->type == "$_DFF_P_"); + if (invert_sr) cell->type = "$__DFFS_PN1_"; + else cell->type = "$__DFFS_PP1_"; + } + } else { + if (cell->type == "$_DFF_N_") { + if (invert_sr) cell->type = "$__DFFS_NN0_"; + else cell->type = "$__DFFS_NP0_"; + } else { + log_assert(cell->type == "$_DFF_P_"); + if (invert_sr) cell->type = "$__DFFS_PN0_"; + else cell->type = "$__DFFS_PP0_"; + } + } + cell->setPort("\\R", sr_sig); + cell->setPort("\\D", bit_d); + } + } + } +} Dff2dffsPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/techmap/dffinit.cc b/yosys/passes/techmap/dffinit.cc index d737b3424..48390488e 100644 --- a/yosys/passes/techmap/dffinit.cc +++ b/yosys/passes/techmap/dffinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct DffinitPass : public Pass { DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -38,15 +38,44 @@ struct DffinitPass : public Pass { log(" operate on the specified cell type. this option can be used\n"); log(" multiple times.\n"); log("\n"); + log(" -highlow\n"); + log(" use the string values \"high\" and \"low\" to represent a single-bit\n"); + log(" initial value of 1 or 0. (multi-bit values are not supported in this\n"); + log(" mode.)\n"); + log("\n"); + log(" -strinit \n"); + log(" use string values in the command line to represent a single-bit\n"); + log(" initial value of 1 or 0. (multi-bit values are not supported in this\n"); + log(" mode.)\n"); + log("\n"); + log(" -noreinit\n"); + log(" fail if the FF cell has already a defined initial value set in other\n"); + log(" passes and the initial value of the net it drives is not equal to\n"); + log(" the already defined initial value.\n"); + log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); dict> ff_types; + bool highlow_mode = false, noreinit = false; + std::string high_string, low_string; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-highlow") { + highlow_mode = true; + high_string = "high"; + low_string = "low"; + continue; + } + if (args[argidx] == "-strinit" && argidx+2 < args.size()) { + highlow_mode = true; + high_string = args[++argidx]; + low_string = args[++argidx]; + continue; + } if (args[argidx] == "-ff" && argidx+3 < args.size()) { IdString cell_name = RTLIL::escape_id(args[++argidx]); IdString output_port = RTLIL::escape_id(args[++argidx]); @@ -54,6 +83,10 @@ struct DffinitPass : public Pass { ff_types[cell_name][output_port] = init_param; continue; } + if (args[argidx] == "-noreinit") { + noreinit = true; + continue; + } break; } extra_args(args, argidx, design); @@ -102,10 +135,24 @@ struct DffinitPass : public Pass { continue; while (GetSize(value.bits) <= i) value.bits.push_back(State::S0); + if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i])) + log_error("Trying to assign a different init value for %s.%s.%s which technically " + "have a conflicted init value.\n", + log_id(module), log_id(cell), log_id(it.second)); value.bits[i] = init_bits.at(sig[i]); cleanup_bits.insert(sig[i]); } + if (highlow_mode && GetSize(value) != 0) { + if (GetSize(value) != 1) + log_error("Multi-bit init value for %s.%s.%s is incompatible with -highlow mode.\n", + log_id(module), log_id(cell), log_id(it.second)); + if (value[0] == State::S1) + value = Const(high_string); + else + value = Const(low_string); + } + log("Setting %s.%s.%s (port=%s, net=%s) to %s.\n", log_id(module), log_id(cell), log_id(it.second), log_id(it.first), log_signal(sig), log_signal(value)); cell->setParam(it.second, value); diff --git a/yosys/passes/techmap/dfflibmap.cc b/yosys/passes/techmap/dfflibmap.cc index c8104fb7e..b0528d473 100644 --- a/yosys/passes/techmap/dfflibmap.cc +++ b/yosys/passes/techmap/dfflibmap.cc @@ -100,6 +100,18 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name, for (auto child : cell->children) if (child->id == "pin" && child->args.size() == 1 && child->args[0] == pin_name) return true; + + /* If we end up here, the pin specified in the attribute does not exist, which is an error, + or, the attribute contains an expression which we do not yet support. + For now, we'll simply produce a warning to let the user know something is up. + */ + if (pin_name.find_first_of("^*|&") == std::string::npos) { + log_warning("Malformed liberty file - cannot find pin '%s' in cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str()); + } + else { + log_warning("Found unsupported expression '%s' in pin attribute of cell '%s' - skipping.\n", pin_name.c_str(), cell->args[0].c_str()); + } + return false; } @@ -240,6 +252,10 @@ static void find_cell_sr(LibertyAst *ast, std::string cell_type, bool clkpol, bo 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; @@ -478,11 +494,15 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare auto cell_type = cell->type; auto cell_name = cell->name; auto cell_connections = cell->connections(); + std::string src = cell->get_src_attribute(); + 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); + new_cell->set_src_attribute(src); + bool has_q = false, has_qn = false; for (auto &port : cm.ports) { if (port.second == 'Q') has_q = true; @@ -529,7 +549,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare struct DfflibmapPass : public Pass { DfflibmapPass() : Pass("dfflibmap", "technology mapping of flip-flops") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" dfflibmap [-prepare] -liberty [selection]\n"); @@ -545,7 +565,7 @@ struct DfflibmapPass : public Pass { log("liberty file.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); diff --git a/yosys/passes/techmap/dffsr2dff.cc b/yosys/passes/techmap/dffsr2dff.cc index 0d4d53627..086a1d2fa 100644 --- a/yosys/passes/techmap/dffsr2dff.cc +++ b/yosys/passes/techmap/dffsr2dff.cc @@ -176,7 +176,7 @@ void adff_worker(SigMap &sigmap, Module *module, Cell *cell) struct Dffsr2dffPass : public Pass { Dffsr2dffPass() : Pass("dffsr2dff", "convert DFFSR cells to simpler FF cell types") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -186,7 +186,7 @@ struct Dffsr2dffPass : public Pass { log("$_DFF_???_) to simpler FF cell types when any of the set/reset inputs is unused.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing DFFSR2DFF pass (mapping DFFSR cells to simpler FFs).\n"); diff --git a/yosys/passes/techmap/extract.cc b/yosys/passes/techmap/extract.cc index 71e29c60b..fff90f13c 100644 --- a/yosys/passes/techmap/extract.cc +++ b/yosys/passes/techmap/extract.cc @@ -352,7 +352,7 @@ bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right) struct ExtractPass : public Pass { ExtractPass() : Pass("extract", "find subcircuits and replace them with cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -440,7 +440,7 @@ struct ExtractPass : public Pass { log("See 'help techmap' for a pass that does the opposite thing.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n"); log_push(); diff --git a/yosys/techlibs/greenpak4/greenpak4_counters.cc b/yosys/passes/techmap/extract_counter.cc similarity index 54% rename from yosys/techlibs/greenpak4/greenpak4_counters.cc rename to yosys/passes/techmap/extract_counter.cc index 998bb73bd..a8d0bc834 100644 --- a/yosys/techlibs/greenpak4/greenpak4_counters.cc +++ b/yosys/passes/techmap/extract_counter.cc @@ -1,7 +1,7 @@ /* * yosys -- Yosys Open SYnthesis Suite * - * Copyright (C) 2016 Clifford Wolf + * Copyright (C) 2017 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -66,11 +66,11 @@ bool is_full_bus( else if(!other_conns_allowed) return false; } - + if( (!found_a) || (!found_b) ) return false; } - + return true; } @@ -83,40 +83,53 @@ bool is_unconnected(const RTLIL::SigSpec& port, ModIndex& index) 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 + int width; //counter width + RTLIL::Wire* rwire; //the register output + bool has_reset; //true if we have a reset + bool has_ce; //true if we have a clock enable + RTLIL::SigSpec rst; //reset pin + bool rst_inverted; //true if reset is active low + bool rst_to_max; //true if we reset to max instead of 0 + int count_value; //value we count from + RTLIL::SigSpec ce; //clock signal + RTLIL::SigSpec clk; //clock enable, if any + 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 + pool pouts; //Ports that take a parallel output from us }; -//attempt to extract a counter centered on the given cell -int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction& extract) +//attempt to extract a counter centered on the given adder cell +//For now we only support DOWN counters. +//TODO: up/down support +int counter_tryextract( + ModIndex& index, + Cell *cell, + CounterExtraction& extract, + pool& parallel_cells, + int maxwidth) { SigMap& sigmap = index.sigmap; - - //GreenPak does not support counters larger than 14 bits so immediately skip anything bigger + + //A counter with less than 2 bits makes no sense + //TODO: configurable min threshold int a_width = cell->getParam("\\A_WIDTH").as_int(); extract.width = a_width; - if(a_width > 14) + if( (a_width < 2) || (a_width > maxwidth) ) 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(); @@ -128,7 +141,7 @@ int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction 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) ) @@ -136,13 +149,13 @@ int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction 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")); @@ -161,70 +174,152 @@ int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction 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 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 + + //Y connection of the mux must have exactly one load, the counter's internal register, if there's no clock enable + //If we have a clock enable, Y drives the B input of a mux. A of that mux must come from our register const RTLIL::SigSpec muxy = sigmap(count_mux->getPort("\\Y")); pool muxy_loads = get_other_cells(muxy, index, count_mux); if(muxy_loads.size() != 1) return 14; - Cell* count_reg = *muxy_loads.begin(); + Cell* muxload = *muxy_loads.begin(); + Cell* count_reg = muxload; + Cell* cemux = NULL; + RTLIL::SigSpec cey; + if(muxload->type == "$mux") + { + //This mux is probably a clock enable mux. + //Find our count register (should be our only load) + cemux = muxload; + cey = sigmap(cemux->getPort("\\Y")); + pool cey_loads = get_other_cells(cey, index, cemux); + if(cey_loads.size() != 1) + return 24; + count_reg = *cey_loads.begin(); + + //Mux should have A driven by count Q, and B by muxy + //TODO: if A and B are swapped, CE polarity is inverted + if(sigmap(cemux->getPort("\\B")) != muxy) + return 24; + if(sigmap(cemux->getPort("\\A")) != sigmap(count_reg->getPort("\\Q"))) + return 24; + if(sigmap(cemux->getPort("\\Y")) != sigmap(count_reg->getPort("\\D"))) + return 24; + + //Select of the mux is our clock enable + extract.has_ce = true; + extract.ce = sigmap(cemux->getPort("\\S")); + } + else + extract.has_ce = false; + 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) + + //Check polarity of reset - we may have to add an inverter later on! + extract.rst_inverted = (count_reg->getParam("\\ARST_POLARITY").as_int() != 1); + + //Verify ARST_VALUE is zero or full scale + int rst_value = count_reg->getParam("\\ARST_VALUE").as_int(); + if(rst_value == 0) + extract.rst_to_max = false; + else if(rst_value == extract.count_value) + extract.rst_to_max = true; + else 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")) + + //Sanity check that we use the ALU output properly + if(extract.has_ce) + { + if(!is_full_bus(muxy, index, count_mux, "\\Y", cemux, "\\B")) + return 16; + if(!is_full_bus(cey, index, cemux, "\\Y", count_reg, "\\D")) + return 16; + } + else 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")); + //(unless we have a parallel output!) + //If we have a clock enable, 3 is OK + const RTLIL::SigSpec qport = count_reg->getPort("\\Q"); + const RTLIL::SigSpec cnout = sigmap(qport); pool cnout_loads = get_other_cells(cnout, index, count_reg); - if(cnout_loads.size() != 2) - return 17; + unsigned int max_loads = 2; + if(extract.has_ce) + max_loads = 3; + if(cnout_loads.size() > max_loads) + { + for(auto c : cnout_loads) + { + if(c == underflow_inv) + continue; + if(c == cell) + continue; + if(c == muxload) + continue; + + //If we specified a limited set of cells for parallel output, check that we only drive them + if(!parallel_cells.empty()) + { + //Make sure we're in the whitelist + if( parallel_cells.find(c->type) == parallel_cells.end()) + return 17; + } + + //Figure out what port(s) are driven by it + //TODO: this can probably be done more efficiently w/o multiple iterations over our whole net? + for(auto b : qport) + { + pool ports = index.query_ports(b); + for(auto x : ports) + { + if(x.cell != c) + continue; + extract.pouts.insert(ModIndex::PortInfo(c, x.port, 0)); + } + } + } + } 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()) @@ -232,22 +327,25 @@ int greenpak4_counters_tryextract(ModIndex& index, Cell *cell, CounterExtraction int rinit = extract.rwire->attributes["\\init"].as_int(); if(rinit != extract.count_value) return 21; - + return 0; } -void greenpak4_counters_worker( +void counter_worker( ModIndex& index, Cell *cell, unsigned int& total_counters, - pool& cells_to_remove) + pool& cells_to_remove, + pool>& cells_to_rename, + pool& parallel_cells, + int maxwidth) { 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")); @@ -268,7 +366,7 @@ void greenpak4_counters_worker( 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") @@ -280,23 +378,23 @@ void greenpak4_counters_worker( 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); - + int reason = counter_tryextract(index, cell, extract, parallel_cells, maxwidth); + //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]= + static const char* reasons[25]= { "no problem", //0 - "counter is larger than 14 bits", //1 + "counter is too large/small", //1 "counter does not count by one", //2 "counter uses signed math", //3 "counter does not count by one", //4 @@ -312,15 +410,16 @@ void greenpak4_counters_worker( "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 + "Count register is used outside counter, but not by an allowed cell", //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 + "RESERVED, not implemented", //22, kept for compatibility but not used anymore + "Reset is not to zero or COUNT_TO", //23 + "Clock enable configuration is unsupported" //24 }; - + if(force_extract) { log_error( @@ -330,27 +429,10 @@ void greenpak4_counters_worker( } 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()); - + + //Get new cell name + string countname = string("$COUNTx$") + log_id(extract.rwire->name.str()); + //Wipe all of the old connections to the ALU cell->unsetPort("\\A"); cell->unsetPort("\\B"); @@ -366,77 +448,204 @@ void greenpak4_counters_worker( cell->unsetParam("\\Y_WIDTH"); //Change the cell type - cell->type = celltype; - + cell->type = "$__COUNT_"; + //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); + + //If the reset is active low, infer an inverter ($__COUNT_ cells always have active high reset) + if(extract.rst_inverted) + { + auto realreset = cell->module->addWire(NEW_ID); + cell->module->addNot(NEW_ID, extract.rst, RTLIL::SigSpec(realreset)); + cell->setPort("\\RST", realreset); + } + else + 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("\\CLKIN_DIVIDE", RTLIL::Const(1)); cell->setParam("\\COUNT_TO", RTLIL::Const(extract.count_value)); - + cell->setParam("\\WIDTH", RTLIL::Const(extract.width)); cell->setPort("\\CLK", extract.clk); cell->setPort("\\OUT", extract.outsig); - + + //Hook up clock enable + if(extract.has_ce) + { + cell->setParam("\\HAS_CE", RTLIL::Const(1)); + cell->setPort("\\CE", extract.ce); + } + else + cell->setParam("\\HAS_CE", RTLIL::Const(0)); + + //Hook up hard-wired ports (for now up/down are not supported), default to no parallel output + cell->setParam("\\HAS_POUT", RTLIL::Const(0)); + cell->setParam("\\RESET_TO_MAX", RTLIL::Const(0)); + cell->setParam("\\DIRECTION", RTLIL::Const("DOWN")); + cell->setPort("\\CE", RTLIL::Const(1)); + cell->setPort("\\UP", RTLIL::Const(0)); + + //Hook up any parallel outputs + for(auto load : extract.pouts) + { + log(" Counter has parallel output to cell %s port %s\n", log_id(load.cell->name), log_id(load.port)); + + //Find the wire hooked to the old port + auto sig = load.cell->getPort(load.port); + + //Connect it to our parallel output + //(this is OK to do more than once b/c they all go to the same place) + cell->setPort("\\POUT", sig); + cell->setParam("\\HAS_POUT", RTLIL::Const(1)); + } + //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); + + //Log it + total_counters ++; + string reset_type = "non-resettable"; + if(extract.has_reset) + { + if(extract.rst_inverted) + reset_type = "negative"; + else + reset_type = "positive"; + + //TODO: support other kind of reset + reset_type += " async resettable"; + } + log(" Found %d-bit (%s) down counter %s (counting from %d) for register %s, declared at %s\n", + extract.width, + reset_type.c_str(), + countname.c_str(), + extract.count_value, + log_id(extract.rwire->name), + count_reg_src.c_str()); + + //Optimize the counter + //If we have no parallel output, and we have redundant bits, shrink us + if(extract.pouts.empty()) + { + //TODO: Need to update this when we add support for counters with nonzero reset values + //to make sure the reset value fits in our bit space too + + //Optimize it + int newbits = ceil(log2(extract.count_value)); + if(extract.width != newbits) + { + cell->setParam("\\WIDTH", RTLIL::Const(newbits)); + log(" Optimizing out %d unused high-order bits (new width is %d)\n", + extract.width - newbits, + newbits); + } + } + + //Finally, rename the cell + cells_to_rename.insert(pair(cell, countname)); } -struct Greenpak4CountersPass : public Pass { - Greenpak4CountersPass() : Pass("greenpak4_counters", "Extract GreenPak4 counter cells") { } - virtual void help() +struct ExtractCounterPass : public Pass { + ExtractCounterPass() : Pass("extract_counter", "Extract GreenPak4 counter cells") { } + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" greenpak4_counters [options] [selection]\n"); + log(" extract_counter [options] [selection]\n"); + log("\n"); + log("This pass converts non-resettable or async resettable down counters to\n"); + log("counter cells. Use a target-specific 'techmap' map file to convert those cells\n"); + log("to the actual target cells.\n"); + log("\n"); + log(" -maxwidth N\n"); + log(" Only extract counters up to N bits wide\n"); + log("\n"); + log(" -pout X,Y,...\n"); + log(" Only allow parallel output from the counter to the listed cell types\n"); + log(" (if not specified, parallel outputs are not restricted)\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 args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - log_header(design, "Executing GREENPAK4_COUNTERS pass (mapping counters to hard IP blocks).\n"); - + log_header(design, "Executing EXTRACT_COUNTER pass (find counters in netlist).\n"); + + int maxwidth = 64; size_t argidx; + pool parallel_cells; for (argidx = 1; argidx < args.size(); argidx++) { - // if (args[argidx] == "-v") { - // continue; - // } - break; + if (args[argidx] == "-pout") + { + if(argidx + 1 >= args.size()) + { + log_error("extract_counter -pout requires an argument\n"); + return; + } + + std::string pouts = args[++argidx]; + std::string tmp; + for(size_t i=0; iselected_modules()) { pool cells_to_remove; - + pool> cells_to_rename; + ModIndex index(module); for (auto cell : module->selected_cells()) - greenpak4_counters_worker(index, cell, total_counters, cells_to_remove); - + counter_worker(index, cell, total_counters, cells_to_remove, cells_to_rename, parallel_cells, maxwidth); + for(auto cell : cells_to_remove) + { + //log("Removing cell %s\n", log_id(cell->name)); module->remove(cell); + } + + for(auto cpair : cells_to_rename) + { + //log("Renaming cell %s to %s\n", log_id(cpair.first->name), cpair.second.c_str()); + module->rename(cpair.first, cpair.second); + } } - + if(total_counters) log("Extracted %u counters\n", total_counters); } -} Greenpak4CountersPass; +} ExtractCounterPass; PRIVATE_NAMESPACE_END diff --git a/yosys/passes/techmap/extract_fa.cc b/yosys/passes/techmap/extract_fa.cc new file mode 100644 index 000000000..9e6dc0d24 --- /dev/null +++ b/yosys/passes/techmap/extract_fa.cc @@ -0,0 +1,605 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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/consteval.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ExtractFaConfig +{ + bool enable_fa = false; + bool enable_ha = false; + bool verbose = false; + int maxdepth = 20; + int maxbreadth = 6; +}; + +// http://svn.clifford.at/handicraft/2016/bindec/bindec.c +int bindec(unsigned char v) +{ + int r = v & 1; + r += (~((v & 2) - 1)) & 10; + r += (~((v & 4) - 1)) & 100; + r += (~((v & 8) - 1)) & 1000; + r += (~((v & 16) - 1)) & 10000; + r += (~((v & 32) - 1)) & 100000; + r += (~((v & 64) - 1)) & 1000000; + r += (~((v & 128) - 1)) & 10000000; + return r; +} + +struct ExtractFaWorker +{ + const ExtractFaConfig &config; + Module *module; + ConstEval ce; + SigMap &sigmap; + + dict driver; + pool handled_bits; + + const int xor2_func = 0x6, xnor2_func = 0x9; + const int xor3_func = 0x96, xnor3_func = 0x69; + + pool> xorxnor2; + pool> xorxnor3; + + dict, dict>> func2; + dict, dict>> func3; + + int count_func2; + int count_func3; + + struct func2_and_info_t { + bool inv_a, inv_b, inv_y; + }; + + struct func3_maj_info_t { + bool inv_a, inv_b, inv_c, inv_y; + }; + + dict func2_and_info; + dict func3_maj_info; + + ExtractFaWorker(const ExtractFaConfig &config, Module *module) : + config(config), module(module), ce(module), sigmap(ce.assign_map) + { + for (auto cell : module->selected_cells()) + { + if (cell->type.in( "$_BUF_", "$_NOT_", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", + "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_", "$_MUX_", + "$_AOI3_", "$_OAI3_", "$_AOI4_", "$_OAI4_")) + { + SigBit y = sigmap(SigBit(cell->getPort("\\Y"))); + log_assert(driver.count(y) == 0); + driver[y] = cell; + } + } + + for (int ia = 0; ia < 2; ia++) + for (int ib = 0; ib < 2; ib++) + { + func2_and_info_t f2i; + + f2i.inv_a = ia; + f2i.inv_b = ib; + f2i.inv_y = false; + + int func = 0; + for (int i = 0; i < 4; i++) + { + bool a = (i & 1) ? !f2i.inv_a : f2i.inv_a; + bool b = (i & 2) ? !f2i.inv_b : f2i.inv_b; + if (a && b) func |= 1 << i; + } + + log_assert(func2_and_info.count(func) == 0); + func2_and_info[func] = f2i; + + f2i.inv_y = true; + func ^= 15; + + log_assert(func2_and_info.count(func) == 0); + func2_and_info[func] = f2i; + } + + for (int ia = 0; ia < 2; ia++) + for (int ib = 0; ib < 2; ib++) + for (int ic = 0; ic < 2; ic++) + { + func3_maj_info_t f3i; + + f3i.inv_a = ia; + f3i.inv_b = ib; + f3i.inv_c = ic; + f3i.inv_y = false; + + int func = 0; + for (int i = 0; i < 8; i++) + { + bool a = (i & 1) ? !f3i.inv_a : f3i.inv_a; + bool b = (i & 2) ? !f3i.inv_b : f3i.inv_b; + bool c = (i & 4) ? !f3i.inv_c : f3i.inv_c; + if ((a && b) || (a && c) || (b &&c)) func |= 1 << i; + } + + log_assert(func3_maj_info.count(func) == 0); + func3_maj_info[func] = f3i; + + // f3i.inv_y = true; + // func ^= 255; + + // log_assert(func3_maj_info.count(func) == 0); + // func3_maj_info[func] = f3i; + } + } + + void check_partition(SigBit root, pool &leaves) + { + if (config.enable_ha && GetSize(leaves) == 2) + { + leaves.sort(); + + SigBit A = SigSpec(leaves)[0]; + SigBit B = SigSpec(leaves)[1]; + + int func = 0; + for (int i = 0; i < 4; i++) + { + bool a_value = (i & 1) != 0; + bool b_value = (i & 2) != 0; + + ce.push(); + ce.set(A, a_value ? State::S1 : State::S0); + ce.set(B, b_value ? State::S1 : State::S0); + + SigSpec sig = root; + + if (!ce.eval(sig)) + log_abort(); + + if (sig == State::S1) + func |= 1 << i; + + ce.pop(); + } + + // log("%04d %s %s -> %s\n", bindec(func), log_signal(A), log_signal(B), log_signal(root)); + + if (func == xor2_func || func == xnor2_func) + xorxnor2.insert(tuple(A, B)); + + count_func2++; + func2[tuple(A, B)][func].insert(root); + } + + if (config.enable_fa && GetSize(leaves) == 3) + { + leaves.sort(); + + SigBit A = SigSpec(leaves)[0]; + SigBit B = SigSpec(leaves)[1]; + SigBit C = SigSpec(leaves)[2]; + + int func = 0; + for (int i = 0; i < 8; i++) + { + bool a_value = (i & 1) != 0; + bool b_value = (i & 2) != 0; + bool c_value = (i & 4) != 0; + + ce.push(); + ce.set(A, a_value ? State::S1 : State::S0); + ce.set(B, b_value ? State::S1 : State::S0); + ce.set(C, c_value ? State::S1 : State::S0); + + SigSpec sig = root; + + if (!ce.eval(sig)) + log_abort(); + + if (sig == State::S1) + func |= 1 << i; + + ce.pop(); + } + + // log("%08d %s %s %s -> %s\n", bindec(func), log_signal(A), log_signal(B), log_signal(C), log_signal(root)); + + if (func == xor3_func || func == xnor3_func) + xorxnor3.insert(tuple(A, B, C)); + + count_func3++; + func3[tuple(A, B, C)][func].insert(root); + } + } + + void find_partitions(SigBit root, pool &leaves, pool> &cache, int maxdepth, int maxbreadth) + { + if (cache.count(leaves)) + return; + + // log("%*s[%d] %s:", 20-maxdepth, "", maxdepth, log_signal(root)); + // for (auto bit : leaves) + // log(" %s", log_signal(bit)); + // log("\n"); + + cache.insert(leaves); + check_partition(root, leaves); + + if (maxdepth == 0) + return; + + for (SigBit bit : leaves) + { + if (driver.count(bit) == 0) + continue; + + Cell *cell = driver.at(bit); + pool new_leaves = leaves; + + new_leaves.erase(bit); + if (cell->hasPort("\\A")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\A")))); + if (cell->hasPort("\\B")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\B")))); + if (cell->hasPort("\\C")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\C")))); + if (cell->hasPort("\\D")) new_leaves.insert(sigmap(SigBit(cell->getPort("\\D")))); + + if (GetSize(new_leaves) > maxbreadth) + continue; + + find_partitions(root, new_leaves, cache, maxdepth-1, maxbreadth); + } + } + + void assign_new_driver(SigBit bit, SigBit new_driver) + { + Cell *cell = driver.at(bit); + if (sigmap(cell->getPort("\\Y")) == bit) { + cell->setPort("\\Y", module->addWire(NEW_ID)); + module->connect(bit, new_driver); + } + } + + void run() + { + log("Extracting full/half adders from %s:\n", log_id(module)); + + for (auto it : driver) + { + if (it.second->type.in("$_BUF_", "$_NOT_")) + continue; + + SigBit root = it.first; + pool leaves = { root }; + pool> cache; + + if (config.verbose) + log(" checking %s\n", log_signal(it.first)); + + count_func2 = 0; + count_func3 = 0; + + find_partitions(root, leaves, cache, config.maxdepth, config.maxbreadth); + + if (config.verbose && count_func2 > 0) + log(" extracted %d two-input functions\n", count_func2); + + if (config.verbose && count_func3 > 0) + log(" extracted %d three-input functions\n", count_func3); + } + + for (auto &key : xorxnor3) + { + SigBit A = get<0>(key); + SigBit B = get<1>(key); + SigBit C = get<2>(key); + + log(" 3-Input XOR/XNOR %s %s %s:\n", log_signal(A), log_signal(B), log_signal(C)); + + for (auto &it : func3.at(key)) + { + if (it.first != xor3_func && it.first != xnor3_func) + continue; + + log(" %08d ->", bindec(it.first)); + for (auto bit : it.second) + log(" %s", log_signal(bit)); + log("\n"); + } + + dict> facache; + + for (auto &it : func3_maj_info) + { + int func = it.first; + auto f3i = it.second; + + if (func3.at(key).count(func) == 0) + continue; + + if (func3.at(key).count(xor3_func) == 0 && func3.at(key).count(xnor3_func) != 0) { + f3i.inv_a = !f3i.inv_a; + f3i.inv_b = !f3i.inv_b; + f3i.inv_c = !f3i.inv_c; + f3i.inv_y = !f3i.inv_y; + } + + if (!f3i.inv_a && !f3i.inv_b && !f3i.inv_c && !f3i.inv_y) { + log(" Majority without inversions:\n"); + } else { + log(" Majority with inverted"); + if (f3i.inv_a) log(" A"); + if (f3i.inv_b) log(" B"); + if (f3i.inv_c) log(" C"); + if (f3i.inv_y) log(" Y"); + log(":\n"); + } + + log(" %08d ->", bindec(func)); + for (auto bit : func3.at(key).at(func)) + log(" %s", log_signal(bit)); + log("\n"); + + int fakey = 0; + if (f3i.inv_a) fakey |= 1; + if (f3i.inv_b) fakey |= 2; + if (f3i.inv_c) fakey |= 4; + + int fakey_inv = fakey ^ 7; + bool invert_xy = false; + SigBit X, Y; + + if (facache.count(fakey)) + { + auto &fa = facache.at(fakey); + X = get<0>(fa); + Y = get<1>(fa); + log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); + } + else + if (facache.count(fakey_inv)) + { + auto &fa = facache.at(fakey_inv); + invert_xy = true; + X = get<0>(fa); + Y = get<1>(fa); + log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); + } + else + { + Cell *cell = module->addCell(NEW_ID, "$fa"); + cell->setParam("\\WIDTH", 1); + + log(" Created $fa cell %s.\n", log_id(cell)); + + cell->setPort("\\A", f3i.inv_a ? module->NotGate(NEW_ID, A) : A); + cell->setPort("\\B", f3i.inv_b ? module->NotGate(NEW_ID, B) : B); + cell->setPort("\\C", f3i.inv_c ? module->NotGate(NEW_ID, C) : C); + + X = module->addWire(NEW_ID); + Y = module->addWire(NEW_ID); + + cell->setPort("\\X", X); + cell->setPort("\\Y", Y); + + facache[fakey] = make_tuple(X, Y, cell); + } + + if (func3.at(key).count(xor3_func)) { + SigBit YY = invert_xy ? module->NotGate(NEW_ID, Y) : Y; + for (auto bit : func3.at(key).at(xor3_func)) + assign_new_driver(bit, YY); + } + + if (func3.at(key).count(xnor3_func)) { + SigBit YY = invert_xy ? Y : module->NotGate(NEW_ID, Y); + for (auto bit : func3.at(key).at(xnor3_func)) + assign_new_driver(bit, YY); + } + + SigBit XX = invert_xy != f3i.inv_y ? module->NotGate(NEW_ID, X) : X; + + for (auto bit : func3.at(key).at(func)) + assign_new_driver(bit, XX); + } + } + + for (auto &key : xorxnor2) + { + SigBit A = get<0>(key); + SigBit B = get<1>(key); + + log(" 2-Input XOR/XNOR %s %s:\n", log_signal(A), log_signal(B)); + + for (auto &it : func2.at(key)) + { + if (it.first != xor2_func && it.first != xnor2_func) + continue; + + log(" %04d ->", bindec(it.first)); + for (auto bit : it.second) + log(" %s", log_signal(bit)); + log("\n"); + } + + dict> facache; + + for (auto &it : func2_and_info) + { + int func = it.first; + auto &f2i = it.second; + + if (func2.at(key).count(func) == 0) + continue; + + if (!f2i.inv_a && !f2i.inv_b && !f2i.inv_y) { + log(" AND without inversions:\n"); + } else { + log(" AND with inverted"); + if (f2i.inv_a) log(" A"); + if (f2i.inv_b) log(" B"); + if (f2i.inv_y) log(" Y"); + log(":\n"); + } + + log(" %04d ->", bindec(func)); + for (auto bit : func2.at(key).at(func)) + log(" %s", log_signal(bit)); + log("\n"); + + int fakey = 0; + if (f2i.inv_a) fakey |= 1; + if (f2i.inv_b) fakey |= 2; + + int fakey_inv = fakey ^ 3; + bool invert_xy = false; + SigBit X, Y; + + if (facache.count(fakey)) + { + auto &fa = facache.at(fakey); + X = get<0>(fa); + Y = get<1>(fa); + log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); + } + else + if (facache.count(fakey_inv)) + { + auto &fa = facache.at(fakey_inv); + invert_xy = true; + X = get<0>(fa); + Y = get<1>(fa); + log(" Reusing $fa cell %s.\n", log_id(get<2>(fa))); + } + else + { + Cell *cell = module->addCell(NEW_ID, "$fa"); + cell->setParam("\\WIDTH", 1); + + log(" Created $fa cell %s.\n", log_id(cell)); + + cell->setPort("\\A", f2i.inv_a ? module->NotGate(NEW_ID, A) : A); + cell->setPort("\\B", f2i.inv_b ? module->NotGate(NEW_ID, B) : B); + cell->setPort("\\C", State::S0); + + X = module->addWire(NEW_ID); + Y = module->addWire(NEW_ID); + + cell->setPort("\\X", X); + cell->setPort("\\Y", Y); + } + + if (func2.at(key).count(xor2_func)) { + SigBit YY = invert_xy ? module->NotGate(NEW_ID, Y) : Y; + for (auto bit : func2.at(key).at(xor2_func)) + assign_new_driver(bit, YY); + } + + if (func2.at(key).count(xnor2_func)) { + SigBit YY = invert_xy ? Y : module->NotGate(NEW_ID, Y); + for (auto bit : func2.at(key).at(xnor2_func)) + assign_new_driver(bit, YY); + } + + SigBit XX = invert_xy != f2i.inv_y ? module->NotGate(NEW_ID, X) : X; + + for (auto bit : func2.at(key).at(func)) + assign_new_driver(bit, XX); + } + } + } +}; + +struct ExtractFaPass : public Pass { + ExtractFaPass() : Pass("extract_fa", "find and extract full/half adders") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" extract_fa [options] [selection]\n"); + log("\n"); + log("This pass extracts full/half adders from a gate-level design.\n"); + log("\n"); + log(" -fa, -ha\n"); + log(" Enable cell types (fa=full adder, ha=half adder)\n"); + log(" All types are enabled if none of this options is used\n"); + log("\n"); + log(" -d \n"); + log(" Set maximum depth for extracted logic cones (default=20)\n"); + log("\n"); + log(" -b \n"); + log(" Set maximum breadth for extracted logic cones (default=6)\n"); + log("\n"); + log(" -v\n"); + log(" Verbose output\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + ExtractFaConfig config; + + log_header(design, "Executing EXTRACT_FA pass (find and extract full/half adders).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-fa") { + config.enable_fa = true; + continue; + } + if (args[argidx] == "-ha") { + config.enable_ha = true; + continue; + } + if (args[argidx] == "-v") { + config.verbose = true; + continue; + } + if (args[argidx] == "-d" && argidx+2 < args.size()) { + config.maxdepth = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-b" && argidx+2 < args.size()) { + config.maxbreadth = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!config.enable_fa && !config.enable_ha) { + config.enable_fa = true; + config.enable_ha = true; + } + + for (auto module : design->selected_modules()) + { + ExtractFaWorker worker(config, module); + worker.run(); + } + + log_pop(); + } +} ExtractFaPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/techmap/extract_reduce.cc b/yosys/passes/techmap/extract_reduce.cc new file mode 100644 index 000000000..a77bbc0b7 --- /dev/null +++ b/yosys/passes/techmap/extract_reduce.cc @@ -0,0 +1,324 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 Robert Ou + * + * 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 + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ExtractReducePass : public Pass +{ + enum GateType { + And, + Or, + Xor + }; + + ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" extract_reduce [options] [selection]\n"); + log("\n"); + log("converts gate chains into $reduce_* cells\n"); + log("\n"); + log("This command finds chains of $_AND_, $_OR_, and $_XOR_ cells and replaces them\n"); + log("with their corresponding $reduce_* cells. Because this command only operates on\n"); + log("these cell types, it is recommended to map the design to only these cell types\n"); + log("using the `abc -g` command. Note that, in some cases, it may be more effective\n"); + log("to map the design to only $_AND_ cells, run extract_reduce, map the remaining\n"); + log("parts of the design to AND/OR/XOR cells, and run extract_reduce a second time.\n"); + log("\n"); + log(" -allow-off-chain\n"); + log(" Allows matching of cells that have loads outside the chain. These cells\n"); + log(" will be replicated and folded into the $reduce_* cell, but the original\n"); + log(" cell will remain, driving its original loads.\n"); + log("\n"); + } + + inline bool IsRightType(Cell* cell, GateType gt) + { + return (cell->type == "$_AND_" && gt == GateType::And) || + (cell->type == "$_OR_" && gt == GateType::Or) || + (cell->type == "$_XOR_" && gt == GateType::Xor); + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing EXTRACT_REDUCE pass.\n"); + log_push(); + + size_t argidx; + bool allow_off_chain = false; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-allow-off-chain") + { + allow_off_chain = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + { + SigMap sigmap(module); + + // Index all of the nets in the module + dict sig_to_driver; + dict> sig_to_sink; + for (auto cell : module->selected_cells()) + { + for (auto &conn : cell->connections()) + { + if (cell->output(conn.first)) + for (auto bit : sigmap(conn.second)) + sig_to_driver[bit] = cell; + + if (cell->input(conn.first)) + { + for (auto bit : sigmap(conn.second)) + { + if (sig_to_sink.count(bit) == 0) + sig_to_sink[bit] = pool(); + sig_to_sink[bit].insert(cell); + } + } + } + } + + // Need to check if any wires connect to module ports + pool port_sigs; + for (auto wire : module->selected_wires()) + if (wire->port_input || wire->port_output) + for (auto bit : sigmap(wire)) + port_sigs.insert(bit); + + // Actual logic starts here + pool consumed_cells; + for (auto cell : module->selected_cells()) + { + if (consumed_cells.count(cell)) + continue; + + GateType gt; + + if (cell->type == "$_AND_") + gt = GateType::And; + else if (cell->type == "$_OR_") + gt = GateType::Or; + else if (cell->type == "$_XOR_") + gt = GateType::Xor; + else + continue; + + log("Working on cell %s...\n", cell->name.c_str()); + + // If looking for a single chain, follow linearly to the sink + pool sinks; + if(!allow_off_chain) + { + Cell* head_cell = cell; + Cell* x = cell; + while (true) + { + if(!IsRightType(x, gt)) + break; + + head_cell = x; + + auto y = sigmap(x->getPort("\\Y")); + log_assert(y.size() == 1); + + // Should only continue if there is one fanout back into a cell (not to a port) + if (sig_to_sink[y[0]].size() != 1) + break; + + x = *sig_to_sink[y[0]].begin(); + } + + sinks.insert(head_cell); + } + + //If off-chain loads are allowed, we have to do a wider traversal to see what the longest chain is + else + { + //BFS, following all chains until they hit a cell of a different type + //Pick the longest one + auto y = sigmap(cell->getPort("\\Y")); + pool current_loads = sig_to_sink[y]; + pool next_loads; + + while(!current_loads.empty()) + { + //Find each sink and see what they are + for(auto x : current_loads) + { + //Not one of our gates? Don't follow any further + //(but add the originating cell to the list of sinks) + if(!IsRightType(x, gt)) + { + sinks.insert(cell); + continue; + } + + //If this signal drives a port, add it to the sinks + //(even though it may not be the end of a chain) + if(port_sigs.count(x) && !consumed_cells.count(x)) + sinks.insert(x); + + //It's a match, search everything out from it + auto& next = sig_to_sink[x]; + for(auto z : next) + next_loads.insert(z); + } + + //If we couldn't find any downstream loads, stop. + //Create a reduction for each of the max-length chains we found + if(next_loads.empty()) + { + for(auto s : current_loads) + { + //Not one of our gates? Don't follow any further + if(!IsRightType(s, gt)) + continue; + + sinks.insert(s); + } + break; + } + + //Otherwise, continue down the chain + current_loads = next_loads; + next_loads.clear(); + } + } + + //We have our list, go act on it + for(auto head_cell : sinks) + { + log(" Head cell is %s\n", head_cell->name.c_str()); + + //Avoid duplication if we already were covered + if(consumed_cells.count(head_cell)) + continue; + + pool cur_supercell; + std::deque bfs_queue = {head_cell}; + while (bfs_queue.size()) + { + Cell* x = bfs_queue.front(); + bfs_queue.pop_front(); + + cur_supercell.insert(x); + + auto a = sigmap(x->getPort("\\A")); + log_assert(a.size() == 1); + + // Must have only one sink unless we're going off chain + // XXX: Check that it is indeed this node? + if( allow_off_chain || (sig_to_sink[a[0]].size() + port_sigs.count(a[0]) == 1) ) + { + Cell* cell_a = sig_to_driver[a[0]]; + if(cell_a && IsRightType(cell_a, gt)) + { + // The cell here is the correct type, and it's definitely driving + // this current cell. + bfs_queue.push_back(cell_a); + } + } + + auto b = sigmap(x->getPort("\\B")); + log_assert(b.size() == 1); + + // Must have only one sink + // XXX: Check that it is indeed this node? + if( allow_off_chain || (sig_to_sink[b[0]].size() + port_sigs.count(b[0]) == 1) ) + { + Cell* cell_b = sig_to_driver[b[0]]; + if(cell_b && IsRightType(cell_b, gt)) + { + // The cell here is the correct type, and it's definitely driving only + // this current cell. + bfs_queue.push_back(cell_b); + } + } + } + + log(" Cells:\n"); + for (auto x : cur_supercell) + log(" %s\n", x->name.c_str()); + + if (cur_supercell.size() > 1) + { + // Worth it to create reduce cell + log(" Creating $reduce_* cell!\n"); + + pool input_pool; + pool input_pool_intermed; + for (auto x : cur_supercell) + { + input_pool.insert(sigmap(x->getPort("\\A"))[0]); + input_pool.insert(sigmap(x->getPort("\\B"))[0]); + input_pool_intermed.insert(sigmap(x->getPort("\\Y"))[0]); + } + SigSpec input; + for (auto b : input_pool) + if (input_pool_intermed.count(b) == 0) + input.append_bit(b); + + SigBit output = sigmap(head_cell->getPort("\\Y")[0]); + + auto new_reduce_cell = module->addCell(NEW_ID, + gt == GateType::And ? "$reduce_and" : + gt == GateType::Or ? "$reduce_or" : + gt == GateType::Xor ? "$reduce_xor" : ""); + new_reduce_cell->setParam("\\A_SIGNED", 0); + new_reduce_cell->setParam("\\A_WIDTH", input.size()); + new_reduce_cell->setParam("\\Y_WIDTH", 1); + new_reduce_cell->setPort("\\A", input); + new_reduce_cell->setPort("\\Y", output); + + if(allow_off_chain) + consumed_cells.insert(head_cell); + else + { + for (auto x : cur_supercell) + consumed_cells.insert(x); + } + } + } + } + + // Remove all of the head cells, since we supplant them. + // Do not remove the upstream cells since some might still be in use ("clean" will get rid of unused ones) + for (auto cell : consumed_cells) + module->remove(cell); + } + + log_pop(); + } +} ExtractReducePass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/techmap/flowmap.cc b/yosys/passes/techmap/flowmap.cc new file mode 100644 index 000000000..be70b579b --- /dev/null +++ b/yosys/passes/techmap/flowmap.cc @@ -0,0 +1,954 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 whitequark + * + * 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]] +// Jason Cong; Yuzheng Ding, "An Optimal Technology Mapping Algorithm for Delay Optimization in Lookup-Table Based FPGA Designs," +// Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on, Vol. 13, pp. 1-12, Jan. 1994. +// doi: 10.1109/43.273754 + +// Required reading material: +// +// Min-cut max-flow theorem: +// https://www.coursera.org/lecture/algorithms-part2/maxflow-mincut-theorem-beb9G +// FlowMap paper: +// http://cadlab.cs.ucla.edu/~cong/papers/iccad92.pdf (short version) +// https://limsk.ece.gatech.edu/book/papers/flowmap.pdf (long version) + +// Notes on implementation: +// +// 1. In the paper, the nodes are logic elements (analogous to Yosys cells) and edges are wires. However, in our implementation, we use +// an inverted approach: the nodes are Yosys wire bits, and the edges are derived from (but aren't represented by) Yosys cells. +// This may seem counterintuitive. Three observations may help understanding this. First, for a cell with a 1-bit Y output that is +// the sole driver of its output net (which is the typical case), these representations are equivalent, because there is an exact +// correspondence between cells and output wires. Second, in the paper, primary inputs (analogous to Yosys cell or module ports) are +// nodes, and in Yosys, inputs are wires; our approach allows a direct mapping from both primary inputs and 1-output logic elements to +// flow graph nodes. Third, Yosys cells may have multiple outputs or multi-bit outputs, and by using Yosys wire bits as flow graph nodes, +// such cells are supported without any additional effort; any Yosys cell with n output wire bits ends up being split into n flow graph +// nodes. +// +// 2. The paper introduces three networks: Nt, Nt', and Nt''. The network Nt is directly represented by a subgraph of RTLIL graph, +// which is parsed into an equivalent but easier to traverse representation in FlowmapWorker. The network Nt' is built explicitly +// from a subgraph of Nt, and uses a similar representation in FlowGraph. The network Nt'' is implicit in FlowGraph, which is possible +// because of the following observation: each Nt' node corresponds to an Nt'' edge of capacity 1, and each Nt' edge corresponds to +// an Nt'' edge of capacity ∞. Therefore, we only need to explicitly record flow for Nt' edges and through Nt' nodes. +// +// 3. The paper ambiguously states: "Moreover, we can find such a cut (X′′, X̅′′) by performing a depth first search starting at the source s, +// and including in X′′ all the nodes which are reachable from s." This actually refers to a specific kind of search, mincut computation. +// Mincut computation involves computing the set of nodes reachable from s by an undirected path with no full (i.e. zero capacity) forward +// edges or empty (i.e. no flow) backward edges. In addition, the depth first search is required to compute a max-volume max-flow min-cut +// specifically, because a max-flow min-cut is not, in general, unique. + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/modtools.h" +#include "kernel/consteval.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct GraphStyle +{ + string label; + string color, fillcolor; + + GraphStyle(string label = "", string color = "black", string fillcolor = "") : + label(label), color(color), fillcolor(fillcolor) {} +}; + +static string dot_escape(string value) +{ + std::string escaped; + for (char c : value) { + if (c == '\n') + { + escaped += "\\n"; + continue; + } + if (c == '\\' || c == '"') + escaped += "\\"; + escaped += c; + } + return escaped; +} + +static void dump_dot_graph(string filename, + pool nodes, dict> edges, + pool inputs, pool outputs, + std::function node_style = + [](RTLIL::SigBit) { return GraphStyle{}; }, + std::function edge_style = + [](RTLIL::SigBit, RTLIL::SigBit) { return GraphStyle{}; }, + string name = "") +{ + FILE *f = fopen(filename.c_str(), "w"); + fprintf(f, "digraph \"%s\" {\n", name.c_str()); + fprintf(f, " rankdir=\"TB\";\n"); + + dict ids; + for (auto node : nodes) + { + ids[node] = ids.size(); + + string shape = "ellipse"; + if (inputs[node]) + shape = "box"; + if (outputs[node]) + shape = "octagon"; + auto prop = node_style(node); + string style = ""; + if (!prop.fillcolor.empty()) + style = "filled"; + fprintf(f, " n%d [ shape=%s, fontname=\"Monospace\", label=\"%s\", color=\"%s\", fillcolor=\"%s\", style=\"%s\" ];\n", + ids[node], shape.c_str(), dot_escape(prop.label.c_str()).c_str(), prop.color.c_str(), prop.fillcolor.c_str(), style.c_str()); + } + + fprintf(f, " { rank=\"source\"; "); + for (auto input : inputs) + if (nodes[input]) + fprintf(f, "n%d; ", ids[input]); + fprintf(f, "}\n"); + + fprintf(f, " { rank=\"sink\"; "); + for (auto output : outputs) + if (nodes[output]) + fprintf(f, "n%d; ", ids[output]); + fprintf(f, "}\n"); + + for (auto edge : edges) + { + auto source = edge.first; + for (auto sink : edge.second) { + if (nodes[source] && nodes[sink]) + { + auto prop = edge_style(source, sink); + fprintf(f, " n%d -> n%d [ label=\"%s\", color=\"%s\", fillcolor=\"%s\" ];\n", + ids[source], ids[sink], dot_escape(prop.label.c_str()).c_str(), prop.color.c_str(), prop.fillcolor.c_str()); + } + } + } + + fprintf(f, "}\n"); + fclose(f); +} + +struct FlowGraph +{ + const RTLIL::SigBit source; + RTLIL::SigBit sink; + pool nodes = {source}; + dict> edges_fw, edges_bw; + + const int MAX_NODE_FLOW = 1; + dict node_flow; + dict, int> edge_flow; + + dict> collapsed; + + void dump_dot_graph(string filename) + { + auto node_style = [&](RTLIL::SigBit node) { + string label = (node == source) ? "(source)" : log_signal(node); + for (auto collapsed_node : collapsed[node]) + label += stringf(" %s", log_signal(collapsed_node)); + int flow = node_flow[node]; + if (node != source && node != sink) + label += stringf("\n%d/%d", flow, MAX_NODE_FLOW); + else + label += stringf("\n%d/∞", flow); + return GraphStyle{label, flow < MAX_NODE_FLOW ? "green" : "black"}; + }; + auto edge_style = [&](RTLIL::SigBit source, RTLIL::SigBit sink) { + int flow = edge_flow[{source, sink}]; + return GraphStyle{stringf("%d/∞", flow), flow > 0 ? "blue" : "black"}; + }; + ::dump_dot_graph(filename, nodes, edges_fw, {source}, {sink}, node_style, edge_style); + } + + // Here, we are working on the Nt'' network, but our representation is the Nt' network. + // The difference between these is that where in Nt' we have a subgraph: + // + // v1 -> v2 -> v3 + // + // in Nt'' we have a corresponding subgraph: + // + // v'1b -∞-> v'2t -f-> v'2b -∞-> v'3t + // + // To address this, we split each node v into two nodes, v't and v'b. This representation is virtual, + // in the sense that nodes v't and v'b are overlaid on top of the original node v, and only exist + // in paths and worklists. + + struct NodePrime + { + RTLIL::SigBit node; + bool is_bottom; + + NodePrime(RTLIL::SigBit node, bool is_bottom) : + node(node), is_bottom(is_bottom) {} + + bool operator==(const NodePrime &other) const + { + return node == other.node && is_bottom == other.is_bottom; + } + bool operator!=(const NodePrime &other) const + { + return !(*this == other); + } + unsigned int hash() const + { + return hash_ops>::hash({node, is_bottom}); + } + + static NodePrime top(RTLIL::SigBit node) + { + return NodePrime(node, /*is_bottom=*/false); + } + + static NodePrime bottom(RTLIL::SigBit node) + { + return NodePrime(node, /*is_bottom=*/true); + } + + NodePrime as_top() const + { + log_assert(is_bottom); + return top(node); + } + + NodePrime as_bottom() const + { + log_assert(!is_bottom); + return bottom(node); + } + }; + + bool find_augmenting_path(bool commit) + { + NodePrime source_prime = {source, true}; + NodePrime sink_prime = {sink, false}; + vector path = {source_prime}; + pool visited = {}; + bool found; + do { + found = false; + + auto node_prime = path.back(); + visited.insert(node_prime); + + if (!node_prime.is_bottom) // vt + { + if (!visited[node_prime.as_bottom()] && node_flow[node_prime.node] < MAX_NODE_FLOW) + { + path.push_back(node_prime.as_bottom()); + found = true; + } + else + { + for (auto node_pred : edges_bw[node_prime.node]) + { + if (!visited[NodePrime::bottom(node_pred)] && edge_flow[{node_pred, node_prime.node}] > 0) + { + path.push_back(NodePrime::bottom(node_pred)); + found = true; + break; + } + } + } + } + else // vb + { + if (!visited[node_prime.as_top()] && node_flow[node_prime.node] > 0) + { + path.push_back(node_prime.as_top()); + found = true; + } + else + { + for (auto node_succ : edges_fw[node_prime.node]) + { + if (!visited[NodePrime::top(node_succ)] /* && edge_flow[...] < ∞ */) + { + path.push_back(NodePrime::top(node_succ)); + found = true; + break; + } + } + } + } + + if (!found && path.size() > 1) + { + path.pop_back(); + found = true; + } + } while(path.back() != sink_prime && found); + + if (commit && path.back() == sink_prime) + { + auto prev_prime = path.front(); + for (auto node_prime : path) + { + if (node_prime == source_prime) + continue; + + log_assert(prev_prime.is_bottom ^ node_prime.is_bottom); + if (prev_prime.node == node_prime.node) + { + auto node = node_prime.node; + if (!prev_prime.is_bottom && node_prime.is_bottom) + { + log_assert(node_flow[node] == 0); + node_flow[node]++; + } + else + { + log_assert(node_flow[node] != 0); + node_flow[node]--; + } + } + else + { + if (prev_prime.is_bottom && !node_prime.is_bottom) + { + log_assert(true /* edge_flow[...] < ∞ */); + edge_flow[{prev_prime.node, node_prime.node}]++; + } + else + { + log_assert((edge_flow[{node_prime.node, prev_prime.node}] > 0)); + edge_flow[{node_prime.node, prev_prime.node}]--; + } + } + prev_prime = node_prime; + } + + node_flow[source]++; + node_flow[sink]++; + } + return path.back() == sink_prime; + } + + int maximum_flow(int order) + { + int flow = 0; + while (flow < order && find_augmenting_path(/*commit=*/true)) + flow++; + return flow + find_augmenting_path(/*commit=*/false); + } + + pair, pool> edge_cut() + { + pool x, xi; + + NodePrime source_prime = {source, true}; + NodePrime sink_prime = {sink, false}; + pool visited; + vector worklist = {source_prime}; + while (!worklist.empty()) + { + auto node_prime = worklist.back(); + worklist.pop_back(); + if (visited[node_prime]) + continue; + visited.insert(node_prime); + + if (!node_prime.is_bottom) + x.insert(node_prime.node); + + // Mincut is constructed by traversing a graph in an undirected way along forward edges that aren't full, or backward edges + // that aren't empty. + if (!node_prime.is_bottom) // top + { + if (node_flow[node_prime.node] < MAX_NODE_FLOW) + worklist.push_back(node_prime.as_bottom()); + for (auto node_pred : edges_bw[node_prime.node]) + if (edge_flow[{node_pred, node_prime.node}] > 0) + worklist.push_back(NodePrime::bottom(node_pred)); + } + else // bottom + { + if (node_flow[node_prime.node] > 0) + worklist.push_back(node_prime.as_top()); + for (auto node_succ : edges_fw[node_prime.node]) + if (true /* edge_flow[...] < ∞ */) + worklist.push_back(NodePrime::top(node_succ)); + } + } + + for (auto node : nodes) + if (!x[node]) + xi.insert(node); + + for (auto collapsed_node : collapsed[sink]) + xi.insert(collapsed_node); + + log_assert(!x[sink] && xi[sink]); + return {x, xi}; + } +}; + +struct FlowmapWorker +{ + int order; + bool debug; + + RTLIL::Module *module; + SigMap sigmap; + ModIndex index; + + dict node_origins; + + pool nodes, inputs, outputs; + dict> edges_fw, edges_bw; + dict labels; + + pool lut_nodes; + dict> lut_gates; + dict> lut_edges_fw, lut_edges_bw; + + int gate_count = 0, lut_count = 0, packed_count = 0; + int gate_area = 0, lut_area = 0; + + enum class GraphMode { + Label, + Cut, + }; + + void dump_dot_graph(string filename, GraphMode mode, + pool subgraph_nodes = {}, dict> subgraph_edges = {}, + dict> collapsed = {}, + pair, pool> cut = {}) + { + if (subgraph_nodes.empty()) + subgraph_nodes = nodes; + if (subgraph_edges.empty()) + subgraph_edges = edges_fw; + + auto node_style = [&](RTLIL::SigBit node) { + string label = log_signal(node); + for (auto collapsed_node : collapsed[node]) + if (collapsed_node != node) + label += stringf(" %s", log_signal(collapsed_node)); + switch (mode) + { + case GraphMode::Label: + if (labels[node] == -1) + { + label += "\nl=?"; + return GraphStyle{label}; + } + else + { + label += stringf("\nl=%d", labels[node]); + string fillcolor = stringf("/set311/%d", 1 + labels[node] % 11); + return GraphStyle{label, "", fillcolor}; + } + + case GraphMode::Cut: + if (cut.first[node]) + return GraphStyle{label, "blue"}; + if (cut.second[node]) + return GraphStyle{label, "red"}; + return GraphStyle{label}; + } + return GraphStyle{label}; + }; + auto edge_style = [&](RTLIL::SigBit, RTLIL::SigBit) { + return GraphStyle{}; + }; + ::dump_dot_graph(filename, subgraph_nodes, subgraph_edges, inputs, outputs, node_style, edge_style, module->name.str()); + } + + pool find_subgraph(RTLIL::SigBit sink) + { + pool subgraph; + pool worklist = {sink}; + while (!worklist.empty()) + { + auto node = worklist.pop(); + subgraph.insert(node); + for (auto source : edges_bw[node]) + { + if (!subgraph[source]) + worklist.insert(source); + } + } + return subgraph; + } + + FlowGraph build_flow_graph(RTLIL::SigBit sink, int p) + { + FlowGraph flow_graph; + flow_graph.sink = sink; + + pool worklist = {sink}, visited; + while (!worklist.empty()) + { + auto node = worklist.pop(); + visited.insert(node); + + auto collapsed_node = labels[node] == p ? sink : node; + if (node != collapsed_node) + flow_graph.collapsed[collapsed_node].insert(node); + flow_graph.nodes.insert(collapsed_node); + + for (auto node_pred : edges_bw[node]) + { + auto collapsed_node_pred = labels[node_pred] == p ? sink : node_pred; + if (node_pred != collapsed_node_pred) + flow_graph.collapsed[collapsed_node_pred].insert(node_pred); + if (collapsed_node != collapsed_node_pred) + { + flow_graph.edges_bw[collapsed_node].insert(collapsed_node_pred); + flow_graph.edges_fw[collapsed_node_pred].insert(collapsed_node); + } + if (inputs[node_pred]) + { + flow_graph.edges_bw[collapsed_node_pred].insert(flow_graph.source); + flow_graph.edges_fw[flow_graph.source].insert(collapsed_node_pred); + } + + if (!visited[node_pred]) + worklist.insert(node_pred); + } + } + return flow_graph; + } + + void discover_nodes(pool cell_types) + { + for (auto cell : module->selected_cells()) + { + if (!cell_types[cell->type]) + continue; + + if (!cell->known()) + log_error("Cell %s (%s.%s) is unknown.\n", cell->type.c_str(), log_id(module), log_id(cell)); + + pool fanout; + for (auto conn : cell->connections()) + { + if (!cell->output(conn.first)) continue; + int offset = -1; + for (auto bit : conn.second) + { + offset++; + if (!bit.wire) continue; + auto mapped_bit = sigmap(bit); + if (nodes[mapped_bit]) + log_error("Multiple drivers found for wire %s.\n", log_signal(mapped_bit)); + nodes.insert(mapped_bit); + node_origins[mapped_bit] = ModIndex::PortInfo(cell, conn.first, offset); + fanout.insert(mapped_bit); + } + } + + int fanin = 0; + for (auto conn : cell->connections()) + { + if (!cell->input(conn.first)) continue; + for (auto bit : sigmap(conn.second)) + { + if (!bit.wire) continue; + for (auto fanout_bit : fanout) + { + edges_fw[bit].insert(fanout_bit); + edges_bw[fanout_bit].insert(bit); + } + fanin++; + } + } + + if (fanin > order) + log_error("Cell %s (%s.%s) with fan-in %d cannot be mapped to a %d-LUT.\n", + cell->type.c_str(), log_id(module), log_id(cell), fanin, order); + + gate_count++; + gate_area += 1 << fanin; + } + + for (auto edge : edges_fw) + { + if (!nodes[edge.first]) + { + inputs.insert(edge.first); + nodes.insert(edge.first); + } + } + + for (auto node : nodes) + { + auto node_info = index.query(node); + if (node_info->is_output && !inputs[node]) + outputs.insert(node); + for (auto port : node_info->ports) + if (!cell_types[port.cell->type] && !inputs[node]) + outputs.insert(node); + } + + if (debug) + { + dump_dot_graph("flowmap-initial.dot", GraphMode::Label); + log("Dumped initial graph to `flowmap-initial.dot`.\n"); + } + } + + void label_nodes() + { + for (auto node : nodes) + labels[node] = -1; + for (auto input : inputs) + { + if (input.wire->attributes.count("\\$flowmap_level")) + labels[input] = input.wire->attributes["\\$flowmap_level"].as_int(); + else + labels[input] = 0; + } + + pool worklist = nodes; + int debug_num = 0; + while (!worklist.empty()) + { + auto sink = worklist.pop(); + if (labels[sink] != -1) + continue; + + bool inputs_have_labels = true; + for (auto sink_input : edges_bw[sink]) + { + if (labels[sink_input] == -1) + { + inputs_have_labels = false; + break; + } + } + if (!inputs_have_labels) + continue; + + if (debug) + { + debug_num++; + log("Examining subgraph %d rooted in %s.\n", debug_num, log_signal(sink)); + } + + pool subgraph = find_subgraph(sink); + + int p = 1; + for (auto subgraph_node : subgraph) + p = max(p, labels[subgraph_node]); + + FlowGraph flow_graph = build_flow_graph(sink, p); + int flow = flow_graph.maximum_flow(order); + pool x, xi; + if (flow <= order) + { + labels[sink] = p; + auto cut = flow_graph.edge_cut(); + x = cut.first; + xi = cut.second; + } + else + { + labels[sink] = p + 1; + x = subgraph; + x.erase(sink); + xi.insert(sink); + } + lut_gates[sink] = xi; + + pool k; + for (auto xi_node : xi) + { + for (auto xi_node_pred : edges_bw[xi_node]) + if (x[xi_node_pred]) + k.insert(xi_node_pred); + } + log_assert((int)k.size() <= order); + lut_edges_bw[sink] = k; + for (auto k_node : k) + lut_edges_fw[k_node].insert(sink); + + if (debug) + { + log(" Maximum flow: %d. Assigned label %d.\n", flow, labels[sink]); + dump_dot_graph(stringf("flowmap-%d-sub.dot", debug_num), GraphMode::Cut, subgraph, {}, {}, {x, xi}); + log(" Dumped subgraph to `flowmap-%d-sub.dot`.\n", debug_num); + flow_graph.dump_dot_graph(stringf("flowmap-%d-flow.dot", debug_num)); + log(" Dumped flow graph to `flowmap-%d-flow.dot`.\n", debug_num); + log(" LUT inputs:"); + for (auto k_node : k) + log(" %s", log_signal(k_node)); + log(".\n"); + log(" LUT packed gates:"); + for (auto xi_node : xi) + log(" %s", log_signal(xi_node)); + log(".\n"); + } + + for (auto sink_succ : edges_fw[sink]) + worklist.insert(sink_succ); + } + + if (debug) + { + dump_dot_graph("flowmap-labeled.dot", GraphMode::Label); + log("Dumped labeled graph to `flowmap-labeled.dot`.\n"); + } + } + + int pack_luts() + { + pool worklist = outputs; + while (!worklist.empty()) + { + auto lut_node = worklist.pop(); + lut_nodes.insert(lut_node); + for (auto input_node : lut_edges_bw[lut_node]) + if (!lut_nodes[input_node] && !inputs[input_node]) + worklist.insert(input_node); + } + + int depth = 0; + for (auto label : labels) + depth = max(depth, label.second); + log("Solved to %d LUTs in %d levels.\n", (int)lut_nodes.size(), depth); + + if (debug) + { + pool lut_and_input_nodes; + lut_and_input_nodes.insert(lut_nodes.begin(), lut_nodes.end()); + lut_and_input_nodes.insert(inputs.begin(), inputs.end()); + dump_dot_graph("flowmap-packed.dot", GraphMode::Label, lut_and_input_nodes, lut_edges_fw, lut_gates); + log("Dumped packed graph to `flowmap-packed.dot`.\n"); + } + + return depth; + } + + void map_cells(int minlut) + { + ConstEval ce(module); + for (auto input_node : inputs) + ce.stop(input_node); + + pool mapped_nodes; + for (auto node : lut_nodes) + { + if (node_origins.count(node)) + { + auto origin = node_origins[node]; + if (origin.cell->getPort(origin.port).size() == 1) + log("Mapping %s.%s.%s (%s).\n", + log_id(module), log_id(origin.cell), origin.port.c_str(), log_signal(node)); + else + log("Mapping %s.%s.%s [%d] (%s).\n", + log_id(module), log_id(origin.cell), origin.port.c_str(), origin.offset, log_signal(node)); + } + else + { + log("Mapping %s.%s.\n", log_id(module), log_signal(node)); + } + + for (auto gate_node : lut_gates[node]) + { + log_assert(node_origins.count(gate_node)); + + if (gate_node == node) + continue; + + auto gate_origin = node_origins[gate_node]; + if (gate_origin.cell->getPort(gate_origin.port).size() == 1) + log(" Packing %s.%s.%s (%s).\n", + log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), log_signal(gate_node)); + else + log(" Packing %s.%s.%s [%d] (%s).\n", + log_id(module), log_id(gate_origin.cell), gate_origin.port.c_str(), gate_origin.offset, log_signal(gate_node)); + } + + vector input_nodes(lut_edges_bw[node].begin(), lut_edges_bw[node].end()); + RTLIL::Const lut_table(State::Sx, max(1 << input_nodes.size(), 1 << minlut)); + for (unsigned i = 0; i < (1 << input_nodes.size()); i++) + { + ce.push(); + for (size_t n = 0; n < input_nodes.size(); n++) + ce.set(input_nodes[n], ((i >> n) & 1) ? State::S1 : State::S0); + + RTLIL::SigSpec value = node, undef; + if (!ce.eval(value, undef)) + { + string env; + for (auto input_node : input_nodes) + env += stringf(" %s = %s\n", log_signal(input_node), log_signal(ce.values_map(input_node))); + log_error("Cannot evaluate %s because %s is not defined.\nEvaluation environment:\n%s", + log_signal(node), log_signal(undef), env.c_str()); + } + + lut_table[i] = value.as_bool() ? State::S1 : State::S0; + ce.pop(); + } + + RTLIL::SigSpec lut_a, lut_y = node; + for (auto input_node : input_nodes) + lut_a.append_bit(input_node); + lut_a.append(RTLIL::Const(State::Sx, minlut - input_nodes.size())); + + RTLIL::Cell *lut = module->addLut(NEW_ID, lut_a, lut_y, lut_table); + mapped_nodes.insert(node); + for (auto gate_node : lut_gates[node]) + { + auto gate_origin = node_origins[gate_node]; + lut->add_strpool_attribute("\\src", gate_origin.cell->get_strpool_attribute("\\src")); + packed_count++; + } + lut_count++; + lut_area += lut_table.size(); + + if ((int)input_nodes.size() >= minlut) + log(" Packed into a %d-LUT %s.%s.\n", (int)input_nodes.size(), log_id(module), log_id(lut)); + else + log(" Packed into a %d-LUT %s.%s (implemented as %d-LUT).\n", (int)input_nodes.size(), log_id(module), log_id(lut), minlut); + } + + for (auto node : mapped_nodes) + { + auto origin = node_origins[node]; + RTLIL::SigSpec driver = origin.cell->getPort(origin.port); + driver[origin.offset] = module->addWire(NEW_ID); + origin.cell->setPort(origin.port, driver); + } + } + + FlowmapWorker(int order, int minlut, pool cell_types, bool debug, RTLIL::Module *module) : + order(order), debug(debug), module(module), sigmap(module), index(module) + { + log("Labeling cells.\n"); + discover_nodes(cell_types); + label_nodes(); + pack_luts(); + + log("\n"); + log("Mapping cells.\n"); + map_cells(minlut); + } +}; + +static void split(std::vector &tokens, const std::string &text, char sep) +{ + size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != std::string::npos) { + tokens.push_back(text.substr(start, end - start)); + start = end + 1; + } + tokens.push_back(text.substr(start)); +} + +struct FlowmapPass : public Pass { + FlowmapPass() : Pass("flowmap", "pack LUTs with FlowMap") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" flowmap [options] [selection]\n"); + log("\n"); + log("This pass uses the FlowMap technology mapping algorithm to pack logic gates\n"); + log("into k-LUTs with optimal depth. It allows mapping any circuit elements that can\n"); + log("be evaluated with the `eval` pass, including cells with multiple output ports\n"); + log("and multi-bit input and output ports.\n"); + log("\n"); + log(" -maxlut k\n"); + log(" perform technology mapping for a k-LUT architecture. if not specified,\n"); + log(" defaults to 3.\n"); + log("\n"); + log(" -minlut n\n"); + log(" only produce n-input or larger LUTs. if not specified, defaults to 1.\n"); + log("\n"); + log(" -cells [,,...]\n"); + log(" map specified cells. if not specified, maps $_NOT_, $_AND_, $_OR_,\n"); + log(" $_XOR_ and $_MUX_, which are the outputs of the `simplemap` pass.\n"); + log("\n"); + log(" -debug\n"); + log(" dump intermediate graphs.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + int order = 3; + int minlut = 1; + vector cells; + bool debug = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-maxlut" && argidx + 1 < args.size()) + { + order = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-minlut" && argidx + 1 < args.size()) + { + minlut = atoi(args[++argidx].c_str()); + continue; + } + if (args[argidx] == "-cells" && argidx + 1 < args.size()) + { + split(cells, args[++argidx], ','); + continue; + } + if (args[argidx] == "-debug") + { + debug = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + pool cell_types; + if (!cells.empty()) + { + for (auto &cell : cells) + cell_types.insert(cell); + } + else + { + cell_types = {"$_NOT_", "$_AND_", "$_OR_", "$_XOR_", "$_MUX_"}; + } + + log_header(design, "Executing FLOWMAP pass (pack LUTs with FlowMap).\n"); + + int gate_count = 0, lut_count = 0, packed_count = 0; + int gate_area = 0, lut_area = 0; + for (auto module : design->selected_modules()) + { + FlowmapWorker worker(order, minlut, cell_types, debug, module); + gate_count += worker.gate_count; + lut_count += worker.lut_count; + packed_count += worker.packed_count; + gate_area += worker.gate_area; + lut_area += worker.lut_area; + } + + log("\n"); + log("Mapped %d LUTs.\n", lut_count); + log("Packed %d cells; duplicated %d cells.\n", packed_count, packed_count - gate_count); + log("Solution has %.1f%% area overhead.\n", (lut_area - gate_area) * 100.0 / gate_area); + } +} FlowmapPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/passes/techmap/hilomap.cc b/yosys/passes/techmap/hilomap.cc index 82cecac26..9ec651aef 100644 --- a/yosys/passes/techmap/hilomap.cc +++ b/yosys/passes/techmap/hilomap.cc @@ -55,7 +55,7 @@ void hilomap_worker(RTLIL::SigSpec &sig) struct HilomapPass : public Pass { HilomapPass() : Pass("hilomap", "technology mapping of constant hi- and/or lo-drivers") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" hilomap [options] [selection]\n"); @@ -74,7 +74,7 @@ struct HilomapPass : public Pass { log(" each constant bit.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n"); diff --git a/yosys/passes/techmap/insbuf.cc b/yosys/passes/techmap/insbuf.cc index aa81468dc..2173049b4 100644 --- a/yosys/passes/techmap/insbuf.cc +++ b/yosys/passes/techmap/insbuf.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct InsbufPass : public Pass { InsbufPass() : Pass("insbuf", "insert buffer cells for connected wires") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" insbuf [options] [selection]\n"); @@ -37,7 +37,7 @@ struct InsbufPass : public Pass { log(" call to \"clean\" will remove all $_BUF_ in the design.)\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing INSBUF pass (insert buffer cells for connected wires).\n"); diff --git a/yosys/passes/techmap/iopadmap.cc b/yosys/passes/techmap/iopadmap.cc index 4acbf7c0d..efcc082d5 100644 --- a/yosys/passes/techmap/iopadmap.cc +++ b/yosys/passes/techmap/iopadmap.cc @@ -34,7 +34,7 @@ void split_portname_pair(std::string &port1, std::string &port2) struct IopadmapPass : public Pass { IopadmapPass() : Pass("iopadmap", "technology mapping of i/o pads (or buffers)") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" iopadmap [options] [selection]\n"); @@ -78,7 +78,7 @@ struct IopadmapPass : public Pass { log("Tristate PADS (-toutpad, -tinoutpad) always operate in -bits mode.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); @@ -146,11 +146,37 @@ struct IopadmapPass : public Pass { for (auto module : design->selected_modules()) { dict> skip_wires; + pool skip_wire_bits; + SigMap sigmap(module); + + for (auto cell : module->cells()) + { + if (cell->type == RTLIL::escape_id(inpad_celltype) && cell->hasPort(RTLIL::escape_id(inpad_portname2))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inpad_portname2)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(outpad_celltype) && cell->hasPort(RTLIL::escape_id(outpad_portname2))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(outpad_portname2)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(inoutpad_celltype) && cell->hasPort(RTLIL::escape_id(inoutpad_portname2))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(inoutpad_portname2)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(toutpad_celltype) && cell->hasPort(RTLIL::escape_id(toutpad_portname3))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(toutpad_portname3)))) + skip_wire_bits.insert(bit); + + if (cell->type == RTLIL::escape_id(tinoutpad_celltype) && cell->hasPort(RTLIL::escape_id(tinoutpad_portname4))) + for (auto bit : sigmap(cell->getPort(RTLIL::escape_id(tinoutpad_portname4)))) + skip_wire_bits.insert(bit); + } if (!toutpad_celltype.empty() || !tinoutpad_celltype.empty()) { - SigMap sigmap(module); dict>> tbuf_bits; + pool> norewrites; + SigMap rewrites; for (auto cell : module->cells()) if (cell->type == "$_TBUF_") { @@ -177,6 +203,9 @@ struct IopadmapPass : public Pass { if (tbuf_bits.count(mapped_wire_bit) == 0) continue; + if (skip_wire_bits.count(mapped_wire_bit)) + continue; + auto &tbuf_cache = tbuf_bits.at(mapped_wire_bit); Cell *tbuf_cell = module->cell(tbuf_cache.first); @@ -219,6 +248,9 @@ struct IopadmapPass : public Pass { module->remove(tbuf_cell); skip_wires[wire->name].insert(i); + + norewrites.insert(make_pair(cell->name, RTLIL::escape_id(tinoutpad_portname4))); + rewrites.add(sigmap(wire_bit), owire); continue; } @@ -256,6 +288,22 @@ struct IopadmapPass : public Pass { } } } + + if (GetSize(norewrites)) + { + for (auto cell : module->cells()) + for (auto port : cell->connections()) + { + if (norewrites.count(make_pair(cell->name, port.first))) + continue; + + SigSpec orig_sig = sigmap(port.second); + SigSpec new_sig = rewrites(orig_sig); + + if (orig_sig != new_sig) + cell->setPort(port.first, new_sig); + } + } } for (auto wire : module->selected_wires()) @@ -272,6 +320,13 @@ struct IopadmapPass : public Pass { skip_bit_indices = skip_wires.at(wire->name); } + for (int i = 0; i < GetSize(wire); i++) + if (skip_wire_bits.count(sigmap(SigBit(wire, i)))) + skip_bit_indices.insert(i); + + if (GetSize(wire) == GetSize(skip_bit_indices)) + continue; + 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)); diff --git a/yosys/passes/techmap/libparse.cc b/yosys/passes/techmap/libparse.cc index d5254c029..3927a657b 100644 --- a/yosys/passes/techmap/libparse.cc +++ b/yosys/passes/techmap/libparse.cc @@ -90,18 +90,25 @@ int LibertyParser::lexer(std::string &str) 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 == '.') { + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || 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 == '.') + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.' || c == '[' || c == ']') str += c; else break; } f.unget(); - // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); - return 'v'; + if (str == "+" || str == "-") { + /* Single operator is not an identifier */ + // fprintf(stderr, "LEX: char >>%s<<\n", str.c_str()); + return str[0]; + } + else { + // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); + return 'v'; + } } if (c == '"') { @@ -152,7 +159,7 @@ int LibertyParser::lexer(std::string &str) if (c == '\n') { line++; - return ';'; + return 'n'; } // if (c >= 32 && c < 255) @@ -168,7 +175,7 @@ LibertyAst *LibertyParser::parse() int tok = lexer(str); - while (tok == ';') + while (tok == 'n') tok = lexer(str); if (tok == '}' || tok < 0) @@ -187,10 +194,26 @@ LibertyAst *LibertyParser::parse() if (tok == ';') break; + if (tok == 'n') + continue; + if (tok == ':' && ast->value.empty()) { tok = lexer(ast->value); if (tok != 'v') error(); + tok = lexer(str); + while (tok == '+' || tok == '-' || tok == '*' || tok == '/') { + ast->value += tok; + tok = lexer(str); + if (tok != 'v') + error(); + ast->value += str; + tok = lexer(str); + } + if (tok == ';') + break; + else + error(); continue; } @@ -229,14 +252,14 @@ LibertyAst *LibertyParser::parse() void LibertyParser::error() { - log_error("Syntax error in line %d.\n", line); + log_error("Syntax error in liberty file on line %d.\n", line); } #else void LibertyParser::error() { - fprintf(stderr, "Syntax error in line %d.\n", line); + fprintf(stderr, "Syntax error in liberty file on line %d.\n", line); exit(1); } diff --git a/yosys/passes/techmap/lut2mux.cc b/yosys/passes/techmap/lut2mux.cc index 2bb0bd8b4..a4ed79550 100644 --- a/yosys/passes/techmap/lut2mux.cc +++ b/yosys/passes/techmap/lut2mux.cc @@ -32,7 +32,7 @@ int lut2mux(Cell *cell) if (GetSize(sig_a) == 1) { - cell->module->addMuxGate(NEW_ID, lut[0], lut[1], sig_a, sig_y); + cell->module->addMuxGate(NEW_ID, lut.extract(0)[0], lut.extract(1)[0], sig_a, sig_y); } else { @@ -56,7 +56,7 @@ int lut2mux(Cell *cell) struct Lut2muxPass : public Pass { Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -65,7 +65,7 @@ struct Lut2muxPass : public Pass { log("This pass converts $lut cells to $_MUX_ gates.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n"); diff --git a/yosys/passes/techmap/maccmap.cc b/yosys/passes/techmap/maccmap.cc index 32569d076..3e8e59e6b 100644 --- a/yosys/passes/techmap/maccmap.cc +++ b/yosys/passes/techmap/maccmap.cc @@ -365,7 +365,7 @@ PRIVATE_NAMESPACE_BEGIN struct MaccmapPass : public Pass { MaccmapPass() : Pass("maccmap", "mapping macc cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -375,7 +375,7 @@ struct MaccmapPass : public Pass { log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool unmap_mode = false; diff --git a/yosys/passes/techmap/muxcover.cc b/yosys/passes/techmap/muxcover.cc index 1dc649587..12da9ed0c 100644 --- a/yosys/passes/techmap/muxcover.cc +++ b/yosys/passes/techmap/muxcover.cc @@ -561,7 +561,7 @@ struct MuxcoverWorker struct MuxcoverPass : public Pass { MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -579,7 +579,7 @@ struct MuxcoverPass : public Pass { log(" less efficient than the original circuit.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n"); diff --git a/yosys/passes/techmap/nlutmap.cc b/yosys/passes/techmap/nlutmap.cc index 6fcdf82bd..cc765d89c 100644 --- a/yosys/passes/techmap/nlutmap.cc +++ b/yosys/passes/techmap/nlutmap.cc @@ -92,7 +92,7 @@ struct NlutmapWorker for (auto bit : sigmap(conn.second)) bit_lut_count[bit]++; } - + for (auto &cand : candidate_ratings) { for (auto &conn : cand.first->connections()) @@ -129,7 +129,7 @@ struct NlutmapWorker struct NlutmapPass : public Pass { NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -149,7 +149,7 @@ struct NlutmapPass : public Pass { log("to generic logic gates ($_AND_, etc.).\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { NlutmapConfig config; diff --git a/yosys/passes/techmap/pmuxtree.cc b/yosys/passes/techmap/pmuxtree.cc index c626dbcc5..b7a22dc3b 100644 --- a/yosys/passes/techmap/pmuxtree.cc +++ b/yosys/passes/techmap/pmuxtree.cc @@ -67,7 +67,7 @@ static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, struct PmuxtreePass : public Pass { PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -76,7 +76,7 @@ struct PmuxtreePass : public Pass { log("This pass transforms $pmux cells to a trees of $mux cells.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing PMUXTREE pass.\n"); diff --git a/yosys/passes/techmap/shregmap.cc b/yosys/passes/techmap/shregmap.cc index 6936b499e..f20863ba0 100644 --- a/yosys/passes/techmap/shregmap.cc +++ b/yosys/passes/techmap/shregmap.cc @@ -391,7 +391,7 @@ struct ShregmapWorker struct ShregmapPass : public Pass { ShregmapPass() : Pass("shregmap", "map shift registers") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -449,7 +449,7 @@ struct ShregmapPass : public Pass { log(" map to greenpak4 shift registers.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { ShregmapOptions opts; string clkpol, enpol; diff --git a/yosys/passes/techmap/simplemap.cc b/yosys/passes/techmap/simplemap.cc index c6b932bdc..660b60601 100644 --- a/yosys/passes/techmap/simplemap.cc +++ b/yosys/passes/techmap/simplemap.cc @@ -575,7 +575,7 @@ PRIVATE_NAMESPACE_BEGIN struct SimplemapPass : public Pass { SimplemapPass() : Pass("simplemap", "mapping simple coarse-grain cells") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -590,7 +590,7 @@ struct SimplemapPass : public Pass { log(" $sr, $ff, $dff, $dffsr, $adff, $dlatch\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); diff --git a/yosys/passes/techmap/techmap.cc b/yosys/passes/techmap/techmap.cc index 6784f48c3..d0e5e2236 100644 --- a/yosys/passes/techmap/techmap.cc +++ b/yosys/passes/techmap/techmap.cc @@ -171,18 +171,15 @@ struct TechmapWorker } std::string orig_cell_name; - pool extra_src_attrs; + pool extra_src_attrs = cell->get_strpool_attribute("\\src"); - if (!flatten_mode) - { + 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 memory_renames; @@ -247,6 +244,9 @@ struct TechmapWorker continue; } + if (GetSize(it.second) == 0) + continue; + RTLIL::Wire *w = tpl->wires_.at(portname); RTLIL::SigSig c, extra_connect; @@ -305,10 +305,15 @@ struct TechmapWorker // approach that yields nicer outputs: // replace internal wires that are connected to external wires - if (w->port_output) + if (w->port_output && !w->port_input) { port_signal_map.add(c.second, c.first); - else + } else + if (!w->port_output && w->port_input) { port_signal_map.add(c.first, c.second); + } else { + module->connect(c); + extra_connect = SigSig(); + } for (auto &attr : w->attributes) { if (attr.first == "\\src") @@ -322,8 +327,9 @@ struct TechmapWorker for (auto &it : tpl->cells_) { std::string c_name = it.second->name.str(); + bool techmap_replace_cell = (!flatten_mode) && (c_name == "\\_TECHMAP_REPLACE_"); - if (!flatten_mode && c_name == "\\_TECHMAP_REPLACE_") + if (techmap_replace_cell) c_name = orig_cell_name; else apply_prefix(cell->name.str(), c_name); @@ -353,6 +359,11 @@ struct TechmapWorker if (c->attributes.count("\\src")) c->add_strpool_attribute("\\src", extra_src_attrs); + + if (techmap_replace_cell) + for (auto attr : cell->attributes) + if (!c->attributes.count(attr.first)) + c->attributes[attr.first] = attr.second; } for (auto &it : tpl->connections()) { @@ -451,6 +462,7 @@ struct TechmapWorker 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); @@ -498,6 +510,8 @@ struct TechmapWorker extmapper_module = extmapper_design->addModule(m_name); RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell); + extmapper_cell->set_src_attribute(cell->get_src_attribute()); + int port_counter = 1; for (auto &c : extmapper_cell->connections_) { RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second)); @@ -877,7 +891,7 @@ struct TechmapWorker struct TechmapPass : public Pass { TechmapPass() : Pass("techmap", "generic technology mapper") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -919,7 +933,7 @@ struct TechmapPass : public Pass { log(" -D , -I \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(" '-nooverwrite' 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"); @@ -1000,7 +1014,7 @@ struct TechmapPass : public Pass { 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("and attributes 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"); @@ -1008,7 +1022,7 @@ struct TechmapPass : public Pass { log("essentially techmap but using the design itself as map library).\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); @@ -1017,7 +1031,7 @@ struct TechmapPass : public Pass { simplemap_get_mappers(worker.simplemap_mappers); std::vector map_files; - std::string verilog_frontend = "verilog -ignore_redef"; + std::string verilog_frontend = "verilog -nooverwrite"; int max_iter = -1; size_t argidx; @@ -1076,6 +1090,7 @@ struct TechmapPass : public Pass { std::ifstream f; rewrite_filename(fn); f.open(fn.c_str()); + yosys_input_files.insert(fn); 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); @@ -1126,7 +1141,7 @@ struct TechmapPass : public Pass { struct FlattenPass : public Pass { FlattenPass() : Pass("flatten", "flatten design") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1140,7 +1155,7 @@ struct FlattenPass : public Pass { log("flattened by this command.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing FLATTEN pass (flatten design).\n"); log_push(); diff --git a/yosys/passes/techmap/tribuf.cc b/yosys/passes/techmap/tribuf.cc index 03629082c..587cb9038 100644 --- a/yosys/passes/techmap/tribuf.cc +++ b/yosys/passes/techmap/tribuf.cc @@ -139,7 +139,7 @@ struct TribufWorker { struct TribufPass : public Pass { TribufPass() : Pass("tribuf", "infer tri-state buffers") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -156,7 +156,7 @@ struct TribufPass : public Pass { log(" to non-tristate logic. this option implies -merge.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { TribufConfig config; diff --git a/yosys/passes/techmap/zinit.cc b/yosys/passes/techmap/zinit.cc index a577e1235..b46147fb9 100644 --- a/yosys/passes/techmap/zinit.cc +++ b/yosys/passes/techmap/zinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct ZinitPass : public Pass { ZinitPass() : Pass("zinit", "add inverters so all FF are zero-initialized") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -37,7 +37,7 @@ struct ZinitPass : public Pass { log(" also add zero initialization to uninitialized FFs\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { bool all_mode = false; diff --git a/yosys/passes/tests/flowmap/flow.v b/yosys/passes/tests/flowmap/flow.v new file mode 100644 index 000000000..297ef910e --- /dev/null +++ b/yosys/passes/tests/flowmap/flow.v @@ -0,0 +1,22 @@ +// Exact reproduction of Figure 2(a) from 10.1109/43.273754. +module top(...); + input a,b,c,d,e,f; + wire nA = b&c; + wire A = !nA; + wire nB = c|d; + wire B = !nB; + wire nC = e&f; + wire C = !nC; + wire D = A|B; + wire E = a&D; + wire nF = D&C; + wire F = !nF; + wire nG = F|B; + wire G = !nG; + wire H = a&F; + wire I = E|G; + wire J = G&C; + wire np = H&I; + output p = !np; + output q = A|J; +endmodule diff --git a/yosys/passes/tests/flowmap/flowp.v b/yosys/passes/tests/flowmap/flowp.v new file mode 100644 index 000000000..2fb40ffa4 --- /dev/null +++ b/yosys/passes/tests/flowmap/flowp.v @@ -0,0 +1,16 @@ +// Like flow.v, but results in a network identical to Figure 2(b). +module top(...); + input a,b,c,d,e,f; + wire A = b&c; + wire B = c|d; + wire C = e&f; + wire D = A|B; + wire E = a&D; + wire F = D&C; + wire G = F|B; + wire H = a&F; + wire I = E|G; + wire J = G&C; + output p = H&I; + output q = A|J; +endmodule diff --git a/yosys/passes/tests/test_abcloop.cc b/yosys/passes/tests/test_abcloop.cc index 09cb41954..5d5466afe 100644 --- a/yosys/passes/tests/test_abcloop.cc +++ b/yosys/passes/tests/test_abcloop.cc @@ -244,7 +244,7 @@ static void test_abcloop() struct TestAbcloopPass : public Pass { TestAbcloopPass() : Pass("test_abcloop", "automatically test handling of loops in abc command") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -259,7 +259,7 @@ struct TestAbcloopPass : public Pass { log(" use this value as rng seed value (default = unix time).\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design*) + void execute(std::vector args, RTLIL::Design*) YS_OVERRIDE { int num_iter = 100; xorshift32_state = 0; diff --git a/yosys/passes/tests/test_autotb.cc b/yosys/passes/tests/test_autotb.cc index cb31056f4..bfb1d6642 100644 --- a/yosys/passes/tests/test_autotb.cc +++ b/yosys/passes/tests/test_autotb.cc @@ -324,7 +324,7 @@ static void autotest(std::ostream &f, RTLIL::Design *design, int num_iter, int s struct TestAutotbBackend : public Backend { TestAutotbBackend() : Backend("=test_autotb", "generate simple test benches") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -349,7 +349,7 @@ struct TestAutotbBackend : public Backend { 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 args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) YS_OVERRIDE { int num_iter = 1000; int seed = 0; diff --git a/yosys/passes/tests/test_cell.cc b/yosys/passes/tests/test_cell.cc index 049c20533..e360b5edb 100644 --- a/yosys/passes/tests/test_cell.cc +++ b/yosys/passes/tests/test_cell.cc @@ -652,7 +652,7 @@ static void run_eval_test(RTLIL::Design *design, bool verbose, bool nosat, std:: struct TestCellPass : public Pass { TestCellPass() : Pass("test_cell", "automatically test the implementation of a cell type") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -712,7 +712,7 @@ struct TestCellPass : public Pass { log(" create a Verilog test bench to test simlib and write_verilog\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design*) + void execute(std::vector args, RTLIL::Design*) YS_OVERRIDE { int num_iter = 100; std::string techmap_cmd = "techmap -assert"; @@ -852,8 +852,6 @@ struct TestCellPass : public Pass { // cell_types["$slice"] = "A"; // cell_types["$concat"] = "A"; - // cell_types["$assert"] = "A"; - // cell_types["$assume"] = "A"; cell_types["$lut"] = "*"; cell_types["$sop"] = "*"; diff --git a/yosys/techlibs/achronix/Makefile.inc b/yosys/techlibs/achronix/Makefile.inc new file mode 100755 index 000000000..994cf0015 --- /dev/null +++ b/yosys/techlibs/achronix/Makefile.inc @@ -0,0 +1,6 @@ + +OBJS += techlibs/achronix/synth_achronix.o + +$(eval $(call add_share_file,share/achronix/speedster22i/,techlibs/achronix/speedster22i/cells_sim.v)) +$(eval $(call add_share_file,share/achronix/speedster22i/,techlibs/achronix/speedster22i/cells_map.v)) + diff --git a/yosys/techlibs/achronix/speedster22i/cells_arith.v b/yosys/techlibs/achronix/speedster22i/cells_arith.v new file mode 100755 index 000000000..e2194cbd7 --- /dev/null +++ b/yosys/techlibs/achronix/speedster22i/cells_arith.v @@ -0,0 +1,65 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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: This is still WIP. +(* techmap_celltype = "$alu" *) +module _80_altera_max10_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; + output CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 4; + + 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:0] C = {CO, CI}; + wire [Y_WIDTH+1:0] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + /* Start implementation */ + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b0000_0000_1010_1010), .sum_lutc_input("cin")) carry_start (.cout(COx[0]), .dataa(C[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) begin + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b1111_0000_1110_0000), .sum_lutc_input("cin")) carry_end (.combout(COx[Y_WIDTH]), .dataa(1'b1), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[Y_WIDTH])); + assign CO = COx[Y_WIDTH]; + end + else + fiftyfivenm_lcell_comb #(.lut_mask(16'b1001_0110_1110_1000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(COx[i+1]), .dataa(AA[i]), .datab(BB[i]), .datac(1'b1), .datad(1'b1), .cin(C[i+1])); + end: slice + endgenerate + /* End implementation */ + assign X = AA ^ BB; + +endmodule diff --git a/yosys/techlibs/achronix/speedster22i/cells_map.v b/yosys/techlibs/achronix/speedster22i/cells_map.v new file mode 100755 index 000000000..9f647cbef --- /dev/null +++ b/yosys/techlibs/achronix/speedster22i/cells_map.v @@ -0,0 +1,72 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Achronix eFPGA technology mapping. User must first simulate the generated \ +// > netlist before going to test it on board/custom chip. + +// > Input/Output buffers < +// Input buffer map +module \$__inpad (input I, output O); + PADIN _TECHMAP_REPLACE_ (.padout(O), .padin(I)); +endmodule +// Output buffer map +module \$__outpad (input I, output O); + PADOUT _TECHMAP_REPLACE_ (.padout(O), .padin(I), .oe(1'b1)); +endmodule +// > end buffers < + +// > Look-Up table < +// > VT: I still think Achronix folks would have chosen a better \ +// > logic architecture. +// LUT Map +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + generate + if (WIDTH == 1) begin + // VT: This is not consistent and ACE will complain: assign Y = ~A[0]; + LUT4 #(.lut_function({4{LUT}})) _TECHMAP_REPLACE_ + (.dout(Y), .din0(A[0]), .din1(1'b0), .din2(1'b0), .din3(1'b0)); + end else + if (WIDTH == 2) begin + LUT4 #(.lut_function({4{LUT}})) _TECHMAP_REPLACE_ + (.dout(Y), .din0(A[0]), .din1(A[1]), .din2(1'b0), .din3(1'b0)); + end else + if(WIDTH == 3) begin + LUT4 #(.lut_function({2{LUT}})) _TECHMAP_REPLACE_ + (.dout(Y), .din0(A[0]), .din1(A[1]), .din2(A[2]), .din3(1'b0)); + end else + if(WIDTH == 4) begin + LUT4 #(.lut_function(LUT)) _TECHMAP_REPLACE_ + (.dout(Y), .din0(A[0]), .din1(A[1]), .din2(A[2]), .din3(A[3])); + end else + wire _TECHMAP_FAIL_ = 1; + endgenerate +endmodule +// > end LUT < + +// > Flops < +// DFF flop +module \$_DFF_P_ (input D, C, output Q); + DFF _TECHMAP_REPLACE_ + (.q(Q), .d(D), .ck(C)); +endmodule + diff --git a/yosys/techlibs/achronix/speedster22i/cells_sim.v b/yosys/techlibs/achronix/speedster22i/cells_sim.v new file mode 100755 index 000000000..a94dce9b1 --- /dev/null +++ b/yosys/techlibs/achronix/speedster22i/cells_sim.v @@ -0,0 +1,80 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Achronix eFPGA technology sim models. User must first simulate the generated \ +// > netlist before going to test it on board/custom chip. +// > Changelog: 1) Removed unused VCC/GND modules +// > 2) Altera comments here (?). Removed. +// > 3) Reusing LUT sim model, removed wrong wires and parameters. + +module PADIN (output padout, input padin); + assign padout = padin; +endmodule + +module PADOUT (output padout, input padin, input oe); + assign padout = padin; + assign oe = oe; +endmodule + +module LUT4 (output dout, + input din0, din1, din2, din3); + +parameter [15:0] lut_function = 16'hFFFF; +reg combout_rt; +wire dataa_w; +wire datab_w; +wire datac_w; +wire datad_w; + +assign dataa_w = din0; +assign datab_w = din1; +assign datac_w = din2; +assign datad_w = din3; + +function lut_data; +input [15:0] mask; +input dataa, datab, datac, datad; +reg [7:0] s3; +reg [3:0] s2; +reg [1:0] s1; + begin + s3 = datad ? mask[15:8] : mask[7:0]; + s2 = datac ? s3[7:4] : s3[3:0]; + s1 = datab ? s2[3:2] : s2[1:0]; + lut_data = dataa ? s1[1] : s1[0]; + end +endfunction + +always @(dataa_w or datab_w or datac_w or datad_w) begin + combout_rt = lut_data(lut_function, dataa_w, datab_w, + datac_w, datad_w); +end +assign dout = combout_rt & 1'b1; +endmodule + +module DFF (output q, + input d, ck); + reg q; + always @(posedge ck) + q <= d; + +endmodule + + + diff --git a/yosys/techlibs/achronix/synth_achronix.cc b/yosys/techlibs/achronix/synth_achronix.cc new file mode 100755 index 000000000..626860d9c --- /dev/null +++ b/yosys/techlibs/achronix/synth_achronix.cc @@ -0,0 +1,188 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 SynthAchronixPass : public ScriptPass { + SynthAchronixPass() : ScriptPass("synth_achronix", "synthesis for Acrhonix Speedster22i FPGAs.") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_achronix [options]\n"); + log("\n"); + log("This command runs synthesis for Achronix Speedster eFPGAs. This work is still experimental.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -vout \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 :\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, family_opt, vout_file; + bool retime, flatten; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + vout_file = ""; + retime = false; + flatten = true; + } + + void execute(std::vector 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] == "-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 command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_ACHRONIX pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -sv -lib +/achronix/speedster22i/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-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 (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine -full"); + run("memory_map"); + run("opt -undriven -fine"); + run("dffsr2dff"); + run("dff2dffe -direct-match $_DFF_*"); + run("opt -fine"); + run("techmap -map +/techmap.v"); + run("opt -full"); + run("clean -purge"); + run("setundef -undriven -zero"); + if (retime || help_mode) + run("abc -markgroups -dff", "(only if -retime)"); + } + + if (check_label("map_luts")) + { + run("abc -lut 4" + string(retime ? " -dff" : "")); + run("clean"); + } + + if (check_label("map_cells")) + { + run("iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I"); + run("techmap -map +/achronix/speedster22i/cells_map.v"); + // VT: not done yet run("dffinit -highlow -ff DFF q power_up"); + 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 -nodec -attr2comment -defparam -renameprefix syn_ %s", + help_mode ? "" : vout_file.c_str())); + } + } +} SynthAchronixPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/anlogic/Makefile.inc b/yosys/techlibs/anlogic/Makefile.inc new file mode 100644 index 000000000..67cf9cf10 --- /dev/null +++ b/yosys/techlibs/anlogic/Makefile.inc @@ -0,0 +1,12 @@ + +OBJS += techlibs/anlogic/synth_anlogic.o +OBJS += techlibs/anlogic/anlogic_eqn.o +OBJS += techlibs/anlogic/anlogic_determine_init.o + +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_map.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/arith_map.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/cells_sim.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/eagle_bb.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams.txt)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/drams_map.v)) +$(eval $(call add_share_file,share/anlogic,techlibs/anlogic/dram_init_16x4.vh)) diff --git a/yosys/techlibs/anlogic/anlogic_determine_init.cc b/yosys/techlibs/anlogic/anlogic_determine_init.cc new file mode 100644 index 000000000..34b1d4f8a --- /dev/null +++ b/yosys/techlibs/anlogic/anlogic_determine_init.cc @@ -0,0 +1,72 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 Icenowy Zheng + * + * 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 AnlogicDetermineInitPass : public Pass { + AnlogicDetermineInitPass() : Pass("anlogic_determine_init", "Anlogic: Determine the init value of cells") { } + void help() YS_OVERRIDE + { + log("\n"); + log(" anlogic_determine_init [selection]\n"); + log("\n"); + log("Determine the init value of cells that doesn't allow unknown init value.\n"); + log("\n"); + } + + Const determine_init(Const init) + { + for (int i = 0; i < GetSize(init); i++) { + if (init[i] != State::S0 && init[i] != State::S1) + init[i] = State::S0; + } + + return init; + } + + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ANLOGIC_DETERMINE_INIT pass (determine init value for cells).\n"); + + extra_args(args, args.size(), design); + + size_t cnt = 0; + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\EG_LOGIC_DRAM16X4") + { + cell->setParam("\\INIT_D0", determine_init(cell->getParam("\\INIT_D0"))); + cell->setParam("\\INIT_D1", determine_init(cell->getParam("\\INIT_D1"))); + cell->setParam("\\INIT_D2", determine_init(cell->getParam("\\INIT_D2"))); + cell->setParam("\\INIT_D3", determine_init(cell->getParam("\\INIT_D3"))); + cnt++; + } + } + } + log_header(design, "Updated %lu cells with determined init value.\n", cnt); + } +} AnlogicDetermineInitPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/anlogic/anlogic_eqn.cc b/yosys/techlibs/anlogic/anlogic_eqn.cc new file mode 100644 index 000000000..c025c65c4 --- /dev/null +++ b/yosys/techlibs/anlogic/anlogic_eqn.cc @@ -0,0 +1,113 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 AnlogicEqnPass : public Pass { + AnlogicEqnPass() : Pass("anlogic_eqn", "Anlogic: Calculate equations for luts") { } + void help() YS_OVERRIDE + { + log("\n"); + log(" anlogic_eqn [selection]\n"); + log("\n"); + log("Calculate equations for luts since bitstream generator depends on it.\n"); + log("\n"); + } + + Const init2eqn(Const init, int inputs) + { + std::string init_bits = init.as_string(); + const char* names[] = { "A" , "B", "C", "D", "E", "F" }; + + std::string eqn; + int width = (int)pow(2,inputs); + for(int i=0;i args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ANLOGIC_EQN pass (calculate equations for luts).\n"); + + extra_args(args, args.size(), design); + + size_t cnt = 0; + for (auto module : design->selected_modules()) + { + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\AL_MAP_LUT1") + { + cell->setParam("\\EQN", init2eqn(cell->getParam("\\INIT"),1)); + cnt++; + } + if (cell->type == "\\AL_MAP_LUT2") + { + cell->setParam("\\EQN", init2eqn(cell->getParam("\\INIT"),2)); + cnt++; + } + if (cell->type == "\\AL_MAP_LUT3") + { + cell->setParam("\\EQN", init2eqn(cell->getParam("\\INIT"),3)); + cnt++; + } + if (cell->type == "\\AL_MAP_LUT4") + { + cell->setParam("\\EQN", init2eqn(cell->getParam("\\INIT"),4)); + cnt++; + } + if (cell->type == "\\AL_MAP_LUT5") + { + cell->setParam("\\EQN", init2eqn(cell->getParam("\\INIT"),5)); + cnt++; + } + if (cell->type == "\\AL_MAP_LUT6") + { + cell->setParam("\\EQN", init2eqn(cell->getParam("\\INIT"),6)); + cnt++; + } + } + } + log_header(design, "Updated %lu of AL_MAP_LUT* elements with equation.\n", cnt); + } +} AnlogicEqnPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/anlogic/arith_map.v b/yosys/techlibs/anlogic/arith_map.v new file mode 100644 index 000000000..11cd140ec --- /dev/null +++ b/yosys/techlibs/anlogic/arith_map.v @@ -0,0 +1,84 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 Miodrag Milanovic + * Copyright (C) 2012 Clifford Wolf + * + * 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_anlogic_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 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] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + wire dummy; + (* keep *) + AL_MAP_ADDER #( + .ALUTYPE("ADD_CARRY")) + adder_cin ( + .a(C[0]), + .o({COx[0], dummy}) + ); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) begin + (* keep *) + AL_MAP_ADDER #( + .ALUTYPE("ADD")) + adder_cout ( + .c(C[Y_WIDTH]), + .o(COx[Y_WIDTH]) + ); + assign CO = COx[Y_WIDTH]; + end + else + begin + (* keep *) + AL_MAP_ADDER #( + .ALUTYPE("ADD") + ) adder_i ( + .a(AA[i]), + .b(BB[i]), + .c(C[i+1]), + .o({COx[i+1],Y[i]}) + ); + end + end: slice + endgenerate + /* End implementation */ + assign X = AA ^ BB; +endmodule \ No newline at end of file diff --git a/yosys/techlibs/anlogic/cells_map.v b/yosys/techlibs/anlogic/cells_map.v new file mode 100644 index 000000000..cfc743a4b --- /dev/null +++ b/yosys/techlibs/anlogic/cells_map.v @@ -0,0 +1,61 @@ +module \$_DFF_N_ (input D, C, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(1'b0)); endmodule +module \$_DFF_P_ (input D, C, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(1'b0)); endmodule + +module \$_DFFE_NN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_NP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_PN_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule +module \$_DFFE_PP_ (input D, C, E, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'bx), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(E), .sr(1'b0)); endmodule + +module \$_DFF_NN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(~C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C) , .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("INV"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PP0_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b0), .SRMUX("SR"), .SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); AL_MAP_SEQ #(.DFFMODE("FF"), .REGSET(1'b1), .SRMUX("SR"), . SRMODE("SYNC")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .ce(1'b1), .sr(R)); endmodule + +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 + +`ifndef NO_LUT +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + AL_MAP_LUT1 #(.EQN(""),.INIT(LUT)) _TECHMAP_REPLACE_ (.o(Y), .a(A[0])); + end else + if (WIDTH == 2) begin + AL_MAP_LUT2 #(.EQN(""),.INIT(LUT)) _TECHMAP_REPLACE_ (.o(Y), .a(A[0]), .b(A[1])); + end else + if (WIDTH == 3) begin + AL_MAP_LUT3 #(.EQN(""),.INIT(LUT)) _TECHMAP_REPLACE_ (.o(Y), .a(A[0]), .b(A[1]), .c(A[2])); + end else + if (WIDTH == 4) begin + AL_MAP_LUT4 #(.EQN(""),.INIT(LUT)) _TECHMAP_REPLACE_ (.o(Y), .a(A[0]), .b(A[1]), .c(A[2]), .d(A[3])); + end else + if (WIDTH == 5) begin + AL_MAP_LUT5 #(.EQN(""),.INIT(LUT)) _TECHMAP_REPLACE_ (.o(Y), .a(A[0]), .b(A[1]), .c(A[2]), .d(A[3]), .e(A[4])); + end else + if (WIDTH == 6) begin + AL_MAP_LUT6 #(.EQN(""),.INIT(LUT)) _TECHMAP_REPLACE_ (.o(Y), .a(A[0]), .b(A[1]), .c(A[2]), .d(A[3]), .e(A[4]), .f(A[5])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule +`endif diff --git a/yosys/techlibs/anlogic/cells_sim.v b/yosys/techlibs/anlogic/cells_sim.v new file mode 100644 index 000000000..60a367928 --- /dev/null +++ b/yosys/techlibs/anlogic/cells_sim.v @@ -0,0 +1,103 @@ +module AL_MAP_SEQ ( + output q, + input ce, + input clk, + input sr, + input d +); + parameter DFFMODE = "FF"; //FF,LATCH + parameter REGSET = "RESET"; //RESET/SET + parameter SRMUX = "SR"; //SR/INV + parameter SRMODE = "SYNC"; //SYNC/ASYNC +endmodule + +module AL_MAP_LUT1 ( + output o, + input a +); + parameter [1:0] INIT = 2'h0; + parameter EQN = "(A)"; + assign Y = INIT >> A; +endmodule + +module AL_MAP_LUT2 ( + output o, + input a, + input b +); + parameter [3:0] INIT = 4'h0; + parameter EQN = "(A)"; + assign o = INIT >> {b, a}; +endmodule + +module AL_MAP_LUT3 ( + output o, + input a, + input b, + input c +); + parameter [7:0] INIT = 8'h0; + parameter EQN = "(A)"; + assign o = INIT >> {c, b, a}; +endmodule + +module AL_MAP_LUT4 ( + output o, + input a, + input b, + input c, + input d +); + parameter [15:0] INIT = 16'h0; + parameter EQN = "(A)"; + assign o = INIT >> {d, c, b, a}; +endmodule + +module AL_MAP_LUT5 ( + output o, + input a, + input b, + input c, + input d, + input e +); + parameter [31:0] INIT = 32'h0; + parameter EQN = "(A)"; + assign o = INIT >> {e, d, c, b, a}; +endmodule + + +module AL_MAP_LUT6 ( + output o, + input a, + input b, + input c, + input d, + input e, + input f +); + parameter [63:0] INIT = 64'h0; + parameter EQN = "(A)"; + assign o = INIT >> {f, e, d, c, b, a}; +endmodule + +module AL_MAP_ALU2B ( + input cin, + input a0, b0, c0, d0, + input a1, b1, c1, d1, + output s0, s1, cout +); + parameter [15:0] INIT0 = 16'h0000; + parameter [15:0] INIT1 = 16'h0000; + parameter FUNC0 = "NO"; + parameter FUNC1 = "NO"; +endmodule + +module AL_MAP_ADDER ( + input a, + input b, + input c, + output [1:0] o +); + parameter ALUTYPE = "ADD"; +endmodule diff --git a/yosys/techlibs/anlogic/dram_init_16x4.vh b/yosys/techlibs/anlogic/dram_init_16x4.vh new file mode 100644 index 000000000..32fb1578c --- /dev/null +++ b/yosys/techlibs/anlogic/dram_init_16x4.vh @@ -0,0 +1,16 @@ +.INIT_D0({INIT[15*4+0], INIT[14*4+0], INIT[13*4+0], INIT[12*4+0], + INIT[11*4+0], INIT[10*4+0], INIT[9*4+0], INIT[8*4+0], + INIT[7*4+0], INIT[6*4+0], INIT[5*4+0], INIT[4*4+0], + INIT[3*4+0], INIT[2*4+0], INIT[1*4+0], INIT[0*4+0]}), +.INIT_D1({INIT[15*4+1], INIT[14*4+1], INIT[13*4+1], INIT[12*4+1], + INIT[11*4+1], INIT[10*4+1], INIT[9*4+1], INIT[8*4+1], + INIT[7*4+1], INIT[6*4+1], INIT[5*4+1], INIT[4*4+1], + INIT[3*4+1], INIT[2*4+1], INIT[1*4+1], INIT[0*4+1]}), +.INIT_D2({INIT[15*4+2], INIT[14*4+2], INIT[13*4+2], INIT[12*4+2], + INIT[11*4+2], INIT[10*4+2], INIT[9*4+2], INIT[8*4+2], + INIT[7*4+2], INIT[6*4+2], INIT[5*4+2], INIT[4*4+2], + INIT[3*4+2], INIT[2*4+2], INIT[1*4+2], INIT[0*4+2]}), +.INIT_D3({INIT[15*4+3], INIT[14*4+3], INIT[13*4+3], INIT[12*4+3], + INIT[11*4+3], INIT[10*4+3], INIT[9*4+3], INIT[8*4+3], + INIT[7*4+3], INIT[6*4+3], INIT[5*4+3], INIT[4*4+3], + INIT[3*4+3], INIT[2*4+3], INIT[1*4+3], INIT[0*4+3]}) diff --git a/yosys/techlibs/anlogic/drams.txt b/yosys/techlibs/anlogic/drams.txt new file mode 100644 index 000000000..4e903c0a2 --- /dev/null +++ b/yosys/techlibs/anlogic/drams.txt @@ -0,0 +1,16 @@ +bram $__ANLOGIC_DRAM16X4 + init 1 + abits 4 + dbits 4 + groups 2 + ports 1 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 0 1 + clkpol 0 1 +endbram + +match $__ANLOGIC_DRAM16X4 + make_outreg +endmatch diff --git a/yosys/techlibs/anlogic/drams_map.v b/yosys/techlibs/anlogic/drams_map.v new file mode 100644 index 000000000..084e2a25f --- /dev/null +++ b/yosys/techlibs/anlogic/drams_map.v @@ -0,0 +1,22 @@ +module \$__ANLOGIC_DRAM16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); + parameter [63:0]INIT = 64'bx; + input CLK1; + + input [3:0] A1ADDR; + output [3:0] A1DATA; + + input [3:0] B1ADDR; + input [3:0] B1DATA; + input B1EN; + + EG_LOGIC_DRAM16X4 #( + `include "dram_init_16x4.vh" + ) _TECHMAP_REPLACE_ ( + .di(B1DATA), + .waddr(B1ADDR), + .wclk(CLK1), + .we(B1EN), + .raddr(A1ADDR), + .do(A1DATA) + ); +endmodule diff --git a/yosys/techlibs/anlogic/eagle_bb.v b/yosys/techlibs/anlogic/eagle_bb.v new file mode 100644 index 000000000..7cbec331a --- /dev/null +++ b/yosys/techlibs/anlogic/eagle_bb.v @@ -0,0 +1,1028 @@ +// Anlogic Eagle - Blackbox cells +// FIXME: Create sim models + +(* blackbox *) +module EG_LOGIC_BUF( + output o, + input i +); +endmodule + +(* blackbox *) +module EG_LOGIC_BUFG( + output o, + input i +); +endmodule + +(* blackbox *) +module EG_LOGIC_BUFIO( + input clki, + input rst, + input coe, + output clko, + output clkdiv1, + output clkdivx +); + parameter GSR = "DISABLE"; + parameter DIV = 2; + parameter STOPCLK = "DISABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_BUFGMUX( + output o, + input i0, + input i1, + input s +); + parameter INIT_OUT = "0"; + parameter PRESELECT_I0 = "TRUE"; + parameter PRESELECT_I1 = "FALSE"; +endmodule + +(* blackbox *) +module EG_LOGIC_MBOOT( + input rebootn, + input [7:0] dynamic_addr +); + parameter ADDR_SOURCE_SEL = "STATIC"; + parameter STATIC_ADDR = 8'b00000000; +endmodule + +(* blackbox *) +module EG_LOGIC_DNA( + output dout, + input clk, + input din, + input shift_en +); +endmodule + +(* blackbox *) +module EG_LOGIC_GCTRL( + output done, + output highz +); +endmodule + +(* blackbox *) +module EG_LOGIC_GSRN( + input gsrn, + input sync_clk +); + parameter GSRN_SYNC_SEL = "DISABLE"; + parameter USR_GSRN_EN = "DISABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_CCLK( + output cclk, + input en +); + parameter FREQ = "4.5"; +endmodule + +(* blackbox *) +module EG_LOGIC_IDELAY( + output o, + input i +); + parameter INDEL = 0; +endmodule + +(* blackbox *) +module EG_LOGIC_IDDR( + output q1, + output q0, + input clk, + input d, + input rst +); + parameter ASYNCRST = "ENABLE"; + parameter PIPEMODE = "PIPED"; +endmodule + +(* blackbox *) +module EG_LOGIC_ODDR( + output q, + input clk, + input d1, + input d0, + input rst +); + parameter ASYNCRST = "ENABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_IDDRx2( + output q3, + output q2, + output q1, + output q0, + input pclk, + input sclk, + input d, + input rst +); + parameter ASYNCRST = "ENABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_ODELAY( + output o, + input i +); + parameter OUTDEL = 0; +endmodule + +(* blackbox *) +module EG_LOGIC_ODDRx2( + output q, + input pclk, + input sclk, + input d3, + input d2, + input d1, + input d0, + input rst +); + parameter ASYNCRST = "ENABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_ODDRx2l( + output q, + input sclk, + input d3, + input d2, + input d1, + input d0, + input rst +); + parameter ASYNCRST = "ENABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_FIFO( + input rst, + input [DATA_WIDTH_W-1:0] di, + output [DATA_WIDTH_R-1:0] do, + input clkw, + input we, + input clkr, + input re, + input ore, + input [2:0] csw, + input [2:0] csr, + output empty_flag, + output aempty_flag, + output full_flag, + output afull_flag +); + parameter DATA_WIDTH_W = 9; + parameter DATA_WIDTH_R = DATA_WIDTH_W; + parameter DATA_DEPTH_W = 1024; + parameter DATA_DEPTH_R = DATA_WIDTH_W * DATA_DEPTH_W / DATA_WIDTH_R; + parameter MODE = "FIFO8K"; + parameter REGMODE_W = "NOREG"; + parameter REGMODE_R = "NOREG"; + parameter E = 0; + parameter AE = 6; + parameter AF = DATA_DEPTH_W - 6; + parameter F = DATA_DEPTH_W; + parameter GSR = "DISABLE"; + parameter RESETMODE = "ASYNC"; + parameter ASYNC_RESET_RELEASE = "SYNC"; + parameter ENDIAN = "LITTLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_DRAM( + input [DATA_WIDTH_W-1:0] di, + input [ADDR_WIDTH_W-1:0] waddr, + input wclk, + input we, + output [DATA_WIDTH_R-1:0] do, + input [ADDR_WIDTH_R-1:0] raddr +); + parameter DATA_WIDTH_W = 9; + parameter ADDR_WIDTH_W = 10; + parameter DATA_DEPTH_W = 2 ** ADDR_WIDTH_W; + parameter DATA_WIDTH_R = 9; + parameter ADDR_WIDTH_R = 10; + parameter DATA_DEPTH_R = 2 ** ADDR_WIDTH_R; + parameter INIT_FILE = "NONE"; +endmodule + +(* blackbox *) +module EG_LOGIC_DRAM16X4( + input [3:0] di, + input [3:0] waddr, + input wclk, + input we, + input [3:0]raddr, + output [3:0]do +); + parameter INIT_D0=16'h0000; + parameter INIT_D1=16'h0000; + parameter INIT_D2=16'h0000; + parameter INIT_D3=16'h0000; +endmodule + +(* blackbox *) +module EG_LOGIC_MULT( + output [OUTPUT_WIDTH-1:0] p, + input [INPUT_WIDTH_A-1:0] a, + input [INPUT_WIDTH_B-1:0] b, + input cea, + input ceb, + input cepd, + input clk, + input rstan, + input rstbn, + input rstpdn +); + parameter INPUT_WIDTH_A = 18; + parameter INPUT_WIDTH_B = 18; + parameter OUTPUT_WIDTH = 36; + parameter INPUTFORMAT = "SIGNED"; + parameter INPUTREGA = "ENABLE"; + parameter INPUTREGB = "ENABLE"; + parameter OUTPUTREG = "ENABLE"; + parameter SRMODE = "ASYNC"; + parameter IMPLEMENT = "AUTO"; +endmodule + +(* blackbox *) +module EG_LOGIC_SEQ_DIV( + input clk, + input rst, + input start, + input [NUMER_WIDTH-1:0] numer, + input [DENOM_WIDTH-1:0] denom, + output [NUMER_WIDTH-1:0] quotient, + output [DENOM_WIDTH-1:0] remain, + output done +); + parameter NUMER_WIDTH = 16; + parameter DENOM_WIDTH = 16; +endmodule + +(* blackbox *) +module EG_PHY_BRAM( + output [8:0] doa, + output [8:0] dob, + input [8:0] dia, + input [8:0] dib, + input [2:0] csa, + input [2:0] csb, + input cea, + input ocea, + input clka, + input wea, + input rsta, + input ceb, + input oceb, + input clkb, + input web, + input rstb, + input [12:0] addra, + input [12:0] addrb +); + parameter MODE = "DP8K"; + parameter DATA_WIDTH_A = "9"; + parameter DATA_WIDTH_B = "9"; + parameter READBACK = "OFF"; + parameter REGMODE_A = "NOREG"; + parameter REGMODE_B = "NOREG"; + parameter WRITEMODE_A = "NORMAL"; + parameter WRITEMODE_B = "NORMAL"; + parameter GSR = "ENABLE"; + parameter RESETMODE = "SYNC"; + parameter ASYNC_RESET_RELEASE = "SYNC"; + parameter CEAMUX = "SIG"; + parameter CEBMUX = "SIG"; + parameter OCEAMUX = "SIG"; + parameter OCEBMUX = "SIG"; + parameter RSTAMUX = "SIG"; + parameter RSTBMUX = "SIG"; + parameter CLKAMUX = "SIG"; + parameter CLKBMUX = "SIG"; + parameter WEAMUX = "SIG"; + parameter WEBMUX = "SIG"; + parameter CSA0 = "SIG" ; + parameter CSA1 = "SIG" ; + parameter CSA2 = "SIG" ; + parameter CSB0 = "SIG" ; + parameter CSB1 = "SIG" ; + parameter CSB2 = "SIG" ; + parameter INIT_FILE = "NONE"; + parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_02 = 256'h0000000000000000000000000000000000000000000000000000000000000000; + parameter INITP_03 = 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; +endmodule + +(* blackbox *) +module EG_PHY_BRAM32K( + output [15:0] doa, + output [15:0] dob, + input [15:0] dia, + input [15:0] dib, + input [10:0] addra, + input [10:0] addrb, + input bytea, + input bytewea, + input byteb, + input byteweb, + input csa, + input wea, + input csb, + input web, + input clka, + input rsta, + input clkb, + input rstb, + input ocea, + input oceb +); + parameter MODE = "DP16K"; + parameter DATA_WIDTH_A = "16"; + parameter DATA_WIDTH_B = "16"; + parameter REGMODE_A = "NOREG"; + parameter REGMODE_B = "NOREG"; + parameter WRITEMODE_A = "NORMAL"; + parameter WRITEMODE_B = "NORMAL"; + parameter SRMODE = "SYNC"; + parameter CSAMUX = "SIG"; + parameter CSBMUX = "SIG"; + parameter OCEAMUX = "SIG"; + parameter OCEBMUX = "SIG"; + parameter RSTAMUX = "SIG"; + parameter RSTBMUX = "SIG"; + parameter CLKAMUX = "SIG"; + parameter CLKBMUX = "SIG"; + parameter WEAMUX = "SIG"; + parameter WEBMUX = "SIG"; + parameter READBACK = "OFF"; + parameter INIT_FILE = ""; + 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; +endmodule + +(* blackbox *) +module EG_PHY_FIFO( + input [8:0] dia, + input [8:0] dib, + input [2:0] csr, + input [2:0] csw, + input we, + input re, + input clkw, + input clkr, + input rst, + input rprst, + input orea, + input oreb, + output [8:0] dob, + output [8:0] doa, + output empty_flag, + output aempty_flag, + output afull_flag, + output full_flag +); + parameter MODE = "FIFO8K"; + parameter DATA_WIDTH_A = "18"; + parameter DATA_WIDTH_B = "18"; + parameter READBACK = "OFF"; + parameter REGMODE_A = "NOREG"; + parameter REGMODE_B = "NOREG"; + parameter [13:0] AE = 14'b00000001100000; + parameter [13:0] AF = 14'b01111110010000; + parameter [13:0] F = 14'b01111111110000; + parameter [13:0] AEP1 = 14'b00000001110000; + parameter [13:0] AFM1 = 14'b01111110000000; + parameter [13:0] FM1 = 14'b01111111100000; + parameter [4:0] E = 5'b00000; + parameter [5:0] EP1 = 6'b010000; + parameter GSR = "ENABLE"; + parameter RESETMODE = "ASYNC"; + parameter ASYNC_RESET_RELEASE = "SYNC"; + parameter CEA = "SIG"; + parameter CEB = "SIG"; + parameter OCEA = "SIG"; + parameter OCEB = "SIG"; + parameter RSTA = "SIG"; + parameter RSTB = "SIG"; + parameter CLKA = "SIG"; + parameter CLKB = "SIG"; + parameter WEA = "SIG"; + parameter WEB = "SIG"; + parameter CSA0 = "SIG"; + parameter CSA1 = "SIG"; + parameter CSA2 = "SIG"; + parameter CSB0 = "SIG"; + parameter CSB1 = "SIG"; + parameter CSB2 = "SIG"; +endmodule + +(* blackbox *) +module EG_PHY_MULT18( + output [17:0] acout, + output [17:0] bcout, + output [35:0] p, + input signeda, + input signedb, + input [17:0] a, + input [17:0] b, + input [17:0] acin, + input [17:0] bcin, + input cea, + input ceb, + input cepd, + input clk, + input rstan, + input rstbn, + input rstpdn, + input sourcea, + input sourceb +); + parameter INPUTREGA = "ENABLE"; + parameter INPUTREGB = "ENABLE"; + parameter OUTPUTREG = "ENABLE"; + parameter SRMODE = "ASYNC"; + parameter MODE = "MULT18X18C"; + parameter CEAMUX = "SIG"; + parameter CEBMUX = "SIG"; + parameter CEPDMUX = "SIG"; + parameter RSTANMUX = "SIG"; + parameter RSTBNMUX = "SIG"; + parameter RSTPDNMUX = "SIG"; + parameter CLKMUX = "SIG"; + parameter SIGNEDAMUX = "SIG"; + parameter SIGNEDBMUX = "SIG"; + parameter SOURCEAMUX = "SIG"; + parameter SOURCEBMUX = "SIG"; +endmodule + +(* blackbox *) +module EG_PHY_GCLK( + input clki, + output clko +); +endmodule + +(* blackbox *) +module EG_PHY_IOCLK( + input clki, + input stop, + output clko +); + parameter STOPCLK = "DISABLE"; +endmodule + +(* blackbox *) +module EG_PHY_CLKDIV( + output clkdiv1, + output clkdivx, + input clki, + input rst, + input rls +); + parameter GSR = "DISABLE"; + parameter DIV = 2; +endmodule + +(* blackbox *) +module EG_PHY_CONFIG( + output jrstn, + output [1:0] jrti, + output jshift, + output jtck, + output jtdi, + output jupdate, + output [1:0] jscanen, + output jtms, + input [1:0] jtdo, + input [7:0] jtag8_ipa, + input [7:0] jtag8_ipb, + output done, + output highz, + output cclk, + input cclk_en, + input gsrn_sync_clk, + input usr_gsrn, + output dna_dout, + input dna_clk, + input dna_din, + input dna_shift_en, + input mboot_rebootn, + input [7:0] mboot_dynamic_addr +); + parameter MBOOT_AUTO_SEL = "DISABLE"; + parameter ADDR_SOURCE_SEL = "STATIC"; + parameter STATIC_ADDR = 8'b0; + parameter DONE_PERSISTN = "ENABLE"; + parameter INIT_PERSISTN = "ENABLE"; + parameter PROGRAMN_PERSISTN = "DISABLE"; + parameter JTAG_PERSISTN = "DISABLE"; + parameter GSRN_SYNC_SEL = "DISABLE"; + parameter FREQ = "2.5"; + parameter USR_GSRN_EN = "DISABLE"; +endmodule + +(* blackbox *) +module EG_PHY_OSC( + input osc_dis, + output osc_clk +); + parameter STDBY = "DISABLE"; +endmodule + +(* blackbox *) +module EG_PHY_PWRMNT( + output pwr_dwn_n, + input sel_pwr, + input pwr_mnt_pd +); + parameter MNT_LVL = 0; +endmodule + +(* blackbox *) +module EG_PHY_DDR_8M_16( + input clk, + input clk_n, + input ras_n, + input cas_n, + input we_n, + input cs_n, + input [11:0] addr, + input [1:0] ba, + inout [15:0] dq, + input ldqs, + input udqs, + input ldm, + input udm, + input cke +); +endmodule + +(* blackbox *) +module EG_PHY_SDRAM_2M_32( + input clk, + input ras_n, + input cas_n, + input we_n, + input [10:0] addr, + input [1:0] ba, + inout [31:0] dq, + input cs_n, + input dm0, + input dm1, + input dm2, + input dm3, + input cke +); +endmodule + +(* blackbox *) +module EG_PHY_PAD( + input ipad, + output opad, + inout bpad, + input rst, + input ce, + input isclk, + input ipclk, + input osclk, + input opclk, + input ts, + input [3:0] do, + output di, + output [3:0] diq +); + parameter DEDCLK = "DISABLE"; + parameter GSR = "ENABLE"; + parameter SRMODE = "SYNC"; + parameter TSMUX = "1"; + parameter INSCLKMUX = "0"; + parameter INPCLKMUX = "CLK"; + parameter INCEMUX = "CE"; + parameter INRSTMUX = "0"; + parameter IN_REGSET = "RESET"; + parameter IN_DFFMODE = "NONE"; + parameter IDDRMODE = "OFF"; + parameter IDDRPIPEMODE = "NONE"; + parameter INDELMUX = "NODEL"; + parameter INDEL = 0; + parameter OUTSCLKMUX = "0"; + parameter OUTPCLKMUX = "CLK"; + parameter OUTCEMUX = "CE"; + parameter OUTRSTMUX = "0"; + parameter DO_REGSET = "RESET"; + parameter DO_DFFMODE = "NONE"; + parameter ODDRMODE = "OFF"; + parameter OUTDELMUX = "NODEL"; + parameter OUTDEL = 0; + parameter TO_REGSET = "RESET"; + parameter TO_DFFMODE = "NONE"; + parameter MODE = "IN"; + parameter DRIVE = "NONE"; + parameter IOTYPE = "LVCMOS25"; +endmodule + +(* blackbox *) +module EG_PHY_MSLICE( + input [1:0] a, + input [1:0] b, + input [1:0] c, + input [1:0] d, + input [1:0] mi, + input clk, + input ce, + input sr, + input fci, + output [1:0] f, + output [1:0] fx, + output [1:0] q, + output fco, + input dpram_mode, + input [1:0] dpram_di, + input dpram_we, + input dpram_wclk, + input [3:0] dpram_waddr +); + parameter INIT_LUT0 = 16'h0000; + parameter INIT_LUT1 = 16'h0000; + parameter MODE = "LOGIC"; + parameter ALUTYPE = "ADD"; + parameter MSFXMUX = "OFF"; + parameter GSR = "ENABLE"; + parameter TESTMODE = "OFF"; + parameter CEMUX = "CE"; + parameter SRMUX = "SR"; + parameter CLKMUX = "CLK"; + parameter SRMODE = "ASYNC"; + parameter DFFMODE = "FF"; + parameter REG0_SD = "MI"; + parameter REG1_SD = "MI"; + parameter REG0_REGSET = "SET"; + parameter REG1_REGSET = "SET"; +endmodule + +(* blackbox *) +module EG_PHY_LSLICE( + input [1:0] a, + input [1:0] b, + input [1:0] c, + input [1:0] d, + input [1:0] e, + input [1:0] mi, + input clk, + input ce, + input sr, + input fci, + output [1:0] f, + output [1:0] fx, + output [1:0] q, + output fco, + output [3:0] dpram_di, + output [3:0] dpram_waddr, + output dpram_wclk, + output dpram_we, + output dpram_mode +); + parameter INIT_LUTF0 = 16'h0000; + parameter INIT_LUTG0 = 16'h0000; + parameter INIT_LUTF1 = 16'h0000; + parameter INIT_LUTG1 = 16'h0000; + parameter MODE = "LOGIC"; + parameter GSR = "ENABLE"; + parameter TESTMODE = "OFF"; + parameter CEMUX = "1"; + parameter SRMUX = "SR"; + parameter CLKMUX = "CLK"; + parameter SRMODE = "ASYNC"; + parameter DFFMODE = "FF"; + parameter REG0_SD = "MI"; + parameter REG1_SD = "MI"; + parameter REG0_REGSET = "SET"; + parameter REG1_REGSET = "SET"; + parameter DEMUX0 = "D"; + parameter DEMUX1 = "D"; + parameter CMIMUX0 = "C"; + parameter CMIMUX1 = "C"; + parameter LSFMUX0 = "LUTF"; + parameter LSFXMUX0 = "LUTG"; + parameter LSFMUX1 = "LUTF"; + parameter LSFXMUX1 = "LUTG"; +endmodule + +(* blackbox *) +module EG_PHY_PLL( + output [4:0] clkc, + output extlock, + input stdby, + input refclk, + input fbclk, + input reset, + output psdone, + input psclk, + input psdown, + input psstep, + input [2:0] psclksel, + output [7:0] do, + input dclk, + input dcs, + input dwe, + input [7:0] di, + input [5:0] daddr +); + parameter DYNCFG = "DISABLE"; + parameter IF_ESCLKSTSW = "DISABLE"; + parameter REFCLK_SEL = "INTERNAL"; + parameter FIN = "100.0000"; + parameter REFCLK_DIV = 1; + parameter FBCLK_DIV = 1; + parameter CLKC0_DIV = 1; + parameter CLKC1_DIV = 1; + parameter CLKC2_DIV = 1; + parameter CLKC3_DIV = 1; + parameter CLKC4_DIV = 1; + parameter CLKC0_ENABLE = "DISABLE"; + parameter CLKC1_ENABLE = "DISABLE"; + parameter CLKC2_ENABLE = "DISABLE"; + parameter CLKC3_ENABLE = "DISABLE"; + parameter CLKC4_ENABLE = "DISABLE"; + parameter CLKC0_DIV2_ENABLE = "DISABLE"; + parameter CLKC1_DIV2_ENABLE = "DISABLE"; + parameter CLKC2_DIV2_ENABLE = "DISABLE"; + parameter CLKC3_DIV2_ENABLE = "DISABLE"; + parameter CLKC4_DIV2_ENABLE = "DISABLE"; + parameter FEEDBK_MODE = "NORMAL"; + parameter FEEDBK_PATH = "VCO_PHASE_0"; + parameter STDBY_ENABLE = "ENABLE"; + parameter CLKC0_FPHASE = 0; + parameter CLKC1_FPHASE = 0; + parameter CLKC2_FPHASE = 0; + parameter CLKC3_FPHASE = 0; + parameter CLKC4_FPHASE = 0; + parameter CLKC0_CPHASE = 1; + parameter CLKC1_CPHASE = 1; + parameter CLKC2_CPHASE = 1; + parameter CLKC3_CPHASE = 1; + parameter CLKC4_CPHASE = 1; + parameter GMC_GAIN = 7; + parameter GMC_TEST = 14; + parameter ICP_CURRENT = 14; + parameter KVCO = 7; + parameter LPF_CAPACITOR = 3; + parameter LPF_RESISTOR = 1; + parameter PLLRST_ENA = "ENABLE"; + parameter PLLMRST_ENA = "DISABLE"; + parameter PLLC2RST_ENA = "DISABLE"; + parameter PLLC34RST_ENA = "DISABLE"; + parameter PREDIV_MUXC0 = "VCO"; + parameter PREDIV_MUXC1 = "VCO"; + parameter PREDIV_MUXC2 = "VCO"; + parameter PREDIV_MUXC3 = "VCO"; + parameter PREDIV_MUXC4 = "VCO"; + parameter ODIV_MUXC0 = "DIV"; + parameter ODIV_MUXC1 = "DIV"; + parameter ODIV_MUXC2 = "DIV"; + parameter ODIV_MUXC3 = "DIV"; + parameter ODIV_MUXC4 = "DIV"; + parameter FREQ_LOCK_ACCURACY = 2; + parameter PLL_LOCK_MODE = 0; + parameter INTFB_WAKE = "DISABLE"; + parameter DPHASE_SOURCE = "DISABLE"; + parameter VCO_NORESET = "DISABLE"; + parameter STDBY_VCO_ENA = "DISABLE"; + parameter NORESET = "DISABLE"; + parameter SYNC_ENABLE = "ENABLE"; + parameter DERIVE_PLL_CLOCKS = "DISABLE"; + parameter GEN_BASIC_CLOCK = "DISABLE"; +endmodule + +(* blackbox *) +module EG_LOGIC_BRAM( + output [DATA_WIDTH_A-1:0] doa, + output [DATA_WIDTH_B-1:0] dob, + input [DATA_WIDTH_A-1:0] dia, + input [DATA_WIDTH_B-1:0] dib, + input cea, + input ocea, + input clka, + input wea, + input rsta, + input ceb, + input oceb, + input clkb, + input web, + input rstb, + input [BYTE_A - 1 : 0] bea, + input [BYTE_B - 1 : 0] beb, + input [ADDR_WIDTH_A-1:0] addra, + input [ADDR_WIDTH_B-1:0] addrb +); + parameter DATA_WIDTH_A = 9; + parameter DATA_WIDTH_B = DATA_WIDTH_A; + parameter ADDR_WIDTH_A = 10; + parameter ADDR_WIDTH_B = ADDR_WIDTH_A; + parameter DATA_DEPTH_A = 2 ** ADDR_WIDTH_A; + parameter DATA_DEPTH_B = 2 ** ADDR_WIDTH_B; + parameter BYTE_ENABLE = 0; + parameter BYTE_A = BYTE_ENABLE == 0 ? 1 : DATA_WIDTH_A / BYTE_ENABLE; + parameter BYTE_B = BYTE_ENABLE == 0 ? 1 : DATA_WIDTH_B / BYTE_ENABLE; + parameter MODE = "DP"; + parameter REGMODE_A = "NOREG"; + parameter REGMODE_B = "NOREG"; + parameter WRITEMODE_A = "NORMAL"; + parameter WRITEMODE_B = "NORMAL"; + parameter RESETMODE = "SYNC"; + parameter DEBUGGABLE = "NO"; + parameter PACKABLE = "NO"; + parameter FORCE_KEEP = "OFF"; + parameter INIT_FILE = "NONE"; + parameter FILL_ALL = "NONE"; + parameter IMPLEMENT = "9K"; +endmodule + +(* blackbox *) +module EG_PHY_ADC( + input clk, + input pd, + input [2:0] s, + input soc, + output eoc, + output [11:0] dout +); + parameter CH0 = "DISABLE"; + parameter CH1 = "DISABLE"; + parameter CH2 = "DISABLE"; + parameter CH3 = "DISABLE"; + parameter CH4 = "DISABLE"; + parameter CH5 = "DISABLE"; + parameter CH6 = "DISABLE"; + parameter CH7 = "DISABLE"; + parameter VREF = "DISABLE"; +endmodule diff --git a/yosys/techlibs/anlogic/synth_anlogic.cc b/yosys/techlibs/anlogic/synth_anlogic.cc new file mode 100644 index 000000000..620bf3965 --- /dev/null +++ b/yosys/techlibs/anlogic/synth_anlogic.cc @@ -0,0 +1,213 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2018 Miodrag Milanovic + * Copyright (C) 2018 Clifford Wolf + * + * 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 SynthAnlogicPass : public ScriptPass +{ + SynthAnlogicPass() : ScriptPass("synth_anlogic", "synthesis for Anlogic FPGAs") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_anlogic [options]\n"); + log("\n"); + log("This command runs synthesis for Anlogic FPGAs.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module\n"); + log("\n"); + log(" -edif \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(" -json \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 :\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, edif_file, json_file; + bool flatten, retime; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + edif_file = ""; + json_file = ""; + flatten = true; + retime = false; + } + + void execute(std::vector 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] == "-edif" && argidx+1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-json" && argidx+1 < args.size()) { + json_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] == "-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 command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_ANLOGIC pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/anlogic/cells_sim.v +/anlogic/eagle_bb.v"); + run(stringf("hierarchy -check %s", help_mode ? "-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 (check_label("dram")) + { + run("memory_bram -rules +/anlogic/drams.txt"); + run("techmap -map +/anlogic/drams_map.v"); + run("anlogic_determine_init"); + } + + if (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine"); + run("memory_map"); + run("opt -undriven -fine"); + run("techmap -map +/techmap.v -map +/anlogic/arith_map.v"); + if (retime || help_mode) + run("abc -dff", "(only if -retime)"); + } + + if (check_label("map_ffs")) + { + run("dffsr2dff"); + run("techmap -D NO_LUT -map +/anlogic/cells_map.v"); + run("dffinit -strinit SET RESET -ff AL_MAP_SEQ q REGSET -noreinit"); + run("opt_expr -mux_undef"); + run("simplemap"); + } + + if (check_label("map_luts")) + { + run("abc -lut 4:6"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("techmap -map +/anlogic/cells_map.v"); + run("clean"); + run("anlogic_eqn"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("edif")) + { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif %s", help_mode ? "" : edif_file.c_str())); + } + + if (check_label("json")) + { + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "" : json_file.c_str())); + } + } +} SynthAnlogicPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/common/Makefile.inc b/yosys/techlibs/common/Makefile.inc index 236d6c551..0e05620bc 100644 --- a/yosys/techlibs/common/Makefile.inc +++ b/yosys/techlibs/common/Makefile.inc @@ -24,5 +24,7 @@ $(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/dff2ff.v)) +$(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) +$(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cells.lib)) - diff --git a/yosys/techlibs/common/cmp2lut.v b/yosys/techlibs/common/cmp2lut.v new file mode 100644 index 000000000..8aa1eb957 --- /dev/null +++ b/yosys/techlibs/common/cmp2lut.v @@ -0,0 +1,105 @@ +// Certain arithmetic operations between a signal of width n and a constant can be directly mapped +// to a single k-LUT (where n <= k). This is preferable to normal alumacc techmapping process +// because for many targets, arithmetic techmapping creates hard logic (such as carry cells) which often +// cannot be optimized further. +// +// TODO: Currently, only comparisons with 1-bit output are mapped. Potentially, all arithmetic cells +// with n <= k inputs should be techmapped in this way, because this shortens the critical path +// from n to 1 by avoiding carry chains. + +(* techmap_celltype = "$eq $ne $lt $le $gt $ge" *) +module _90_lut_cmp_ (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; + +parameter _TECHMAP_CELLTYPE_ = ""; + +parameter _TECHMAP_CONSTMSK_A_ = 0; +parameter _TECHMAP_CONSTVAL_A_ = 0; +parameter _TECHMAP_CONSTMSK_B_ = 0; +parameter _TECHMAP_CONSTVAL_B_ = 0; + +function automatic integer gen_lut; + input integer width; + input integer operation; + input integer swap; + input integer sign; + input integer operand; + integer n, i_var, i_cst, lhs, rhs, o_bit; + begin + gen_lut = width'b0; + for (n = 0; n < (1 << width); n++) begin + if (sign) + i_var = n[width-1:0]; + else + i_var = n; + i_cst = operand; + if (swap) begin + lhs = i_cst; + rhs = i_var; + end else begin + lhs = i_var; + rhs = i_cst; + end + if (operation == 0) + o_bit = (lhs < rhs); + if (operation == 1) + o_bit = (lhs <= rhs); + if (operation == 2) + o_bit = (lhs > rhs); + if (operation == 3) + o_bit = (lhs >= rhs); + if (operation == 4) + o_bit = (lhs == rhs); + if (operation == 5) + o_bit = (lhs != rhs); + gen_lut = gen_lut | (o_bit << n); + end + end +endfunction + +generate + if (_TECHMAP_CELLTYPE_ == "$lt") + localparam operation = 0; + if (_TECHMAP_CELLTYPE_ == "$le") + localparam operation = 1; + if (_TECHMAP_CELLTYPE_ == "$gt") + localparam operation = 2; + if (_TECHMAP_CELLTYPE_ == "$ge") + localparam operation = 3; + if (_TECHMAP_CELLTYPE_ == "$eq") + localparam operation = 4; + if (_TECHMAP_CELLTYPE_ == "$ne") + localparam operation = 5; + + if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1) + wire _TECHMAP_FAIL_ = 1; + else if (&_TECHMAP_CONSTMSK_B_) + \$lut #( + .WIDTH(A_WIDTH), + .LUT({ gen_lut(A_WIDTH, operation, 0, A_SIGNED && B_SIGNED, _TECHMAP_CONSTVAL_B_) }) + ) _TECHMAP_REPLACE_ ( + .A(A), + .Y(Y) + ); + else if (&_TECHMAP_CONSTMSK_A_) + \$lut #( + .WIDTH(B_WIDTH), + .LUT({ gen_lut(B_WIDTH, operation, 1, A_SIGNED && B_SIGNED, _TECHMAP_CONSTVAL_A_) }) + ) _TECHMAP_REPLACE_ ( + .A(B), + .Y(Y) + ); + else + wire _TECHMAP_FAIL_ = 1; +endgenerate + +endmodule diff --git a/yosys/techlibs/common/dff2ff.v b/yosys/techlibs/common/dff2ff.v new file mode 100644 index 000000000..2dc4d20d3 --- /dev/null +++ b/yosys/techlibs/common/dff2ff.v @@ -0,0 +1,14 @@ +(* techmap_celltype = "$dff" *) +module dff2ff (CLK, D, Q); + parameter WIDTH = 1; + parameter CLK_POLARITY = 1; + + input CLK; + input [WIDTH-1:0] D; + output reg [WIDTH-1:0] Q; + + wire [1023:0] _TECHMAP_DO_ = "proc;;"; + + always @($global_clock) + Q <= D; +endmodule diff --git a/yosys/techlibs/common/gate2lut.v b/yosys/techlibs/common/gate2lut.v new file mode 100644 index 000000000..99c123f4a --- /dev/null +++ b/yosys/techlibs/common/gate2lut.v @@ -0,0 +1,87 @@ +(* techmap_celltype = "$_NOT_" *) +module _90_lut_not (A, Y); + input A; + output Y; + + wire [`LUT_WIDTH-1:0] AA; + assign AA = {A}; + + \$lut #( + .WIDTH(`LUT_WIDTH), + .LUT(4'b01) + ) lut ( + .A(AA), + .Y(Y) + ); +endmodule + +(* techmap_celltype = "$_OR_" *) +module _90_lut_or (A, B, Y); + input A, B; + output Y; + + wire [`LUT_WIDTH-1:0] AA; + assign AA = {B, A}; + + \$lut #( + .WIDTH(`LUT_WIDTH), + .LUT(4'b1110) + ) lut ( + .A(AA), + .Y(Y) + ); +endmodule + +(* techmap_celltype = "$_AND_" *) +module _90_lut_and (A, B, Y); + input A, B; + output Y; + + wire [`LUT_WIDTH-1:0] AA; + assign AA = {B, A}; + + \$lut #( + .WIDTH(`LUT_WIDTH), + .LUT(4'b1000) + ) lut ( + .A(AA), + .Y(Y) + ); +endmodule + +(* techmap_celltype = "$_XOR_" *) +module _90_lut_xor (A, B, Y); + input A, B; + output Y; + + wire [`LUT_WIDTH-1:0] AA; + assign AA = {B, A}; + + \$lut #( + .WIDTH(`LUT_WIDTH), + .LUT(4'b0110) + ) lut ( + .A(AA), + .Y(Y) + ); +endmodule + +(* techmap_celltype = "$_MUX_" *) +module _90_lut_mux (A, B, S, Y); + input A, B, S; + output Y; + + wire [`LUT_WIDTH-1:0] AA; + assign AA = {S, B, A}; + + \$lut #( + .WIDTH(`LUT_WIDTH), + // A 1010 1010 + // B 1100 1100 + // S 1111 0000 + .LUT(8'b_1100_1010) + ) lut ( + .A(AA), + .Y(Y) + ); +endmodule diff --git a/yosys/techlibs/common/prep.cc b/yosys/techlibs/common/prep.cc index 71534983d..86fb4d6c6 100644 --- a/yosys/techlibs/common/prep.cc +++ b/yosys/techlibs/common/prep.cc @@ -29,7 +29,7 @@ struct PrepPass : public ScriptPass { PrepPass() : ScriptPass("prep", "generic synthesis script") { } - virtual void help() YS_OVERRIDE + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -55,15 +55,16 @@ struct PrepPass : public ScriptPass 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(" using the 'memory_memx' pass.\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(" -rdff\n"); + log(" do not pass -nordff to 'memory_dff'. This enables merging of FFs into\n"); + log(" memory read ports.\n"); log("\n"); - log(" -nokeepdc\n"); + log(" -nokeepdc\n"); log(" do not call opt_* with -keepdc\n"); log("\n"); log(" -run [:]\n"); @@ -77,13 +78,12 @@ struct PrepPass : public ScriptPass log("\n"); } - string top_module, fsm_opts, memory_opts; - bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc; + string top_module, fsm_opts; + bool autotop, flatten, ifxmode, memxmode, nomemmode, nokeepdc, nordff; - virtual void clear_flags() YS_OVERRIDE + void clear_flags() YS_OVERRIDE { top_module.clear(); - memory_opts.clear(); autotop = false; flatten = false; @@ -91,9 +91,10 @@ struct PrepPass : public ScriptPass memxmode = false; nomemmode = false; nokeepdc = false; + nordff = true; } - virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { string run_from, run_to; @@ -129,7 +130,6 @@ struct PrepPass : public ScriptPass } if (args[argidx] == "-memx") { memxmode = true; - memory_opts += " -nordff"; continue; } if (args[argidx] == "-nomem") { @@ -137,7 +137,11 @@ struct PrepPass : public ScriptPass continue; } if (args[argidx] == "-nordff") { - memory_opts += " -nordff"; + nordff = true; + continue; + } + if (args[argidx] == "-rdff") { + nordff = false; continue; } if (args[argidx] == "-nokeepdc") { @@ -149,7 +153,7 @@ struct PrepPass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing PREP pass.\n"); log_push(); @@ -159,7 +163,7 @@ struct PrepPass : public ScriptPass log_pop(); } - virtual void script() YS_OVERRIDE + void script() YS_OVERRIDE { if (check_label("begin")) @@ -196,7 +200,7 @@ struct PrepPass : public ScriptPass run(memxmode ? "wreduce -memx" : "wreduce"); } if (!nomemmode) { - run("memory_dff" + (help_mode ? " [-nordff]" : memory_opts)); + run(string("memory_dff") + (help_mode ? " [-nordff]" : nordff ? " -nordff" : "")); if (help_mode || memxmode) run("memory_memx", "(if -memx)"); run("opt_clean"); diff --git a/yosys/techlibs/common/simcells.v b/yosys/techlibs/common/simcells.v index e770c5453..40a75f54d 100644 --- a/yosys/techlibs/common/simcells.v +++ b/yosys/techlibs/common/simcells.v @@ -20,14 +20,14 @@ * 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 + * 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) +//- $_BUF (A, Y) //- //- A buffer. This cell type is always optimized away by the opt_clean pass. //- @@ -36,7 +36,7 @@ //- 0 | 0 //- 1 | 1 //- -module \$_BUF_ (A, Y); +module BUF (A, Y); input A; output Y; assign Y = A; @@ -44,7 +44,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_NOT_ (A, Y) +//- $_NOT (A, Y) //- //- An inverter gate. //- @@ -53,7 +53,7 @@ endmodule //- 0 | 1 //- 1 | 0 //- -module \$_NOT_ (A, Y); +module NOT (A, Y); input A; output Y; assign Y = ~A; @@ -61,7 +61,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_AND_ (A, B, Y) +//- $_AND (A, B, Y) //- //- A 2-input AND gate. //- @@ -72,7 +72,7 @@ endmodule //- 1 0 | 0 //- 1 1 | 1 //- -module \$_AND_ (A, B, Y); +module AND (A, B, Y); input A, B; output Y; assign Y = A & B; @@ -80,7 +80,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_NAND_ (A, B, Y) +//- $_NAND (A, B, Y) //- //- A 2-input NAND gate. //- @@ -91,7 +91,7 @@ endmodule //- 1 0 | 1 //- 1 1 | 0 //- -module \$_NAND_ (A, B, Y); +module NAND (A, B, Y); input A, B; output Y; assign Y = ~(A & B); @@ -99,7 +99,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_OR_ (A, B, Y) +//- $_OR (A, B, Y) //- //- A 2-input OR gate. //- @@ -110,7 +110,7 @@ endmodule //- 1 0 | 1 //- 1 1 | 1 //- -module \$_OR_ (A, B, Y); +module OR (A, B, Y); input A, B; output Y; assign Y = A | B; @@ -118,7 +118,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_NOR_ (A, B, Y) +//- $_NOR (A, B, Y) //- //- A 2-input NOR gate. //- @@ -129,7 +129,7 @@ endmodule //- 1 0 | 0 //- 1 1 | 0 //- -module \$_NOR_ (A, B, Y); +module NOR (A, B, Y); input A, B; output Y; assign Y = ~(A | B); @@ -137,7 +137,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_XOR_ (A, B, Y) +//- $_XOR (A, B, Y) //- //- A 2-input XOR gate. //- @@ -148,7 +148,7 @@ endmodule //- 1 0 | 1 //- 1 1 | 0 //- -module \$_XOR_ (A, B, Y); +module XOR (A, B, Y); input A, B; output Y; assign Y = A ^ B; @@ -156,7 +156,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_XNOR_ (A, B, Y) +//- $_XNOR (A, B, Y) //- //- A 2-input XNOR gate. //- @@ -167,7 +167,7 @@ endmodule //- 1 0 | 0 //- 1 1 | 1 //- -module \$_XNOR_ (A, B, Y); +module XNOR (A, B, Y); input A, B; output Y; assign Y = ~(A ^ B); @@ -175,7 +175,45 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_MUX_ (A, B, S, Y) +//- $_ANDNOT (A, B, Y) +//- +//- A 2-input AND-NOT gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 0 +//- 0 1 | 0 +//- 1 0 | 1 +//- 1 1 | 0 +//- +module ANDNOT (A, B, Y); +input A, B; +output Y; +assign Y = A & (~B); +endmodule + +// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| +//- +//- $_ORNOT (A, B, Y) +//- +//- A 2-input OR-NOT gate. +//- +//- Truth table: A B | Y +//- -----+--- +//- 0 0 | 1 +//- 0 1 | 0 +//- 1 0 | 1 +//- 1 1 | 1 +//- +module ORNOT (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. //- @@ -184,7 +222,7 @@ endmodule //- a - 0 | a //- - b 1 | b //- -module \$_MUX_ (A, B, S, Y); +module MUX (A, B, S, Y); input A, B, S; output Y; assign Y = S ? B : A; @@ -192,7 +230,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_MUX4_ (A, B, C, D, S, T, Y) +//- $_MUX4 (A, B, C, D, S, T, Y) //- //- A 4-input MUX gate. //- @@ -203,7 +241,7 @@ endmodule //- - - c - 0 1 | c //- - - - d 1 1 | d //- -module \$_MUX4_ (A, B, C, D, S, T, Y); +module MUX4 (A, B, C, D, S, T, Y); input A, B, C, D, S, T; output Y; assign Y = T ? (S ? D : C) : @@ -212,7 +250,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y) +//- $_MUX8 (A, B, C, D, E, F, G, H, S, T, U, Y) //- //- An 8-input MUX gate. //- @@ -227,7 +265,7 @@ endmodule //- - - - - - - g - 0 1 1 | g //- - - - - - - - h 1 1 1 | h //- -module \$_MUX8_ (A, B, C, D, E, F, G, H, S, T, U, Y); +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) : @@ -238,7 +276,7 @@ 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) +//- $_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. //- @@ -261,7 +299,7 @@ endmodule //- - - - - - - - - - - - - - - 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); +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) : @@ -276,7 +314,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_AOI3_ (A, B, C, Y) +//- $_AOI3 (A, B, C, Y) //- //- A 3-input And-Or-Invert gate. //- @@ -291,7 +329,7 @@ endmodule //- 1 1 0 | 0 //- 1 1 1 | 0 //- -module \$_AOI3_ (A, B, C, Y); +module AOI3 (A, B, C, Y); input A, B, C; output Y; assign Y = ~((A & B) | C); @@ -299,7 +337,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_OAI3_ (A, B, C, Y) +//- $_OAI3 (A, B, C, Y) //- //- A 3-input Or-And-Invert gate. //- @@ -314,7 +352,7 @@ endmodule //- 1 1 0 | 1 //- 1 1 1 | 0 //- -module \$_OAI3_ (A, B, C, Y); +module OAI3 (A, B, C, Y); input A, B, C; output Y; assign Y = ~((A | B) & C); @@ -322,7 +360,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_AOI4_ (A, B, C, Y) +//- $_AOI4 (A, B, C, Y) //- //- A 4-input And-Or-Invert gate. //- @@ -345,7 +383,7 @@ endmodule //- 1 1 1 0 | 0 //- 1 1 1 1 | 0 //- -module \$_AOI4_ (A, B, C, D, Y); +module AOI4 (A, B, C, D, Y); input A, B, C, D; output Y; assign Y = ~((A & B) | (C & D)); @@ -353,7 +391,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_OAI4_ (A, B, C, Y) +//- $_OAI4 (A, B, C, Y) //- //- A 4-input Or-And-Invert gate. //- @@ -376,7 +414,7 @@ endmodule //- 1 1 1 0 | 0 //- 1 1 1 1 | 0 //- -module \$_OAI4_ (A, B, C, D, Y); +module OAI4 (A, B, C, D, Y); input A, B, C, D; output Y; assign Y = ~((A | B) & (C | D)); @@ -384,7 +422,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_TBUF_ (A, E, Y) +//- $_TBUF (A, E, Y) //- //- A tri-state buffer. //- @@ -393,7 +431,7 @@ endmodule //- a 1 | a //- - 0 | z //- -module \$_TBUF_ (A, E, Y); +module TBUF (A, E, Y); input A, E; output Y; assign Y = E ? A : 1'bz; @@ -401,7 +439,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_SR_NN_ (S, R, Q) +//- $_SR_NN (S, R, Q) //- //- A set-reset latch with negative polarity SET and RESET. //- @@ -412,7 +450,7 @@ endmodule //- 1 0 | 0 //- 1 1 | y //- -module \$_SR_NN_ (S, R, Q); +module SR_NN (S, R, Q); input S, R; output reg Q; always @(negedge S, negedge R) begin @@ -425,9 +463,9 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_SR_NP_ (S, R, Q) +//- $_SR_NP (S, R, Q) //- -//- A set-reset latch with negative polarity SET and positive polarioty RESET. +//- A set-reset latch with negative polarity SET and positive polarity RESET. //- //- Truth table: S R | Q //- -----+--- @@ -436,7 +474,7 @@ endmodule //- 1 1 | 0 //- 1 0 | y //- -module \$_SR_NP_ (S, R, Q); +module SR_NP (S, R, Q); input S, R; output reg Q; always @(negedge S, posedge R) begin @@ -449,9 +487,9 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_SR_PN_ (S, R, Q) +//- $_SR_PN (S, R, Q) //- -//- A set-reset latch with positive polarity SET and negative polarioty RESET. +//- A set-reset latch with positive polarity SET and negative polarity RESET. //- //- Truth table: S R | Q //- -----+--- @@ -460,7 +498,7 @@ endmodule //- 0 0 | 0 //- 0 1 | y //- -module \$_SR_PN_ (S, R, Q); +module SR_PN (S, R, Q); input S, R; output reg Q; always @(posedge S, negedge R) begin @@ -473,7 +511,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_SR_PP_ (S, R, Q) +//- $_SR_PP (S, R, Q) //- //- A set-reset latch with positive polarity SET and RESET. //- @@ -484,7 +522,7 @@ endmodule //- 0 1 | 0 //- 0 0 | y //- -module \$_SR_PP_ (S, R, Q); +module SR_PP (S, R, Q); input S, R; output reg Q; always @(posedge S, posedge R) begin @@ -498,12 +536,12 @@ endmodule `ifdef SIMCELLS_FF // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_FF_ (D, Q) +//- $_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); +module FF (D, Q); input D; output reg Q; always @($global_clock) begin @@ -514,7 +552,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_N_ (D, C, Q) +//- $_DFF_N (D, C, Q) //- //- A negative edge D-type flip-flop. //- @@ -523,7 +561,7 @@ endmodule //- d \ | d //- - - | q //- -module \$_DFF_N_ (D, C, Q); +module DFF_N (D, C, Q); input D, C; output reg Q; always @(negedge C) begin @@ -533,7 +571,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_P_ (D, C, Q) +//- $_DFF_P (D, C, Q) //- //- A positive edge D-type flip-flop. //- @@ -542,7 +580,7 @@ endmodule //- d / | d //- - - | q //- -module \$_DFF_P_ (D, C, Q); +module DFF (D, C, Q); input D, C; output reg Q; always @(posedge C) begin @@ -552,7 +590,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFE_NN_ (D, C, E, Q) +//- $_DFFE_NN (D, C, E, Q) //- //- A negative edge D-type flip-flop with negative polarity enable. //- @@ -561,7 +599,7 @@ endmodule //- d \ 0 | d //- - - - | q //- -module \$_DFFE_NN_ (D, C, E, Q); +module DFFE_NN (D, C, E, Q); input D, C, E; output reg Q; always @(negedge C) begin @@ -571,7 +609,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFE_NP_ (D, C, E, Q) +//- $_DFFE_NP (D, C, E, Q) //- //- A negative edge D-type flip-flop with positive polarity enable. //- @@ -580,7 +618,7 @@ endmodule //- d \ 1 | d //- - - - | q //- -module \$_DFFE_NP_ (D, C, E, Q); +module DFFE_NP (D, C, E, Q); input D, C, E; output reg Q; always @(negedge C) begin @@ -590,7 +628,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFE_PN_ (D, C, E, Q) +//- $_DFFE_PN (D, C, E, Q) //- //- A positive edge D-type flip-flop with negative polarity enable. //- @@ -599,7 +637,7 @@ endmodule //- d / 0 | d //- - - - | q //- -module \$_DFFE_PN_ (D, C, E, Q); +module DFFE_PN (D, C, E, Q); input D, C, E; output reg Q; always @(posedge C) begin @@ -609,7 +647,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFE_PP_ (D, C, E, Q) +//- $_DFFE_PP (D, C, E, Q) //- //- A positive edge D-type flip-flop with positive polarity enable. //- @@ -618,7 +656,7 @@ endmodule //- d / 1 | d //- - - - | q //- -module \$_DFFE_PP_ (D, C, E, Q); +module DFFE_PP (D, C, E, Q); input D, C, E; output reg Q; always @(posedge C) begin @@ -628,7 +666,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_NN0_ (D, C, R, Q) +//- $_DFF_NN0 (D, C, R, Q) //- //- A negative edge D-type flip-flop with negative polarity reset. //- @@ -638,7 +676,7 @@ endmodule //- d \ - | d //- - - - | q //- -module \$_DFF_NN0_ (D, C, R, Q); +module DFF_NN0 (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or negedge R) begin @@ -651,7 +689,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_NN1_ (D, C, R, Q) +//- $_DFF_NN1 (D, C, R, Q) //- //- A negative edge D-type flip-flop with negative polarity set. //- @@ -661,7 +699,7 @@ endmodule //- d \ - | d //- - - - | q //- -module \$_DFF_NN1_ (D, C, R, Q); +module DFF_NN1 (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or negedge R) begin @@ -674,7 +712,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_NP0_ (D, C, R, Q) +//- $_DFF_NP0 (D, C, R, Q) //- //- A negative edge D-type flip-flop with positive polarity reset. //- @@ -684,7 +722,7 @@ endmodule //- d \ - | d //- - - - | q //- -module \$_DFF_NP0_ (D, C, R, Q); +module DFF_NP0 (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or posedge R) begin @@ -697,7 +735,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_NP1_ (D, C, R, Q) +//- $_DFF_NP1 (D, C, R, Q) //- //- A negative edge D-type flip-flop with positive polarity set. //- @@ -707,7 +745,7 @@ endmodule //- d \ - | d //- - - - | q //- -module \$_DFF_NP1_ (D, C, R, Q); +module DFF_NP1 (D, C, R, Q); input D, C, R; output reg Q; always @(negedge C or posedge R) begin @@ -720,7 +758,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_PN0_ (D, C, R, Q) +//- $_DFF_PN0 (D, C, R, Q) //- //- A positive edge D-type flip-flop with negative polarity reset. //- @@ -730,7 +768,7 @@ endmodule //- d / - | d //- - - - | q //- -module \$_DFF_PN0_ (D, C, R, Q); +module DFF_PN0 (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or negedge R) begin @@ -743,7 +781,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_PN1_ (D, C, R, Q) +//- $_DFF_PN1 (D, C, R, Q) //- //- A positive edge D-type flip-flop with negative polarity set. //- @@ -753,7 +791,7 @@ endmodule //- d / - | d //- - - - | q //- -module \$_DFF_PN1_ (D, C, R, Q); +module DFF_PN1 (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or negedge R) begin @@ -766,7 +804,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_PP0_ (D, C, R, Q) +//- $_DFF_PP0 (D, C, R, Q) //- //- A positive edge D-type flip-flop with positive polarity reset. //- @@ -776,7 +814,7 @@ endmodule //- d / - | d //- - - - | q //- -module \$_DFF_PP0_ (D, C, R, Q); +module DFF_PP0 (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or posedge R) begin @@ -789,7 +827,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFF_PP1_ (D, C, R, Q) +//- $_DFF_PP1 (D, C, R, Q) //- //- A positive edge D-type flip-flop with positive polarity set. //- @@ -799,7 +837,7 @@ endmodule //- d / - | d //- - - - | q //- -module \$_DFF_PP1_ (D, C, R, Q); +module DFF_PP1 (D, C, R, Q); input D, C, R; output reg Q; always @(posedge C or posedge R) begin @@ -812,7 +850,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_NNN_ (C, S, R, D, Q) +//- $_DFFSR_NNN (C, S, R, D, Q) //- //- A negative edge D-type flip-flop with negative polarity set and reset. //- @@ -823,7 +861,7 @@ endmodule //- \ - - d | d //- - - - - | q //- -module \$_DFFSR_NNN_ (C, S, R, 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 @@ -838,7 +876,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_NNP_ (C, S, R, D, Q) +//- $_DFFSR_NNP (C, S, R, D, Q) //- //- A negative edge D-type flip-flop with negative polarity set and positive //- polarity reset. @@ -850,7 +888,7 @@ endmodule //- \ - - d | d //- - - - - | q //- -module \$_DFFSR_NNP_ (C, S, R, 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 @@ -865,7 +903,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_NPN_ (C, S, R, D, Q) +//- $_DFFSR_NPN (C, S, R, D, Q) //- //- A negative edge D-type flip-flop with positive polarity set and negative //- polarity reset. @@ -877,7 +915,7 @@ endmodule //- \ - - d | d //- - - - - | q //- -module \$_DFFSR_NPN_ (C, S, R, 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 @@ -892,7 +930,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_NPP_ (C, S, R, D, Q) +//- $_DFFSR_NPP (C, S, R, D, Q) //- //- A negative edge D-type flip-flop with positive polarity set and reset. //- @@ -903,7 +941,7 @@ endmodule //- \ - - d | d //- - - - - | q //- -module \$_DFFSR_NPP_ (C, S, R, 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 @@ -918,7 +956,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_PNN_ (C, S, R, D, Q) +//- $_DFFSR_PNN (C, S, R, D, Q) //- //- A positive edge D-type flip-flop with negative polarity set and reset. //- @@ -929,7 +967,7 @@ endmodule //- / - - d | d //- - - - - | q //- -module \$_DFFSR_PNN_ (C, S, R, 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 @@ -944,7 +982,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_PNP_ (C, S, R, D, Q) +//- $_DFFSR_PNP (C, S, R, D, Q) //- //- A positive edge D-type flip-flop with negative polarity set and positive //- polarity reset. @@ -956,7 +994,7 @@ endmodule //- / - - d | d //- - - - - | q //- -module \$_DFFSR_PNP_ (C, S, R, 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 @@ -971,7 +1009,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_PPN_ (C, S, R, D, Q) +//- $_DFFSR_PPN (C, S, R, D, Q) //- //- A positive edge D-type flip-flop with positive polarity set and negative //- polarity reset. @@ -983,7 +1021,7 @@ endmodule //- / - - d | d //- - - - - | q //- -module \$_DFFSR_PPN_ (C, S, R, 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 @@ -998,7 +1036,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DFFSR_PPP_ (C, S, R, D, Q) +//- $_DFFSR_PPP (C, S, R, D, Q) //- //- A positive edge D-type flip-flop with positive polarity set and reset. //- @@ -1009,7 +1047,7 @@ endmodule //- / - - d | d //- - - - - | q //- -module \$_DFFSR_PPP_ (C, S, R, 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 @@ -1024,7 +1062,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCH_N_ (E, D, Q) +//- $_DLATCH_N (E, D, Q) //- //- A negative enable D-type latch. //- @@ -1033,7 +1071,7 @@ endmodule //- 0 d | d //- - - | q //- -module \$_DLATCH_N_ (E, D, Q); +module DLATCH_N (E, D, Q); input E, D; output reg Q; always @* begin @@ -1044,7 +1082,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCH_P_ (E, D, Q) +//- $_DLATCH_P (E, D, Q) //- //- A positive enable D-type latch. //- @@ -1053,7 +1091,7 @@ endmodule //- 1 d | d //- - - | q //- -module \$_DLATCH_P_ (E, D, Q); +module DLATCH_P (E, D, Q); input E, D; output reg Q; always @* begin @@ -1064,7 +1102,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_NNN_ (E, S, R, D, Q) +//- $_DLATCHSR_NNN (E, S, R, D, Q) //- //- A negative enable D-type latch with negative polarity set and reset. //- @@ -1075,7 +1113,7 @@ endmodule //- 0 - - d | d //- - - - - | q //- -module \$_DLATCHSR_NNN_ (E, S, R, D, Q); +module DLATCHSR_NNN (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1090,7 +1128,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_NNP_ (E, S, R, D, Q) +//- $_DLATCHSR_NNP (E, S, R, D, Q) //- //- A negative enable D-type latch with negative polarity set and positive polarity //- reset. @@ -1102,7 +1140,7 @@ endmodule //- 0 - - d | d //- - - - - | q //- -module \$_DLATCHSR_NNP_ (E, S, R, D, Q); +module DLATCHSR_NNP (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1117,7 +1155,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_NPN_ (E, S, R, D, Q) +//- $_DLATCHSR_NPN (E, S, R, D, Q) //- //- A negative enable D-type latch with positive polarity set and negative polarity //- reset. @@ -1129,7 +1167,7 @@ endmodule //- 0 - - d | d //- - - - - | q //- -module \$_DLATCHSR_NPN_ (E, S, R, D, Q); +module DLATCHSR_NPN (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1144,7 +1182,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_NPP_ (E, S, R, D, Q) +//- $_DLATCHSR_NPP (E, S, R, D, Q) //- //- A negative enable D-type latch with positive polarity set and reset. //- @@ -1155,7 +1193,7 @@ endmodule //- 0 - - d | d //- - - - - | q //- -module \$_DLATCHSR_NPP_ (E, S, R, D, Q); +module DLATCHSR_NPP (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1170,7 +1208,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_PNN_ (E, S, R, D, Q) +//- $_DLATCHSR_PNN (E, S, R, D, Q) //- //- A positive enable D-type latch with negative polarity set and reset. //- @@ -1181,7 +1219,7 @@ endmodule //- 1 - - d | d //- - - - - | q //- -module \$_DLATCHSR_PNN_ (E, S, R, D, Q); +module DLATCHSR_PNN (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1196,7 +1234,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_PNP_ (E, S, R, D, Q) +//- $_DLATCHSR_PNP (E, S, R, D, Q) //- //- A positive enable D-type latch with negative polarity set and positive polarity //- reset. @@ -1208,7 +1246,7 @@ endmodule //- 1 - - d | d //- - - - - | q //- -module \$_DLATCHSR_PNP_ (E, S, R, D, Q); +module DLATCHSR_PNP (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1223,7 +1261,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_PPN_ (E, S, R, D, Q) +//- $_DLATCHSR_PPN (E, S, R, D, Q) //- //- A positive enable D-type latch with positive polarity set and negative polarity //- reset. @@ -1235,7 +1273,7 @@ endmodule //- 1 - - d | d //- - - - - | q //- -module \$_DLATCHSR_PPN_ (E, S, R, D, Q); +module DLATCHSR_PPN (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin @@ -1250,7 +1288,7 @@ endmodule // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| //- -//- $_DLATCHSR_PPP_ (E, S, R, D, Q) +//- $_DLATCHSR_PPP (E, S, R, D, Q) //- //- A positive enable D-type latch with positive polarity set and reset. //- @@ -1261,7 +1299,7 @@ endmodule //- 1 - - d | d //- - - - - | q //- -module \$_DLATCHSR_PPP_ (E, S, R, D, Q); +module DLATCHSR_PPP (E, S, R, D, Q); input E, S, R, D; output reg Q; always @* begin diff --git a/yosys/techlibs/common/simlib.v b/yosys/techlibs/common/simlib.v index 2c4db1ac6..faa0fef5c 100644 --- a/yosys/techlibs/common/simlib.v +++ b/yosys/techlibs/common/simlib.v @@ -1275,14 +1275,14 @@ module \$assert (A, EN); input A, EN; -`ifndef SIMLIB_NOCHECKS +/*`ifndef SIMLIB_NOCHECKS always @* begin if (A !== 1'b1 && EN === 1'b1) begin $display("Assertion %m failed!"); $stop; end end -`endif +`endif */ endmodule @@ -1292,19 +1292,43 @@ module \$assume (A, EN); input A, EN; -`ifndef SIMLIB_NOCHECKS +/*`ifndef SIMLIB_NOCHECKS always @* begin if (A !== 1'b1 && EN === 1'b1) begin $display("Assumption %m failed!"); $stop; end end -`endif +`endif */ endmodule // -------------------------------------------------------- +module \$live (A, EN); + +input A, EN; + +endmodule + +// -------------------------------------------------------- + +module \$fair (A, EN); + +input A, EN; + +endmodule + +// -------------------------------------------------------- + +module \$cover (A, EN); + +input A, EN; + +endmodule + +// -------------------------------------------------------- +/* module \$initstate (Y); output reg Y = 1; @@ -1319,7 +1343,7 @@ always @(cnt, trig) begin end endmodule - +*/ // -------------------------------------------------------- module \$anyconst (Y); @@ -1346,6 +1370,30 @@ endmodule // -------------------------------------------------------- +module \$allconst (Y); + +parameter WIDTH = 0; + +output [WIDTH-1:0] Y; + +assign Y = 'bx; + +endmodule + +// -------------------------------------------------------- + +module \$allseq (Y); + +parameter WIDTH = 0; + +output [WIDTH-1:0] Y; + +assign Y = 'bx; + +endmodule + +// -------------------------------------------------------- + module \$equiv (A, B, Y); input A, B; @@ -1353,14 +1401,14 @@ output Y; assign Y = (A !== 1'bx && A !== B) ? 1'bx : A; -`ifndef SIMLIB_NOCHECKS +/*`ifndef SIMLIB_NOCHECKS always @* begin if (A !== 1'bx && A !== B) begin $display("Equivalence failed!"); $stop; end end -`endif +`endif */ endmodule diff --git a/yosys/techlibs/common/synth.cc b/yosys/techlibs/common/synth.cc index 11ebe5339..ccfa76e02 100644 --- a/yosys/techlibs/common/synth.cc +++ b/yosys/techlibs/common/synth.cc @@ -29,7 +29,7 @@ struct SynthPass : public ScriptPass { SynthPass() : ScriptPass("synth", "generic synthesis script") { } - virtual void help() YS_OVERRIDE + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -51,6 +51,9 @@ struct SynthPass : public ScriptPass log(" -encfile \n"); log(" passed to 'fsm_recode' via 'fsm'\n"); log("\n"); + log(" -lut \n"); + log(" perform synthesis for a k-LUT architecture.\n"); + log("\n"); log(" -nofsm\n"); log(" do not run FSM optimization\n"); log("\n"); @@ -64,6 +67,9 @@ struct SynthPass : public ScriptPass log(" -nordff\n"); log(" passed to 'memory'. prohibits merging of FFs into memory read ports\n"); log("\n"); + log(" -noshare\n"); + log(" do not run SAT-based resource sharing\n"); + log("\n"); log(" -run [:]\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"); @@ -76,9 +82,10 @@ struct SynthPass : public ScriptPass } string top_module, fsm_opts, memory_opts; - bool autotop, flatten, noalumacc, nofsm, noabc; + bool autotop, flatten, noalumacc, nofsm, noabc, noshare; + int lut; - virtual void clear_flags() YS_OVERRIDE + void clear_flags() YS_OVERRIDE { top_module.clear(); fsm_opts.clear(); @@ -86,12 +93,14 @@ struct SynthPass : public ScriptPass autotop = false; flatten = false; + lut = 0; noalumacc = false; nofsm = false; noabc = false; + noshare = false; } - virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { string run_from, run_to; clear_flags(); @@ -126,6 +135,10 @@ struct SynthPass : public ScriptPass flatten = true; continue; } + if (args[argidx] == "-lut") { + lut = atoi(args[++argidx].c_str()); + continue; + } if (args[argidx] == "-nofsm") { nofsm = true; continue; @@ -142,12 +155,16 @@ struct SynthPass : public ScriptPass memory_opts += " -nordff"; continue; } + if (args[argidx] == "-noshare") { + noshare = 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_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH pass.\n"); log_push(); @@ -157,7 +174,7 @@ struct SynthPass : public ScriptPass log_pop(); } - virtual void script() YS_OVERRIDE + void script() YS_OVERRIDE { if (check_label("begin")) { @@ -178,18 +195,23 @@ struct SynthPass : public ScriptPass { run("proc"); if (help_mode || flatten) - run("flatten", "(if -flatten)"); + run("flatten", " (if -flatten)"); run("opt_expr"); run("opt_clean"); run("check"); run("opt"); run("wreduce"); + if (help_mode) + run("techmap -map +/cmp2lut.v", " (if -lut)"); + else + run(stringf("techmap -map +/cmp2lut.v -D LUT_WIDTH=%d", lut)); if (!noalumacc) - run("alumacc"); - run("share"); + run("alumacc", " (unless -noalumacc)"); + if (!noshare) + run("share", " (unless -noshare)"); run("opt"); if (!nofsm) - run("fsm" + fsm_opts); + run("fsm" + fsm_opts, " (unless -nofsm)"); run("opt -fast"); run("memory -nomap" + memory_opts); run("opt_clean"); @@ -201,12 +223,33 @@ struct SynthPass : public ScriptPass run("memory_map"); run("opt -full"); run("techmap"); + if (help_mode) + { + run("techmap -map +/gate2lut.v", "(if -noabc and -lut)"); + run("clean; opt_lut", " (if -noabc and -lut)"); + } + else if (noabc && lut) + { + run(stringf("techmap -map +/gate2lut.v -D LUT_WIDTH=%d", lut)); + run("clean; opt_lut"); + } run("opt -fast"); if (!noabc) { #ifdef YOSYS_ENABLE_ABC - run("abc -fast"); - run("opt -fast"); + if (help_mode) + { + run("abc -fast", " (unless -noabc, unless -lut)"); + run("abc -fast -lut k", "(unless -noabc, if -lut)"); + } + else + { + if (lut) + run(stringf("abc -fast -lut %d", lut)); + else + run("abc -fast"); + } + run("opt -fast", " (unless -noabc)"); #endif } } diff --git a/yosys/techlibs/coolrunner2/Makefile.inc b/yosys/techlibs/coolrunner2/Makefile.inc new file mode 100644 index 000000000..d62c9960c --- /dev/null +++ b/yosys/techlibs/coolrunner2/Makefile.inc @@ -0,0 +1,8 @@ + +OBJS += techlibs/coolrunner2/synth_coolrunner2.o +OBJS += techlibs/coolrunner2/coolrunner2_sop.o + +$(eval $(call add_share_file,share/coolrunner2,techlibs/coolrunner2/cells_latch.v)) +$(eval $(call add_share_file,share/coolrunner2,techlibs/coolrunner2/cells_sim.v)) +$(eval $(call add_share_file,share/coolrunner2,techlibs/coolrunner2/tff_extract.v)) +$(eval $(call add_share_file,share/coolrunner2,techlibs/coolrunner2/xc2_dff.lib)) diff --git a/yosys/techlibs/coolrunner2/cells_latch.v b/yosys/techlibs/coolrunner2/cells_latch.v new file mode 100644 index 000000000..f1e19da3a --- /dev/null +++ b/yosys/techlibs/coolrunner2/cells_latch.v @@ -0,0 +1,19 @@ +module $_DLATCH_P_(input E, input D, output Q); + LDCP _TECHMAP_REPLACE_ ( + .D(D), + .G(E), + .Q(Q), + .PRE(1'b0), + .CLR(1'b0) + ); +endmodule + +module $_DLATCH_N_(input E, input D, output Q); + LDCP_N _TECHMAP_REPLACE_ ( + .D(D), + .G(E), + .Q(Q), + .PRE(1'b0), + .CLR(1'b0) + ); +endmodule diff --git a/yosys/techlibs/coolrunner2/cells_sim.v b/yosys/techlibs/coolrunner2/cells_sim.v new file mode 100644 index 000000000..562fb1efd --- /dev/null +++ b/yosys/techlibs/coolrunner2/cells_sim.v @@ -0,0 +1,310 @@ +module IBUF(input I, output O); + assign O = I; +endmodule + +module IOBUFE(input I, input E, output O, inout IO); + assign O = IO; + assign IO = E ? I : 1'bz; +endmodule + +module ANDTERM(IN, IN_B, OUT); + parameter TRUE_INP = 0; + parameter COMP_INP = 0; + + input [TRUE_INP-1:0] IN; + input [COMP_INP-1:0] IN_B; + output reg OUT; + + integer i; + + always @(*) begin + OUT = 1; + for (i = 0; i < TRUE_INP; i=i+1) + OUT = OUT & IN[i]; + for (i = 0; i < COMP_INP; i=i+1) + OUT = OUT & ~IN_B[i]; + end +endmodule + +module ORTERM(IN, OUT); + parameter WIDTH = 0; + + input [WIDTH-1:0] IN; + output reg OUT; + + integer i; + + always @(*) begin + OUT = 0; + for (i = 0; i < WIDTH; i=i+1) begin + OUT = OUT | IN[i]; + end + end +endmodule + +module MACROCELL_XOR(IN_PTC, IN_ORTERM, OUT); + parameter INVERT_OUT = 0; + + input IN_PTC; + input IN_ORTERM; + output wire OUT; + + wire xor_intermed; + + assign OUT = INVERT_OUT ? ~xor_intermed : xor_intermed; + assign xor_intermed = IN_ORTERM ^ IN_PTC; +endmodule + +module FDCP (C, PRE, CLR, D, Q); + parameter INIT = 0; + + input C, PRE, CLR, D; + output reg Q; + + initial begin + Q <= INIT; + end + + always @(posedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q <= 0; + else if (PRE == 1) + Q <= 1; + else + Q <= D; + end +endmodule + +module FDCP_N (C, PRE, CLR, D, Q); + parameter INIT = 0; + + input C, PRE, CLR, D; + output reg Q; + + initial begin + Q <= INIT; + end + + always @(negedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q <= 0; + else if (PRE == 1) + Q <= 1; + else + Q <= D; + end +endmodule + +module LDCP (G, PRE, CLR, D, Q); + parameter INIT = 0; + + input G, PRE, CLR, D; + output reg Q; + + initial begin + Q <= INIT; + end + + always @* begin + if (CLR == 1) + Q <= 0; + else if (G == 1) + Q <= D; + else if (PRE == 1) + Q <= 1; + end +endmodule + +module LDCP_N (G, PRE, CLR, D, Q); + parameter INIT = 0; + + input G, PRE, CLR, D; + output reg Q; + + initial begin + Q <= INIT; + end + + always @* begin + if (CLR == 1) + Q <= 0; + else if (G == 0) + Q <= D; + else if (PRE == 1) + Q <= 1; + end +endmodule + +module BUFG(I, O); + input I; + output O; + + assign O = I; +endmodule + +module BUFGSR(I, O); + parameter INVERT = 0; + + input I; + output O; + + assign O = INVERT ? ~I : I; +endmodule + +module BUFGTS(I, O); + parameter INVERT = 0; + + input I; + output O; + + assign O = INVERT ? ~I : I; +endmodule + +module FDDCP (C, PRE, CLR, D, Q); + parameter INIT = 0; + + input C, PRE, CLR, D; + output reg Q; + + initial begin + Q <= INIT; + end + + always @(posedge C, negedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q <= 0; + else if (PRE == 1) + Q <= 1; + else + Q <= D; + end +endmodule + +module FTCP (C, PRE, CLR, T, Q); + parameter INIT = 0; + + input C, PRE, CLR, T; + output wire Q; + reg Q_; + + initial begin + Q_ <= INIT; + end + + always @(posedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q_ <= 0; + else if (PRE == 1) + Q_ <= 1; + else if (T == 1) + Q_ <= ~Q_; + end + + assign Q = Q_; +endmodule + +module FTCP_N (C, PRE, CLR, T, Q); + parameter INIT = 0; + + input C, PRE, CLR, T; + output wire Q; + reg Q_; + + initial begin + Q_ <= INIT; + end + + always @(negedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q_ <= 0; + else if (PRE == 1) + Q_ <= 1; + else if (T == 1) + Q_ <= ~Q_; + end + + assign Q = Q_; +endmodule + +module FTDCP (C, PRE, CLR, T, Q); + parameter INIT = 0; + + input C, PRE, CLR, T; + output wire Q; + reg Q_; + + initial begin + Q_ <= INIT; + end + + always @(posedge C, negedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q_ <= 0; + else if (PRE == 1) + Q_ <= 1; + else if (T == 1) + Q_ <= ~Q_; + end + + assign Q = Q_; +endmodule + +module FDCPE (C, PRE, CLR, D, Q, CE); + parameter INIT = 0; + + input C, PRE, CLR, D, CE; + output reg Q; + + initial begin + Q <= INIT; + end + + always @(posedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q <= 0; + else if (PRE == 1) + Q <= 1; + else if (CE == 1) + Q <= D; + end +endmodule + +module FDCPE_N (C, PRE, CLR, D, Q, CE); + parameter INIT = 0; + + input C, PRE, CLR, D, CE; + output reg Q; + + initial begin + Q <= INIT; + end + + always @(negedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q <= 0; + else if (PRE == 1) + Q <= 1; + else if (CE == 1) + Q <= D; + end +endmodule + +module FDDCPE (C, PRE, CLR, D, Q, CE); + parameter INIT = 0; + + input C, PRE, CLR, D, CE; + output reg Q; + + initial begin + Q <= INIT; + end + + always @(posedge C, negedge C, posedge PRE, posedge CLR) begin + if (CLR == 1) + Q <= 0; + else if (PRE == 1) + Q <= 1; + else if (CE == 1) + Q <= D; + end +endmodule diff --git a/yosys/techlibs/coolrunner2/coolrunner2_sop.cc b/yosys/techlibs/coolrunner2/coolrunner2_sop.cc new file mode 100644 index 000000000..48da0d8ad --- /dev/null +++ b/yosys/techlibs/coolrunner2/coolrunner2_sop.cc @@ -0,0 +1,320 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 Robert Ou + * + * 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 Coolrunner2SopPass : public Pass { + Coolrunner2SopPass() : Pass("coolrunner2_sop", "break $sop cells into ANDTERM/ORTERM cells") { } + void help() YS_OVERRIDE + { + log("\n"); + log(" coolrunner2_sop [options] [selection]\n"); + log("\n"); + log("Break $sop cells into ANDTERM/ORTERM cells.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing COOLRUNNER2_SOP pass (break $sop cells into ANDTERM/ORTERM cells).\n"); + extra_args(args, 1, design); + + for (auto module : design->selected_modules()) + { + pool cells_to_remove; + SigMap sigmap(module); + + // Find all the $_NOT_ cells + dict> not_cells; + for (auto cell : module->selected_cells()) + { + if (cell->type == "$_NOT_") + { + auto not_input = sigmap(cell->getPort("\\A")[0]); + auto not_output = sigmap(cell->getPort("\\Y")[0]); + not_cells[not_input] = tuple(not_output, cell); + } + } + + // Find wires that need to become special product terms + dict>> special_pterms_no_inv; + dict>> special_pterms_inv; + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\FDCP" || cell->type == "\\FDCP_N" || cell->type == "\\FDDCP" || + cell->type == "\\FTCP" || cell->type == "\\FTCP_N" || cell->type == "\\FTDCP" || + cell->type == "\\FDCPE" || cell->type == "\\FDCPE_N" || cell->type == "\\FDDCPE" || + cell->type == "\\LDCP" || cell->type == "\\LDCP_N") + { + if (cell->hasPort("\\PRE")) + special_pterms_no_inv[sigmap(cell->getPort("\\PRE")[0])].insert( + tuple(cell, "\\PRE")); + if (cell->hasPort("\\CLR")) + special_pterms_no_inv[sigmap(cell->getPort("\\CLR")[0])].insert( + tuple(cell, "\\CLR")); + if (cell->hasPort("\\CE")) + special_pterms_no_inv[sigmap(cell->getPort("\\CE")[0])].insert( + tuple(cell, "\\CE")); + + if (cell->hasPort("\\C")) + special_pterms_inv[sigmap(cell->getPort("\\C")[0])].insert( + tuple(cell, "\\C")); + if (cell->hasPort("\\G")) + special_pterms_inv[sigmap(cell->getPort("\\G")[0])].insert( + tuple(cell, "\\G")); + } + } + + // Process $sop cells + for (auto cell : module->selected_cells()) + { + if (cell->type == "$sop") + { + // Read the inputs/outputs/parameters of the $sop cell + auto sop_inputs = sigmap(cell->getPort("\\A")); + auto sop_output = sigmap(cell->getPort("\\Y"))[0]; + auto sop_depth = cell->getParam("\\DEPTH").as_int(); + auto sop_width = cell->getParam("\\WIDTH").as_int(); + auto sop_table = cell->getParam("\\TABLE"); + + // Check for a $_NOT_ at the output + bool has_invert = false; + if (not_cells.count(sop_output)) + { + auto not_cell = not_cells.at(sop_output); + + has_invert = true; + sop_output = std::get<0>(not_cell); + + // remove the $_NOT_ cell because it gets folded into the xor + cells_to_remove.insert(std::get<1>(not_cell)); + } + + // Check for special P-term usage + bool is_special_pterm = false; + bool special_pterm_can_invert = false; + if (special_pterms_no_inv.count(sop_output) || special_pterms_inv.count(sop_output)) + { + is_special_pterm = true; + if (!special_pterms_no_inv[sop_output].size()) + special_pterm_can_invert = true; + } + + // Construct AND cells + pool intermed_wires; + for (int i = 0; i < sop_depth; i++) { + // Wire for the output + auto and_out = module->addWire(NEW_ID); + intermed_wires.insert(and_out); + + // Signals for the inputs + pool and_in_true; + pool and_in_comp; + for (int j = 0; j < sop_width; j++) + { + if (sop_table[2 * (i * sop_width + j) + 0]) + { + and_in_comp.insert(sop_inputs[j]); + } + if (sop_table[2 * (i * sop_width + j) + 1]) + { + and_in_true.insert(sop_inputs[j]); + } + } + + // Construct the cell + auto and_cell = module->addCell(NEW_ID, "\\ANDTERM"); + and_cell->setParam("\\TRUE_INP", GetSize(and_in_true)); + and_cell->setParam("\\COMP_INP", GetSize(and_in_comp)); + and_cell->setPort("\\OUT", and_out); + and_cell->setPort("\\IN", and_in_true); + and_cell->setPort("\\IN_B", and_in_comp); + } + + if (sop_depth == 1) + { + // If there is only one term, don't construct an OR cell. Directly construct the XOR gate + auto xor_cell = module->addCell(NEW_ID, "\\MACROCELL_XOR"); + xor_cell->setParam("\\INVERT_OUT", has_invert); + xor_cell->setPort("\\IN_PTC", *intermed_wires.begin()); + xor_cell->setPort("\\OUT", sop_output); + + // Special P-term handling + if (is_special_pterm) + { + if (!has_invert || special_pterm_can_invert) + { + // Can connect the P-term directly to the special term sinks + for (auto x : special_pterms_inv[sop_output]) + std::get<0>(x)->setPort(std::get<1>(x), *intermed_wires.begin()); + for (auto x : special_pterms_no_inv[sop_output]) + std::get<0>(x)->setPort(std::get<1>(x), *intermed_wires.begin()); + } + + if (has_invert) + { + if (special_pterm_can_invert) + { + log_assert(special_pterms_no_inv[sop_output].size() == 0); + + for (auto x : special_pterms_inv[sop_output]) + { + auto cell = std::get<0>(x); + // Need to invert the polarity of the cell + if (cell->type == "\\FDCP") cell->type = "\\FDCP_N"; + else if (cell->type == "\\FDCP_N") cell->type = "\\FDCP"; + else if (cell->type == "\\FTCP") cell->type = "\\FTCP_N"; + else if (cell->type == "\\FTCP_N") cell->type = "\\FTCP"; + else if (cell->type == "\\FDCPE") cell->type = "\\FDCPE_N"; + else if (cell->type == "\\FDCPE_N") cell->type = "\\FDCPE"; + else if (cell->type == "\\LDCP") cell->type = "\\LDCP_N"; + else if (cell->type == "\\LDCP_N") cell->type = "\\LDCP"; + else log_assert(!"Internal error! Bad cell type!"); + } + } + else + { + // Need to construct a feed-through term + auto feedthrough_out = module->addWire(NEW_ID); + auto feedthrough_cell = module->addCell(NEW_ID, "\\ANDTERM"); + feedthrough_cell->setParam("\\TRUE_INP", 1); + feedthrough_cell->setParam("\\COMP_INP", 0); + feedthrough_cell->setPort("\\OUT", feedthrough_out); + feedthrough_cell->setPort("\\IN", sop_output); + feedthrough_cell->setPort("\\IN_B", SigSpec()); + + for (auto x : special_pterms_inv[sop_output]) + std::get<0>(x)->setPort(std::get<1>(x), feedthrough_out); + for (auto x : special_pterms_no_inv[sop_output]) + std::get<0>(x)->setPort(std::get<1>(x), feedthrough_out); + } + } + } + } + else + { + // Wire from OR to XOR + auto or_to_xor_wire = module->addWire(NEW_ID); + + // Construct the OR cell + auto or_cell = module->addCell(NEW_ID, "\\ORTERM"); + or_cell->setParam("\\WIDTH", sop_depth); + or_cell->setPort("\\IN", intermed_wires); + or_cell->setPort("\\OUT", or_to_xor_wire); + + // Construct the XOR cell + auto xor_cell = module->addCell(NEW_ID, "\\MACROCELL_XOR"); + xor_cell->setParam("\\INVERT_OUT", has_invert); + xor_cell->setPort("\\IN_ORTERM", or_to_xor_wire); + xor_cell->setPort("\\OUT", sop_output); + + if (is_special_pterm) + { + // Need to construct a feed-through term + auto feedthrough_out = module->addWire(NEW_ID); + auto feedthrough_cell = module->addCell(NEW_ID, "\\ANDTERM"); + feedthrough_cell->setParam("\\TRUE_INP", 1); + feedthrough_cell->setParam("\\COMP_INP", 0); + feedthrough_cell->setPort("\\OUT", feedthrough_out); + feedthrough_cell->setPort("\\IN", sop_output); + feedthrough_cell->setPort("\\IN_B", SigSpec()); + + for (auto x : special_pterms_inv[sop_output]) + std::get<0>(x)->setPort(std::get<1>(x), feedthrough_out); + for (auto x : special_pterms_no_inv[sop_output]) + std::get<0>(x)->setPort(std::get<1>(x), feedthrough_out); + } + } + + // Finally, remove the $sop cell + cells_to_remove.insert(cell); + } + } + + // In some cases we can get a FF feeding straight into an FF. This is not possible, so we need to insert + // some AND/XOR cells in the middle to make it actually work. + + // Find all the FF outputs + pool sig_fed_by_ff; + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\FDCP" || cell->type == "\\FDCP_N" || cell->type == "\\FDDCP" || + cell->type == "\\LDCP" || cell->type == "\\LDCP_N" || + cell->type == "\\FTCP" || cell->type == "\\FTCP_N" || cell->type == "\\FTDCP" || + cell->type == "\\FDCPE" || cell->type == "\\FDCPE_N" || cell->type == "\\FDDCPE") + { + auto output = sigmap(cell->getPort("\\Q")[0]); + sig_fed_by_ff.insert(output); + } + } + + // Look at all the FF inputs + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\FDCP" || cell->type == "\\FDCP_N" || cell->type == "\\FDDCP" || + cell->type == "\\LDCP" || cell->type == "\\LDCP_N" || + cell->type == "\\FTCP" || cell->type == "\\FTCP_N" || cell->type == "\\FTDCP" || + cell->type == "\\FDCPE" || cell->type == "\\FDCPE_N" || cell->type == "\\FDDCPE") + { + SigBit input; + if (cell->type == "\\FTCP" || cell->type == "\\FTCP_N" || cell->type == "\\FTDCP") + input = sigmap(cell->getPort("\\T")[0]); + else + input = sigmap(cell->getPort("\\D")[0]); + + if (sig_fed_by_ff[input]) + { + printf("Buffering input to \"%s\"\n", cell->name.c_str()); + + auto and_to_xor_wire = module->addWire(NEW_ID); + auto xor_to_ff_wire = module->addWire(NEW_ID); + + auto and_cell = module->addCell(NEW_ID, "\\ANDTERM"); + and_cell->setParam("\\TRUE_INP", 1); + and_cell->setParam("\\COMP_INP", 0); + and_cell->setPort("\\OUT", and_to_xor_wire); + and_cell->setPort("\\IN", input); + and_cell->setPort("\\IN_B", SigSpec()); + + auto xor_cell = module->addCell(NEW_ID, "\\MACROCELL_XOR"); + xor_cell->setParam("\\INVERT_OUT", false); + xor_cell->setPort("\\IN_PTC", and_to_xor_wire); + xor_cell->setPort("\\OUT", xor_to_ff_wire); + + if (cell->type == "\\FTCP" || cell->type == "\\FTCP_N" || cell->type == "\\FTDCP") + cell->setPort("\\T", xor_to_ff_wire); + else + cell->setPort("\\D", xor_to_ff_wire); + } + } + } + + // Actually do the removal now that we aren't iterating + for (auto cell : cells_to_remove) + { + module->remove(cell); + } + } + } +} Coolrunner2SopPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/coolrunner2/synth_coolrunner2.cc b/yosys/techlibs/coolrunner2/synth_coolrunner2.cc new file mode 100644 index 000000000..21bbcaef4 --- /dev/null +++ b/yosys/techlibs/coolrunner2/synth_coolrunner2.cc @@ -0,0 +1,202 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2017 Robert Ou + * + * 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 SynthCoolrunner2Pass : public ScriptPass +{ + SynthCoolrunner2Pass() : ScriptPass("synth_coolrunner2", "synthesis for Xilinx Coolrunner-II CPLDs") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_coolrunner2 [options]\n"); + log("\n"); + log("This command runs synthesis for Coolrunner-II CPLDs. This work is experimental.\n"); + log("It is intended to be used with https://github.com/azonenberg/openfpga as the\n"); + log("place-and-route.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -json \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 :\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, json_file; + bool flatten, retime; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + json_file = ""; + flatten = true; + retime = false; + } + + void execute(std::vector 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] == "-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 command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_COOLRUNNER2 pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/coolrunner2/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-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("opt -fast -full"); + run("techmap"); + run("techmap -map +/coolrunner2/cells_latch.v"); + run("dfflibmap -prepare -liberty +/coolrunner2/xc2_dff.lib"); + } + + if (check_label("map_tff")) + { + // This is quite hacky. By telling abc that it can only use AND and XOR gates, abc will try and use XOR + // gates "whenever possible." This will hopefully cause toggle flip-flop structures to turn into an XOR + // connected to a D flip-flop. We then match on these and convert them into XC2 TFF cells. + run("abc -g AND,XOR"); + run("clean"); + run("extract -map +/coolrunner2/tff_extract.v"); + } + + if (check_label("map_pla")) + { + run("abc -sop -I 40 -P 56"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("dfflibmap -liberty +/coolrunner2/xc2_dff.lib"); + run("dffinit -ff FDCP Q INIT"); + run("dffinit -ff FDCP_N Q INIT"); + run("dffinit -ff FTCP Q INIT"); + run("dffinit -ff FTCP_N Q INIT"); + run("dffinit -ff LDCP Q INIT"); + run("dffinit -ff LDCP_N Q INIT"); + run("coolrunner2_sop"); + run("iopadmap -bits -inpad IBUF O:I -outpad IOBUFE I:IO -inoutpad IOBUFE O:IO -toutpad IOBUFE E:I:IO -tinoutpad IOBUFE E:O:I:IO"); + run("attrmvcp -attr src -attr LOC t:IOBUFE n:*"); + run("attrmvcp -attr src -attr LOC -driven t:IBUF n:*"); + run("splitnets"); + 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 ? "" : json_file.c_str())); + } + + log_pop(); + } +} SynthCoolrunner2Pass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/coolrunner2/tff_extract.v b/yosys/techlibs/coolrunner2/tff_extract.v new file mode 100644 index 000000000..b4237dd18 --- /dev/null +++ b/yosys/techlibs/coolrunner2/tff_extract.v @@ -0,0 +1,41 @@ +module FTCP (C, PRE, CLR, T, Q); + input C, PRE, CLR, T; + output wire Q; + + wire xorout; + + $_XOR_ xorgate ( + .A(T), + .B(Q), + .Y(xorout), + ); + + $_DFFSR_PPP_ dff ( + .C(C), + .D(xorout), + .Q(Q), + .S(PRE), + .R(CLR), + ); +endmodule + +module FTCP_N (C, PRE, CLR, T, Q); + input C, PRE, CLR, T; + output wire Q; + + wire xorout; + + $_XOR_ xorgate ( + .A(T), + .B(Q), + .Y(xorout), + ); + + $_DFFSR_NPP_ dff ( + .C(C), + .D(xorout), + .Q(Q), + .S(PRE), + .R(CLR), + ); +endmodule diff --git a/yosys/techlibs/coolrunner2/xc2_dff.lib b/yosys/techlibs/coolrunner2/xc2_dff.lib new file mode 100644 index 000000000..b578493a1 --- /dev/null +++ b/yosys/techlibs/coolrunner2/xc2_dff.lib @@ -0,0 +1,31 @@ +library(xc2_dff) { + cell(FDCP) { + area: 1; + ff("IQ", "IQN") { clocked_on: C; + next_state: D; + clear: "CLR"; + preset: "PRE"; } + pin(C) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + pin(CLR) { direction: input; } + pin(PRE) { direction: input; } + } + + cell(FDCP_N) { + area: 1; + ff("IQ", "IQN") { clocked_on: "!C"; + next_state: D; + clear: "CLR"; + preset: "PRE"; } + pin(C) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + pin(CLR) { direction: input; } + pin(PRE) { direction: input; } + } +} diff --git a/yosys/techlibs/easic/Makefile.inc b/yosys/techlibs/easic/Makefile.inc new file mode 100644 index 000000000..fb05a8a88 --- /dev/null +++ b/yosys/techlibs/easic/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += techlibs/easic/synth_easic.o + diff --git a/yosys/techlibs/easic/synth_easic.cc b/yosys/techlibs/easic/synth_easic.cc new file mode 100644 index 000000000..dd9e3dab7 --- /dev/null +++ b/yosys/techlibs/easic/synth_easic.cc @@ -0,0 +1,188 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 SynthEasicPass : public ScriptPass +{ + SynthEasicPass() : ScriptPass("synth_easic", "synthesis for eASIC platform") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_easic [options]\n"); + log("\n"); + log("This command runs synthesis for eASIC platform.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module\n"); + log("\n"); + log(" -vlog \n"); + log(" write the design to the specified structural Verilog file. writing of\n"); + log(" an output file is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -etools \n"); + log(" set path to the eTools installation. (default=/opt/eTools)\n"); + log("\n"); + log(" -run :\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, vlog_file, etools_path; + bool flatten, retime; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + vlog_file = ""; + etools_path = "/opt/eTools"; + flatten = true; + retime = false; + } + + void execute(std::vector 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] == "-vlog" && argidx+1 < args.size()) { + vlog_file = args[++argidx]; + continue; + } + if (args[argidx] == "-etools" && argidx+1 < args.size()) { + etools_path = 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 command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_EASIC pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + string phys_clk_lib = stringf("%s/data_ruby28/design_libs/logical/timing/gp/n3x_phys_clk_0v893ff125c.lib", etools_path.c_str()); + string logic_lut_lib = stringf("%s/data_ruby28/design_libs/logical/timing/gp/n3x_logic_lut_0v893ff125c.lib", etools_path.c_str()); + + if (check_label("begin")) + { + run(stringf("read_liberty -lib %s", help_mode ? "" : phys_clk_lib.c_str())); + run(stringf("read_liberty -lib %s", help_mode ? "" : logic_lut_lib.c_str())); + run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt.c_str())); + } + + if (flatten && check_label("flatten", "(unless -noflatten)")) + { + run("proc"); + run("flatten"); + } + + 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("opt -fast"); + if (retime || help_mode) { + run("abc -dff", " (only if -retime)"); + run("opt_clean", "(only if -retime)"); + } + } + + if (check_label("map")) + { + run(stringf("dfflibmap -liberty %s", help_mode ? "" : phys_clk_lib.c_str())); + run(stringf("abc -liberty %s", help_mode ? "" : logic_lut_lib.c_str())); + run("opt_clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("vlog")) + { + if (!vlog_file.empty() || help_mode) + run(stringf("write_verilog -noexpr -attr2comment %s", help_mode ? "" : vlog_file.c_str())); + } + } +} SynthEasicPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/ecp5/.gitignore b/yosys/techlibs/ecp5/.gitignore new file mode 100644 index 000000000..54c329735 --- /dev/null +++ b/yosys/techlibs/ecp5/.gitignore @@ -0,0 +1,9 @@ +bram_init_1_2_4.vh +bram_init_9_18_36.vh +brams_init.mk +bram_conn_1.vh +bram_conn_2.vh +bram_conn_4.vh +bram_conn_9.vh +bram_conn_18.vh +brams_connect.mk diff --git a/yosys/techlibs/ecp5/Makefile.inc b/yosys/techlibs/ecp5/Makefile.inc new file mode 100644 index 000000000..8df02be5f --- /dev/null +++ b/yosys/techlibs/ecp5/Makefile.inc @@ -0,0 +1,44 @@ + +OBJS += techlibs/ecp5/synth_ecp5.o + +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_sim.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_bb.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/drams_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/dram.txt)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/brams_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v)) +$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v)) + +EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk +.SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk + +techlibs/ecp5/brams_init.mk: techlibs/ecp5/brams_init.py + $(Q) mkdir -p techlibs/ecp5 + $(P) python3 $< + $(Q) touch $@ + +techlibs/ecp5/brams_connect.mk: techlibs/ecp5/brams_connect.py + $(Q) mkdir -p techlibs/ecp5 + $(P) python3 $< + $(Q) touch $@ + + +techlibs/ecp5/bram_init_1_2_4.vh: techlibs/ecp5/brams_init.mk +techlibs/ecp5/bram_init_9_18_36.vh: techlibs/ecp5/brams_init.mk + +techlibs/ecp5/bram_conn_1.vh: techlibs/ecp5/brams_connect.mk +techlibs/ecp5/bram_conn_2.vh: techlibs/ecp5/brams_connect.mk +techlibs/ecp5/bram_conn_4.vh: techlibs/ecp5/brams_connect.mk +techlibs/ecp5/bram_conn_9.vh: techlibs/ecp5/brams_connect.mk +techlibs/ecp5/bram_conn_18.vh: techlibs/ecp5/brams_connect.mk + +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_1_2_4.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_init_9_18_36.vh)) + +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_1.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_2.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_4.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_9.vh)) +$(eval $(call add_gen_share_file,share/ecp5,techlibs/ecp5/bram_conn_18.vh)) diff --git a/yosys/techlibs/ecp5/arith_map.v b/yosys/techlibs/ecp5/arith_map.v new file mode 100644 index 000000000..1094c5f8a --- /dev/null +++ b/yosys/techlibs/ecp5/arith_map.v @@ -0,0 +1,79 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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_ecp5_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)); + + function integer round_up2; + input integer N; + begin + round_up2 = ((N + 1) / 2) * 2; + end + endfunction + + localparam Y_WIDTH2 = round_up2(Y_WIDTH); + + wire [Y_WIDTH2-1:0] AA = A_buf; + wire [Y_WIDTH2-1:0] BB = BI ? ~B_buf : B_buf; + wire [Y_WIDTH2-1:0] C = {CO, CI}; + wire [Y_WIDTH2-1:0] FCO, Y1; + + genvar i; + generate for (i = 0; i < Y_WIDTH2; i = i + 2) begin:slice + CCU2C #( + .INIT0(16'b0110011010101010), + .INIT1(16'b0110011010101010), + .INJECT1_0("NO"), + .INJECT1_1("NO") + ) ccu2c_i ( + .CIN(C[i]), + .A0(AA[i]), .B0(BB[i]), .C0(1'b0), .D0(1'b1), + .A1(AA[i+1]), .B1(BB[i+1]), .C1(1'b0), .D1(1'b1), + .S0(Y[i]), .S1(Y1[i]), + .COUT(FCO[i]) + ); + + assign CO[i] = (AA[i] && BB[i]) || (C[i] && (AA[i] || BB[i])); + if (i+1 < Y_WIDTH) begin + assign CO[i+1] = FCO[i]; + assign Y[i+1] = Y1[i]; + end + end endgenerate + + assign X = AA ^ BB; +endmodule diff --git a/yosys/techlibs/ecp5/bram.txt b/yosys/techlibs/ecp5/bram.txt new file mode 100644 index 000000000..f223a42b8 --- /dev/null +++ b/yosys/techlibs/ecp5/bram.txt @@ -0,0 +1,29 @@ +bram $__ECP5_DP16KD + 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 1 0 + enable 2 1 @a10d18 + enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1 + transp 0 2 + clocks 2 3 + clkpol 2 3 +endbram + +match $__ECP5_DP16KD + min bits 2048 + min efficiency 5 + shuffle_enable B +endmatch diff --git a/yosys/techlibs/ecp5/brams_connect.py b/yosys/techlibs/ecp5/brams_connect.py new file mode 100755 index 000000000..f86dcfcf0 --- /dev/null +++ b/yosys/techlibs/ecp5/brams_connect.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +def write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits): + ada_conn = [".ADA%d(%s)" % (i, ada_bits[i]) for i in range(len(ada_bits))] + adb_conn = [".ADB%d(%s)" % (i, adb_bits[i]) for i in range(len(adb_bits))] + dia_conn = [".DIA%d(%s)" % (i, dia_bits[i]) for i in range(len(dia_bits))] + dob_conn = [".DOB%d(%s)" % (i, dob_bits[i]) for i in range(len(dob_bits))] + print(" %s," % ", ".join(ada_conn), file=f) + print(" %s," % ", ".join(adb_conn), file=f) + print(" %s," % ", ".join(dia_conn), file=f) + print(" %s," % ", ".join(dob_conn), file=f) + +with open("techlibs/ecp5/bram_conn_1.vh", "w") as f: + ada_bits = ["A1ADDR[%d]" % i for i in range(14)] + adb_bits = ["B1ADDR[%d]" % i for i in range(14)] + dia_bits = ["A1DATA[0]"] + ["1'b0" for i in range(17)] + dob_bits = ["B1DATA[0]"] + write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) + +with open("techlibs/ecp5/bram_conn_2.vh", "w") as f: + ada_bits = ["1'b0"] + ["A1ADDR[%d]" % i for i in range(13)] + adb_bits = ["1'b0"] + ["B1ADDR[%d]" % i for i in range(13)] + dia_bits = ["A1DATA[%d]" % i for i in range(2)] + ["1'b0" for i in range(16)] + dob_bits = ["B1DATA[%d]" % i for i in range(2)] + write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) + +with open("techlibs/ecp5/bram_conn_4.vh", "w") as f: + ada_bits = ["1'b0", "1'b0"] + ["A1ADDR[%d]" % i for i in range(12)] + adb_bits = ["1'b0", "1'b0"] + ["B1ADDR[%d]" % i for i in range(12)] + dia_bits = ["A1DATA[%d]" % i for i in range(4)] + ["1'b0" for i in range(14)] + dob_bits = ["B1DATA[%d]" % i for i in range(4)] + write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) + +with open("techlibs/ecp5/bram_conn_9.vh", "w") as f: + ada_bits = ["1'b0", "1'b0", "1'b0"] + ["A1ADDR[%d]" % i for i in range(11)] + adb_bits = ["1'b0", "1'b0", "1'b0"] + ["B1ADDR[%d]" % i for i in range(11)] + dia_bits = ["A1DATA[%d]" % i for i in range(9)] + ["1'b0" for i in range(9)] + dob_bits = ["B1DATA[%d]" % i for i in range(9)] + write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) + +with open("techlibs/ecp5/bram_conn_18.vh", "w") as f: + ada_bits = ["A1EN[0]", "A1EN[1]", "1'b0", "1'b0"] + ["A1ADDR[%d]" % i for i in range(10)] + adb_bits = ["1'b0", "1'b0", "1'b0", "1'b0"] + ["B1ADDR[%d]" % i for i in range(10)] + dia_bits = ["A1DATA[%d]" % i for i in range(18)] + dob_bits = ["B1DATA[%d]" % i for i in range(18)] + write_bus_ports(f, ada_bits, adb_bits, dia_bits, dob_bits) diff --git a/yosys/techlibs/ecp5/brams_init.py b/yosys/techlibs/ecp5/brams_init.py new file mode 100755 index 000000000..96a47bdcd --- /dev/null +++ b/yosys/techlibs/ecp5/brams_init.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +with open("techlibs/ecp5/bram_init_1_2_4.vh", "w") as f: + for i in range(0, 0x40): + init_snippets = [] + for j in range(32): + init_snippets.append("INIT[%4d*8 +: 8]" % (32 * i + j)) + init_snippets.append("3'b000" if (j % 2 == 1) else "1'b0") + init_snippets = list(reversed(init_snippets)) + for k in range(8, 64, 8): + init_snippets[k] = "\n " + init_snippets[k] + print(".INITVAL_%02X({%s})," % (i, ", ".join(init_snippets)), file=f) + +with open("techlibs/ecp5/bram_init_9_18_36.vh", "w") as f: + for i in range(0, 0x40): + init_snippets = [] + for j in range(16): + init_snippets.append("INIT[%3d*18 +: 18]" % (16 * i + j)) + init_snippets.append("2'b00") + init_snippets = list(reversed(init_snippets)) + for k in range(8, 32, 8): + init_snippets[k] = "\n " + init_snippets[k] + print(".INITVAL_%02X({%s})," % (i, ", ".join(init_snippets)), file=f) diff --git a/yosys/techlibs/ecp5/brams_map.v b/yosys/techlibs/ecp5/brams_map.v new file mode 100644 index 000000000..b2c136863 --- /dev/null +++ b/yosys/techlibs/ecp5/brams_map.v @@ -0,0 +1,115 @@ +module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 10; + parameter CFG_DBITS = 18; + parameter CFG_ENABLE_A = 2; + + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [18431:0] INIT = 18432'bx; + parameter TRANSP2 = 0; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + input [CFG_DBITS-1:0] A1DATA; + input [CFG_ENABLE_A-1:0] A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + output [CFG_DBITS-1:0] B1DATA; + input B1EN; + + localparam CLKAMUX = CLKPOL2 ? "CLKA" : "INV"; + localparam CLKBMUX = CLKPOL3 ? "CLKB" : "INV"; + + localparam WRITEMODE_A = TRANSP2 ? "WRITETHROUGH" : "READBEFOREWRITE"; + + generate if (CFG_DBITS == 1) begin + DP16KD #( + `include "bram_init_1_2_4.vh" + .DATA_WIDTH_A(1), + .DATA_WIDTH_B(1), + .CLKAMUX(CLKAMUX), + .CLKBMUX(CLKBMUX), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B("READBEFOREWRITE"), + .GSR("DISABLED") + ) _TECHMAP_REPLACE_ ( + `include "bram_conn_1.vh" + .CLKA(CLK2), .CLKB(CLK3), + .WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1), + .WEB(1'b0), .CEB(B1EN), .OCEB(1'b1), + .RSTA(1'b0), .RSTB(1'b0) + ); + end else if (CFG_DBITS == 2) begin + DP16KD #( + `include "bram_init_1_2_4.vh" + .DATA_WIDTH_A(2), + .DATA_WIDTH_B(2), + .CLKAMUX(CLKAMUX), + .CLKBMUX(CLKBMUX), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B("READBEFOREWRITE"), + .GSR("DISABLED") + ) _TECHMAP_REPLACE_ ( + `include "bram_conn_2.vh" + .CLKA(CLK2), .CLKB(CLK3), + .WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1), + .WEB(1'b0), .CEB(B1EN), .OCEB(1'b1), + .RSTA(1'b0), .RSTB(1'b0) + ); + end else if (CFG_DBITS <= 4) begin + DP16KD #( + `include "bram_init_1_2_4.vh" + .DATA_WIDTH_A(4), + .DATA_WIDTH_B(4), + .CLKAMUX(CLKAMUX), + .CLKBMUX(CLKBMUX), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B("READBEFOREWRITE"), + .GSR("DISABLED") + ) _TECHMAP_REPLACE_ ( + `include "bram_conn_4.vh" + .CLKA(CLK2), .CLKB(CLK3), + .WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1), + .WEB(1'b0), .CEB(B1EN), .OCEB(1'b1), + .RSTA(1'b0), .RSTB(1'b0) + ); + end else if (CFG_DBITS <= 9) begin + DP16KD #( + `include "bram_init_9_18_36.vh" + .DATA_WIDTH_A(9), + .DATA_WIDTH_B(9), + .CLKAMUX(CLKAMUX), + .CLKBMUX(CLKBMUX), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B("READBEFOREWRITE"), + .GSR("DISABLED") + ) _TECHMAP_REPLACE_ ( + `include "bram_conn_9.vh" + .CLKA(CLK2), .CLKB(CLK3), + .WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1), + .WEB(1'b0), .CEB(B1EN), .OCEB(1'b1), + .RSTA(1'b0), .RSTB(1'b0) + ); + end else if (CFG_DBITS <= 18) begin + DP16KD #( + `include "bram_init_9_18_36.vh" + .DATA_WIDTH_A(18), + .DATA_WIDTH_B(18), + .CLKAMUX(CLKAMUX), + .CLKBMUX(CLKBMUX), + .WRITEMODE_A(WRITEMODE_A), + .WRITEMODE_B("READBEFOREWRITE"), + .GSR("DISABLED") + ) _TECHMAP_REPLACE_ ( + `include "bram_conn_18.vh" + .CLKA(CLK2), .CLKB(CLK3), + .WEA(|A1EN), .CEA(1'b1), .OCEA(1'b1), + .WEB(1'b0), .CEB(B1EN), .OCEB(1'b1), + .RSTA(1'b0), .RSTB(1'b0) + ); + end else begin + wire TECHMAP_FAIL = 1'b1; + end endgenerate +endmodule diff --git a/yosys/techlibs/ecp5/cells_bb.v b/yosys/techlibs/ecp5/cells_bb.v new file mode 100644 index 000000000..425d62d24 --- /dev/null +++ b/yosys/techlibs/ecp5/cells_bb.v @@ -0,0 +1,514 @@ +// ECP5 Blackbox cells +// FIXME: Create sim models + +(* blackbox *) +module MULT18X18D( + input A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, + input B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, + input C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, + input SIGNEDA, SIGNEDB, SOURCEA, SOURCEB, + input CLK0, CLK1, CLK2, CLK3, + input CE0, CE1, CE2, CE3, + input RST0, RST1, RST2, RST3, + input SRIA0, SRIA1, SRIA2, SRIA3, SRIA4, SRIA5, SRIA6, SRIA7, SRIA8, SRIA9, SRIA10, SRIA11, SRIA12, SRIA13, SRIA14, SRIA15, SRIA16, SRIA17, + input SRIB0, SRIB1, SRIB2, SRIB3, SRIB4, SRIB5, SRIB6, SRIB7, SRIB8, SRIB9, SRIB10, SRIB11, SRIB12, SRIB13, SRIB14, SRIB15, SRIB16, SRIB17, + output SROA0, SROA1, SROA2, SROA3, SROA4, SROA5, SROA6, SROA7, SROA8, SROA9, SROA10, SROA11, SROA12, SROA13, SROA14, SROA15, SROA16, SROA17, + output SROB0, SROB1, SROB2, SROB3, SROB4, SROB5, SROB6, SROB7, SROB8, SROB9, SROB10, SROB11, SROB12, SROB13, SROB14, SROB15, SROB16, SROB17, + output ROA0, ROA1, ROA2, ROA3, ROA4, ROA5, ROA6, ROA7, ROA8, ROA9, ROA10, ROA11, ROA12, ROA13, ROA14, ROA15, ROA16, ROA17, + output ROB0, ROB1, ROB2, ROB3, ROB4, ROB5, ROB6, ROB7, ROB8, ROB9, ROB10, ROB11, ROB12, ROB13, ROB14, ROB15, ROB16, ROB17, + output ROC0, ROC1, ROC2, ROC3, ROC4, ROC5, ROC6, ROC7, ROC8, ROC9, ROC10, ROC11, ROC12, ROC13, ROC14, ROC15, ROC16, ROC17, + output P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, P23, P24, P25, P26, P27, P28, P29, P30, P31, P32, P33, P34, P35, + output SIGNEDP +); + parameter REG_INPUTA_CLK = "NONE"; + parameter REG_INPUTA_CE = "CE0"; + parameter REG_INPUTA_RST = "RST0"; + parameter REG_INPUTB_CLK = "NONE"; + parameter REG_INPUTB_CE = "CE0"; + parameter REG_INPUTB_RST = "RST0"; + parameter REG_INPUTC_CLK = "NONE"; + parameter REG_PIPELINE_CLK = "NONE"; + parameter REG_PIPELINE_CE = "CE0"; + parameter REG_PIPELINE_RST = "RST0"; + parameter REG_OUTPUT_CLK = "NONE"; + parameter [127:0] CLK0_DIV = "ENABLED"; + parameter [127:0] CLK1_DIV = "ENABLED"; + parameter [127:0] CLK2_DIV = "ENABLED"; + parameter [127:0] CLK3_DIV = "ENABLED"; + parameter [127:0] GSR = "ENABLED"; + parameter [127:0] SOURCEB_MODE = "B_SHIFT"; + parameter [127:0] RESETMODE = "SYNC"; +endmodule + +(* blackbox *) +module ALU54B( + input CLK0, CLK1, CLK2, CLK3, + input CE0, CE1, CE2, CE3, + input RST0, RST1, RST2, RST3, + input SIGNEDIA, SIGNEDIB, SIGNEDCIN, + input A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, + input B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16, B17, B18, B19, B20, B21, B22, B23, B24, B25, B26, B27, B28, B29, B30, B31, B32, B33, B34, B35, + input C0, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C18, C19, C20, C21, C22, C23, C24, C25, C26, C27, C28, C29, C30, C31, C32, C33, C34, C35, C36, C37, C38, C39, C40, C41, C42, C43, C44, C45, C46, C47, C48, C49, C50, C51, C52, C53, + input CFB0, CFB1, CFB2, CFB3, CFB4, CFB5, CFB6, CFB7, CFB8, CFB9, CFB10, CFB11, CFB12, CFB13, CFB14, CFB15, CFB16, CFB17, CFB18, CFB19, CFB20, CFB21, CFB22, CFB23, CFB24, CFB25, CFB26, CFB27, CFB28, CFB29, CFB30, CFB31, CFB32, CFB33, CFB34, CFB35, CFB36, CFB37, CFB38, CFB39, CFB40, CFB41, CFB42, CFB43, CFB44, CFB45, CFB46, CFB47, CFB48, CFB49, CFB50, CFB51, CFB52, CFB53, + input MA0, MA1, MA2, MA3, MA4, MA5, MA6, MA7, MA8, MA9, MA10, MA11, MA12, MA13, MA14, MA15, MA16, MA17, MA18, MA19, MA20, MA21, MA22, MA23, MA24, MA25, MA26, MA27, MA28, MA29, MA30, MA31, MA32, MA33, MA34, MA35, + input MB0, MB1, MB2, MB3, MB4, MB5, MB6, MB7, MB8, MB9, MB10, MB11, MB12, MB13, MB14, MB15, MB16, MB17, MB18, MB19, MB20, MB21, MB22, MB23, MB24, MB25, MB26, MB27, MB28, MB29, MB30, MB31, MB32, MB33, MB34, MB35, + input CIN0, CIN1, CIN2, CIN3, CIN4, CIN5, CIN6, CIN7, CIN8, CIN9, CIN10, CIN11, CIN12, CIN13, CIN14, CIN15, CIN16, CIN17, CIN18, CIN19, CIN20, CIN21, CIN22, CIN23, CIN24, CIN25, CIN26, CIN27, CIN28, CIN29, CIN30, CIN31, CIN32, CIN33, CIN34, CIN35, CIN36, CIN37, CIN38, CIN39, CIN40, CIN41, CIN42, CIN43, CIN44, CIN45, CIN46, CIN47, CIN48, CIN49, CIN50, CIN51, CIN52, CIN53, + input OP0, OP1, OP2, OP3, OP4, OP5, OP6, OP7, OP8, OP9, OP10, + output R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R22, R23, R24, R25, R26, R27, R28, R29, R30, R31, R32, R33, R34, R35, R36, R37, R38, R39, R40, R41, R42, R43, R44, R45, R46, R47, R48, R49, R50, R51, R52, R53, + output CO0, CO1, CO2, CO3, CO4, CO5, CO6, CO7, CO8, CO9, CO10, CO11, CO12, CO13, CO14, CO15, CO16, CO17, CO18, CO19, CO20, CO21, CO22, CO23, CO24, CO25, CO26, CO27, CO28, CO29, CO30, CO31, CO32, CO33, CO34, CO35, CO36, CO37, CO38, CO39, CO40, CO41, CO42, CO43, CO44, CO45, CO46, CO47, CO48, CO49, CO50, CO51, CO52, CO53, + output EQZ, EQZM, EQOM, EQPAT, EQPATB, + output OVER, UNDER, OVERUNDER, + output SIGNEDR +); + parameter REG_INPUTC0_CLK = "NONE"; + parameter REG_INPUTC1_CLK = "NONE"; + parameter REG_OPCODEOP0_0_CLK = "NONE"; + parameter REG_OPCODEOP0_0_CE = "CE0"; + parameter REG_OPCODEOP0_0_RST = "RST0"; + parameter REG_OPCODEOP1_0_CLK = "NONE"; + parameter REG_OPCODEOP0_1_CLK = "NONE"; + parameter REG_OPCODEOP0_1_CE = "CE0"; + parameter REG_OPCODEOP0_1_RST = "RST0"; + parameter REG_OPCODEIN_0_CLK = "NONE"; + parameter REG_OPCODEIN_0_CE = "CE0"; + parameter REG_OPCODEIN_0_RST = "RST0"; + parameter REG_OPCODEIN_1_CLK = "NONE"; + parameter REG_OPCODEIN_1_CE = "CE0"; + parameter REG_OPCODEIN_1_RST = "RST0"; + parameter REG_OUTPUT0_CLK = "NONE"; + parameter REG_OUTPUT1_CLK = "NONE"; + parameter REG_FLAG_CLK = "NONE"; + parameter [127:0] MCPAT_SOURCE = "STATIC"; + parameter [127:0] MASKPAT_SOURCE = "STATIC"; + parameter MASK01 = "0x00000000000000"; + parameter [127:0] CLK0_DIV = "ENABLED"; + parameter [127:0] CLK1_DIV = "ENABLED"; + parameter [127:0] CLK2_DIV = "ENABLED"; + parameter [127:0] CLK3_DIV = "ENABLED"; + parameter MCPAT = "0x00000000000000"; + parameter MASKPAT = "0x00000000000000"; + parameter RNDPAT = "0x00000000000000"; + parameter [127:0] GSR = "ENABLED"; + parameter [127:0] RESETMODE = "SYNC"; + parameter FORCE_ZERO_BARREL_SHIFT = "DISABLED"; + parameter LEGACY = "DISABLED"; +endmodule + +(* blackbox *) +module EHXPLLL ( + input CLKI, CLKFB, + input PHASESEL1, PHASESEL0, PHASEDIR, PHASESTEP, PHASELOADREG, + input STDBY, PLLWAKESYNC, + input RST, ENCLKOP, ENCLKOS, ENCLKOS2, ENCLKOS3, + output CLKOP, CLKOS, CLKOS2, CLKOS3, + output LOCK, INTLOCK, + output REFCLK, CLKINTFB +); + parameter CLKI_DIV = 1; + parameter CLKFB_DIV = 1; + parameter CLKOP_DIV = 8; + parameter CLKOS_DIV = 8; + parameter CLKOS2_DIV = 8; + parameter CLKOS3_DIV = 8; + parameter CLKOP_ENABLE = "ENABLED"; + parameter CLKOS_ENABLE = "DISABLED"; + parameter CLKOS2_ENABLE = "DISABLED"; + parameter CLKOS3_ENABLE = "DISABLED"; + parameter CLKOP_CPHASE = 0; + parameter CLKOS_CPHASE = 0; + parameter CLKOS2_CPHASE = 0; + parameter CLKOS3_CPHASE = 0; + parameter CLKOP_FPHASE = 0; + parameter CLKOS_FPHASE = 0; + parameter CLKOS2_FPHASE = 0; + parameter CLKOS3_FPHASE = 0; + parameter FEEDBK_PATH = "CLKOP"; + parameter CLKOP_TRIM_POL = "RISING"; + parameter CLKOP_TRIM_DELAY = 0; + parameter CLKOS_TRIM_POL = "RISING"; + parameter CLKOS_TRIM_DELAY = 0; + parameter OUTDIVIDER_MUXA = "DIVA"; + parameter OUTDIVIDER_MUXB = "DIVB"; + parameter OUTDIVIDER_MUXC = "DIVC"; + parameter OUTDIVIDER_MUXD = "DIVD"; + parameter PLL_LOCK_MODE = 0; + parameter PLL_LOCK_DELAY = 200; + parameter STDBY_ENABLE = "DISABLED"; + parameter REFIN_RESET = "DISABLED"; + parameter SYNC_ENABLE = "DISABLED"; + parameter INT_LOCK_STICKY = "ENABLED"; + parameter DPHASE_SOURCE = "DISABLED"; + parameter PLLRST_ENA = "DISABLED"; + parameter INTFB_WAKE = "DISABLED"; +endmodule + +(* blackbox *) +module DTR( + input STARTPULSE, + output DTROUT7, DTROUT6, DTROUT5, DTROUT4, DTROUT3, DTROUT2, DTROUT1, DTROUT0 +); +endmodule + +(* blackbox *) +module OSCG( + output OSC +); +parameter DIV = 128; +endmodule + +(* blackbox *) +module IDDRX1F( + input D, SCLK, RST, + output Q0, Q1 +); + parameter GSR = "ENABLED"; +endmodule + +(* blackbox *) +module ODDRX1F( + input SCLK, RST, D0, D1, + output Q +); + parameter GSR = "ENABLED"; +endmodule + +(* blackbox *) +module CLKDIVF( + input CLKI, RST, ALIGNWD, + output CDIVX +); + parameter GSR = "DISABLED"; + parameter DIV = "2.0"; +endmodule + +(* blackbox *) +module DCCA( + input CLKI, CE, + output CLKO +); +endmodule + +(* blackbox *) (* keep *) +module DCUA( + input CH0_HDINP, CH1_HDINP, CH0_HDINN, CH1_HDINN, + input D_TXBIT_CLKP_FROM_ND, D_TXBIT_CLKN_FROM_ND, D_SYNC_ND, D_TXPLL_LOL_FROM_ND, + input CH0_RX_REFCLK, CH1_RX_REFCLK, CH0_FF_RXI_CLK, CH1_FF_RXI_CLK, CH0_FF_TXI_CLK, CH1_FF_TXI_CLK, CH0_FF_EBRD_CLK, CH1_FF_EBRD_CLK, + input CH0_FF_TX_D_0, CH1_FF_TX_D_0, CH0_FF_TX_D_1, CH1_FF_TX_D_1, CH0_FF_TX_D_2, CH1_FF_TX_D_2, CH0_FF_TX_D_3, CH1_FF_TX_D_3, + input CH0_FF_TX_D_4, CH1_FF_TX_D_4, CH0_FF_TX_D_5, CH1_FF_TX_D_5, CH0_FF_TX_D_6, CH1_FF_TX_D_6, CH0_FF_TX_D_7, CH1_FF_TX_D_7, + input CH0_FF_TX_D_8, CH1_FF_TX_D_8, CH0_FF_TX_D_9, CH1_FF_TX_D_9, CH0_FF_TX_D_10, CH1_FF_TX_D_10, CH0_FF_TX_D_11, CH1_FF_TX_D_11, + input CH0_FF_TX_D_12, CH1_FF_TX_D_12, CH0_FF_TX_D_13, CH1_FF_TX_D_13, CH0_FF_TX_D_14, CH1_FF_TX_D_14, CH0_FF_TX_D_15, CH1_FF_TX_D_15, + input CH0_FF_TX_D_16, CH1_FF_TX_D_16, CH0_FF_TX_D_17, CH1_FF_TX_D_17, CH0_FF_TX_D_18, CH1_FF_TX_D_18, CH0_FF_TX_D_19, CH1_FF_TX_D_19, + input CH0_FF_TX_D_20, CH1_FF_TX_D_20, CH0_FF_TX_D_21, CH1_FF_TX_D_21, CH0_FF_TX_D_22, CH1_FF_TX_D_22, CH0_FF_TX_D_23, CH1_FF_TX_D_23, + input CH0_FFC_EI_EN, CH1_FFC_EI_EN, CH0_FFC_PCIE_DET_EN, CH1_FFC_PCIE_DET_EN, CH0_FFC_PCIE_CT, CH1_FFC_PCIE_CT, CH0_FFC_SB_INV_RX, CH1_FFC_SB_INV_RX, + input CH0_FFC_ENABLE_CGALIGN, CH1_FFC_ENABLE_CGALIGN, CH0_FFC_SIGNAL_DETECT, CH1_FFC_SIGNAL_DETECT, CH0_FFC_FB_LOOPBACK, CH1_FFC_FB_LOOPBACK, CH0_FFC_SB_PFIFO_LP, CH1_FFC_SB_PFIFO_LP, + input CH0_FFC_PFIFO_CLR, CH1_FFC_PFIFO_CLR, CH0_FFC_RATE_MODE_RX, CH1_FFC_RATE_MODE_RX, CH0_FFC_RATE_MODE_TX, CH1_FFC_RATE_MODE_TX, CH0_FFC_DIV11_MODE_RX, CH1_FFC_DIV11_MODE_RX, CH0_FFC_RX_GEAR_MODE, CH1_FFC_RX_GEAR_MODE, CH0_FFC_TX_GEAR_MODE, CH1_FFC_TX_GEAR_MODE, + input CH0_FFC_DIV11_MODE_TX, CH1_FFC_DIV11_MODE_TX, CH0_FFC_LDR_CORE2TX_EN, CH1_FFC_LDR_CORE2TX_EN, CH0_FFC_LANE_TX_RST, CH1_FFC_LANE_TX_RST, CH0_FFC_LANE_RX_RST, CH1_FFC_LANE_RX_RST, + input CH0_FFC_RRST, CH1_FFC_RRST, CH0_FFC_TXPWDNB, CH1_FFC_TXPWDNB, CH0_FFC_RXPWDNB, CH1_FFC_RXPWDNB, CH0_LDR_CORE2TX, CH1_LDR_CORE2TX, + input D_SCIWDATA0, D_SCIWDATA1, D_SCIWDATA2, D_SCIWDATA3, D_SCIWDATA4, D_SCIWDATA5, D_SCIWDATA6, D_SCIWDATA7, + input D_SCIADDR0, D_SCIADDR1, D_SCIADDR2, D_SCIADDR3, D_SCIADDR4, D_SCIADDR5, D_SCIENAUX, D_SCISELAUX, + input CH0_SCIEN, CH1_SCIEN, CH0_SCISEL, CH1_SCISEL, D_SCIRD, D_SCIWSTN, D_CYAWSTN, D_FFC_SYNC_TOGGLE, + input D_FFC_DUAL_RST, D_FFC_MACRO_RST, D_FFC_MACROPDB, D_FFC_TRST, CH0_FFC_CDR_EN_BITSLIP, CH1_FFC_CDR_EN_BITSLIP, D_SCAN_ENABLE, D_SCAN_IN_0, + input D_SCAN_IN_1, D_SCAN_IN_2, D_SCAN_IN_3, D_SCAN_IN_4, D_SCAN_IN_5, D_SCAN_IN_6, D_SCAN_IN_7, D_SCAN_MODE, + input D_SCAN_RESET, D_CIN0, D_CIN1, D_CIN2, D_CIN3, D_CIN4, D_CIN5, D_CIN6,D_CIN7, D_CIN8, D_CIN9, D_CIN10, D_CIN11, + output CH0_HDOUTP, CH1_HDOUTP, CH0_HDOUTN, CH1_HDOUTN, D_TXBIT_CLKP_TO_ND, D_TXBIT_CLKN_TO_ND, D_SYNC_PULSE2ND, D_TXPLL_LOL_TO_ND, + output CH0_FF_RX_F_CLK, CH1_FF_RX_F_CLK, CH0_FF_RX_H_CLK, CH1_FF_RX_H_CLK, CH0_FF_TX_F_CLK, CH1_FF_TX_F_CLK, CH0_FF_TX_H_CLK, CH1_FF_TX_H_CLK, + output CH0_FF_RX_PCLK, CH1_FF_RX_PCLK, CH0_FF_TX_PCLK, CH1_FF_TX_PCLK, CH0_FF_RX_D_0, CH1_FF_RX_D_0, CH0_FF_RX_D_1, CH1_FF_RX_D_1, + output CH0_FF_RX_D_2, CH1_FF_RX_D_2, CH0_FF_RX_D_3, CH1_FF_RX_D_3, CH0_FF_RX_D_4, CH1_FF_RX_D_4, CH0_FF_RX_D_5, CH1_FF_RX_D_5, + output CH0_FF_RX_D_6, CH1_FF_RX_D_6, CH0_FF_RX_D_7, CH1_FF_RX_D_7, CH0_FF_RX_D_8, CH1_FF_RX_D_8, CH0_FF_RX_D_9, CH1_FF_RX_D_9, + output CH0_FF_RX_D_10, CH1_FF_RX_D_10, CH0_FF_RX_D_11, CH1_FF_RX_D_11, CH0_FF_RX_D_12, CH1_FF_RX_D_12, CH0_FF_RX_D_13, CH1_FF_RX_D_13, + output CH0_FF_RX_D_14, CH1_FF_RX_D_14, CH0_FF_RX_D_15, CH1_FF_RX_D_15, CH0_FF_RX_D_16, CH1_FF_RX_D_16, CH0_FF_RX_D_17, CH1_FF_RX_D_17, + output CH0_FF_RX_D_18, CH1_FF_RX_D_18, CH0_FF_RX_D_19, CH1_FF_RX_D_19, CH0_FF_RX_D_20, CH1_FF_RX_D_20, CH0_FF_RX_D_21, CH1_FF_RX_D_21, + output CH0_FF_RX_D_22, CH1_FF_RX_D_22, CH0_FF_RX_D_23, CH1_FF_RX_D_23, CH0_FFS_PCIE_DONE, CH1_FFS_PCIE_DONE, CH0_FFS_PCIE_CON, CH1_FFS_PCIE_CON, + output CH0_FFS_RLOS, CH1_FFS_RLOS, CH0_FFS_LS_SYNC_STATUS, CH1_FFS_LS_SYNC_STATUS, CH0_FFS_CC_UNDERRUN, CH1_FFS_CC_UNDERRUN, CH0_FFS_CC_OVERRUN, CH1_FFS_CC_OVERRUN, + output CH0_FFS_RXFBFIFO_ERROR, CH1_FFS_RXFBFIFO_ERROR, CH0_FFS_TXFBFIFO_ERROR, CH1_FFS_TXFBFIFO_ERROR, CH0_FFS_RLOL, CH1_FFS_RLOL, CH0_FFS_SKP_ADDED, CH1_FFS_SKP_ADDED, + output CH0_FFS_SKP_DELETED, CH1_FFS_SKP_DELETED, CH0_LDR_RX2CORE, CH1_LDR_RX2CORE, D_SCIRDATA0, D_SCIRDATA1, D_SCIRDATA2, D_SCIRDATA3, + output D_SCIRDATA4, D_SCIRDATA5, D_SCIRDATA6, D_SCIRDATA7, D_SCIINT, D_SCAN_OUT_0, D_SCAN_OUT_1, D_SCAN_OUT_2, D_SCAN_OUT_3, D_SCAN_OUT_4, D_SCAN_OUT_5, D_SCAN_OUT_6, D_SCAN_OUT_7, + output D_COUT0, D_COUT1, D_COUT2, D_COUT3, D_COUT4, D_COUT5, D_COUT6, D_COUT7, D_COUT8, D_COUT9, D_COUT10, D_COUT11, D_COUT12, D_COUT13, D_COUT14, D_COUT15, D_COUT16, D_COUT17, D_COUT18, D_COUT19, + + input D_REFCLKI, + output D_FFS_PLOL +); + parameter CH0_AUTO_CALIB_EN = "0b0"; + parameter CH0_AUTO_FACQ_EN = "0b0"; + parameter CH0_BAND_THRESHOLD = "0b000000"; + parameter CH0_CALIB_CK_MODE = "0b0"; + parameter CH0_CC_MATCH_1 = "0b0000000000"; + parameter CH0_CC_MATCH_2 = "0b0000000000"; + parameter CH0_CC_MATCH_3 = "0b0000000000"; + parameter CH0_CC_MATCH_4 = "0b0000000000"; + parameter CH0_CDR_CNT4SEL = "0b00"; + parameter CH0_CDR_CNT8SEL = "0b00"; + parameter CH0_CTC_BYPASS = "0b0"; + parameter CH0_DCOATDCFG = "0b00"; + parameter CH0_DCOATDDLY = "0b00"; + parameter CH0_DCOBYPSATD = "0b0"; + parameter CH0_DCOCALDIV = "0b000"; + parameter CH0_DCOCTLGI = "0b000"; + parameter CH0_DCODISBDAVOID = "0b0"; + parameter CH0_DCOFLTDAC = "0b00"; + parameter CH0_DCOFTNRG = "0b000"; + parameter CH0_DCOIOSTUNE = "0b000"; + parameter CH0_DCOITUNE = "0b00"; + parameter CH0_DCOITUNE4LSB = "0b000"; + parameter CH0_DCOIUPDNX2 = "0b0"; + parameter CH0_DCONUOFLSB = "0b000"; + parameter CH0_DCOSCALEI = "0b00"; + parameter CH0_DCOSTARTVAL = "0b000"; + parameter CH0_DCOSTEP = "0b00"; + parameter CH0_DEC_BYPASS = "0b0"; + parameter CH0_ENABLE_CG_ALIGN = "0b0"; + parameter CH0_ENC_BYPASS = "0b0"; + parameter CH0_FF_RX_F_CLK_DIS = "0b0"; + parameter CH0_FF_RX_H_CLK_EN = "0b0"; + parameter CH0_FF_TX_F_CLK_DIS = "0b0"; + parameter CH0_FF_TX_H_CLK_EN = "0b0"; + parameter CH0_GE_AN_ENABLE = "0b0"; + parameter CH0_INVERT_RX = "0b0"; + parameter CH0_INVERT_TX = "0b0"; + parameter CH0_LDR_CORE2TX_SEL = "0b0"; + parameter CH0_LDR_RX2CORE_SEL = "0b0"; + parameter CH0_LEQ_OFFSET_SEL = "0b0"; + parameter CH0_LEQ_OFFSET_TRIM = "0b000"; + parameter CH0_LSM_DISABLE = "0b0"; + parameter CH0_MATCH_2_ENABLE = "0b0"; + parameter CH0_MATCH_4_ENABLE = "0b0"; + parameter CH0_MIN_IPG_CNT = "0b00"; + parameter CH0_PCIE_EI_EN = "0b0"; + parameter CH0_PCIE_MODE = "0b0"; + parameter CH0_PCS_DET_TIME_SEL = "0b00"; + parameter CH0_PDEN_SEL = "0b0"; + parameter CH0_PRBS_ENABLE = "0b0"; + parameter CH0_PRBS_LOCK = "0b0"; + parameter CH0_PRBS_SELECTION = "0b0"; + parameter CH0_RATE_MODE_RX = "0b0"; + parameter CH0_RATE_MODE_TX = "0b0"; + parameter CH0_RCV_DCC_EN = "0b0"; + parameter CH0_REG_BAND_OFFSET = "0b0000"; + parameter CH0_REG_BAND_SEL = "0b000000"; + parameter CH0_REG_IDAC_EN = "0b0"; + parameter CH0_REG_IDAC_SEL = "0b0000000000"; + parameter CH0_REQ_EN = "0b0"; + parameter CH0_REQ_LVL_SET = "0b00"; + parameter CH0_RIO_MODE = "0b0"; + parameter CH0_RLOS_SEL = "0b0"; + parameter CH0_RPWDNB = "0b0"; + parameter CH0_RTERM_RX = "0b00000"; + parameter CH0_RTERM_TX = "0b00000"; + parameter CH0_RXIN_CM = "0b00"; + parameter CH0_RXTERM_CM = "0b00"; + parameter CH0_RX_DCO_CK_DIV = "0b000"; + parameter CH0_RX_DIV11_SEL = "0b0"; + parameter CH0_RX_GEAR_BYPASS = "0b0"; + parameter CH0_RX_GEAR_MODE = "0b0"; + parameter CH0_RX_LOS_CEQ = "0b00"; + parameter CH0_RX_LOS_EN = "0b0"; + parameter CH0_RX_LOS_HYST_EN = "0b0"; + parameter CH0_RX_LOS_LVL = "0b000"; + parameter CH0_RX_RATE_SEL = "0b0000"; + parameter CH0_RX_SB_BYPASS = "0b0"; + parameter CH0_SB_BYPASS = "0b0"; + parameter CH0_SEL_SD_RX_CLK = "0b0"; + parameter CH0_TDRV_DAT_SEL = "0b00"; + parameter CH0_TDRV_POST_EN = "0b0"; + parameter CH0_TDRV_PRE_EN = "0b0"; + parameter CH0_TDRV_SLICE0_CUR = "0b000"; + parameter CH0_TDRV_SLICE0_SEL = "0b00"; + parameter CH0_TDRV_SLICE1_CUR = "0b000"; + parameter CH0_TDRV_SLICE1_SEL = "0b00"; + parameter CH0_TDRV_SLICE2_CUR = "0b00"; + parameter CH0_TDRV_SLICE2_SEL = "0b00"; + parameter CH0_TDRV_SLICE3_CUR = "0b00"; + parameter CH0_TDRV_SLICE3_SEL = "0b00"; + parameter CH0_TDRV_SLICE4_CUR = "0b00"; + parameter CH0_TDRV_SLICE4_SEL = "0b00"; + parameter CH0_TDRV_SLICE5_CUR = "0b00"; + parameter CH0_TDRV_SLICE5_SEL = "0b00"; + parameter CH0_TPWDNB = "0b0"; + parameter CH0_TX_CM_SEL = "0b00"; + parameter CH0_TX_DIV11_SEL = "0b0"; + parameter CH0_TX_GEAR_BYPASS = "0b0"; + parameter CH0_TX_GEAR_MODE = "0b0"; + parameter CH0_TX_POST_SIGN = "0b0"; + parameter CH0_TX_PRE_SIGN = "0b0"; + parameter CH0_UC_MODE = "0b0"; + parameter CH0_UDF_COMMA_A = "0b0000000000"; + parameter CH0_UDF_COMMA_B = "0b0000000000"; + parameter CH0_UDF_COMMA_MASK = "0b0000000000"; + parameter CH0_WA_BYPASS = "0b0"; + parameter CH0_WA_MODE = "0b0"; + parameter CH1_AUTO_CALIB_EN = "0b0"; + parameter CH1_AUTO_FACQ_EN = "0b0"; + parameter CH1_BAND_THRESHOLD = "0b000000"; + parameter CH1_CALIB_CK_MODE = "0b0"; + parameter CH1_CC_MATCH_1 = "0b0000000000"; + parameter CH1_CC_MATCH_2 = "0b0000000000"; + parameter CH1_CC_MATCH_3 = "0b0000000000"; + parameter CH1_CC_MATCH_4 = "0b0000000000"; + parameter CH1_CDR_CNT4SEL = "0b00"; + parameter CH1_CDR_CNT8SEL = "0b00"; + parameter CH1_CTC_BYPASS = "0b0"; + parameter CH1_DCOATDCFG = "0b00"; + parameter CH1_DCOATDDLY = "0b00"; + parameter CH1_DCOBYPSATD = "0b0"; + parameter CH1_DCOCALDIV = "0b000"; + parameter CH1_DCOCTLGI = "0b000"; + parameter CH1_DCODISBDAVOID = "0b0"; + parameter CH1_DCOFLTDAC = "0b00"; + parameter CH1_DCOFTNRG = "0b000"; + parameter CH1_DCOIOSTUNE = "0b000"; + parameter CH1_DCOITUNE = "0b00"; + parameter CH1_DCOITUNE4LSB = "0b000"; + parameter CH1_DCOIUPDNX2 = "0b0"; + parameter CH1_DCONUOFLSB = "0b000"; + parameter CH1_DCOSCALEI = "0b00"; + parameter CH1_DCOSTARTVAL = "0b000"; + parameter CH1_DCOSTEP = "0b00"; + parameter CH1_DEC_BYPASS = "0b0"; + parameter CH1_ENABLE_CG_ALIGN = "0b0"; + parameter CH1_ENC_BYPASS = "0b0"; + parameter CH1_FF_RX_F_CLK_DIS = "0b0"; + parameter CH1_FF_RX_H_CLK_EN = "0b0"; + parameter CH1_FF_TX_F_CLK_DIS = "0b0"; + parameter CH1_FF_TX_H_CLK_EN = "0b0"; + parameter CH1_GE_AN_ENABLE = "0b0"; + parameter CH1_INVERT_RX = "0b0"; + parameter CH1_INVERT_TX = "0b0"; + parameter CH1_LDR_CORE2TX_SEL = "0b0"; + parameter CH1_LDR_RX2CORE_SEL = "0b0"; + parameter CH1_LEQ_OFFSET_SEL = "0b0"; + parameter CH1_LEQ_OFFSET_TRIM = "0b000"; + parameter CH1_LSM_DISABLE = "0b0"; + parameter CH1_MATCH_2_ENABLE = "0b0"; + parameter CH1_MATCH_4_ENABLE = "0b0"; + parameter CH1_MIN_IPG_CNT = "0b00"; + parameter CH1_PCIE_EI_EN = "0b0"; + parameter CH1_PCIE_MODE = "0b0"; + parameter CH1_PCS_DET_TIME_SEL = "0b00"; + parameter CH1_PDEN_SEL = "0b0"; + parameter CH1_PRBS_ENABLE = "0b0"; + parameter CH1_PRBS_LOCK = "0b0"; + parameter CH1_PRBS_SELECTION = "0b0"; + parameter CH1_RATE_MODE_RX = "0b0"; + parameter CH1_RATE_MODE_TX = "0b0"; + parameter CH1_RCV_DCC_EN = "0b0"; + parameter CH1_REG_BAND_OFFSET = "0b0000"; + parameter CH1_REG_BAND_SEL = "0b000000"; + parameter CH1_REG_IDAC_EN = "0b0"; + parameter CH1_REG_IDAC_SEL = "0b0000000000"; + parameter CH1_REQ_EN = "0b0"; + parameter CH1_REQ_LVL_SET = "0b00"; + parameter CH1_RIO_MODE = "0b0"; + parameter CH1_RLOS_SEL = "0b0"; + parameter CH1_RPWDNB = "0b0"; + parameter CH1_RTERM_RX = "0b00000"; + parameter CH1_RTERM_TX = "0b00000"; + parameter CH1_RXIN_CM = "0b00"; + parameter CH1_RXTERM_CM = "0b00"; + parameter CH1_RX_DCO_CK_DIV = "0b000"; + parameter CH1_RX_DIV11_SEL = "0b0"; + parameter CH1_RX_GEAR_BYPASS = "0b0"; + parameter CH1_RX_GEAR_MODE = "0b0"; + parameter CH1_RX_LOS_CEQ = "0b00"; + parameter CH1_RX_LOS_EN = "0b0"; + parameter CH1_RX_LOS_HYST_EN = "0b0"; + parameter CH1_RX_LOS_LVL = "0b000"; + parameter CH1_RX_RATE_SEL = "0b0000"; + parameter CH1_RX_SB_BYPASS = "0b0"; + parameter CH1_SB_BYPASS = "0b0"; + parameter CH1_SEL_SD_RX_CLK = "0b0"; + parameter CH1_TDRV_DAT_SEL = "0b00"; + parameter CH1_TDRV_POST_EN = "0b0"; + parameter CH1_TDRV_PRE_EN = "0b0"; + parameter CH1_TDRV_SLICE0_CUR = "0b000"; + parameter CH1_TDRV_SLICE0_SEL = "0b00"; + parameter CH1_TDRV_SLICE1_CUR = "0b000"; + parameter CH1_TDRV_SLICE1_SEL = "0b00"; + parameter CH1_TDRV_SLICE2_CUR = "0b00"; + parameter CH1_TDRV_SLICE2_SEL = "0b00"; + parameter CH1_TDRV_SLICE3_CUR = "0b00"; + parameter CH1_TDRV_SLICE3_SEL = "0b00"; + parameter CH1_TDRV_SLICE4_CUR = "0b00"; + parameter CH1_TDRV_SLICE4_SEL = "0b00"; + parameter CH1_TDRV_SLICE5_CUR = "0b00"; + parameter CH1_TDRV_SLICE5_SEL = "0b00"; + parameter CH1_TPWDNB = "0b0"; + parameter CH1_TX_CM_SEL = "0b00"; + parameter CH1_TX_DIV11_SEL = "0b0"; + parameter CH1_TX_GEAR_BYPASS = "0b0"; + parameter CH1_TX_GEAR_MODE = "0b0"; + parameter CH1_TX_POST_SIGN = "0b0"; + parameter CH1_TX_PRE_SIGN = "0b0"; + parameter CH1_UC_MODE = "0b0"; + parameter CH1_UDF_COMMA_A = "0b0000000000"; + parameter CH1_UDF_COMMA_B = "0b0000000000"; + parameter CH1_UDF_COMMA_MASK = "0b0000000000"; + parameter CH1_WA_BYPASS = "0b0"; + parameter CH1_WA_MODE = "0b0"; + parameter D_BITCLK_FROM_ND_EN = "0b0"; + parameter D_BITCLK_LOCAL_EN = "0b0"; + parameter D_BITCLK_ND_EN = "0b0"; + parameter D_BUS8BIT_SEL = "0b0"; + parameter D_CDR_LOL_SET = "0b00"; + parameter D_CMUSETBIASI = "0b00"; + parameter D_CMUSETI4CPP = "0b0000"; + parameter D_CMUSETI4CPZ = "0b0000"; + parameter D_CMUSETI4VCO = "0b00"; + parameter D_CMUSETICP4P = "0b00"; + parameter D_CMUSETICP4Z = "0b000"; + parameter D_CMUSETINITVCT = "0b00"; + parameter D_CMUSETISCL4VCO = "0b000"; + parameter D_CMUSETP1GM = "0b000"; + parameter D_CMUSETP2AGM = "0b000"; + parameter D_CMUSETZGM = "0b000"; + parameter D_DCO_CALIB_TIME_SEL = "0b00"; + parameter D_HIGH_MARK = "0b0000"; + parameter D_IB_PWDNB = "0b0"; + parameter D_ISETLOS = "0b00000000"; + parameter D_LOW_MARK = "0b0000"; + parameter D_MACROPDB = "0b0"; + parameter D_PD_ISET = "0b00"; + parameter D_PLL_LOL_SET = "0b00"; + parameter D_REFCK_MODE = "0b000"; + parameter D_REQ_ISET = "0b000"; + parameter D_RG_EN = "0b0"; + parameter D_RG_SET = "0b00"; + parameter D_SETICONST_AUX = "0b00"; + parameter D_SETICONST_CH = "0b00"; + parameter D_SETIRPOLY_AUX = "0b00"; + parameter D_SETIRPOLY_CH = "0b00"; + parameter D_SETPLLRC = "0b000000"; + parameter D_SYNC_LOCAL_EN = "0b0"; + parameter D_SYNC_ND_EN = "0b0"; + parameter D_TXPLL_PWDNB = "0b0"; + parameter D_TX_VCO_CK_DIV = "0b000"; + parameter D_XGE_MODE = "0b0"; + +// These parameters don't do anything but are +// needed for compatibility with Diamond + parameter D_TX_MAX_RATE = "2.5"; + parameter D_RX_MAX_RATE = "2.5"; + parameter CH0_TXAMPLITUDE = "0d1300"; + parameter CH1_TXAMPLITUDE = "0d1300"; + parameter CH0_PROTOCOL = "8B10B"; + parameter CH1_PROTOCOL = "8B10B"; + parameter CH0_CDR_MAX_RATE = "2.5"; + parameter CH1_CDR_MAX_RATE = "2.5"; +endmodule + +(* blackbox *) +module EXTREFB ( + input REFCLKP, REFCLKN, + output REFCLKO +); + parameter REFCK_PWDNB = "0b0"; + parameter REFCK_RTERM = "0b0"; + parameter REFCK_DCBIAS_EN = "0b0"; +endmodule + +(* blackbox *) +module PCSCLKDIV ( + input CLKI, RST, SEL2, SEL1, SEL0, + output CDIV1, CDIVX +); + parameter GSR = "DISABLED"; +endmodule diff --git a/yosys/techlibs/ecp5/cells_map.v b/yosys/techlibs/ecp5/cells_map.v new file mode 100644 index 000000000..23182bdeb --- /dev/null +++ b/yosys/techlibs/ecp5/cells_map.v @@ -0,0 +1,135 @@ +module \$_DFF_N_ (input D, C, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule +module \$_DFF_P_ (input D, C, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +module \$_DFFE_NN_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("INV"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule +module \$_DFFE_PN_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("INV"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +module \$_DFFE_NP_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule +module \$_DFFE_PP_ (input D, C, E, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(1'b0), .DI(D), .Q(Q)); endmodule + +module \$_DFF_NN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$_DFF_NN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$_DFF_NP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_NP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$_DFF_PP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule + +module \$__DFFS_NN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_NN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PN0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PN1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$__DFFS_NP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_NP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PP0_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFS_PP1_ (input D, C, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .LSR(R), .DI(D), .Q(Q)); endmodule + +module \$__DFFE_NN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_NN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$__DFFE_NP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule + +module \$__DFFSE_NN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_NN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PN0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PN1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(!R), .DI(D), .Q(Q)); endmodule + +module \$__DFFSE_NP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_NP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("INV"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PP0 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule +module \$__DFFSE_PP1 (input D, C, E, R, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(C), .CE(E), .LSR(R), .DI(D), .Q(Q)); endmodule + +`ifndef NO_LUT +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[0]), .B(1'b0), .C(1'b0), .D(1'b0)); + end else + if (WIDTH == 2) begin + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[0]), .B(A[1]), .C(1'b0), .D(1'b0)); + end else + if (WIDTH == 3) begin + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[0]), .B(A[1]), .C(A[2]), .D(1'b0)); + end else + if (WIDTH == 4) begin + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + `ifndef NO_PFUMUX + end else + if (WIDTH == 5) begin + wire f0, f1; + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y)); + end else + if (WIDTH == 6) begin + wire f0, f1, f2, f3, g0, g1; + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); + L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y)); + end else + if (WIDTH == 7) begin + wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1; + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); + PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2)); + PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3)); + L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0)); + L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1)); + L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y)); + `endif + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule +`endif diff --git a/yosys/techlibs/ecp5/cells_sim.v b/yosys/techlibs/ecp5/cells_sim.v new file mode 100644 index 000000000..507ab1beb --- /dev/null +++ b/yosys/techlibs/ecp5/cells_sim.v @@ -0,0 +1,549 @@ +// --------------------------------------- + +module LUT4(input A, B, C, D, output Z); + parameter [15:0] INIT = 16'h0000; + wire [7:0] s3 = D ? INIT[15:8] : INIT[7:0]; + wire [3:0] s2 = C ? s3[ 7:4] : s3[3:0]; + wire [1:0] s1 = B ? s2[ 3:2] : s2[1:0]; + assign Z = A ? s1[1] : s1[0]; +endmodule + +// --------------------------------------- + +module L6MUX21 (input D0, D1, SD, output Z); + assign Z = SD ? D1 : D0; +endmodule + +// --------------------------------------- + +module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1, + output S0, S1, COUT); + + parameter [15:0] INIT0 = 16'h0000; + parameter [15:0] INIT1 = 16'h0000; + parameter INJECT1_0 = "YES"; + parameter INJECT1_1 = "YES"; + + // First half + wire LUT4_0, LUT2_0; + LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0)); + LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0)); + + wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN; + assign S0 = LUT4_0 ^ gated_cin_0; + + wire gated_lut2_0 = (INJECT1_0 == "YES") ? 1'b0 : LUT2_0; + wire cout_0 = (~LUT4_0 & gated_lut2_0) | (LUT4_0 & CIN); + + // Second half + wire LUT4_1, LUT2_1; + LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1)); + LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1)); + + wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0; + assign S1 = LUT4_1 ^ gated_cin_1; + + wire gated_lut2_1 = (INJECT1_1 == "YES") ? 1'b0 : LUT2_1; + assign COUT = (~LUT4_1 & gated_lut2_1) | (LUT4_1 & cout_0); + +endmodule + +// --------------------------------------- + +module TRELLIS_RAM16X2 ( + input DI0, DI1, + input WAD0, WAD1, WAD2, WAD3, + input WRE, WCK, + input RAD0, RAD1, RAD2, RAD3, + output DO0, DO1 +); + parameter WCKMUX = "WCK"; + parameter WREMUX = "WRE"; + parameter INITVAL_0 = 16'h0000; + parameter INITVAL_1 = 16'h0000; + + reg [1:0] mem[15:0]; + + integer i; + initial begin + for (i = 0; i < 16; i = i + 1) + mem[i] <= {INITVAL_1[i], INITVAL_0[i]}; + end + + wire muxwck = (WCKMUX == "INV") ? ~WCK : WCK; + + reg muxwre; + always @(*) + case (WREMUX) + "1": muxwre = 1'b1; + "0": muxwre = 1'b0; + "INV": muxwre = ~WRE; + default: muxwre = WRE; + endcase + + + always @(posedge muxwck) + if (muxwre) + mem[{WAD3, WAD2, WAD1, WAD0}] <= {DI1, DI0}; + + assign {DO1, DO0} = mem[{RAD3, RAD2, RAD1, RAD0}]; +endmodule + +// --------------------------------------- + +module PFUMX (input ALUT, BLUT, C0, output Z); + assign Z = C0 ? ALUT : BLUT; +endmodule + +// --------------------------------------- + +module TRELLIS_DPR16X4 ( + input [3:0] DI, + input [3:0] WAD, + input WRE, WCK, + input [3:0] RAD, + output [3:0] DO +); + parameter WCKMUX = "WCK"; + parameter WREMUX = "WRE"; + parameter [63:0] INITVAL = 64'h0000000000000000; + + reg [3:0] mem[15:0]; + + integer i; + initial begin + for (i = 0; i < 16; i = i + 1) + mem[i] <= {INITVAL[i+3], INITVAL[i+2], INITVAL[i+1], INITVAL[i]}; + end + + wire muxwck = (WCKMUX == "INV") ? ~WCK : WCK; + + reg muxwre; + always @(*) + case (WREMUX) + "1": muxwre = 1'b1; + "0": muxwre = 1'b0; + "INV": muxwre = ~WRE; + default: muxwre = WRE; + endcase + + always @(posedge muxwck) + if (muxwre) + mem[WAD] <= DI; + + assign DO = mem[RAD]; +endmodule + +// --------------------------------------- + +module DPR16X4C ( + input [3:0] DI, + input WCK, WRE, + input [3:0] RAD, + input [3:0] WAD, + output [3:0] DO +); + // For legacy Lattice compatibility, INITIVAL is a hex + // string rather than a numeric parameter + parameter INITVAL = "0x0000000000000000"; + + function [63:0] convert_initval; + input [143:0] hex_initval; + reg done; + reg [63:0] temp; + reg [7:0] char; + integer i; + begin + done = 1'b0; + temp = 0; + for (i = 0; i < 16; i = i + 1) begin + if (!done) begin + char = hex_initval[8*i +: 8]; + if (char == "x") begin + done = 1'b1; + end else begin + if (char >= "0" && char <= "9") + temp[4*i +: 4] = char - "0"; + else if (char >= "A" && char <= "F") + temp[4*i +: 4] = 10 + char - "A"; + else if (char >= "a" && char <= "f") + temp[4*i +: 4] = 10 + char - "a"; + end + end + end + convert_initval = temp; + end + endfunction + + localparam conv_initval = convert_initval(INITVAL); + + reg [3:0] ram[0:15]; + integer i; + initial begin + for (i = 0; i < 15; i = i + 1) begin + ram[i] <= conv_initval[4*i +: 4]; + end + end + + always @(posedge WCK) + if (WRE) + ram[WAD] <= DI; + + assign DO = ram[RAD]; + +endmodule + +// --------------------------------------- + +module LUT2(input A, B, output Z); + parameter [3:0] INIT = 4'h0; + wire [1:0] s1 = B ? INIT[ 3:2] : INIT[1:0]; + assign Z = A ? s1[1] : s1[0]; +endmodule + +// --------------------------------------- + +module TRELLIS_FF(input CLK, LSR, CE, DI, output reg Q); + parameter GSR = "ENABLED"; + parameter [127:0] CEMUX = "1"; + parameter CLKMUX = "CLK"; + parameter LSRMUX = "LSR"; + parameter SRMODE = "LSR_OVER_CE"; + parameter REGSET = "RESET"; + + reg muxce; + always @(*) + case (CEMUX) + "1": muxce = 1'b1; + "0": muxce = 1'b0; + "INV": muxce = ~CE; + default: muxce = CE; + endcase + + wire muxlsr = (LSRMUX == "INV") ? ~LSR : LSR; + wire muxclk = (CLKMUX == "INV") ? ~CLK : CLK; + + localparam srval = (REGSET == "SET") ? 1'b1 : 1'b0; + + initial Q = srval; + + generate + if (SRMODE == "ASYNC") begin + always @(posedge muxclk, posedge muxlsr) + if (muxlsr) + Q <= srval; + else if (muxce) + Q <= DI; + end else begin + always @(posedge muxclk) + if (muxlsr) + Q <= srval; + else if (muxce) + Q <= DI; + end + endgenerate +endmodule + +// --------------------------------------- + +module OBZ(input I, T, output O); +assign O = T ? 1'bz : I; +endmodule + +// --------------------------------------- + +module IB(input I, output O); +assign O = I; +endmodule + +// --------------------------------------- +(* keep *) +module TRELLIS_IO( + inout B, + input I, + input T, + output O +); + parameter DIR = "INPUT"; + reg T_pd; + always @(*) if (T === 1'bz) T_pd <= 1'b0; else T_pd <= T; + + generate + if (DIR == "INPUT") begin + assign B = 1'bz; + assign O = B; + end else if (DIR == "OUTPUT") begin + assign B = T_pd ? 1'bz : I; + assign O = 1'bx; + end else if (DIR == "BIDIR") begin + assign B = T_pd ? 1'bz : I; + assign O = B; + end else begin + ERROR_UNKNOWN_IO_MODE error(); + end + endgenerate + +endmodule + +// --------------------------------------- + +module OB(input I, output O); +assign O = I; +endmodule + +// --------------------------------------- + +module BB(input I, T, output O, inout B); +assign B = T ? 1'bz : I; +assign O = B; +endmodule + +// --------------------------------------- + +module INV(input A, output Z); + assign Z = !A; +endmodule + +// --------------------------------------- + +module TRELLIS_SLICE( + input A0, B0, C0, D0, + input A1, B1, C1, D1, + input M0, M1, + input FCI, FXA, FXB, + + input CLK, LSR, CE, + input DI0, DI1, + + input WD0, WD1, + input WAD0, WAD1, WAD2, WAD3, + input WRE, WCK, + + output F0, Q0, + output F1, Q1, + output FCO, OFX0, OFX1, + + output WDO0, WDO1, WDO2, WDO3, + output WADO0, WADO1, WADO2, WADO3 +); + + parameter MODE = "LOGIC"; + parameter GSR = "ENABLED"; + parameter SRMODE = "LSR_OVER_CE"; + parameter [127:0] CEMUX = "1"; + parameter CLKMUX = "CLK"; + parameter LSRMUX = "LSR"; + parameter LUT0_INITVAL = 16'h0000; + parameter LUT1_INITVAL = 16'h0000; + parameter REG0_SD = "0"; + parameter REG1_SD = "0"; + parameter REG0_REGSET = "RESET"; + parameter REG1_REGSET = "RESET"; + parameter [127:0] CCU2_INJECT1_0 = "NO"; + parameter [127:0] CCU2_INJECT1_1 = "NO"; + parameter WREMUX = "WRE"; + + function [15:0] permute_initval; + input [15:0] initval; + integer i; + begin + for (i = 0; i < 16; i = i + 1) begin + permute_initval[{i[0], i[2], i[1], i[3]}] = initval[i]; + end + end + endfunction + + generate + if (MODE == "LOGIC") begin + // LUTs + LUT4 #( + .INIT(LUT0_INITVAL) + ) lut4_0 ( + .A(A0), .B(B0), .C(C0), .D(D0), + .Z(F0) + ); + LUT4 #( + .INIT(LUT1_INITVAL) + ) lut4_1 ( + .A(A1), .B(B1), .C(C1), .D(D1), + .Z(F1) + ); + // LUT expansion muxes + PFUMX lut5_mux (.ALUT(F1), .BLUT(F0), .C0(M0), .Z(OFX0)); + L6MUX21 lutx_mux (.D0(FXA), .D1(FXB), .SD(M1), .Z(OFX1)); + end else if (MODE == "CCU2") begin + CCU2C #( + .INIT0(LUT0_INITVAL), + .INIT1(LUT1_INITVAL), + .INJECT1_0(CCU2_INJECT1_0), + .INJECT1_1(CCU2_INJECT1_1) + ) ccu2c_i ( + .CIN(FCI), + .A0(A0), .B0(B0), .C0(C0), .D0(D0), + .A1(A1), .B1(B1), .C1(C1), .D1(D1), + .S0(F0), .S1(F1), + .COUT(FCO) + ); + end else if (MODE == "RAMW") begin + assign WDO0 = C1; + assign WDO1 = A1; + assign WDO2 = D1; + assign WDO3 = B1; + assign WADO0 = D0; + assign WADO1 = B0; + assign WADO2 = C0; + assign WADO3 = A0; + end else if (MODE == "DPRAM") begin + TRELLIS_RAM16X2 #( + .INITVAL_0(permute_initval(LUT0_INITVAL)), + .INITVAL_1(permute_initval(LUT1_INITVAL)), + .WREMUX(WREMUX) + ) ram_i ( + .DI0(WD0), .DI1(WD1), + .WAD0(WAD0), .WAD1(WAD1), .WAD2(WAD2), .WAD3(WAD3), + .WRE(WRE), .WCK(WCK), + .RAD0(D0), .RAD1(B0), .RAD2(C0), .RAD3(A0), + .DO0(F0), .DO1(F1) + ); + // TODO: confirm RAD and INITVAL ordering + // DPRAM mode contract? + always @(*) begin + assert(A0==A1); + assert(B0==B1); + assert(C0==C1); + assert(D0==D1); + end + end else begin + ERROR_UNKNOWN_SLICE_MODE error(); + end + endgenerate + + // FF input selection muxes + wire muxdi0 = (REG0_SD == "1") ? DI0 : M0; + wire muxdi1 = (REG1_SD == "1") ? DI1 : M1; + // Flipflops + TRELLIS_FF #( + .GSR(GSR), + .CEMUX(CEMUX), + .CLKMUX(CLKMUX), + .LSRMUX(LSRMUX), + .SRMODE(SRMODE), + .REGSET(REG0_REGSET) + ) ff_0 ( + .CLK(CLK), .LSR(LSR), .CE(CE), + .DI(muxdi0), + .Q(Q0) + ); + TRELLIS_FF #( + .GSR(GSR), + .CEMUX(CEMUX), + .CLKMUX(CLKMUX), + .LSRMUX(LSRMUX), + .SRMODE(SRMODE), + .REGSET(REG1_REGSET) + ) ff_1 ( + .CLK(CLK), .LSR(LSR), .CE(CE), + .DI(muxdi1), + .Q(Q1) + ); +endmodule + +(* blackbox *) +module DP16KD( + input DIA17, DIA16, DIA15, DIA14, DIA13, DIA12, DIA11, DIA10, DIA9, DIA8, DIA7, DIA6, DIA5, DIA4, DIA3, DIA2, DIA1, DIA0, + input ADA13, ADA12, ADA11, ADA10, ADA9, ADA8, ADA7, ADA6, ADA5, ADA4, ADA3, ADA2, ADA1, ADA0, + input CEA, OCEA, CLKA, WEA, RSTA, + input CSA2, CSA1, CSA0, + output DOA17, DOA16, DOA15, DOA14, DOA13, DOA12, DOA11, DOA10, DOA9, DOA8, DOA7, DOA6, DOA5, DOA4, DOA3, DOA2, DOA1, DOA0, + + input DIB17, DIB16, DIB15, DIB14, DIB13, DIB12, DIB11, DIB10, DIB9, DIB8, DIB7, DIB6, DIB5, DIB4, DIB3, DIB2, DIB1, DIB0, + input ADB13, ADB12, ADB11, ADB10, ADB9, ADB8, ADB7, ADB6, ADB5, ADB4, ADB3, ADB2, ADB1, ADB0, + input CEB, OCEB, CLKB, WEB, RSTB, + input CSB2, CSB1, CSB0, + output DOB17, DOB16, DOB15, DOB14, DOB13, DOB12, DOB11, DOB10, DOB9, DOB8, DOB7, DOB6, DOB5, DOB4, DOB3, DOB2, DOB1, DOB0 +); + parameter DATA_WIDTH_A = 18; + parameter DATA_WIDTH_B = 18; + + parameter REGMODE_A = "NOREG"; + parameter REGMODE_B = "NOREG"; + + parameter RESETMODE = "SYNC"; + parameter ASYNC_RESET_RELEASE = "SYNC"; + + parameter CSDECODE_A = "0b000"; + parameter CSDECODE_B = "0b000"; + + parameter WRITEMODE_A = "NORMAL"; + parameter WRITEMODE_B = "NORMAL"; + + parameter CLKAMUX = "CLKA"; + parameter CLKBMUX = "CLKB"; + + parameter GSR = "ENABLED"; + + parameter INITVAL_00 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_01 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_02 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_03 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_04 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_05 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_06 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_07 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_08 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_09 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_0F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_10 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_11 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_12 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_13 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_14 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_15 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_16 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_17 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_18 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_19 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_1F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_20 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_21 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_22 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_23 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_24 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_25 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_26 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_27 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_28 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_29 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_2F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_30 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_31 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_32 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_33 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_34 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_35 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_36 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_37 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_38 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_39 = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3A = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3B = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3C = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3D = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3E = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; + parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000; +endmodule diff --git a/yosys/techlibs/ecp5/dram.txt b/yosys/techlibs/ecp5/dram.txt new file mode 100644 index 000000000..b94357429 --- /dev/null +++ b/yosys/techlibs/ecp5/dram.txt @@ -0,0 +1,17 @@ +bram $__TRELLIS_DPR16X4 + init 1 + abits 4 + dbits 4 + groups 2 + ports 1 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 0 1 + clkpol 0 2 +endbram + +match $__TRELLIS_DPR16X4 + make_outreg + min wports 1 +endmatch diff --git a/yosys/techlibs/ecp5/drams_map.v b/yosys/techlibs/ecp5/drams_map.v new file mode 100644 index 000000000..3b3de831f --- /dev/null +++ b/yosys/techlibs/ecp5/drams_map.v @@ -0,0 +1,28 @@ +module \$__TRELLIS_DPR16X4 (CLK1, A1ADDR, A1DATA, B1ADDR, B1DATA, B1EN); + parameter [63:0] INIT = 64'bx; + parameter CLKPOL2 = 1; + input CLK1; + + input [3:0] A1ADDR; + output [3:0] A1DATA; + + input [3:0] B1ADDR; + input [3:0] B1DATA; + input B1EN; + + localparam WCKMUX = CLKPOL2 ? "WCK" : "INV"; + + TRELLIS_DPR16X4 #( + .INITVAL(INIT), + .WCKMUX(WCKMUX), + .WREMUX("WRE") + ) _TECHMAP_REPLACE_ ( + .RAD(A1ADDR), + .DO(A1DATA), + + .WAD(B1ADDR), + .DI(B1DATA), + .WCK(CLK1), + .WRE(B1EN) + ); +endmodule diff --git a/yosys/techlibs/ecp5/latches_map.v b/yosys/techlibs/ecp5/latches_map.v new file mode 100644 index 000000000..c28f88cf7 --- /dev/null +++ b/yosys/techlibs/ecp5/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/yosys/techlibs/ecp5/synth_ecp5.cc b/yosys/techlibs/ecp5/synth_ecp5.cc new file mode 100644 index 000000000..2e9176a84 --- /dev/null +++ b/yosys/techlibs/ecp5/synth_ecp5.cc @@ -0,0 +1,325 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 Clifford Wolf + * + * 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 SynthEcp5Pass : public ScriptPass +{ + SynthEcp5Pass() : ScriptPass("synth_ecp5", "synthesis for ECP5 FPGAs") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_ecp5 [options]\n"); + log("\n"); + log("This command runs synthesis for ECP5 FPGAs.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module\n"); + log("\n"); + log(" -blif \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 \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(" -json \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 :\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(" -noccu2\n"); + log(" do not use CCU2 cells in output netlist\n"); + log("\n"); + log(" -nodffe\n"); + log(" do not use flipflops with CE in output netlist\n"); + log("\n"); + log(" -nobram\n"); + log(" do not use BRAM cells in output netlist\n"); + log("\n"); + log(" -nodram\n"); + log(" do not use distributed RAM cells in output netlist\n"); + log("\n"); + log(" -nomux\n"); + log(" do not use PFU muxes to implement LUTs larger than LUT4s\n"); + log("\n"); + log(" -abc2\n"); + log(" run two passes of 'abc' for slightly improved logic density\n"); + log("\n"); + log(" -vpr\n"); + log(" generate an output netlist (and BLIF file) suitable for VPR\n"); + log(" (this feature is experimental and incomplete)\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, json_file; + bool noccu2, nodffe, nobram, nodram, nomux, flatten, retime, abc2, vpr; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + blif_file = ""; + edif_file = ""; + json_file = ""; + noccu2 = false; + nodffe = false; + nobram = false; + nodram = false; + nomux = false; + flatten = true; + retime = false; + abc2 = false; + vpr = false; + } + + void execute(std::vector 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] == "-json" && argidx+1 < args.size()) { + json_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] == "-noccu2") { + noccu2 = true; + continue; + } + if (args[argidx] == "-nodffe") { + nodffe = true; + continue; + } + if (args[argidx] == "-nobram") { + nobram = true; + continue; + } + if (args[argidx] == "-nodram") { + nodram = true; + continue; + } + if (args[argidx] == "-nomux") { + nomux = true; + continue; + } + if (args[argidx] == "-abc2") { + abc2 = true; + continue; + } + if (args[argidx] == "-vpr") { + vpr = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_ECP5 pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v"); + run(stringf("hierarchy -check %s", help_mode ? "-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 +/ecp5/bram.txt"); + run("techmap -map +/ecp5/brams_map.v"); + } + + if (!nodram && check_label("dram", "(skip if -nodram)")) + { + run("memory_bram -rules +/ecp5/dram.txt"); + run("techmap -map +/ecp5/drams_map.v"); + } + + if (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine"); + run("memory_map"); + run("opt -undriven -fine"); + if (noccu2) + run("techmap"); + else + run("techmap -map +/techmap.v -map +/ecp5/arith_map.v"); + if (retime || help_mode) + run("abc -dff", "(only if -retime)"); + } + + if (check_label("map_ffs")) + { + run("dffsr2dff"); + run("dff2dffs"); + run("opt_clean"); + if (!nodffe) + run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*"); + run("techmap -D NO_LUT -map +/ecp5/cells_map.v"); + run("opt_expr -mux_undef"); + run("simplemap"); + // TODO +#if 0 + run("ecp5_ffinit"); +#endif + } + + if (check_label("map_luts")) + { + if (abc2 || help_mode) { + run("abc", " (only if -abc2)"); + } + run("techmap -map +/ecp5/latches_map.v"); + if (nomux) + run("abc -lut 4"); + else + run("abc -lut 4:7"); + run("clean"); + } + + if (check_label("map_cells")) + { + if (vpr) + run("techmap -D NO_LUT -map +/ecp5/cells_map.v"); + else + run("techmap -map +/ecp5/cells_map.v", "(with -D NO_LUT in vpr mode)"); + + run("clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("blif")) + { + if (!blif_file.empty() || help_mode) { + if (vpr || help_mode) { + run(stringf("opt_clean -purge"), + " (vpr mode)"); + run(stringf("write_blif -attr -cname -conn -param %s", + help_mode ? "" : blif_file.c_str()), + " (vpr mode)"); + } + if (!vpr) + run(stringf("write_blif -gates -attr -param %s", + help_mode ? "" : blif_file.c_str()), + " (non-vpr mode)"); + } + } + + if (check_label("edif")) + { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif %s", help_mode ? "" : edif_file.c_str())); + } + + if (check_label("json")) + { + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "" : json_file.c_str())); + } + } +} SynthEcp5Pass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/gowin/Makefile.inc b/yosys/techlibs/gowin/Makefile.inc index 679d7eff5..2f82def7d 100644 --- a/yosys/techlibs/gowin/Makefile.inc +++ b/yosys/techlibs/gowin/Makefile.inc @@ -3,4 +3,5 @@ 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)) +$(eval $(call add_share_file,share/gowin,techlibs/gowin/arith_map.v)) diff --git a/yosys/techlibs/gowin/arith_map.v b/yosys/techlibs/gowin/arith_map.v new file mode 100644 index 000000000..25e789e4a --- /dev/null +++ b/yosys/techlibs/gowin/arith_map.v @@ -0,0 +1,59 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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_gw1n_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 + ALU #(.ALU_MODE(32'b0)) + alu(.I0(AA[i]), + .I1(BB[i]), + .I3(1'b0), + .CIN(C[i]), + .COUT(CO[i]), + .SUM(Y[i]) + ); + end endgenerate + assign X = AA ^ BB; +endmodule + diff --git a/yosys/techlibs/gowin/cells_sim.v b/yosys/techlibs/gowin/cells_sim.v index 3a09c157f..14441c2fc 100644 --- a/yosys/techlibs/gowin/cells_sim.v +++ b/yosys/techlibs/gowin/cells_sim.v @@ -1,51 +1,65 @@ module LUT1(output F, input I0); - parameter [1:0] INIT = 0; - assign F = I0 ? INIT[1] : INIT[0]; + 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]; + 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]; + 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]; + 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) + parameter [0:0] INIT = 1'b0; + initial Q = INIT; + always @(posedge CLK) Q <= D; endmodule module DFFN (output reg Q, input CLK, D); - always @(negedge C) + parameter [0:0] INIT = 1'b0; + initial Q = INIT; + always @(negedge CLK) Q <= D; endmodule module VCC(output V); - assign V = 1; + assign V = 1; endmodule module GND(output G); - assign G = 0; + assign G = 0; endmodule module IBUF(output O, input I); - assign O = I; + assign O = I; endmodule module OBUF(output O, input I); - assign O = I; + assign O = I; endmodule + +module GSR (input GSRI); + wire GSRO = GSRI; +endmodule + +module ALU (input I0, input I1, input I3, input CIN, output COUT, output SUM); + parameter [3:0] ALU_MODE = 0; // default 0 = ADD + assign {COUT, SUM} = CIN + I1 + I0; +endmodule // alu + diff --git a/yosys/techlibs/gowin/synth_gowin.cc b/yosys/techlibs/gowin/synth_gowin.cc index 129ab8393..96128a680 100644 --- a/yosys/techlibs/gowin/synth_gowin.cc +++ b/yosys/techlibs/gowin/synth_gowin.cc @@ -29,7 +29,7 @@ struct SynthGowinPass : public ScriptPass { SynthGowinPass() : ScriptPass("synth_gowin", "synthesis for Gowin FPGAs") { } - virtual void help() YS_OVERRIDE + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -49,6 +49,12 @@ struct SynthGowinPass : public ScriptPass log(" from label is synonymous to 'begin', and empty to label is\n"); log(" synonymous to the end of the command list.\n"); log("\n"); + log(" -nobram\n"); + log(" do not use BRAM cells in output netlist\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"); @@ -59,16 +65,18 @@ struct SynthGowinPass : public ScriptPass } string top_opt, vout_file; - bool retime; + bool retime, flatten, nobram; - virtual void clear_flags() YS_OVERRIDE + void clear_flags() YS_OVERRIDE { top_opt = "-auto-top"; vout_file = ""; retime = false; + flatten = true; + nobram = true; } - virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { string run_from, run_to; clear_flags(); @@ -96,12 +104,20 @@ struct SynthGowinPass : public ScriptPass retime = true; continue; } + if (args[argidx] == "-nobram") { + nobram = true; + continue; + } + if (args[argidx] == "-noflatten") { + flatten = false; + continue; + } break; } extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_GOWIN pass.\n"); log_push(); @@ -111,7 +127,7 @@ struct SynthGowinPass : public ScriptPass log_pop(); } - virtual void script() YS_OVERRIDE + void script() YS_OVERRIDE { if (check_label("begin")) { @@ -119,7 +135,7 @@ struct SynthGowinPass : public ScriptPass run(stringf("hierarchy -check %s", help_mode ? "-top " : top_opt.c_str())); } - if (check_label("flatten")) + if (flatten && check_label("flatten", "(unless -noflatten)")) { run("proc"); run("flatten"); @@ -131,13 +147,18 @@ struct SynthGowinPass : public ScriptPass { run("synth -run coarse"); } - + if (!nobram && check_label("bram", "(skip if -nobram)")) + { + run("memory_bram -rules +/gowin/bram.txt"); + run("techmap -map +/gowin/brams_map.v"); + } if (check_label("fine")) { run("opt -fast -mux_undef -undriven -fine"); run("memory_map"); run("opt -undriven -fine"); - run("techmap"); + run("techmap -map +/techmap.v -map +/gowin/arith_map.v"); + run("opt -fine"); run("clean -purge"); run("splitnets -ports"); run("setundef -undriven -zero"); @@ -169,7 +190,7 @@ struct SynthGowinPass : public ScriptPass if (check_label("vout")) { if (!vout_file.empty() || help_mode) - run(stringf("write_verilog -attr2comment -defparam -renameprefix gen %s", + run(stringf("write_verilog -nodec -attr2comment -defparam -renameprefix gen %s", help_mode ? "" : vout_file.c_str())); } } diff --git a/yosys/techlibs/greenpak4/Makefile.inc b/yosys/techlibs/greenpak4/Makefile.inc index 1c9871e2f..b43fb7511 100644 --- a/yosys/techlibs/greenpak4/Makefile.inc +++ b/yosys/techlibs/greenpak4/Makefile.inc @@ -1,8 +1,12 @@ 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_blackbox.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_latch.v)) $(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/cells_sim_ams.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_sim_digital.v)) +$(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/cells_sim_wip.v)) $(eval $(call add_share_file,share/greenpak4,techlibs/greenpak4/gp_dff.lib)) diff --git a/yosys/techlibs/greenpak4/cells_blackbox.v b/yosys/techlibs/greenpak4/cells_blackbox.v new file mode 100644 index 000000000..1895b90de --- /dev/null +++ b/yosys/techlibs/greenpak4/cells_blackbox.v @@ -0,0 +1,18 @@ +module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP); + + input wire CE; + input wire CLK; + output reg OUT; + output reg[WIDTH-1:0] POUT; + input wire RST; + input wire UP; + + parameter COUNT_TO = 1; + parameter RESET_MODE = "RISING"; + parameter RESET_TO_MAX = "1"; + parameter HAS_POUT = 0; + parameter HAS_CE = 0; + parameter WIDTH = 8; + parameter DIRECTION = "DOWN"; + +endmodule diff --git a/yosys/techlibs/greenpak4/cells_latch.v b/yosys/techlibs/greenpak4/cells_latch.v new file mode 100644 index 000000000..2ccdd2031 --- /dev/null +++ b/yosys/techlibs/greenpak4/cells_latch.v @@ -0,0 +1,15 @@ +module $_DLATCH_P_(input E, input D, output Q); + GP_DLATCH _TECHMAP_REPLACE_ ( + .D(D), + .nCLK(!E), + .Q(Q) + ); +endmodule + +module $_DLATCH_N_(input E, input D, output Q); + GP_DLATCH _TECHMAP_REPLACE_ ( + .D(D), + .nCLK(E), + .Q(Q) + ); +endmodule diff --git a/yosys/techlibs/greenpak4/cells_map.v b/yosys/techlibs/greenpak4/cells_map.v index 111a77a14..b971a51fa 100644 --- a/yosys/techlibs/greenpak4/cells_map.v +++ b/yosys/techlibs/greenpak4/cells_map.v @@ -50,6 +50,58 @@ module GP_DFFRI(input D, CLK, nRST, output reg nQ); ); endmodule +module GP_DLATCHS(input D, nCLK, nSET, output reg Q); + parameter [0:0] INIT = 1'bx; + GP_DLATCHSR #( + .INIT(INIT), + .SRMODE(1'b1), + ) _TECHMAP_REPLACE_ ( + .D(D), + .nCLK(nCLK), + .nSR(nSET), + .Q(Q) + ); +endmodule + +module GP_DLATCHR(input D, nCLK, nRST, output reg Q); + parameter [0:0] INIT = 1'bx; + GP_DLATCHSR #( + .INIT(INIT), + .SRMODE(1'b0), + ) _TECHMAP_REPLACE_ ( + .D(D), + .nCLK(nCLK), + .nSR(nRST), + .Q(Q) + ); +endmodule + +module GP_DLATCHSI(input D, nCLK, nSET, output reg nQ); + parameter [0:0] INIT = 1'bx; + GP_DLATCHSRI #( + .INIT(INIT), + .SRMODE(1'b1), + ) _TECHMAP_REPLACE_ ( + .D(D), + .nCLK(nCLK), + .nSR(nSET), + .nQ(nQ) + ); +endmodule + +module GP_DLATCHRI(input D, nCLK, nRST, output reg nQ); + parameter [0:0] INIT = 1'bx; + GP_DLATCHSRI #( + .INIT(INIT), + .SRMODE(1'b0), + ) _TECHMAP_REPLACE_ ( + .D(D), + .nCLK(nCLK), + .nSR(nRST), + .nQ(nQ) + ); +endmodule + module GP_OBUFT(input IN, input OE, output OUT); GP_IOBUF _TECHMAP_REPLACE_ ( .IN(IN), @@ -92,3 +144,116 @@ module \$lut (A, Y); end endgenerate endmodule + +module \$__COUNT_ (CE, CLK, OUT, POUT, RST, UP); + + input wire CE; + input wire CLK; + output reg OUT; + output reg[WIDTH-1:0] POUT; + input wire RST; + input wire UP; + + parameter COUNT_TO = 1; + parameter RESET_MODE = "RISING"; + parameter RESET_TO_MAX = 0; + parameter HAS_POUT = 0; + parameter HAS_CE = 0; + parameter WIDTH = 8; + parameter DIRECTION = "DOWN"; + + //If we have a DIRECTION other than DOWN fail... GP_COUNTx_ADV is not supported yet + if(DIRECTION != "DOWN") begin + initial begin + $display("ERROR: \$__COUNT_ support for GP_COUNTx_ADV is not yet implemented. This counter should never have been extracted (bug in extract_counter pass?)."); + $finish; + end + end + + //If counter is more than 14 bits wide, complain (also shouldn't happen) + else if(WIDTH > 14) begin + initial begin + $display("ERROR: \$__COUNT_ support for cascaded counters is not yet implemented. This counter should never have been extracted (bug in extract_counter pass?)."); + $finish; + end + end + + //If counter is more than 8 bits wide and has parallel output, we have a problem + else if(WIDTH > 8 && HAS_POUT) begin + initial begin + $display("ERROR: \$__COUNT_ support for 9-14 bit counters with parallel output is not yet implemented. This counter should never have been extracted (bug in extract_counter pass?)."); + $finish; + end + end + + //Looks like a legal counter! Do something with it + else if(WIDTH <= 8) begin + if(HAS_CE) begin + wire ce_not; + GP_INV ceinv( + .IN(CE), + .OUT(ce_not) + ); + GP_COUNT8_ADV #( + .COUNT_TO(COUNT_TO), + .RESET_MODE(RESET_MODE), + .RESET_VALUE(RESET_TO_MAX ? "COUNT_TO" : "ZERO"), + .CLKIN_DIVIDE(1) + ) _TECHMAP_REPLACE_ ( + .CLK(CLK), + .RST(RST), + .OUT(OUT), + .UP(1'b0), //always count down for now + .KEEP(ce_not), + .POUT(POUT) + ); + end + else begin + GP_COUNT8 #( + .COUNT_TO(COUNT_TO), + .RESET_MODE(RESET_MODE), + .CLKIN_DIVIDE(1) + ) _TECHMAP_REPLACE_ ( + .CLK(CLK), + .RST(RST), + .OUT(OUT), + .POUT(POUT) + ); + end + end + + else begin + if(HAS_CE) begin + wire ce_not; + GP_INV ceinv( + .IN(CE), + .OUT(ce_not) + ); + GP_COUNT14_ADV #( + .COUNT_TO(COUNT_TO), + .RESET_MODE(RESET_TO_MAX ? "COUNT_TO" : "ZERO"), + .RESET_VALUE("COUNT_TO"), + .CLKIN_DIVIDE(1) + ) _TECHMAP_REPLACE_ ( + .CLK(CLK), + .RST(RST), + .OUT(OUT), + .UP(1'b0), //always count down for now + .KEEP(ce_not), + .POUT(POUT) + ); + end + else begin + GP_COUNT14 #( + .COUNT_TO(COUNT_TO), + .RESET_MODE(RESET_MODE), + .CLKIN_DIVIDE(1) + ) _TECHMAP_REPLACE_ ( + .CLK(CLK), + .RST(RST), + .OUT(OUT) + ); + end + end + +endmodule diff --git a/yosys/techlibs/greenpak4/cells_sim.v b/yosys/techlibs/greenpak4/cells_sim.v index 80746be0f..221bee694 100644 --- a/yosys/techlibs/greenpak4/cells_sim.v +++ b/yosys/techlibs/greenpak4/cells_sim.v @@ -1,461 +1,5 @@ `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 +`include "cells_sim_ams.v" +`include "cells_sim_digital.v" +`include "cells_sim_wip.v" diff --git a/yosys/techlibs/greenpak4/cells_sim_ams.v b/yosys/techlibs/greenpak4/cells_sim_ams.v new file mode 100644 index 000000000..7f8b3de3b --- /dev/null +++ b/yosys/techlibs/greenpak4/cells_sim_ams.v @@ -0,0 +1,110 @@ +`timescale 1ns/1ps + +/* + This file contains analog / mixed signal cells, or other things that are not possible to fully model + in behavioral Verilog. + + It also contains some stuff like oscillators that use non-synthesizeable constructs such as delays. + TODO: do we want a third file for those cells? + */ + +module GP_ABUF(input wire IN, output wire OUT); + + assign OUT = IN; + + //must be 1, 5, 20, 50 + //values >1 only available with Vdd > 2.7V + parameter BANDWIDTH_KHZ = 1; + +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; + +endmodule + +module GP_BANDGAP(output reg OK); + parameter AUTO_PWRDN = 1; + parameter CHOPPER_EN = 1; + parameter OUT_DELAY = 100; + +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_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_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_PWRDET(output reg VDD_LOW); + initial VDD_LOW = 0; +endmodule + +module GP_VREF(input VIN, output reg VOUT); + parameter VIN_DIV = 1; + parameter VREF = 0; + //cannot simulate mixed signal IP +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 diff --git a/yosys/techlibs/greenpak4/cells_sim_digital.v b/yosys/techlibs/greenpak4/cells_sim_digital.v new file mode 100644 index 000000000..43d35d08f --- /dev/null +++ b/yosys/techlibs/greenpak4/cells_sim_digital.v @@ -0,0 +1,794 @@ +`timescale 1ns/1ps + +/* + This file contains simulation models for GreenPAK cells which are possible to fully model using synthesizeable + behavioral Verilog constructs only. + */ + +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 wire IN0, + input wire IN1, + input wire IN2, + input wire IN3, + output wire OUT); + + parameter [15:0] INIT = 0; + assign OUT = INIT[{IN3, IN2, IN1, IN0}]; +endmodule + +module GP_CLKBUF(input wire IN, output wire OUT); + assign OUT = IN; +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; + + reg[13:0] count = COUNT_TO; + + initial begin + if(CLKIN_DIVIDE != 1) begin + $display("ERROR: CLKIN_DIVIDE values other than 1 not implemented"); + $finish; + end + end + + //Combinatorially output underflow flag whenever we wrap low + always @(*) begin + OUT <= (count == 14'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. + generate + case(RESET_MODE) + + "RISING": begin + always @(posedge CLK, posedge RST) begin + if(RST) + count <= 0; + else begin + count <= count - 1'd1; + if(count == 0) + count <= COUNT_TO; + end + end + end + + "FALLING": begin + always @(posedge CLK, negedge RST) begin + if(!RST) + count <= 0; + else begin + count <= count - 1'd1; + if(count == 0) + count <= COUNT_TO; + end + end + end + + "BOTH": begin + initial begin + $display("Both-edge reset mode for GP_COUNT14 not implemented"); + $finish; + end + end + + "LEVEL": begin + always @(posedge CLK, posedge RST) begin + if(RST) + count <= 0; + + else begin + count <= count - 1'd1; + if(count == 0) + count <= COUNT_TO; + end + end + end + + default: begin + initial begin + $display("Invalid RESET_MODE on GP_COUNT14"); + $finish; + end + end + + endcase + endgenerate + +endmodule + +module GP_COUNT14_ADV(input CLK, input RST, output reg OUT, + input UP, input KEEP, output reg[7:0] POUT); + + parameter RESET_MODE = "RISING"; + parameter RESET_VALUE = "ZERO"; + + parameter COUNT_TO = 14'h1; + parameter CLKIN_DIVIDE = 1; + + initial begin + if(CLKIN_DIVIDE != 1) begin + $display("ERROR: CLKIN_DIVIDE values other than 1 not implemented"); + $finish; + end + end + + reg[13:0] count = COUNT_TO; + + //Combinatorially output underflow flag whenever we wrap low + always @(*) begin + if(UP) + OUT <= (count == 14'h3fff); + else + OUT <= (count == 14'h0); + POUT <= count[7:0]; + 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. + generate + case(RESET_MODE) + + "RISING": begin + always @(posedge CLK, posedge RST) begin + + //Resets + if(RST) begin + if(RESET_VALUE == "ZERO") + count <= 0; + else + count <= COUNT_TO; + end + + else if(KEEP) begin + end + else if(UP) begin + count <= count + 1'd1; + if(count == 14'h3fff) + count <= COUNT_TO; + end + else begin + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + end + + end + end + + "FALLING": begin + always @(posedge CLK, negedge RST) begin + + //Resets + if(!RST) begin + if(RESET_VALUE == "ZERO") + count <= 0; + else + count <= COUNT_TO; + end + + else if(KEEP) begin + end + else if(UP) begin + count <= count + 1'd1; + if(count == 14'h3fff) + count <= COUNT_TO; + end + else begin + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + end + + end + end + + "BOTH": begin + initial begin + $display("Both-edge reset mode for GP_COUNT14_ADV not implemented"); + $finish; + end + end + + "LEVEL": begin + always @(posedge CLK, posedge RST) begin + + //Resets + if(RST) begin + if(RESET_VALUE == "ZERO") + count <= 0; + else + count <= COUNT_TO; + end + + else begin + + if(KEEP) begin + end + else if(UP) begin + count <= count + 1'd1; + if(count == 14'h3fff) + count <= COUNT_TO; + end + else begin + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + end + + end + + end + end + + default: begin + initial begin + $display("Invalid RESET_MODE on GP_COUNT14_ADV"); + $finish; + end + end + + endcase + endgenerate + +endmodule + +module GP_COUNT8_ADV(input CLK, input RST, output reg OUT, + input UP, input KEEP, output reg[7:0] POUT); + + parameter RESET_MODE = "RISING"; + parameter RESET_VALUE = "ZERO"; + + parameter COUNT_TO = 8'h1; + parameter CLKIN_DIVIDE = 1; + + reg[7:0] count = COUNT_TO; + + initial begin + if(CLKIN_DIVIDE != 1) begin + $display("ERROR: CLKIN_DIVIDE values other than 1 not implemented"); + $finish; + end + end + + //Combinatorially output underflow flag whenever we wrap low + always @(*) begin + if(UP) + OUT <= (count == 8'hff); + else + OUT <= (count == 8'h0); + POUT <= count; + 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. + generate + case(RESET_MODE) + + "RISING": begin + always @(posedge CLK, posedge RST) begin + + //Resets + if(RST) begin + if(RESET_VALUE == "ZERO") + count <= 0; + else + count <= COUNT_TO; + end + + //Main counter + else if(KEEP) begin + end + else if(UP) begin + count <= count + 1'd1; + if(count == 8'hff) + count <= COUNT_TO; + end + else begin + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + end + + end + end + + "FALLING": begin + always @(posedge CLK, negedge RST) begin + + //Resets + if(!RST) begin + if(RESET_VALUE == "ZERO") + count <= 0; + else + count <= COUNT_TO; + end + + //Main counter + else if(KEEP) begin + end + else if(UP) begin + count <= count + 1'd1; + if(count == 8'hff) + count <= COUNT_TO; + end + else begin + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + end + + end + end + + "BOTH": begin + initial begin + $display("Both-edge reset mode for GP_COUNT8_ADV not implemented"); + $finish; + end + end + + "LEVEL": begin + always @(posedge CLK, posedge RST) begin + + //Resets + if(RST) begin + if(RESET_VALUE == "ZERO") + count <= 0; + else + count <= COUNT_TO; + end + + else begin + + if(KEEP) begin + end + else if(UP) begin + count <= count + 1'd1; + if(count == 8'hff) + count <= COUNT_TO; + end + else begin + count <= count - 1'd1; + + if(count == 0) + count <= COUNT_TO; + end + end + + end + end + + default: begin + initial begin + $display("Invalid RESET_MODE on GP_COUNT8_ADV"); + $finish; + end + end + + endcase + endgenerate + +endmodule + +module GP_COUNT8( + input wire CLK, + input wire RST, + output reg OUT, + output reg[7:0] POUT); + + parameter RESET_MODE = "RISING"; + + parameter COUNT_TO = 8'h1; + parameter CLKIN_DIVIDE = 1; + + initial begin + if(CLKIN_DIVIDE != 1) begin + $display("ERROR: CLKIN_DIVIDE values other than 1 not implemented"); + $finish; + end + end + + reg[7:0] count = COUNT_TO; + + //Combinatorially output underflow flag whenever we wrap low + always @(*) begin + OUT <= (count == 8'h0); + POUT <= count; + 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. + generate + case(RESET_MODE) + + "RISING": begin + always @(posedge CLK, posedge RST) begin + if(RST) + count <= 0; + else begin + count <= count - 1'd1; + if(count == 0) + count <= COUNT_TO; + end + end + end + + "FALLING": begin + always @(posedge CLK, negedge RST) begin + if(!RST) + count <= 0; + else begin + count <= count - 1'd1; + if(count == 0) + count <= COUNT_TO; + end + end + end + + "BOTH": begin + initial begin + $display("Both-edge reset mode for GP_COUNT8 not implemented"); + $finish; + end + end + + "LEVEL": begin + always @(posedge CLK, posedge RST) begin + if(RST) + count <= 0; + + else begin + count <= count - 1'd1; + if(count == 0) + count <= COUNT_TO; + end + end + end + + default: begin + initial begin + $display("Invalid RESET_MODE on GP_COUNT8"); + $finish; + end + end + + endcase + endgenerate + +endmodule + +module GP_DCMPREF(output reg[7:0]OUT); + parameter[7:0] REF_VAL = 8'h00; + initial OUT = REF_VAL; +endmodule + +module GP_DCMPMUX(input[1:0] SEL, input[7:0] IN0, input[7:0] IN1, input[7:0] IN2, input[7:0] IN3, output reg[7:0] OUTA, output reg[7:0] OUTB); + + always @(*) begin + case(SEL) + 2'd00: begin + OUTA <= IN0; + OUTB <= IN3; + end + + 2'd01: begin + OUTA <= IN1; + OUTB <= IN2; + end + + 2'd02: begin + OUTA <= IN2; + OUTB <= IN1; + end + + 2'd03: begin + OUTA <= IN3; + OUTB <= IN0; + end + + endcase + end +endmodule + +module GP_DELAY(input IN, output reg OUT); + + parameter DELAY_STEPS = 1; + parameter GLITCH_FILTER = 0; + + initial OUT = 0; + + generate + + if(GLITCH_FILTER) begin + initial begin + $display("ERROR: GP_DELAY glitch filter mode not implemented"); + $finish; + end + end + + //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_DLATCH(input D, input nCLK, output reg Q); + parameter [0:0] INIT = 1'bx; + initial Q = INIT; + always @(*) begin + if(!nCLK) + Q <= D; + end +endmodule + +module GP_DLATCHI(input D, input nCLK, output reg nQ); + parameter [0:0] INIT = 1'bx; + initial nQ = INIT; + always @(*) begin + if(!nCLK) + nQ <= ~D; + end +endmodule + +module GP_DLATCHR(input D, input nCLK, input nRST, output reg Q); + parameter [0:0] INIT = 1'bx; + initial Q = INIT; + always @(*) begin + if(!nRST) + Q <= 1'b0; + else if(!nCLK) + Q <= D; + end +endmodule + +module GP_DLATCHRI(input D, input nCLK, input nRST, output reg nQ); + parameter [0:0] INIT = 1'bx; + initial nQ = INIT; + always @(*) begin + if(!nRST) + nQ <= 1'b1; + else if(!nCLK) + nQ <= ~D; + end +endmodule + +module GP_DLATCHS(input D, input nCLK, input nSET, output reg Q); + parameter [0:0] INIT = 1'bx; + initial Q = INIT; + always @(*) begin + if(!nSET) + Q <= 1'b1; + else if(!nCLK) + Q <= D; + end +endmodule + +module GP_DLATCHSI(input D, input nCLK, input nSET, output reg nQ); + parameter [0:0] INIT = 1'bx; + initial nQ = INIT; + always @(*) begin + if(!nSET) + nQ <= 1'b0; + else if(!nCLK) + nQ <= ~D; + end +endmodule + +module GP_DLATCHSR(input D, input nCLK, input nSR, output reg Q); + parameter [0:0] INIT = 1'bx; + parameter[0:0] SRMODE = 1'bx; + initial Q = INIT; + always @(*) begin + if(!nSR) + Q <= SRMODE; + else if(!nCLK) + Q <= D; + end +endmodule + +module GP_DLATCHSRI(input D, input nCLK, input nSR, output reg nQ); + parameter [0:0] INIT = 1'bx; + parameter[0:0] SRMODE = 1'bx; + initial nQ = INIT; + always @(*) begin + if(!nSR) + nQ <= ~SRMODE; + else if(!nCLK) + nQ <= ~D; + end +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_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_PGEN(input wire nRST, input wire CLK, output reg OUT); + initial OUT = 0; + parameter PATTERN_DATA = 16'h0; + parameter PATTERN_LEN = 5'd16; + + localparam COUNT_MAX = PATTERN_LEN - 1'h1; + + reg[3:0] count = 0; + always @(posedge CLK, negedge nRST) begin + + if(!nRST) + count <= 0; + + else begin + count <= count - 1'h1; + if(count == 0) + count <= COUNT_MAX; + end + end + + always @(*) + OUT = PATTERN_DATA[count]; + +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 + +module GP_VDD(output OUT); + assign OUT = 1; +endmodule + +module GP_VSS(output OUT); + assign OUT = 0; +endmodule diff --git a/yosys/techlibs/greenpak4/cells_sim_wip.v b/yosys/techlibs/greenpak4/cells_sim_wip.v new file mode 100644 index 000000000..5261b12d2 --- /dev/null +++ b/yosys/techlibs/greenpak4/cells_sim_wip.v @@ -0,0 +1,136 @@ + +//Cells still in this file have INCOMPLETE simulation models, need to finish them + +module GP_DCMP(input[7:0] INP, input[7:0] INN, input CLK, input PWRDN, output reg GREATER, output reg EQUAL); + parameter PWRDN_SYNC = 1'b0; + parameter CLK_EDGE = "RISING"; + parameter GREATER_OR_EQUAL = 1'b0; + + //TODO implement power-down mode + + initial GREATER = 0; + initial EQUAL = 0; + + wire clk_minv = (CLK_EDGE == "RISING") ? CLK : ~CLK; + always @(posedge clk_minv) begin + if(GREATER_OR_EQUAL) + GREATER <= (INP >= INN); + else + GREATER <= (INP > INN); + + EQUAL <= (INP == INN); + 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_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_SPI( + input SCK, + inout SDAT, + input CSN, + input[7:0] TXD_HIGH, + input[7:0] TXD_LOW, + output reg[7:0] RXD_HIGH, + output reg[7:0] RXD_LOW, + output reg INT); + + initial RXD_HIGH = 0; + initial RXD_LOW = 0; + initial INT = 0; + + parameter DATA_WIDTH = 8; //byte or word width + parameter SPI_CPHA = 0; //SPI clock phase + parameter SPI_CPOL = 0; //SPI clock polarity + parameter DIRECTION = "INPUT"; //SPI data direction (either input to chip or output to host) + //parallel output to fabric not yet implemented + + //TODO: write sim model + //TODO: SPI SDIO control... can we use ADC output while SPI is input?? + //TODO: clock sync + +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 diff --git a/yosys/techlibs/greenpak4/greenpak4_dffinv.cc b/yosys/techlibs/greenpak4/greenpak4_dffinv.cc index ff63958e6..d57e978a0 100644 --- a/yosys/techlibs/greenpak4/greenpak4_dffinv.cc +++ b/yosys/techlibs/greenpak4/greenpak4_dffinv.cc @@ -26,6 +26,7 @@ PRIVATE_NAMESPACE_BEGIN void invert_gp_dff(Cell *cell, bool invert_input) { string cell_type = cell->type.str(); + bool cell_type_latch = cell_type.find("LATCH") != string::npos; 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; @@ -79,25 +80,28 @@ void invert_gp_dff(Cell *cell, bool invert_input) cell_type_i = true; } - cell->type = stringf("\\GP_DFF%s%s%s", cell_type_s ? "S" : "", cell_type_r ? "R" : "", cell_type_i ? "I" : ""); + if(cell_type_latch) + cell->type = stringf("\\GP_DLATCH%s%s%s", cell_type_s ? "S" : "", cell_type_r ? "R" : "", cell_type_i ? "I" : ""); + else + 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() + Greenpak4DffInvPass() : Pass("greenpak4_dffinv", "merge greenpak4 inverters and DFF/latches") { } + void help() YS_OVERRIDE { log("\n"); log(" greenpak4_dffinv [options] [selection]\n"); log("\n"); - log("Merge GP_INV cells with GP_DFF* cells.\n"); + log("Merge GP_INV cells with GP_DFF* and GP_DLATCH* cells.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { - log_header(design, "Executing GREENPAK4_DFFINV pass (merge synchronous set/reset into FF cells).\n"); + log_header(design, "Executing GREENPAK4_DFFINV pass (merge input/output inverters into FF/latch cells).\n"); size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -120,6 +124,15 @@ struct Greenpak4DffInvPass : public Pass { gp_dff_types.insert("\\GP_DFFSR"); gp_dff_types.insert("\\GP_DFFSRI"); + gp_dff_types.insert("\\GP_DLATCH"); + gp_dff_types.insert("\\GP_DLATCHI"); + gp_dff_types.insert("\\GP_DLATCHR"); + gp_dff_types.insert("\\GP_DLATCHRI"); + gp_dff_types.insert("\\GP_DLATCHS"); + gp_dff_types.insert("\\GP_DLATCHSI"); + gp_dff_types.insert("\\GP_DLATCHSR"); + gp_dff_types.insert("\\GP_DLATCHSRI"); + for (auto module : design->selected_modules()) { SigMap sigmap(module); diff --git a/yosys/techlibs/greenpak4/synth_greenpak4.cc b/yosys/techlibs/greenpak4/synth_greenpak4.cc index 10e2a1498..eeb001b46 100644 --- a/yosys/techlibs/greenpak4/synth_greenpak4.cc +++ b/yosys/techlibs/greenpak4/synth_greenpak4.cc @@ -29,13 +29,15 @@ struct SynthGreenPAK4Pass : public ScriptPass { SynthGreenPAK4Pass() : ScriptPass("synth_greenpak4", "synthesis for GreenPAK4 FPGAs") { } - virtual void help() YS_OVERRIDE + 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("It is intended to be used with https://github.com/azonenberg/openfpga as the\n"); + log("place-and-route.\n"); log("\n"); log(" -top \n"); log(" use the specified module as top module (default='top')\n"); @@ -68,7 +70,7 @@ struct SynthGreenPAK4Pass : public ScriptPass string top_opt, part, json_file; bool flatten, retime; - virtual void clear_flags() YS_OVERRIDE + void clear_flags() YS_OVERRIDE { top_opt = "-auto-top"; part = "SLG46621V"; @@ -77,7 +79,7 @@ struct SynthGreenPAK4Pass : public ScriptPass retime = false; } - virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { string run_from, run_to; clear_flags(); @@ -118,7 +120,7 @@ struct SynthGreenPAK4Pass : public ScriptPass extra_args(args, argidx, design); if (!design->full_selection()) - log_cmd_error("This comannd only operates on fully selected designs!\n"); + log_cmd_error("This command 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()); @@ -131,7 +133,7 @@ struct SynthGreenPAK4Pass : public ScriptPass log_pop(); } - virtual void script() YS_OVERRIDE + void script() YS_OVERRIDE { if (check_label("begin")) { @@ -153,12 +155,13 @@ struct SynthGreenPAK4Pass : public ScriptPass if (check_label("fine")) { - run("greenpak4_counters"); + run("extract_counter -pout GP_DCMP,GP_DAC -maxwidth 14"); run("clean"); run("opt -fast -mux_undef -undriven -fine"); run("memory_map"); run("opt -undriven -fine"); run("techmap"); + run("techmap -map +/greenpak4/cells_latch.v"); run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib"); run("opt -fast"); if (retime || help_mode) @@ -201,8 +204,6 @@ struct SynthGreenPAK4Pass : public ScriptPass if (!json_file.empty() || help_mode) run(stringf("write_json %s", help_mode ? "" : json_file.c_str())); } - - log_pop(); } } SynthGreenPAK4Pass; diff --git a/yosys/techlibs/ice40/Makefile.inc b/yosys/techlibs/ice40/Makefile.inc index 14761c6c8..2750901c8 100644 --- a/yosys/techlibs/ice40/Makefile.inc +++ b/yosys/techlibs/ice40/Makefile.inc @@ -3,6 +3,7 @@ OBJS += techlibs/ice40/synth_ice40.o OBJS += techlibs/ice40/ice40_ffssr.o OBJS += techlibs/ice40/ice40_ffinit.o OBJS += techlibs/ice40/ice40_opt.o +OBJS += techlibs/ice40/ice40_unlut.o GENFILES += techlibs/ice40/brams_init1.vh GENFILES += techlibs/ice40/brams_init2.vh diff --git a/yosys/techlibs/ice40/cells_map.v b/yosys/techlibs/ice40/cells_map.v index 0227ffadb..d0ddfd02e 100644 --- a/yosys/techlibs/ice40/cells_map.v +++ b/yosys/techlibs/ice40/cells_map.v @@ -27,6 +27,7 @@ module \$__DFFE_NP1 (input D, C, E, R, output Q); SB_DFFNES _TECHMAP_REPLACE_ ( 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 +`ifndef NO_LUT module \$lut (A, Y); parameter WIDTH = 0; parameter LUT = 0; @@ -55,3 +56,4 @@ module \$lut (A, Y); end endgenerate endmodule +`endif diff --git a/yosys/techlibs/ice40/cells_sim.v b/yosys/techlibs/ice40/cells_sim.v index 7778b5519..38ed45981 100644 --- a/yosys/techlibs/ice40/cells_sim.v +++ b/yosys/techlibs/ice40/cells_sim.v @@ -1,6 +1,6 @@ -`define SB_DFF_REG reg Q = 0; -// `define SB_DFF_REG reg Q; +`define SB_DFF_REG reg Q = 0 +// `define SB_DFF_REG reg Q // SiliconBlue IO Cells @@ -132,21 +132,18 @@ endmodule // Positive Edge SiliconBlue FF Cells -module SB_DFF (output Q, input C, D); - `SB_DFF_REG +module SB_DFF (output `SB_DFF_REG, input C, D); always @(posedge C) Q <= D; endmodule -module SB_DFFE (output Q, input C, E, D); - `SB_DFF_REG +module SB_DFFE (output `SB_DFF_REG, input C, E, D); always @(posedge C) if (E) Q <= D; endmodule -module SB_DFFSR (output Q, input C, R, D); - `SB_DFF_REG +module SB_DFFSR (output `SB_DFF_REG, input C, R, D); always @(posedge C) if (R) Q <= 0; @@ -154,8 +151,7 @@ module SB_DFFSR (output Q, input C, R, D); Q <= D; endmodule -module SB_DFFR (output Q, input C, R, D); - `SB_DFF_REG +module SB_DFFR (output `SB_DFF_REG, input C, R, D); always @(posedge C, posedge R) if (R) Q <= 0; @@ -163,8 +159,7 @@ module SB_DFFR (output Q, input C, R, D); Q <= D; endmodule -module SB_DFFSS (output Q, input C, S, D); - `SB_DFF_REG +module SB_DFFSS (output `SB_DFF_REG, input C, S, D); always @(posedge C) if (S) Q <= 1; @@ -172,8 +167,7 @@ module SB_DFFSS (output Q, input C, S, D); Q <= D; endmodule -module SB_DFFS (output Q, input C, S, D); - `SB_DFF_REG +module SB_DFFS (output `SB_DFF_REG, input C, S, D); always @(posedge C, posedge S) if (S) Q <= 1; @@ -181,8 +175,7 @@ module SB_DFFS (output Q, input C, S, D); Q <= D; endmodule -module SB_DFFESR (output Q, input C, E, R, D); - `SB_DFF_REG +module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D); always @(posedge C) if (E) begin if (R) @@ -192,8 +185,7 @@ module SB_DFFESR (output Q, input C, E, R, D); end endmodule -module SB_DFFER (output Q, input C, E, R, D); - `SB_DFF_REG +module SB_DFFER (output `SB_DFF_REG, input C, E, R, D); always @(posedge C, posedge R) if (R) Q <= 0; @@ -201,8 +193,7 @@ module SB_DFFER (output Q, input C, E, R, D); Q <= D; endmodule -module SB_DFFESS (output Q, input C, E, S, D); - `SB_DFF_REG +module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D); always @(posedge C) if (E) begin if (S) @@ -212,8 +203,7 @@ module SB_DFFESS (output Q, input C, E, S, D); end endmodule -module SB_DFFES (output Q, input C, E, S, D); - `SB_DFF_REG +module SB_DFFES (output `SB_DFF_REG, input C, E, S, D); always @(posedge C, posedge S) if (S) Q <= 1; @@ -223,21 +213,18 @@ endmodule // Negative Edge SiliconBlue FF Cells -module SB_DFFN (output Q, input C, D); - `SB_DFF_REG +module SB_DFFN (output `SB_DFF_REG, input C, D); always @(negedge C) Q <= D; endmodule -module SB_DFFNE (output Q, input C, E, D); - `SB_DFF_REG +module SB_DFFNE (output `SB_DFF_REG, input C, E, D); always @(negedge C) if (E) Q <= D; endmodule -module SB_DFFNSR (output Q, input C, R, D); - `SB_DFF_REG +module SB_DFFNSR (output `SB_DFF_REG, input C, R, D); always @(negedge C) if (R) Q <= 0; @@ -245,8 +232,7 @@ module SB_DFFNSR (output Q, input C, R, D); Q <= D; endmodule -module SB_DFFNR (output Q, input C, R, D); - `SB_DFF_REG +module SB_DFFNR (output `SB_DFF_REG, input C, R, D); always @(negedge C, posedge R) if (R) Q <= 0; @@ -254,8 +240,7 @@ module SB_DFFNR (output Q, input C, R, D); Q <= D; endmodule -module SB_DFFNSS (output Q, input C, S, D); - `SB_DFF_REG +module SB_DFFNSS (output `SB_DFF_REG, input C, S, D); always @(negedge C) if (S) Q <= 1; @@ -263,8 +248,7 @@ module SB_DFFNSS (output Q, input C, S, D); Q <= D; endmodule -module SB_DFFNS (output Q, input C, S, D); - `SB_DFF_REG +module SB_DFFNS (output `SB_DFF_REG, input C, S, D); always @(negedge C, posedge S) if (S) Q <= 1; @@ -272,8 +256,7 @@ module SB_DFFNS (output Q, input C, S, D); Q <= D; endmodule -module SB_DFFNESR (output Q, input C, E, R, D); - `SB_DFF_REG +module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D); always @(negedge C) if (E) begin if (R) @@ -283,8 +266,7 @@ module SB_DFFNESR (output Q, input C, E, R, D); end endmodule -module SB_DFFNER (output Q, input C, E, R, D); - `SB_DFF_REG +module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D); always @(negedge C, posedge R) if (R) Q <= 0; @@ -292,8 +274,7 @@ module SB_DFFNER (output Q, input C, E, R, D); Q <= D; endmodule -module SB_DFFNESS (output Q, input C, E, S, D); - `SB_DFF_REG +module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D); always @(negedge C) if (E) begin if (S) @@ -303,8 +284,7 @@ module SB_DFFNESS (output Q, input C, E, S, D); end endmodule -module SB_DFFNES (output Q, input C, E, S, D); - `SB_DFF_REG +module SB_DFFNES (output `SB_DFF_REG, input C, E, S, D); always @(negedge C, posedge S) if (S) Q <= 1; @@ -429,6 +409,7 @@ module SB_RAM40_4K ( initial begin for (i=0; i<16; i=i+1) begin +`ifdef YOSYS 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]; @@ -445,6 +426,24 @@ module SB_RAM40_4K ( 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]; +`else + 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]; +`endif end end @@ -677,7 +676,12 @@ module ICESTORM_LC ( parameter [0:0] SET_NORESET = 0; parameter [0:0] ASYNC_SR = 0; - wire COUT = CARRY_ENABLE ? (I1 && I2) || ((I1 || I2) && CIN) : 1'bx; + parameter [0:0] CIN_CONST = 0; + parameter [0:0] CIN_SET = 0; + + wire mux_cin = CIN_CONST ? CIN_SET : CIN; + + assign COUT = CARRY_ENABLE ? (I1 && I2) || ((I1 || I2) && mux_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]; @@ -881,3 +885,391 @@ module SB_WARMBOOT ( input S0 ); endmodule + +// UltraPlus feature cells +(* blackbox *) +module SB_MAC16 ( + input CLK, + input CE, + input [15:0] C, + input [15:0] A, + input [15:0] B, + input [15:0] D, + input AHOLD, + input BHOLD, + input CHOLD, + input DHOLD, + input IRSTTOP, + input IRSTBOT, + input ORSTTOP, + input ORSTBOT, + input OLOADTOP, + input OLOADBOT, + input ADDSUBTOP, + input ADDSUBBOT, + input OHOLDTOP, + input OHOLDBOT, + input CI, + input ACCUMCI, + input SIGNEXTIN, + output [31:0] O, + output CO, + output ACCUMCO, + output SIGNEXTOUT +); +parameter NEG_TRIGGER = 1'b0; +parameter C_REG = 1'b0; +parameter A_REG = 1'b0; +parameter B_REG = 1'b0; +parameter D_REG = 1'b0; +parameter TOP_8x8_MULT_REG = 1'b0; +parameter BOT_8x8_MULT_REG = 1'b0; +parameter PIPELINE_16x16_MULT_REG1 = 1'b0; +parameter PIPELINE_16x16_MULT_REG2 = 1'b0; +parameter TOPOUTPUT_SELECT = 2'b00; +parameter TOPADDSUB_LOWERINPUT = 2'b00; +parameter TOPADDSUB_UPPERINPUT = 1'b0; +parameter TOPADDSUB_CARRYSELECT = 2'b00; +parameter BOTOUTPUT_SELECT = 2'b00; +parameter BOTADDSUB_LOWERINPUT = 2'b00; +parameter BOTADDSUB_UPPERINPUT = 1'b0; +parameter BOTADDSUB_CARRYSELECT = 2'b00; +parameter MODE_8x8 = 1'b0; +parameter A_SIGNED = 1'b0; +parameter B_SIGNED = 1'b0; +endmodule + +module SB_SPRAM256KA ( + input [13:0] ADDRESS, + input [15:0] DATAIN, + input [3:0] MASKWREN, + input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF, + output reg [15:0] DATAOUT +); +`ifndef BLACKBOX +`ifndef EQUIV + reg [15:0] mem [0:16383]; + wire off = SLEEP || !POWEROFF; + integer i; + + always @(negedge POWEROFF) begin + for (i = 0; i <= 16383; i = i+1) + mem[i] = 'bx; + end + + always @(posedge CLOCK, posedge off) begin + if (off) begin + DATAOUT <= 0; + end else + if (CHIPSELECT && !STANDBY && !WREN) begin + DATAOUT <= mem[ADDRESS]; + end else begin + if (CHIPSELECT && !STANDBY && WREN) begin + if (MASKWREN[0]) mem[ADDRESS][ 3: 0] = DATAIN[ 3: 0]; + if (MASKWREN[1]) mem[ADDRESS][ 7: 4] = DATAIN[ 7: 4]; + if (MASKWREN[2]) mem[ADDRESS][11: 8] = DATAIN[11: 8]; + if (MASKWREN[3]) mem[ADDRESS][15:12] = DATAIN[15:12]; + end + DATAOUT <= 'bx; + end + end +`endif +`endif +endmodule + +(* blackbox *) +module SB_HFOSC( + input CLKHFPU, + input CLKHFEN, + output CLKHF +); +parameter CLKHF_DIV = "0b00"; +endmodule + +(* blackbox *) +module SB_LFOSC( + input CLKLFPU, + input CLKLFEN, + output CLKLF +); +endmodule + +(* blackbox *) +module SB_RGBA_DRV( + input CURREN, + input RGBLEDEN, + input RGB0PWM, + input RGB1PWM, + input RGB2PWM, + output RGB0, + output RGB1, + output RGB2 +); +parameter CURRENT_MODE = "0b0"; +parameter RGB0_CURRENT = "0b000000"; +parameter RGB1_CURRENT = "0b000000"; +parameter RGB2_CURRENT = "0b000000"; +endmodule + +(* blackbox *) +module SB_I2C( + input SBCLKI, + input SBRWI, + input SBSTBI, + input SBADRI7, + input SBADRI6, + input SBADRI5, + input SBADRI4, + input SBADRI3, + input SBADRI2, + input SBADRI1, + input SBADRI0, + input SBDATI7, + input SBDATI6, + input SBDATI5, + input SBDATI4, + input SBDATI3, + input SBDATI2, + input SBDATI1, + input SBDATI0, + input SCLI, + input SDAI, + output SBDATO7, + output SBDATO6, + output SBDATO5, + output SBDATO4, + output SBDATO3, + output SBDATO2, + output SBDATO1, + output SBDATO0, + output SBACKO, + output I2CIRQ, + output I2CWKUP, + output SCLO, //inout in the SB verilog library, but output in the VHDL and PDF libs and seemingly in the HW itself + output SCLOE, + output SDAO, + output SDAOE +); +parameter I2C_SLAVE_INIT_ADDR = "0b1111100001"; +parameter BUS_ADDR74 = "0b0001"; +endmodule + +(* blackbox *) +module SB_SPI ( + input SBCLKI, + input SBRWI, + input SBSTBI, + input SBADRI7, + input SBADRI6, + input SBADRI5, + input SBADRI4, + input SBADRI3, + input SBADRI2, + input SBADRI1, + input SBADRI0, + input SBDATI7, + input SBDATI6, + input SBDATI5, + input SBDATI4, + input SBDATI3, + input SBDATI2, + input SBDATI1, + input SBDATI0, + input MI, + input SI, + input SCKI, + input SCSNI, + output SBDATO7, + output SBDATO6, + output SBDATO5, + output SBDATO4, + output SBDATO3, + output SBDATO2, + output SBDATO1, + output SBDATO0, + output SBACKO, + output SPIIRQ, + output SPIWKUP, + output SO, + output SOE, + output MO, + output MOE, + output SCKO, //inout in the SB verilog library, but output in the VHDL and PDF libs and seemingly in the HW itself + output SCKOE, + output MCSNO3, + output MCSNO2, + output MCSNO1, + output MCSNO0, + output MCSNOE3, + output MCSNOE2, + output MCSNOE1, + output MCSNOE0 +); +parameter BUS_ADDR74 = "0b0000"; +endmodule + +(* blackbox *) +module SB_LEDDA_IP( + input LEDDCS, + input LEDDCLK, + input LEDDDAT7, + input LEDDDAT6, + input LEDDDAT5, + input LEDDDAT4, + input LEDDDAT3, + input LEDDDAT2, + input LEDDDAT1, + input LEDDDAT0, + input LEDDADDR3, + input LEDDADDR2, + input LEDDADDR1, + input LEDDADDR0, + input LEDDDEN, + input LEDDEXE, + input LEDDRST, + output PWMOUT0, + output PWMOUT1, + output PWMOUT2, + output LEDDON +); +endmodule + +(* blackbox *) +module SB_FILTER_50NS( + input FILTERIN, + output FILTEROUT +); +endmodule + +module SB_IO_I3C ( + 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, + input PU_ENB, + input WEAK_PU_ENB +); + parameter [5:0] PIN_TYPE = 6'b000000; + parameter [0:0] PULLUP = 1'b0; + parameter [0:0] WEAK_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_IO_OD ( + inout PACKAGEPIN, + input LATCHINPUTVALUE, + input CLOCKENABLE, + input INPUTCLK, + input OUTPUTCLK, + input OUTPUTENABLE, + input DOUT1, + input DOUT0, + output DIN1, + output DIN0 +); + parameter [5:0] PIN_TYPE = 6'b000000; + parameter [0:0] NEG_TRIGGER = 1'b0; + +`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 INPUTCLK) if (CLOCKENABLE) din_q_0 <= PACKAGEPIN; + always @(negedge INPUTCLK) if (CLOCKENABLE) din_q_1 <= PACKAGEPIN; + always @(posedge OUTPUTCLK) if (CLOCKENABLE) dout_q_0 <= DOUT0; + always @(negedge OUTPUTCLK) if (CLOCKENABLE) dout_q_1 <= DOUT1; + always @(posedge OUTPUTCLK) if (CLOCKENABLE) outena_q <= OUTPUTENABLE; + end else begin + always @(negedge INPUTCLK) if (CLOCKENABLE) din_q_0 <= PACKAGEPIN; + always @(posedge INPUTCLK) if (CLOCKENABLE) din_q_1 <= PACKAGEPIN; + always @(negedge OUTPUTCLK) if (CLOCKENABLE) dout_q_0 <= DOUT0; + always @(posedge OUTPUTCLK) if (CLOCKENABLE) dout_q_1 <= DOUT1; + always @(negedge OUTPUTCLK) if (CLOCKENABLE) outena_q <= OUTPUTENABLE; + end endgenerate + + always @* begin + if (!PIN_TYPE[1] || !LATCHINPUTVALUE) + din_0 = PIN_TYPE[0] ? PACKAGEPIN : 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 <= OUTPUTCLK; + always @* outclk_delayed_2 <= outclk_delayed_1; + + always @* begin + if (PIN_TYPE[3]) + dout = PIN_TYPE[2] ? !dout_q_0 : DOUT0; + else + dout = (outclk_delayed_2 ^ NEG_TRIGGER) || PIN_TYPE[2] ? dout_q_0 : dout_q_1; + end + + assign DIN0 = din_0, DIN1 = din_1; + + generate + if (PIN_TYPE[5:4] == 2'b01) assign PACKAGEPIN = dout ? 1'bz : 1'b0; + if (PIN_TYPE[5:4] == 2'b10) assign PACKAGEPIN = OUTPUTENABLE ? (dout ? 1'bz : 1'b0) : 1'bz; + if (PIN_TYPE[5:4] == 2'b11) assign PACKAGEPIN = outena_q ? (dout ? 1'bz : 1'b0) : 1'bz; + endgenerate +`endif +endmodule diff --git a/yosys/techlibs/ice40/ice40_ffinit.cc b/yosys/techlibs/ice40/ice40_ffinit.cc index c914b20e8..3089d8932 100644 --- a/yosys/techlibs/ice40/ice40_ffinit.cc +++ b/yosys/techlibs/ice40/ice40_ffinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct Ice40FfinitPass : public Pass { Ice40FfinitPass() : Pass("ice40_ffinit", "iCE40: handle FF init values") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -35,7 +35,7 @@ struct Ice40FfinitPass : public Pass { log("nonzero init values.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ICE40_FFINIT pass (implement FF init values).\n"); diff --git a/yosys/techlibs/ice40/ice40_ffssr.cc b/yosys/techlibs/ice40/ice40_ffssr.cc index 9afbc0fce..a7649d7a0 100644 --- a/yosys/techlibs/ice40/ice40_ffssr.cc +++ b/yosys/techlibs/ice40/ice40_ffssr.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct Ice40FfssrPass : public Pass { Ice40FfssrPass() : Pass("ice40_ffssr", "iCE40: merge synchronous set/reset into FF cells") { } - virtual void help() + void help() YS_OVERRIDE { log("\n"); log(" ice40_ffssr [options] [selection]\n"); @@ -33,7 +33,7 @@ struct Ice40FfssrPass : public Pass { log("Merge synchronous set/reset $_MUX_ cells into iCE40 FFs.\n"); log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing ICE40_FFSSR pass (merge synchronous set/reset into FF cells).\n"); @@ -81,6 +81,9 @@ struct Ice40FfssrPass : public Pass { for (auto cell : ff_cells) { + if (cell->get_bool_attribute("\\dont_touch")) + continue; + SigSpec sig_d = cell->getPort("\\D"); if (GetSize(sig_d) < 1) diff --git a/yosys/techlibs/ice40/ice40_opt.cc b/yosys/techlibs/ice40/ice40_opt.cc index ae72f5d64..f528607d6 100644 --- a/yosys/techlibs/ice40/ice40_opt.cc +++ b/yosys/techlibs/ice40/ice40_opt.cc @@ -26,7 +26,14 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -static void run_ice40_opts(Module *module, bool unlut_mode) +static SigBit get_bit_or_zero(const SigSpec &sig) +{ + if (GetSize(sig) == 0) + return State::S0; + return sig[0]; +} + +static void run_ice40_opts(Module *module) { pool optimized_co; vector sb_lut_cells; @@ -45,7 +52,11 @@ static void run_ice40_opts(Module *module, bool unlut_mode) 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")}; + SigBit inbit[3] = { + get_bit_or_zero(cell->getPort("\\I0")), + get_bit_or_zero(cell->getPort("\\I1")), + get_bit_or_zero(cell->getPort("\\CI")) + }; for (int i = 0; i < 3; i++) if (inbit[i].wire == nullptr) { if (inbit[i] == State::S1) @@ -63,8 +74,8 @@ static void run_ice40_opts(Module *module, bool unlut_mode) replacement_output = non_const_inputs; if (GetSize(replacement_output)) { - optimized_co.insert(sigmap(cell->getPort("\\CO"))); - module->connect(cell->getPort("\\CO"), replacement_output); + optimized_co.insert(sigmap(cell->getPort("\\CO")[0])); + module->connect(cell->getPort("\\CO")[0], 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)); @@ -78,15 +89,12 @@ static void run_ice40_opts(Module *module, bool unlut_mode) { SigSpec inbits; - inbits.append(cell->getPort("\\I0")); - inbits.append(cell->getPort("\\I1")); - inbits.append(cell->getPort("\\I2")); - inbits.append(cell->getPort("\\I3")); + inbits.append(get_bit_or_zero(cell->getPort("\\I0"))); + inbits.append(get_bit_or_zero(cell->getPort("\\I1"))); + inbits.append(get_bit_or_zero(cell->getPort("\\I2"))); + inbits.append(get_bit_or_zero(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; @@ -104,8 +112,13 @@ static void run_ice40_opts(Module *module, bool unlut_mode) 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->setPort("\\A", SigSpec({ + get_bit_or_zero(cell->getPort("\\I3")), + get_bit_or_zero(cell->getPort("\\I2")), + get_bit_or_zero(cell->getPort("\\I1")), + get_bit_or_zero(cell->getPort("\\I0")) + })); + cell->setPort("\\Y", cell->getPort("\\O")[0]); cell->unsetPort("\\I0"); cell->unsetPort("\\I1"); cell->unsetPort("\\I2"); @@ -120,7 +133,7 @@ static void run_ice40_opts(Module *module, bool unlut_mode) struct Ice40OptPass : public Pass { Ice40OptPass() : Pass("ice40_opt", "iCE40: perform simple optimizations") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -136,14 +149,10 @@ struct Ice40OptPass : public Pass { log(" opt_clean\n"); log(" while \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 args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { string opt_expr_args = "-mux_undef -undriven"; - bool unlut_mode = false; log_header(design, "Executing ICE40_OPT pass (performing simple optimizations).\n"); log_push(); @@ -154,10 +163,6 @@ struct Ice40OptPass : public Pass { opt_expr_args += " -full"; continue; } - if (args[argidx] == "-unlut") { - unlut_mode = true; - continue; - } break; } extra_args(args, argidx, design); @@ -168,7 +173,7 @@ struct Ice40OptPass : public Pass { log_header(design, "Running ICE40 specific optimizations.\n"); for (auto module : design->selected_modules()) - run_ice40_opts(module, unlut_mode); + run_ice40_opts(module); Pass::call(design, "opt_expr " + opt_expr_args); Pass::call(design, "opt_merge"); diff --git a/yosys/techlibs/ice40/ice40_unlut.cc b/yosys/techlibs/ice40/ice40_unlut.cc new file mode 100644 index 000000000..2428a8e78 --- /dev/null +++ b/yosys/techlibs/ice40/ice40_unlut.cc @@ -0,0 +1,106 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 +#include + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +static SigBit get_bit_or_zero(const SigSpec &sig) +{ + if (GetSize(sig) == 0) + return State::S0; + return sig[0]; +} + +static void run_ice40_unlut(Module *module) +{ + SigMap sigmap(module); + + for (auto cell : module->selected_cells()) + { + if (cell->type == "\\SB_LUT4") + { + SigSpec inbits; + + inbits.append(get_bit_or_zero(cell->getPort("\\I0"))); + inbits.append(get_bit_or_zero(cell->getPort("\\I1"))); + inbits.append(get_bit_or_zero(cell->getPort("\\I2"))); + inbits.append(get_bit_or_zero(cell->getPort("\\I3"))); + sigmap.apply(inbits); + + log("Mapping SB_LUT4 cell %s.%s to $lut.\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({ + get_bit_or_zero(cell->getPort("\\I3")), + get_bit_or_zero(cell->getPort("\\I2")), + get_bit_or_zero(cell->getPort("\\I1")), + get_bit_or_zero(cell->getPort("\\I0")) + })); + cell->setPort("\\Y", cell->getPort("\\O")[0]); + cell->unsetPort("\\I0"); + cell->unsetPort("\\I1"); + cell->unsetPort("\\I2"); + cell->unsetPort("\\I3"); + cell->unsetPort("\\O"); + + cell->check(); + } + } +} + +struct Ice40UnlutPass : public Pass { + Ice40UnlutPass() : Pass("ice40_unlut", "iCE40: perform simple optimizations") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ice40_unlut [options] [selection]\n"); + log("\n"); + log("This command transforms all SB_LUT4 cells to generic $lut cells.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ICE40_UNLUT pass (convert SB_LUT4 to $lut).\n"); + log_push(); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + // if (args[argidx] == "-???") { + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + run_ice40_unlut(module); + } +} Ice40UnlutPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/ice40/synth_ice40.cc b/yosys/techlibs/ice40/synth_ice40.cc index 2533d3af8..f900453e8 100644 --- a/yosys/techlibs/ice40/synth_ice40.cc +++ b/yosys/techlibs/ice40/synth_ice40.cc @@ -29,7 +29,7 @@ struct SynthIce40Pass : public ScriptPass { SynthIce40Pass() : ScriptPass("synth_ice40", "synthesis for iCE40 FPGAs") { } - virtual void help() YS_OVERRIDE + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -38,14 +38,18 @@ struct SynthIce40Pass : public ScriptPass log("This command runs synthesis for iCE40 FPGAs.\n"); log("\n"); log(" -top \n"); - log(" use the specified module as top module (default='top')\n"); + log(" use the specified module as top module\n"); log("\n"); log(" -blif \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 \n"); - log(" write the design to the specified edif file. writing of an output 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(" -json \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 :\n"); @@ -59,37 +63,61 @@ struct SynthIce40Pass : public ScriptPass log(" -retime\n"); log(" run 'abc' with -dff option\n"); log("\n"); + log(" -relut\n"); + log(" combine LUTs after synthesis\n"); + log("\n"); log(" -nocarry\n"); log(" do not use SB_CARRY cells in output netlist\n"); log("\n"); + log(" -nodffe\n"); + log(" do not use SB_DFFE* cells in output netlist\n"); + log("\n"); + log(" -dffe_min_ce_use \n"); + log(" do not use SB_DFFE* cells if the resulting CE line would go to less\n"); + log(" than min_ce_use SB_DFFE*in output netlist\n"); + log("\n"); log(" -nobram\n"); log(" do not use SB_RAM40_4K* cells in output netlist\n"); log("\n"); + log(" -noabc\n"); + log(" use built-in Yosys LUT techmapping instead of abc\n"); + log("\n"); log(" -abc2\n"); log(" run two passes of 'abc' for slightly improved logic density\n"); log("\n"); + log(" -vpr\n"); + log(" generate an output netlist (and BLIF file) suitable for VPR\n"); + log(" (this feature is experimental and incomplete)\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; + string top_opt, blif_file, edif_file, json_file; + bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr; + int min_ce_use; - virtual void clear_flags() YS_OVERRIDE + void clear_flags() YS_OVERRIDE { top_opt = "-auto-top"; blif_file = ""; edif_file = ""; + json_file = ""; nocarry = false; + nodffe = false; + min_ce_use = -1; nobram = false; flatten = true; retime = false; + relut = false; + noabc = false; abc2 = false; + vpr = false; } - virtual void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { string run_from, run_to; clear_flags(); @@ -109,6 +137,10 @@ struct SynthIce40Pass : public ScriptPass edif_file = args[++argidx]; continue; } + if (args[argidx] == "-json" && argidx+1 < args.size()) { + json_file = args[++argidx]; + continue; + } if (args[argidx] == "-run" && argidx+1 < args.size()) { size_t pos = args[argidx+1].find(':'); if (pos == std::string::npos) @@ -129,24 +161,44 @@ struct SynthIce40Pass : public ScriptPass retime = true; continue; } + if (args[argidx] == "-relut") { + relut = true; + continue; + } if (args[argidx] == "-nocarry") { nocarry = true; continue; } + if (args[argidx] == "-nodffe") { + nodffe = true; + continue; + } + if (args[argidx] == "-dffe_min_ce_use" && argidx+1 < args.size()) { + min_ce_use = std::stoi(args[++argidx]); + continue; + } if (args[argidx] == "-nobram") { nobram = true; continue; } + if (args[argidx] == "-noabc") { + noabc = true; + continue; + } if (args[argidx] == "-abc2") { abc2 = true; continue; } + if (args[argidx] == "-vpr") { + vpr = 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_cmd_error("This command only operates on fully selected designs!\n"); log_header(design, "Executing SYNTH_ICE40 pass.\n"); log_push(); @@ -156,7 +208,7 @@ struct SynthIce40Pass : public ScriptPass log_pop(); } - virtual void script() YS_OVERRIDE + void script() YS_OVERRIDE { if (check_label("begin")) { @@ -174,7 +226,7 @@ struct SynthIce40Pass : public ScriptPass if (check_label("coarse")) { - run("synth -run coarse"); + run("synth -lut 4 -run coarse"); } if (!nobram && check_label("bram", "(skip if -nobram)")) @@ -183,11 +235,15 @@ struct SynthIce40Pass : public ScriptPass run("techmap -map +/ice40/brams_map.v"); } - if (check_label("fine")) + if (check_label("map")) { run("opt -fast -mux_undef -undriven -fine"); run("memory_map"); run("opt -undriven -fine"); + } + + if (check_label("map_gates")) + { if (nocarry) run("techmap"); else @@ -200,8 +256,13 @@ struct SynthIce40Pass : public ScriptPass if (check_label("map_ffs")) { run("dffsr2dff"); - run("dff2dffe -direct-match $_DFF_*"); - run("techmap -map +/ice40/cells_map.v"); + if (!nodffe) + run("dff2dffe -direct-match $_DFF_*"); + if (min_ce_use >= 0) { + run("opt_merge"); + run(stringf("dff2dffe -unmap-mince %d", min_ce_use)); + } + run("techmap -D NO_LUT -map +/ice40/cells_map.v"); run("opt_expr -mux_undef"); run("simplemap"); run("ice40_ffinit"); @@ -216,13 +277,27 @@ struct SynthIce40Pass : public ScriptPass run("ice40_opt", "(only if -abc2)"); } run("techmap -map +/ice40/latches_map.v"); - run("abc -lut 4"); + if (noabc || help_mode) { + run("simplemap", " (only if -noabc)"); + run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)"); + } + if (!noabc) { + run("abc -lut 4", "(skip if -noabc)"); + } run("clean"); + if (relut || help_mode) { + run("ice40_unlut", " (only if -relut)"); + run("opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3", "(only if -relut)"); + } } if (check_label("map_cells")) { - run("techmap -map +/ice40/cells_map.v"); + if (vpr) + run("techmap -D NO_LUT -map +/ice40/cells_map.v"); + else + run("techmap -map +/ice40/cells_map.v", "(with -D NO_LUT in vpr mode)"); + run("clean"); } @@ -235,8 +310,19 @@ struct SynthIce40Pass : public ScriptPass if (check_label("blif")) { - if (!blif_file.empty() || help_mode) - run(stringf("write_blif -gates -attr -param %s", help_mode ? "" : blif_file.c_str())); + if (!blif_file.empty() || help_mode) { + if (vpr || help_mode) { + run(stringf("opt_clean -purge"), + " (vpr mode)"); + run(stringf("write_blif -attr -cname -conn -param %s", + help_mode ? "" : blif_file.c_str()), + " (vpr mode)"); + } + if (!vpr) + run(stringf("write_blif -gates -attr -param %s", + help_mode ? "" : blif_file.c_str()), + " (non-vpr mode)"); + } } if (check_label("edif")) @@ -244,6 +330,12 @@ struct SynthIce40Pass : public ScriptPass if (!edif_file.empty() || help_mode) run(stringf("write_edif %s", help_mode ? "" : edif_file.c_str())); } + + if (check_label("json")) + { + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "" : json_file.c_str())); + } } } SynthIce40Pass; diff --git a/yosys/techlibs/intel/Makefile.inc b/yosys/techlibs/intel/Makefile.inc new file mode 100644 index 000000000..ec7cea379 --- /dev/null +++ b/yosys/techlibs/intel/Makefile.inc @@ -0,0 +1,24 @@ + +OBJS += techlibs/intel/synth_intel.o + +$(eval $(call add_share_file,share/intel/common,techlibs/intel/common/m9k_bb.v)) +$(eval $(call add_share_file,share/intel/common,techlibs/intel/common/altpll_bb.v)) +$(eval $(call add_share_file,share/intel/common,techlibs/intel/common/brams.txt)) +$(eval $(call add_share_file,share/intel/common,techlibs/intel/common/brams_map.v)) +$(eval $(call add_share_file,share/intel/max10,techlibs/intel/max10/cells_sim.v)) +$(eval $(call add_share_file,share/intel/a10gx,techlibs/intel/a10gx/cells_sim.v)) +$(eval $(call add_share_file,share/intel/cyclonev,techlibs/intel/cyclonev/cells_sim.v)) +$(eval $(call add_share_file,share/intel/cyclone10,techlibs/intel/cyclone10/cells_sim.v)) +$(eval $(call add_share_file,share/intel/cycloneiv,techlibs/intel/cycloneiv/cells_sim.v)) +$(eval $(call add_share_file,share/intel/cycloneive,techlibs/intel/cycloneive/cells_sim.v)) +$(eval $(call add_share_file,share/intel/max10,techlibs/intel/max10/cells_map.v)) +$(eval $(call add_share_file,share/intel/a10gx,techlibs/intel/a10gx/cells_map.v)) +$(eval $(call add_share_file,share/intel/cyclonev,techlibs/intel/cyclonev/cells_map.v)) +$(eval $(call add_share_file,share/intel/cyclone10,techlibs/intel/cyclone10/cells_map.v)) +$(eval $(call add_share_file,share/intel/cycloneiv,techlibs/intel/cycloneiv/cells_map.v)) +$(eval $(call add_share_file,share/intel/cycloneive,techlibs/intel/cycloneive/cells_map.v)) +#$(eval $(call add_share_file,share/intel/max10,techlibs/intel/max10/arith_map.v)) +#$(eval $(call add_share_file,share/intel/a10gx,techlibs/intel/a10gx/arith_map.v)) +#$(eval $(call add_share_file,share/intel/cycloneiv,techlibs/intel/cycloneiv/arith_map.v)) +#$(eval $(call add_share_file,share/intel/cycloneive,techlibs/intel/cycloneive/arith_map.v)) + diff --git a/yosys/techlibs/intel/a10gx/cells_arith.v b/yosys/techlibs/intel/a10gx/cells_arith.v new file mode 100644 index 000000000..89fb4561f --- /dev/null +++ b/yosys/techlibs/intel/a10gx/cells_arith.v @@ -0,0 +1,65 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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: This is still WIP. +(* techmap_celltype = "$alu" *) +module _80_altera_a10gx_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; + output CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 4; + + 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:0] C = {CO, CI}; + wire [Y_WIDTH+1:0] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + /* Start implementation */ + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b0000_0000_1010_1010), .sum_lutc_input("cin")) carry_start (.cout(COx[0]), .dataa(C[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) begin + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b1111_0000_1110_0000), .sum_lutc_input("cin")) carry_end (.combout(COx[Y_WIDTH]), .dataa(1'b1), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[Y_WIDTH])); + assign CO = COx[Y_WIDTH]; + end + else + fiftyfivenm_lcell_comb #(.lut_mask(16'b1001_0110_1110_1000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(COx[i+1]), .dataa(AA[i]), .datab(BB[i]), .datac(1'b1), .datad(1'b1), .cin(C[i+1])); + end: slice + endgenerate + /* End implementation */ + assign X = AA ^ BB; + +endmodule diff --git a/yosys/techlibs/intel/a10gx/cells_map.v b/yosys/techlibs/intel/a10gx/cells_map.v new file mode 100644 index 000000000..1430e8551 --- /dev/null +++ b/yosys/techlibs/intel/a10gx/cells_map.v @@ -0,0 +1,53 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// Input buffer map +module \$__inpad (input I, output O); + twentynm_io_ibuf _TECHMAP_REPLACE_ (.o(O), .i(I), .ibar(1'b0)); +endmodule + +// Output buffer map +module \$__outpad (input I, output O); + twentynm_io_obuf _TECHMAP_REPLACE_ (.o(O), .i(I), .oe(1'b1)); +endmodule + +// LUT Map +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + generate + if (WIDTH == 1) begin + assign Y = ~A[0]; // Not need to spend 1 logic cell for such an easy function + end else + if (WIDTH == 2) begin + twentynm_lcell_comb #(.lut_mask({16{LUT}}), .shared_arith("off"), .extended_lut("off")) + _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(1'b1),.datad(1'b1), .datae(1'b1), .dataf(1'b1), .datag(1'b1)); + end /*else + if(WIDTH == 3) begin + fiftyfivenm_lcell_comb #(.lut_mask({2{LUT}}), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(A[2]),.datad(1'b1)); + end else + if(WIDTH == 4) begin + fiftyfivenm_lcell_comb #(.lut_mask(LUT), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(A[2]),.datad(A[3])); + end*/ else + wire _TECHMAP_FAIL_ = 1; + endgenerate +endmodule // + + diff --git a/yosys/techlibs/intel/a10gx/cells_sim.v b/yosys/techlibs/intel/a10gx/cells_sim.v new file mode 100644 index 000000000..e892b377e --- /dev/null +++ b/yosys/techlibs/intel/a10gx/cells_sim.v @@ -0,0 +1,59 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +module VCC (output V); + assign V = 1'b1; +endmodule // VCC + +module GND (output G); + assign G = 1'b0; +endmodule // GND + +/* Altera Arria 10 GX devices Input Buffer Primitive */ +module twentynm_io_ibuf (output o, input i, input ibar); + assign ibar = ibar; + assign o = i; +endmodule // twentynm_io_ibuf + +/* Altera Arria 10 GX devices Output Buffer Primitive */ +module twentynm_io_obuf (output o, input i, input oe); + assign o = i; + assign oe = oe; +endmodule // twentynm_io_obuf + +/* Altera Arria 10 GX LUT Primitive */ +module twentynm_lcell_comb (output combout, cout, sumout, + input dataa, datab, datac, datad, + input datae, dataf, datag, cin, + input sharein); + +parameter lut_mask = 64'hFFFFFFFFFFFFFFFF; +parameter dont_touch = "off"; +parameter lpm_type = "twentynm_lcell_comb"; +parameter shared_arith = "off"; +parameter extended_lut = "off"; + +// TODO: This is still WIP +initial begin + $display("Simulation model is still under investigation\n"); +end + +endmodule // twentynm_lcell_comb + + + diff --git a/yosys/techlibs/intel/common/altpll_bb.v b/yosys/techlibs/intel/common/altpll_bb.v new file mode 100644 index 000000000..d2e6a3643 --- /dev/null +++ b/yosys/techlibs/intel/common/altpll_bb.v @@ -0,0 +1,366 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +/* No clearbox model */ +`ifdef NO_CLEARBOX +(* blackbox *) +module altpll + ( inclk, + fbin, + pllena, + clkswitch, + areset, + pfdena, + clkena, + extclkena, + scanclk, + scanaclr, + scanclkena, + scanread, + scanwrite, + scandata, + phasecounterselect, + phaseupdown, + phasestep, + configupdate, + fbmimicbidir, + clk, + extclk, + clkbad, + enable0, + enable1, + activeclock, + clkloss, + locked, + scandataout, + scandone, + sclkout0, + sclkout1, + phasedone, + vcooverrange, + vcounderrange, + fbout, + fref, + icdrclk, + c0, + c1, + c2, + c3, + c4); + + parameter intended_device_family = "MAX 10"; + parameter operation_mode = "NORMAL"; + parameter pll_type = "AUTO"; + parameter qualify_conf_done = "OFF"; + parameter compensate_clock = "CLK0"; + parameter scan_chain = "LONG"; + parameter primary_clock = "inclk0"; + parameter inclk0_input_frequency = 1000; + parameter inclk1_input_frequency = 0; + parameter gate_lock_signal = "NO"; + parameter gate_lock_counter = 0; + parameter lock_high = 1; + parameter lock_low = 0; + parameter valid_lock_multiplier = 1; + parameter invalid_lock_multiplier = 5; + parameter switch_over_type = "AUTO"; + parameter switch_over_on_lossclk = "OFF" ; + parameter switch_over_on_gated_lock = "OFF" ; + parameter enable_switch_over_counter = "OFF"; + parameter switch_over_counter = 0; + parameter feedback_source = "EXTCLK0" ; + parameter bandwidth = 0; + parameter bandwidth_type = "UNUSED"; + parameter lpm_hint = "UNUSED"; + parameter spread_frequency = 0; + parameter down_spread = "0.0"; + parameter self_reset_on_gated_loss_lock = "OFF"; + parameter self_reset_on_loss_lock = "OFF"; + parameter lock_window_ui = "0.05"; + parameter width_clock = 6; + parameter width_phasecounterselect = 4; + parameter charge_pump_current_bits = 9999; + parameter loop_filter_c_bits = 9999; + parameter loop_filter_r_bits = 9999; + parameter scan_chain_mif_file = "UNUSED"; + parameter clk9_multiply_by = 1; + parameter clk8_multiply_by = 1; + parameter clk7_multiply_by = 1; + parameter clk6_multiply_by = 1; + parameter clk5_multiply_by = 1; + parameter clk4_multiply_by = 1; + parameter clk3_multiply_by = 1; + parameter clk2_multiply_by = 1; + parameter clk1_multiply_by = 1; + parameter clk0_multiply_by = 1; + parameter clk9_divide_by = 1; + parameter clk8_divide_by = 1; + parameter clk7_divide_by = 1; + parameter clk6_divide_by = 1; + parameter clk5_divide_by = 1; + parameter clk4_divide_by = 1; + parameter clk3_divide_by = 1; + parameter clk2_divide_by = 1; + parameter clk1_divide_by = 1; + parameter clk0_divide_by = 1; + parameter clk9_phase_shift = "0"; + parameter clk8_phase_shift = "0"; + parameter clk7_phase_shift = "0"; + parameter clk6_phase_shift = "0"; + parameter clk5_phase_shift = "0"; + parameter clk4_phase_shift = "0"; + parameter clk3_phase_shift = "0"; + parameter clk2_phase_shift = "0"; + parameter clk1_phase_shift = "0"; + parameter clk0_phase_shift = "0"; + + parameter clk9_duty_cycle = 50; + parameter clk8_duty_cycle = 50; + parameter clk7_duty_cycle = 50; + parameter clk6_duty_cycle = 50; + parameter clk5_duty_cycle = 50; + parameter clk4_duty_cycle = 50; + parameter clk3_duty_cycle = 50; + parameter clk2_duty_cycle = 50; + parameter clk1_duty_cycle = 50; + parameter clk0_duty_cycle = 50; + + parameter clk9_use_even_counter_mode = "OFF"; + parameter clk8_use_even_counter_mode = "OFF"; + parameter clk7_use_even_counter_mode = "OFF"; + parameter clk6_use_even_counter_mode = "OFF"; + parameter clk5_use_even_counter_mode = "OFF"; + parameter clk4_use_even_counter_mode = "OFF"; + parameter clk3_use_even_counter_mode = "OFF"; + parameter clk2_use_even_counter_mode = "OFF"; + parameter clk1_use_even_counter_mode = "OFF"; + parameter clk0_use_even_counter_mode = "OFF"; + parameter clk9_use_even_counter_value = "OFF"; + parameter clk8_use_even_counter_value = "OFF"; + parameter clk7_use_even_counter_value = "OFF"; + parameter clk6_use_even_counter_value = "OFF"; + parameter clk5_use_even_counter_value = "OFF"; + parameter clk4_use_even_counter_value = "OFF"; + parameter clk3_use_even_counter_value = "OFF"; + parameter clk2_use_even_counter_value = "OFF"; + parameter clk1_use_even_counter_value = "OFF"; + parameter clk0_use_even_counter_value = "OFF"; + + parameter clk2_output_frequency = 0; + parameter clk1_output_frequency = 0; + parameter clk0_output_frequency = 0; + + parameter vco_min = 0; + parameter vco_max = 0; + parameter vco_center = 0; + parameter pfd_min = 0; + parameter pfd_max = 0; + parameter m_initial = 1; + parameter m = 0; + parameter n = 1; + parameter m2 = 1; + parameter n2 = 1; + parameter ss = 0; + parameter l0_high = 1; + parameter l1_high = 1; + parameter g0_high = 1; + parameter g1_high = 1; + parameter g2_high = 1; + parameter g3_high = 1; + parameter e0_high = 1; + parameter e1_high = 1; + parameter e2_high = 1; + parameter e3_high = 1; + parameter l0_low = 1; + parameter l1_low = 1; + parameter g0_low = 1; + parameter g1_low = 1; + parameter g2_low = 1; + parameter g3_low = 1; + parameter e0_low = 1; + parameter e1_low = 1; + parameter e2_low = 1; + parameter e3_low = 1; + parameter l0_initial = 1; + parameter l1_initial = 1; + parameter g0_initial = 1; + parameter g1_initial = 1; + parameter g2_initial = 1; + parameter g3_initial = 1; + parameter e0_initial = 1; + parameter e1_initial = 1; + parameter e2_initial = 1; + parameter e3_initial = 1; + parameter l0_mode = "bypass"; + parameter l1_mode = "bypass"; + parameter g0_mode = "bypass"; + parameter g1_mode = "bypass"; + parameter g2_mode = "bypass"; + parameter g3_mode = "bypass"; + parameter e0_mode = "bypass"; + parameter e1_mode = "bypass"; + parameter e2_mode = "bypass"; + parameter e3_mode = "bypass"; + parameter l0_ph = 0; + parameter l1_ph = 0; + parameter g0_ph = 0; + parameter g1_ph = 0; + parameter g2_ph = 0; + parameter g3_ph = 0; + parameter e0_ph = 0; + parameter e1_ph = 0; + parameter e2_ph = 0; + parameter e3_ph = 0; + parameter m_ph = 0; + parameter l0_time_delay = 0; + parameter l1_time_delay = 0; + parameter g0_time_delay = 0; + parameter g1_time_delay = 0; + parameter g2_time_delay = 0; + parameter g3_time_delay = 0; + parameter e0_time_delay = 0; + parameter e1_time_delay = 0; + parameter e2_time_delay = 0; + parameter e3_time_delay = 0; + parameter m_time_delay = 0; + parameter n_time_delay = 0; + parameter extclk3_counter = "e3" ; + parameter extclk2_counter = "e2" ; + parameter extclk1_counter = "e1" ; + parameter extclk0_counter = "e0" ; + parameter clk9_counter = "c9" ; + parameter clk8_counter = "c8" ; + parameter clk7_counter = "c7" ; + parameter clk6_counter = "c6" ; + parameter clk5_counter = "l1" ; + parameter clk4_counter = "l0" ; + parameter clk3_counter = "g3" ; + parameter clk2_counter = "g2" ; + parameter clk1_counter = "g1" ; + parameter clk0_counter = "g0" ; + parameter enable0_counter = "l0"; + parameter enable1_counter = "l0"; + parameter charge_pump_current = 2; + parameter loop_filter_r = "1.0"; + parameter loop_filter_c = 5; + parameter vco_post_scale = 0; + parameter vco_frequency_control = "AUTO"; + parameter vco_phase_shift_step = 0; + parameter lpm_type = "altpll"; + + parameter port_clkena0 = "PORT_CONNECTIVITY"; + parameter port_clkena1 = "PORT_CONNECTIVITY"; + parameter port_clkena2 = "PORT_CONNECTIVITY"; + parameter port_clkena3 = "PORT_CONNECTIVITY"; + parameter port_clkena4 = "PORT_CONNECTIVITY"; + parameter port_clkena5 = "PORT_CONNECTIVITY"; + parameter port_extclkena0 = "PORT_CONNECTIVITY"; + parameter port_extclkena1 = "PORT_CONNECTIVITY"; + parameter port_extclkena2 = "PORT_CONNECTIVITY"; + parameter port_extclkena3 = "PORT_CONNECTIVITY"; + parameter port_extclk0 = "PORT_CONNECTIVITY"; + parameter port_extclk1 = "PORT_CONNECTIVITY"; + parameter port_extclk2 = "PORT_CONNECTIVITY"; + parameter port_extclk3 = "PORT_CONNECTIVITY"; + parameter port_clk0 = "PORT_CONNECTIVITY"; + parameter port_clk1 = "PORT_CONNECTIVITY"; + parameter port_clk2 = "PORT_CONNECTIVITY"; + parameter port_clk3 = "PORT_CONNECTIVITY"; + parameter port_clk4 = "PORT_CONNECTIVITY"; + parameter port_clk5 = "PORT_CONNECTIVITY"; + parameter port_clk6 = "PORT_CONNECTIVITY"; + parameter port_clk7 = "PORT_CONNECTIVITY"; + parameter port_clk8 = "PORT_CONNECTIVITY"; + parameter port_clk9 = "PORT_CONNECTIVITY"; + parameter port_scandata = "PORT_CONNECTIVITY"; + parameter port_scandataout = "PORT_CONNECTIVITY"; + parameter port_scandone = "PORT_CONNECTIVITY"; + parameter port_sclkout1 = "PORT_CONNECTIVITY"; + parameter port_sclkout0 = "PORT_CONNECTIVITY"; + parameter port_clkbad0 = "PORT_CONNECTIVITY"; + parameter port_clkbad1 = "PORT_CONNECTIVITY"; + parameter port_activeclock = "PORT_CONNECTIVITY"; + parameter port_clkloss = "PORT_CONNECTIVITY"; + parameter port_inclk1 = "PORT_CONNECTIVITY"; + parameter port_inclk0 = "PORT_CONNECTIVITY"; + parameter port_fbin = "PORT_CONNECTIVITY"; + parameter port_fbout = "PORT_CONNECTIVITY"; + parameter port_pllena = "PORT_CONNECTIVITY"; + parameter port_clkswitch = "PORT_CONNECTIVITY"; + parameter port_areset = "PORT_CONNECTIVITY"; + parameter port_pfdena = "PORT_CONNECTIVITY"; + parameter port_scanclk = "PORT_CONNECTIVITY"; + parameter port_scanaclr = "PORT_CONNECTIVITY"; + parameter port_scanread = "PORT_CONNECTIVITY"; + parameter port_scanwrite = "PORT_CONNECTIVITY"; + parameter port_enable0 = "PORT_CONNECTIVITY"; + parameter port_enable1 = "PORT_CONNECTIVITY"; + parameter port_locked = "PORT_CONNECTIVITY"; + parameter port_configupdate = "PORT_CONNECTIVITY"; + parameter port_phasecounterselect = "PORT_CONNECTIVITY"; + parameter port_phasedone = "PORT_CONNECTIVITY"; + parameter port_phasestep = "PORT_CONNECTIVITY"; + parameter port_phaseupdown = "PORT_CONNECTIVITY"; + parameter port_vcooverrange = "PORT_CONNECTIVITY"; + parameter port_vcounderrange = "PORT_CONNECTIVITY"; + parameter port_scanclkena = "PORT_CONNECTIVITY"; + parameter using_fbmimicbidir_port = "ON"; + + input [1:0] inclk; + input fbin; + input pllena; + input clkswitch; + input areset; + input pfdena; + input clkena; + input extclkena; + input scanclk; + input scanaclr; + input scanclkena; + input scanread; + input scanwrite; + input scandata; + input phasecounterselect; + input phaseupdown; + input phasestep; + input configupdate; + inout fbmimicbidir; + + + output [width_clock-1:0] clk; + output [3:0] extclk; + output [1:0] clkbad; + output enable0; + output enable1; + output activeclock; + output clkloss; + output locked; + output scandataout; + output scandone; + output sclkout0; + output sclkout1; + output phasedone; + output vcooverrange; + output vcounderrange; + output fbout; + output fref; + output icdrclk; + output c0, c1, c2, c3, c4; + +endmodule // altpll +`endif diff --git a/yosys/techlibs/intel/common/brams.txt b/yosys/techlibs/intel/common/brams.txt new file mode 100644 index 000000000..3bf21afc9 --- /dev/null +++ b/yosys/techlibs/intel/common/brams.txt @@ -0,0 +1,33 @@ +bram $__M9K_ALTSYNCRAM_SINGLEPORT_FULL + init 1 + abits 13 @M1 + dbits 1 @M1 + abits 12 @M2 + dbits 2 @M2 + abits 11 @M3 + dbits 4 @M3 + abits 10 @M4 + dbits 8 @M4 + abits 10 @M5 + dbits 9 @M5 + abits 9 @M6 + dbits 16 @M6 + abits 9 @M7 + dbits 18 @M7 + abits 8 @M8 + dbits 32 @M8 + abits 8 @M9 + dbits 36 @M9 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +match $__M9K_ALTSYNCRAM_SINGLEPORT_FULL + min efficiency 2 + make_transp +endmatch diff --git a/yosys/techlibs/intel/common/brams_map.v b/yosys/techlibs/intel/common/brams_map.v new file mode 100644 index 000000000..d0f07c1de --- /dev/null +++ b/yosys/techlibs/intel/common/brams_map.v @@ -0,0 +1,93 @@ +module \$__M9K_ALTSYNCRAM_SINGLEPORT_FULL (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + + parameter CFG_ABITS = 8; + parameter CFG_DBITS = 36; + parameter ABITS = 1; + parameter DBITS = 1; + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + + input CLK2; + input CLK3; + //Read data + output [CFG_DBITS-1:0] A1DATA; + input [CFG_ABITS-1:0] A1ADDR; + input A1EN; + //Write data + output [CFG_DBITS-1:0] B1DATA; + input [CFG_ABITS-1:0] B1ADDR; + input B1EN; + + wire [CFG_DBITS-1:0] B1DATA_t; + + localparam MODE = CFG_DBITS == 1 ? 1: + CFG_DBITS == 2 ? 2: + CFG_DBITS == 4 ? 3: + CFG_DBITS == 8 ? 4: + CFG_DBITS == 9 ? 5: + CFG_DBITS == 16 ? 6: + CFG_DBITS == 18 ? 7: + CFG_DBITS == 32 ? 8: + CFG_DBITS == 36 ? 9: + 'bx; + + localparam NUMWORDS = CFG_DBITS == 1 ? 8192: + CFG_DBITS == 2 ? 4096: + CFG_DBITS == 4 ? 2048: + CFG_DBITS == 8 ? 1024: + CFG_DBITS == 9 ? 1024: + CFG_DBITS == 16 ? 512: + CFG_DBITS == 18 ? 512: + CFG_DBITS == 32 ? 256: + CFG_DBITS == 36 ? 256: + 'bx; + + altsyncram #(.clock_enable_input_b ("ALTERNATE" ), + .clock_enable_input_a ("ALTERNATE" ), + .clock_enable_output_b ("NORMAL" ), + .clock_enable_output_a ("NORMAL" ), + .wrcontrol_aclr_a ("NONE" ), + .indata_aclr_a ("NONE" ), + .address_aclr_a ("NONE" ), + .outdata_aclr_a ("NONE" ), + .outdata_reg_a ("UNREGISTERED"), + .operation_mode ("SINGLE_PORT" ), + .intended_device_family ("CYCLONE IVE" ), + .outdata_reg_a ("UNREGISTERED"), + .lpm_type ("altsyncram" ), + .init_type ("unused" ), + .ram_block_type ("AUTO" ), + .lpm_hint ("ENABLE_RUNTIME_MOD=NO"), // Forced value + .power_up_uninitialized ("FALSE"), + .read_during_write_mode_port_a ("NEW_DATA_NO_NBE_READ"), // Forced value + .width_byteena_a (1), // Forced value + .numwords_b ( NUMWORDS ), + .numwords_a ( NUMWORDS ), + .widthad_b ( CFG_DBITS ), + .width_b ( CFG_ABITS ), + .widthad_a ( CFG_DBITS ), + .width_a ( CFG_ABITS ) + ) _TECHMAP_REPLACE_ ( + .data_a(B1DATA), + .address_a(B1ADDR), + .wren_a(B1EN), + .rden_a(A1EN), + .q_a(A1DATA), + .data_b(B1DATA), + .address_b(0), + .wren_b(1'b0), + .rden_b(1'b0), + .q_b(), + .clock0(CLK2), + .clock1(1'b1), // Unused in single port mode + .clocken0(1'b1), + .clocken1(1'b1), + .clocken2(1'b1), + .clocken3(1'b1), + .aclr0(1'b0), + .aclr1(1'b0), + .addressstall_a(1'b0), + .addressstall_b(1'b0)); + +endmodule + diff --git a/yosys/techlibs/intel/common/m9k_bb.v b/yosys/techlibs/intel/common/m9k_bb.v new file mode 100644 index 000000000..b18a752f5 --- /dev/null +++ b/yosys/techlibs/intel/common/m9k_bb.v @@ -0,0 +1,70 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +(* blackbox *) +module altsyncram(data_a, address_a, wren_a, rden_a, q_a, data_b, address_b, wren_b, rden_b, + q_b, clock0, clock1, clocken0, clocken1, clocken2, clocken3, aclr0, aclr1, + addressstall_a, addressstall_b); + + parameter clock_enable_input_b = "ALTERNATE"; + parameter clock_enable_input_a = "ALTERNATE"; + parameter clock_enable_output_b = "NORMAL"; + parameter clock_enable_output_a = "NORMAL"; + parameter wrcontrol_aclr_a = "NONE"; + parameter indata_aclr_a = "NONE"; + parameter address_aclr_a = "NONE"; + parameter outdata_aclr_a = "NONE"; + parameter outdata_reg_a = "UNREGISTERED"; + parameter operation_mode = "SINGLE_PORT"; + parameter intended_device_family = "MAX 10 FPGA"; + parameter outdata_reg_a = "UNREGISTERED"; + parameter lpm_type = "altsyncram"; + parameter init_type = "unused"; + parameter ram_block_type = "AUTO"; + parameter lpm_hint = "ENABLE_RUNTIME_MOD=NO"; + parameter power_up_uninitialized = "FALSE"; + parameter read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ"; + parameter width_byteena_a = 1; + parameter numwords_b = 0; + parameter numwords_a = 0; + parameter widthad_b = 1; + parameter width_b = 1; + parameter widthad_a = 1; + parameter width_a = 1; + + // Port A declarations + output [35:0] q_a; + input [35:0] data_a; + input [7:0] address_a; + input wren_a; + input rden_a; + // Port B declarations + output [35:0] q_b; + input [35:0] data_b; + input [7:0] address_b; + input wren_b; + input rden_b; + // Control signals + input clock0, clock1; + input clocken0, clocken1, clocken2, clocken3; + input aclr0, aclr1; + input addressstall_a; + input addressstall_b; + // TODO: Implement the correct simulation model + +endmodule // altsyncram diff --git a/yosys/techlibs/intel/cyclone10/cells_arith.v b/yosys/techlibs/intel/cyclone10/cells_arith.v new file mode 100644 index 000000000..5ae8d6cea --- /dev/null +++ b/yosys/techlibs/intel/cyclone10/cells_arith.v @@ -0,0 +1,65 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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: This is still WIP. +(* techmap_celltype = "$alu" *) +module _80_altera_a10gx_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; + output CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 4; + + 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:0] C = {CO, CI}; + wire [Y_WIDTH+1:0] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + /* Start implementation */ + (* keep *) cyclone10lp_lcell_comb #(.lut_mask(16'b0000_0000_1010_1010), .sum_lutc_input("cin")) carry_start (.cout(COx[0]), .dataa(C[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) begin + (* keep *) cyclone10lp_lcell_comb #(.lut_mask(16'b1111_0000_1110_0000), .sum_lutc_input("cin")) carry_end (.combout(COx[Y_WIDTH]), .dataa(1'b1), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[Y_WIDTH])); + assign CO = COx[Y_WIDTH]; + end + else + cyclone10lp_lcell_comb #(.lut_mask(16'b1001_0110_1110_1000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(COx[i+1]), .dataa(AA[i]), .datab(BB[i]), .datac(1'b1), .datad(1'b1), .cin(C[i+1])); + end: slice + endgenerate + /* End implementation */ + assign X = AA ^ BB; + +endmodule diff --git a/yosys/techlibs/intel/cyclone10/cells_map.v b/yosys/techlibs/intel/cyclone10/cells_map.v new file mode 100644 index 000000000..c2f6f403c --- /dev/null +++ b/yosys/techlibs/intel/cyclone10/cells_map.v @@ -0,0 +1,109 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Intel FPGA technology mapping. User must first simulate the generated \ +// > netlist before going to test it on board. +// > Changelog: 1) The missing power_up parameter in the techmap introduces a problem in Quartus mapper. Fixed. + +// Normal mode DFF negedge clk, negedge reset +module \$_DFF_N_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Normal mode DFF +module \$_DFF_P_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +// Async Active Low Reset DFF +module \$_DFF_PN0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up("power_up")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Async Active High Reset DFF +module \$_DFF_PP0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire R_i = ~ R; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R_i), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +module \$__DFFE_PP0 (input D, C, E, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire E_i = ~ E; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(E_i), .sload(1'b0)); +endmodule + +// Input buffer map +module \$__inpad (input I, output O); + cyclone10lp_io_ibuf _TECHMAP_REPLACE_ (.o(O), .i(I), .ibar(1'b0)); +endmodule + +// Output buffer map +module \$__outpad (input I, output O); + cyclone10lp_io_obuf _TECHMAP_REPLACE_ (.o(O), .i(I), .oe(1'b1)); +endmodule + +// LUT Map +/* 0 -> datac + 1 -> cin */ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + generate + if (WIDTH == 1) begin + assign Y = ~A[0]; // Not need to spend 1 logic cell for such an easy function + end else + if (WIDTH == 2) begin + cyclone10lp_lcell_comb #(.lut_mask({4{LUT}}), + .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(1'b1), + .datad(1'b1)); + end else + if(WIDTH == 3) begin + cyclone10lp_lcell_comb #(.lut_mask({2{LUT}}), + .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(1'b1)); + end else + if(WIDTH == 4) begin + cyclone10lp_lcell_comb #(.lut_mask(LUT), + .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(A[3])); + end else + wire _TECHMAP_FAIL_ = 1; + endgenerate + +endmodule + + diff --git a/yosys/techlibs/intel/cyclone10/cells_sim.v b/yosys/techlibs/intel/cyclone10/cells_sim.v new file mode 100644 index 000000000..f5a8aee2b --- /dev/null +++ b/yosys/techlibs/intel/cyclone10/cells_sim.v @@ -0,0 +1,137 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +module VCC (output V); + assign V = 1'b1; +endmodule // VCC + +module GND (output G); + assign G = 1'b0; +endmodule // GND + +/* Altera Cyclone 10 LP devices Input Buffer Primitive */ +module cyclone10lp_io_ibuf + (output o, input i, input ibar); + assign ibar = ibar; + assign o = i; +endmodule // cyclone10lp_io_ibuf + +/* Altera Cyclone 10 LP devices Output Buffer Primitive */ +module cyclone10lp_io_obuf + (output o, input i, input oe); + assign o = i; + assign oe = oe; +endmodule // cyclone10lp_io_obuf + +/* Altera Cyclone IV (E) 4-input non-fracturable LUT Primitive */ +module cyclone10lp_lcell_comb + (output combout, cout, + input dataa, datab, datac, datad, cin); + + /* Internal parameters which define the behaviour + of the LUT primitive. + lut_mask define the lut function, can be expressed in 16-digit bin or hex. + sum_lutc_input define the type of LUT (combinational | arithmetic). + dont_touch for retiming || carry options. + lpm_type for WYSIWYG */ + + parameter lut_mask = 16'hFFFF; + parameter dont_touch = "off"; + parameter lpm_type = "cyclone10lp_lcell_comb"; + parameter sum_lutc_input = "datac"; + + reg [1:0] lut_type; + reg cout_rt; + reg combout_rt; + wire dataa_w; + wire datab_w; + wire datac_w; + wire datad_w; + wire cin_w; + + assign dataa_w = dataa; + assign datab_w = datab; + assign datac_w = datac; + assign datad_w = datad; + + function lut_data; + input [15:0] mask; + input dataa, datab, datac, datad; + reg [7:0] s3; + reg [3:0] s2; + reg [1:0] s1; + begin + s3 = datad ? mask[15:8] : mask[7:0]; + s2 = datac ? s3[7:4] : s3[3:0]; + s1 = datab ? s2[3:2] : s2[1:0]; + lut_data = dataa ? s1[1] : s1[0]; + end + + endfunction + + initial begin + if (sum_lutc_input == "datac") lut_type = 0; + else + if (sum_lutc_input == "cin") lut_type = 1; + else begin + $error("Error in sum_lutc_input. Parameter %s is not a valid value.\n", sum_lutc_input); + $finish(); + end + end + + always @(dataa_w or datab_w or datac_w or datad_w or cin_w) begin + if (lut_type == 0) begin // logic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + datac_w, datad_w); + end + else if (lut_type == 1) begin // arithmetic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + cin_w, datad_w); + end + cout_rt = lut_data(lut_mask, dataa_w, datab_w, cin_w, 'b0); + end + + assign combout = combout_rt & 1'b1; + assign cout = cout_rt & 1'b1; + +endmodule // cyclone10lp_lcell_comb + +/* Altera D Flip-Flop Primitive */ +module dffeas + (output q, + input d, clk, clrn, prn, ena, + input asdata, aload, sclr, sload); + + // Timing simulation is not covered + parameter power_up="dontcare"; + parameter is_wysiwyg="false"; + + reg q_tmp; + wire reset; + reg [7:0] debug_net; + + assign reset = (prn && sclr && ~clrn && ena); + assign q = q_tmp & 1'b1; + + always @(posedge clk, posedge aload) begin + if(reset) q_tmp <= 0; + else q_tmp <= d; + end + assign q = q_tmp; + +endmodule // dffeas diff --git a/yosys/techlibs/intel/cycloneiv/cells_arith.v b/yosys/techlibs/intel/cycloneiv/cells_arith.v new file mode 100644 index 000000000..010a4b5da --- /dev/null +++ b/yosys/techlibs/intel/cycloneiv/cells_arith.v @@ -0,0 +1,97 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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: This is still WIP. +(* techmap_celltype = "$alu" *) +/* Uncomment this for LCU???? +module _80_cycloneiv_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; + output 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:0] C = {CO, CI}; + wire [Y_WIDTH+1:0] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + /* Start implementation */ + //cycloneiv_lcell_comb #(.lut_mask(16'b0000_0000_1010_1010), .sum_lutc_input("cin")) carry_start (.cout(COx[0]), .dataa(C[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + /* + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) + (* keep *) cycloneiv_lcell_comb #(.lut_mask(16'b1111_0000_1110_0000), .sum_lutc_input("cin")) carry_end (.combout(CO), .dataa(1'b1), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[Y_WIDTH])); + //assign CO = COx[Y_WIDTH]; + else + cycloneiv_lcell_comb #(.lut_mask(16'b1001_0110_1110_1000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(COx[i+1]), .dataa(AA[i]), .datab(BB[i]), .datac(1'b1), .datad(1'b1), .cin(C[i+1])); + end: slice + endgenerate + /* End implementation */ + /*assign X = AA ^ BB; + +endmodule*/ +module _80_cycloneiv_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:0] CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH < 6; + + 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:0] C = {CO, CI}; + + cycloneiv_lcell_comb #(.lut_mask(16'b0110_0110_1000_1000), .sum_lutc_input("cin")) carry_start (.cout(CO[0]), .dataa(BB[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + genvar i; + generate for (i = 1; i < Y_WIDTH; i = i + 1) begin:slice + cycloneiv_lcell_comb #(.lut_mask(16'b0101_1010_0101_0000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(CO[i]), .dataa(BB[i]), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[i])); + end endgenerate + + assign X = AA ^ BB; +endmodule diff --git a/yosys/techlibs/intel/cycloneiv/cells_map.v b/yosys/techlibs/intel/cycloneiv/cells_map.v new file mode 100644 index 000000000..191488430 --- /dev/null +++ b/yosys/techlibs/intel/cycloneiv/cells_map.v @@ -0,0 +1,93 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Intel FPGA technology mapping. User must first simulate the generated \ +// > netlist before going to test it on board. +// > Changelog: 1) The missing power_up parameter in the techmap introduces a problem in Quartus mapper. Fixed. + +// Normal mode DFF negedge clk, negedge reset +module \$_DFF_N_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Normal mode DFF +module \$_DFF_P_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +// Async Active Low Reset DFF +module \$_DFF_PN0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up("power_up")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Async Active High Reset DFF +module \$_DFF_PP0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire R_i = ~ R; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R_i), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +module \$__DFFE_PP0 (input D, C, E, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire E_i = ~ E; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(E_i), .sload(1'b0)); +endmodule + +// Input buffer map +module \$__inpad (input I, output O); + cycloneiv_io_ibuf _TECHMAP_REPLACE_ (.o(O), .i(I), .ibar(1'b0)); +endmodule + +// Output buffer map +module \$__outpad (input I, output O); + cycloneiv_io_obuf _TECHMAP_REPLACE_ (.o(O), .i(I), .oe(1'b1)); +endmodule + +// LUT Map +/* 0 -> datac + 1 -> cin */ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + generate + if (WIDTH == 1) begin + assign Y = ~A[0]; // Not need to spend 1 logic cell for such an easy function + end else + if (WIDTH == 2) begin + cycloneiv_lcell_comb #(.lut_mask({4{LUT}}), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(1'b1),.datad(1'b1)); + end else + if(WIDTH == 3) begin + cycloneiv_lcell_comb #(.lut_mask({2{LUT}}), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(A[2]),.datad(1'b1)); + end else + if(WIDTH == 4) begin + cycloneiv_lcell_comb #(.lut_mask(LUT), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(A[2]),.datad(A[3])); + end else + wire _TECHMAP_FAIL_ = 1; + endgenerate +endmodule // + + diff --git a/yosys/techlibs/intel/cycloneiv/cells_sim.v b/yosys/techlibs/intel/cycloneiv/cells_sim.v new file mode 100644 index 000000000..2af2ab990 --- /dev/null +++ b/yosys/techlibs/intel/cycloneiv/cells_sim.v @@ -0,0 +1,299 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ + +module VCC (output V); + assign V = 1'b1; +endmodule // VCC + +module GND (output G); + assign G = 1'b0; +endmodule // GND + +/* Altera Cyclone IV (GX) devices Input Buffer Primitive */ +module cycloneiv_io_ibuf + (output o, input i, input ibar); + assign ibar = ibar; + assign o = i; +endmodule // fiftyfivenm_io_ibuf + +/* Altera Cyclone IV (GX) devices Output Buffer Primitive */ +module cycloneiv_io_obuf + (output o, input i, input oe); + assign o = i; + assign oe = oe; +endmodule // fiftyfivenm_io_obuf + +/* Altera Cyclone IV (GX) 4-input non-fracturable LUT Primitive */ +module cycloneiv_lcell_comb + (output combout, cout, + input dataa, datab, datac, datad, cin); + + /* Internal parameters which define the behaviour + of the LUT primitive. + lut_mask define the lut function, can be expressed in 16-digit bin or hex. + sum_lutc_input define the type of LUT (combinational | arithmetic). + dont_touch for retiming || carry options. + lpm_type for WYSIWYG */ + + parameter lut_mask = 16'hFFFF; + parameter dont_touch = "off"; + parameter lpm_type = "cycloneiv_lcell_comb"; + parameter sum_lutc_input = "datac"; + + reg [1:0] lut_type; + reg cout_rt; + reg combout_rt; + wire dataa_w; + wire datab_w; + wire datac_w; + wire datad_w; + wire cin_w; + + assign dataa_w = dataa; + assign datab_w = datab; + assign datac_w = datac; + assign datad_w = datad; + + function lut_data; + input [15:0] mask; + input dataa, datab, datac, datad; + reg [7:0] s3; + reg [3:0] s2; + reg [1:0] s1; + begin + s3 = datad ? mask[15:8] : mask[7:0]; + s2 = datac ? s3[7:4] : s3[3:0]; + s1 = datab ? s2[3:2] : s2[1:0]; + lut_data = dataa ? s1[1] : s1[0]; + end + + endfunction + + initial begin + if (sum_lutc_input == "datac") lut_type = 0; + else + if (sum_lutc_input == "cin") lut_type = 1; + else begin + $error("Error in sum_lutc_input. Parameter %s is not a valid value.\n", sum_lutc_input); + $finish(); + end + end + + always @(dataa_w or datab_w or datac_w or datad_w or cin_w) begin + if (lut_type == 0) begin // logic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + datac_w, datad_w); + end + else if (lut_type == 1) begin // arithmetic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + cin_w, datad_w); + end + cout_rt = lut_data(lut_mask, dataa_w, datab_w, cin_w, 'b0); + end + + assign combout = combout_rt & 1'b1; + assign cout = cout_rt & 1'b1; + +endmodule // cycloneiv_lcell_comb + +/* Altera D Flip-Flop Primitive */ +module dffeas + (output q, + input d, clk, clrn, prn, ena, + input asdata, aload, sclr, sload); + + // Timing simulation is not covered + parameter power_up="dontcare"; + parameter is_wysiwyg="false"; + + reg q_tmp; + wire reset; + reg [7:0] debug_net; + + assign reset = (prn && sclr && ~clrn && ena); + assign q = q_tmp & 1'b1; + + always @(posedge clk, posedge aload) begin + if(reset) q_tmp <= 0; + else q_tmp <= d; + end + assign q = q_tmp; + +endmodule // dffeas + +/* Cyclone IV GX altpll clearbox model */ +(* blackbox *) +module cycloneiv_pll + (inclk, + fbin, + fbout, + clkswitch, + areset, + pfdena, + scanclk, + scandata, + scanclkena, + configupdate, + clk, + phasecounterselect, + phaseupdown, + phasestep, + clkbad, + activeclock, + locked, + scandataout, + scandone, + phasedone, + vcooverrange, + vcounderrange, + fref, + icdrclk); + + parameter operation_mode = "normal"; + parameter pll_type = "auto"; + parameter compensate_clock = "clock0"; + parameter inclk0_input_frequency = 0; + parameter inclk1_input_frequency = 0; + parameter self_reset_on_loss_lock = "off"; + parameter switch_over_type = "auto"; + parameter switch_over_counter = 1; + parameter enable_switch_over_counter = "off"; + parameter bandwidth = 0; + parameter bandwidth_type = "auto"; + parameter use_dc_coupling = "false"; + parameter lock_high = 0; + parameter lock_low = 0; + parameter lock_window_ui = "0.05"; + parameter test_bypass_lock_detect = "off"; + parameter clk0_output_frequency = 0; + parameter clk0_multiply_by = 0; + parameter clk0_divide_by = 0; + parameter clk0_phase_shift = "0"; + parameter clk0_duty_cycle = 50; + parameter clk1_output_frequency = 0; + parameter clk1_multiply_by = 0; + parameter clk1_divide_by = 0; + parameter clk1_phase_shift = "0"; + parameter clk1_duty_cycle = 50; + parameter clk2_output_frequency = 0; + parameter clk2_multiply_by = 0; + parameter clk2_divide_by = 0; + parameter clk2_phase_shift = "0"; + parameter clk2_duty_cycle = 50; + parameter clk3_output_frequency = 0; + parameter clk3_multiply_by = 0; + parameter clk3_divide_by = 0; + parameter clk3_phase_shift = "0"; + parameter clk3_duty_cycle = 50; + parameter clk4_output_frequency = 0; + parameter clk4_multiply_by = 0; + parameter clk4_divide_by = 0; + parameter clk4_phase_shift = "0"; + parameter clk4_duty_cycle = 50; + parameter pfd_min = 0; + parameter pfd_max = 0; + parameter vco_min = 0; + parameter vco_max = 0; + parameter vco_center = 0; + // Advanced user parameters + parameter m_initial = 1; + parameter m = 0; + parameter n = 1; + parameter c0_high = 1; + parameter c0_low = 1; + parameter c0_initial = 1; + parameter c0_mode = "bypass"; + parameter c0_ph = 0; + parameter c1_high = 1; + parameter c1_low = 1; + parameter c1_initial = 1; + parameter c1_mode = "bypass"; + parameter c1_ph = 0; + parameter c2_high = 1; + parameter c2_low = 1; + parameter c2_initial = 1; + parameter c2_mode = "bypass"; + parameter c2_ph = 0; + parameter c3_high = 1; + parameter c3_low = 1; + parameter c3_initial = 1; + parameter c3_mode = "bypass"; + parameter c3_ph = 0; + parameter c4_high = 1; + parameter c4_low = 1; + parameter c4_initial = 1; + parameter c4_mode = "bypass"; + parameter c4_ph = 0; + parameter m_ph = 0; + parameter clk0_counter = "unused"; + parameter clk1_counter = "unused"; + parameter clk2_counter = "unused"; + parameter clk3_counter = "unused"; + parameter clk4_counter = "unused"; + parameter c1_use_casc_in = "off"; + parameter c2_use_casc_in = "off"; + parameter c3_use_casc_in = "off"; + parameter c4_use_casc_in = "off"; + parameter m_test_source = -1; + parameter c0_test_source = -1; + parameter c1_test_source = -1; + parameter c2_test_source = -1; + parameter c3_test_source = -1; + parameter c4_test_source = -1; + parameter vco_multiply_by = 0; + parameter vco_divide_by = 0; + parameter vco_post_scale = 1; + parameter vco_frequency_control = "auto"; + parameter vco_phase_shift_step = 0; + parameter charge_pump_current = 10; + parameter loop_filter_r = "1.0"; + parameter loop_filter_c = 0; + parameter pll_compensation_delay = 0; + parameter lpm_type = "cycloneiv_pll"; + parameter phase_counter_select_width = 3; + + input [1:0] inclk; + input fbin; + input clkswitch; + input areset; + input pfdena; + input [phase_counter_select_width - 1:0] phasecounterselect; + input phaseupdown; + input phasestep; + input scanclk; + input scanclkena; + input scandata; + input configupdate; + + output [4:0] clk; + output [1:0] clkbad; + output activeclock; + output locked; + output scandataout; + output scandone; + output fbout; + output phasedone; + output vcooverrange; + output vcounderrange; + output fref; + output icdrclk; + +endmodule // cycloneive_pll + + diff --git a/yosys/techlibs/intel/cycloneive/arith_map.v b/yosys/techlibs/intel/cycloneive/arith_map.v new file mode 100644 index 000000000..b3a11272b --- /dev/null +++ b/yosys/techlibs/intel/cycloneive/arith_map.v @@ -0,0 +1,99 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +/* TODO: Describe the following mode */ +module fa + (input a_c, + input b_c, + input cin_c, + output cout_t, + output sum_x); + + wire a_c; + wire b_c; + wire cout_t; + wire cin_c; + wire sum_x; + wire VCC; + + assign VCC = 1'b1; + + cycloneiv_lcell_comb gen_sum_0 (.combout(sum_x), + .dataa(a_c), + .datab(b_c), + .datac(cin_c), + .datad(VCC)); + defparam syn__05_.lut_mask = 16'b1001011010010110; + defparam syn__05_.sum_lutc_input = "datac"; + + cycloneiv_lcell_comb gen_cout_0 (.combout(cout_t), + .dataa(cin_c), + .datab(b_c), + .datac(a_c), + .datad(VCC)); + defparam syn__06_.lut_mask = 16'b1110000011100000; + defparam syn__06_.sum_lutc_input = "datac"; + +endmodule // fa + +module f_stage(); + +endmodule // f_stage + +module f_end(); + +endmodule // f_end + +module _80_cycloneive_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:0] CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH < 5; + + 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:0] C = {CO, CI}; + + fa f0 (.a_c(AA[0]), + .b_c(BB[0]), + .cin_c(C[0]), + .cout_t(C0[1]), + .sum_x(Y[0])); + + genvar i; + generate for (i = 1; i < Y_WIDTH; i = i + 1) begin:slice + cycloneive_lcell_comb #(.lut_mask(16'b0101_1010_0101_0000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(CO[i]), .dataa(BB[i]), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[i])); + end endgenerate + + assign X = AA ^ BB; + +endmodule diff --git a/yosys/techlibs/intel/cycloneive/cells_map.v b/yosys/techlibs/intel/cycloneive/cells_map.v new file mode 100644 index 000000000..abeb92eef --- /dev/null +++ b/yosys/techlibs/intel/cycloneive/cells_map.v @@ -0,0 +1,109 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Intel FPGA technology mapping. User must first simulate the generated \ +// > netlist before going to test it on board. +// > Changelog: 1) The missing power_up parameter in the techmap introduces a problem in Quartus mapper. Fixed. + +// Normal mode DFF negedge clk, negedge reset +module \$_DFF_N_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Normal mode DFF +module \$_DFF_P_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +// Async Active Low Reset DFF +module \$_DFF_PN0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up("power_up")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Async Active High Reset DFF +module \$_DFF_PP0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire R_i = ~ R; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R_i), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +module \$__DFFE_PP0 (input D, C, E, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire E_i = ~ E; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(E_i), .sload(1'b0)); +endmodule + +// Input buffer map +module \$__inpad (input I, output O); + cycloneive_io_ibuf _TECHMAP_REPLACE_ (.o(O), .i(I), .ibar(1'b0)); +endmodule + +// Output buffer map +module \$__outpad (input I, output O); + cycloneive_io_obuf _TECHMAP_REPLACE_ (.o(O), .i(I), .oe(1'b1)); +endmodule + +// LUT Map +/* 0 -> datac + 1 -> cin */ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + generate + if (WIDTH == 1) begin + assign Y = ~A[0]; // Not need to spend 1 logic cell for such an easy function + end else + if (WIDTH == 2) begin + cycloneive_lcell_comb #(.lut_mask({4{LUT}}), + .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(1'b1), + .datad(1'b1)); + end else + if(WIDTH == 3) begin + cycloneive_lcell_comb #(.lut_mask({2{LUT}}), + .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(1'b1)); + end else + if(WIDTH == 4) begin + cycloneive_lcell_comb #(.lut_mask(LUT), + .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(A[3])); + end else + wire _TECHMAP_FAIL_ = 1; + endgenerate + +endmodule + + diff --git a/yosys/techlibs/intel/cycloneive/cells_sim.v b/yosys/techlibs/intel/cycloneive/cells_sim.v new file mode 100644 index 000000000..f17b86fdf --- /dev/null +++ b/yosys/techlibs/intel/cycloneive/cells_sim.v @@ -0,0 +1,292 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +module VCC (output V); + assign V = 1'b1; +endmodule // VCC + +module GND (output G); + assign G = 1'b0; +endmodule // GND + +/* Altera Cyclone IV (E) devices Input Buffer Primitive */ +module cycloneive_io_ibuf + (output o, input i, input ibar); + assign ibar = ibar; + assign o = i; +endmodule // fiftyfivenm_io_ibuf + +/* Altera Cyclone IV (E) devices Output Buffer Primitive */ +module cycloneive_io_obuf + (output o, input i, input oe); + assign o = i; + assign oe = oe; +endmodule // fiftyfivenm_io_obuf + +/* Altera Cyclone IV (E) 4-input non-fracturable LUT Primitive */ +module cycloneive_lcell_comb + (output combout, cout, + input dataa, datab, datac, datad, cin); + + /* Internal parameters which define the behaviour + of the LUT primitive. + lut_mask define the lut function, can be expressed in 16-digit bin or hex. + sum_lutc_input define the type of LUT (combinational | arithmetic). + dont_touch for retiming || carry options. + lpm_type for WYSIWYG */ + + parameter lut_mask = 16'hFFFF; + parameter dont_touch = "off"; + parameter lpm_type = "cycloneive_lcell_comb"; + parameter sum_lutc_input = "datac"; + + reg [1:0] lut_type; + reg cout_rt; + reg combout_rt; + wire dataa_w; + wire datab_w; + wire datac_w; + wire datad_w; + wire cin_w; + + assign dataa_w = dataa; + assign datab_w = datab; + assign datac_w = datac; + assign datad_w = datad; + + function lut_data; + input [15:0] mask; + input dataa, datab, datac, datad; + reg [7:0] s3; + reg [3:0] s2; + reg [1:0] s1; + begin + s3 = datad ? mask[15:8] : mask[7:0]; + s2 = datac ? s3[7:4] : s3[3:0]; + s1 = datab ? s2[3:2] : s2[1:0]; + lut_data = dataa ? s1[1] : s1[0]; + end + + endfunction + + initial begin + if (sum_lutc_input == "datac") lut_type = 0; + else + if (sum_lutc_input == "cin") lut_type = 1; + else begin + $error("Error in sum_lutc_input. Parameter %s is not a valid value.\n", sum_lutc_input); + $finish(); + end + end + + always @(dataa_w or datab_w or datac_w or datad_w or cin_w) begin + if (lut_type == 0) begin // logic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + datac_w, datad_w); + end + else if (lut_type == 1) begin // arithmetic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + cin_w, datad_w); + end + cout_rt = lut_data(lut_mask, dataa_w, datab_w, cin_w, 'b0); + end + + assign combout = combout_rt & 1'b1; + assign cout = cout_rt & 1'b1; + +endmodule // cycloneive_lcell_comb + +/* Altera D Flip-Flop Primitive */ +module dffeas + (output q, + input d, clk, clrn, prn, ena, + input asdata, aload, sclr, sload); + + // Timing simulation is not covered + parameter power_up="dontcare"; + parameter is_wysiwyg="false"; + + reg q_tmp; + wire reset; + reg [7:0] debug_net; + + assign reset = (prn && sclr && ~clrn && ena); + assign q = q_tmp & 1'b1; + + always @(posedge clk, posedge aload) begin + if(reset) q_tmp <= 0; + else q_tmp <= d; + end + assign q = q_tmp; + +endmodule // dffeas + +/* Cyclone IV E altpll clearbox model */ +(* blackbox *) +module cycloneive_pll + (inclk, + fbin, + fbout, + clkswitch, + areset, + pfdena, + scanclk, + scandata, + scanclkena, + configupdate, + clk, + phasecounterselect, + phaseupdown, + phasestep, + clkbad, + activeclock, + locked, + scandataout, + scandone, + phasedone, + vcooverrange, + vcounderrange); + + parameter operation_mode = "normal"; + parameter pll_type = "auto"; + parameter compensate_clock = "clock0"; + parameter inclk0_input_frequency = 0; + parameter inclk1_input_frequency = 0; + parameter self_reset_on_loss_lock = "off"; + parameter switch_over_type = "auto"; + parameter switch_over_counter = 1; + parameter enable_switch_over_counter = "off"; + parameter bandwidth = 0; + parameter bandwidth_type = "auto"; + parameter use_dc_coupling = "false"; + parameter lock_high = 0; + parameter lock_low = 0; + parameter lock_window_ui = "0.05"; + parameter test_bypass_lock_detect = "off"; + parameter clk0_output_frequency = 0; + parameter clk0_multiply_by = 0; + parameter clk0_divide_by = 0; + parameter clk0_phase_shift = "0"; + parameter clk0_duty_cycle = 50; + parameter clk1_output_frequency = 0; + parameter clk1_multiply_by = 0; + parameter clk1_divide_by = 0; + parameter clk1_phase_shift = "0"; + parameter clk1_duty_cycle = 50; + parameter clk2_output_frequency = 0; + parameter clk2_multiply_by = 0; + parameter clk2_divide_by = 0; + parameter clk2_phase_shift = "0"; + parameter clk2_duty_cycle = 50; + parameter clk3_output_frequency = 0; + parameter clk3_multiply_by = 0; + parameter clk3_divide_by = 0; + parameter clk3_phase_shift = "0"; + parameter clk3_duty_cycle = 50; + parameter clk4_output_frequency = 0; + parameter clk4_multiply_by = 0; + parameter clk4_divide_by = 0; + parameter clk4_phase_shift = "0"; + parameter clk4_duty_cycle = 50; + parameter pfd_min = 0; + parameter pfd_max = 0; + parameter vco_min = 0; + parameter vco_max = 0; + parameter vco_center = 0; + // Advanced user parameters + parameter m_initial = 1; + parameter m = 0; + parameter n = 1; + parameter c0_high = 1; + parameter c0_low = 1; + parameter c0_initial = 1; + parameter c0_mode = "bypass"; + parameter c0_ph = 0; + parameter c1_high = 1; + parameter c1_low = 1; + parameter c1_initial = 1; + parameter c1_mode = "bypass"; + parameter c1_ph = 0; + parameter c2_high = 1; + parameter c2_low = 1; + parameter c2_initial = 1; + parameter c2_mode = "bypass"; + parameter c2_ph = 0; + parameter c3_high = 1; + parameter c3_low = 1; + parameter c3_initial = 1; + parameter c3_mode = "bypass"; + parameter c3_ph = 0; + parameter c4_high = 1; + parameter c4_low = 1; + parameter c4_initial = 1; + parameter c4_mode = "bypass"; + parameter c4_ph = 0; + parameter m_ph = 0; + parameter clk0_counter = "unused"; + parameter clk1_counter = "unused"; + parameter clk2_counter = "unused"; + parameter clk3_counter = "unused"; + parameter clk4_counter = "unused"; + parameter c1_use_casc_in = "off"; + parameter c2_use_casc_in = "off"; + parameter c3_use_casc_in = "off"; + parameter c4_use_casc_in = "off"; + parameter m_test_source = -1; + parameter c0_test_source = -1; + parameter c1_test_source = -1; + parameter c2_test_source = -1; + parameter c3_test_source = -1; + parameter c4_test_source = -1; + parameter vco_multiply_by = 0; + parameter vco_divide_by = 0; + parameter vco_post_scale = 1; + parameter vco_frequency_control = "auto"; + parameter vco_phase_shift_step = 0; + parameter charge_pump_current = 10; + parameter loop_filter_r = "1.0"; + parameter loop_filter_c = 0; + parameter pll_compensation_delay = 0; + parameter lpm_type = "cycloneive_pll"; + parameter phase_counter_select_width = 3; + + input [1:0] inclk; + input fbin; + input clkswitch; + input areset; + input pfdena; + input [phase_counter_select_width - 1:0] phasecounterselect; + input phaseupdown; + input phasestep; + input scanclk; + input scanclkena; + input scandata; + input configupdate; + + output [4:0] clk; + output [1:0] clkbad; + output activeclock; + output locked; + output scandataout; + output scandone; + output fbout; + output phasedone; + output vcooverrange; + output vcounderrange; + +endmodule // cycloneive_pll diff --git a/yosys/techlibs/intel/cyclonev/cells_arith.v b/yosys/techlibs/intel/cyclonev/cells_arith.v new file mode 100644 index 000000000..89fb4561f --- /dev/null +++ b/yosys/techlibs/intel/cyclonev/cells_arith.v @@ -0,0 +1,65 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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: This is still WIP. +(* techmap_celltype = "$alu" *) +module _80_altera_a10gx_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; + output CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 4; + + 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:0] C = {CO, CI}; + wire [Y_WIDTH+1:0] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + /* Start implementation */ + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b0000_0000_1010_1010), .sum_lutc_input("cin")) carry_start (.cout(COx[0]), .dataa(C[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) begin + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b1111_0000_1110_0000), .sum_lutc_input("cin")) carry_end (.combout(COx[Y_WIDTH]), .dataa(1'b1), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[Y_WIDTH])); + assign CO = COx[Y_WIDTH]; + end + else + fiftyfivenm_lcell_comb #(.lut_mask(16'b1001_0110_1110_1000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(COx[i+1]), .dataa(AA[i]), .datab(BB[i]), .datac(1'b1), .datad(1'b1), .cin(C[i+1])); + end: slice + endgenerate + /* End implementation */ + assign X = AA ^ BB; + +endmodule diff --git a/yosys/techlibs/intel/cyclonev/cells_map.v b/yosys/techlibs/intel/cyclonev/cells_map.v new file mode 100644 index 000000000..bd60d4e17 --- /dev/null +++ b/yosys/techlibs/intel/cyclonev/cells_map.v @@ -0,0 +1,162 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Intel FPGA technology mapping. User must first simulate the generated \ +// > netlist before going to test it on board. +// > Changelog: 1) The missing power_up parameter in the techmap introduces a problem in Quartus mapper. Fixed. +// 2) Cyclone V 7-input LUT function was wrong implemented. Removed abc option to map this function \ +// and added the explanation in this file instead. Such function needs to be implemented. + +// Normal mode DFF negedge clk, negedge reset +module \$_DFF_N_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Normal mode DFF +module \$_DFF_P_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +// Async Active Low Reset DFF +module \$_DFF_PN0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up("power_up")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Async Active High Reset DFF +module \$_DFF_PP0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire R_i = ~ R; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R_i), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +module \$__DFFE_PP0 (input D, C, E, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire E_i = ~ E; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(E_i), .sload(1'b0)); +endmodule + +// Input buffer map +module \$__inpad (input I, output O); + cyclonev_io_ibuf _TECHMAP_REPLACE_ (.o(O), .i(I), .ibar(1'b0)); +endmodule + +// Output buffer map +module \$__outpad (input I, output O); + cyclonev_io_obuf _TECHMAP_REPLACE_ (.o(O), .i(I), .oe(1'b1)); +endmodule + +// LUT Map +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + wire VCC; + wire GND; + assign {VCC,GND} = {1'b1,1'b0}; + + generate + if (WIDTH == 1) begin + assign Y = ~A[0]; // Not need to spend 1 logic cell for such an easy function + end + else + if (WIDTH == 2) begin + cyclonev_lcell_comb #(.lut_mask({16{LUT}}), .shared_arith("off"), .extended_lut("off")) + _TECHMAP_REPLACE_ + (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(VCC), + .datad(VCC), + .datae(VCC), + .dataf(VCC), + .datag(VCC)); + end + else + if(WIDTH == 3) begin + cyclonev_lcell_comb #(.lut_mask({8{LUT}}), .shared_arith("off"), .extended_lut("off")) + _TECHMAP_REPLACE_ + (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(VCC), + .datae(VCC), + .dataf(VCC), + .datag(VCC)); + end + else + if(WIDTH == 4) begin + cyclonev_lcell_comb #(.lut_mask({4{LUT}}), .shared_arith("off"), .extended_lut("off")) + _TECHMAP_REPLACE_ + (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(A[3]), + .datae(VCC), + .dataf(VCC), + .datag(VCC)); + end + else + if(WIDTH == 5) begin + cyclonev_lcell_comb #(.lut_mask({2{LUT}}), .shared_arith("off"), .extended_lut("off")) + _TECHMAP_REPLACE_ + (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(A[3]), + .datae(A[4]), + .dataf(VCC), + .datag(VCC)); + end + else + if(WIDTH == 6) begin + cyclonev_lcell_comb #(.lut_mask(LUT), .shared_arith("off"), .extended_lut("off")) + _TECHMAP_REPLACE_ + (.combout(Y), + .dataa(A[0]), + .datab(A[1]), + .datac(A[2]), + .datad(A[3]), + .datae(A[4]), + .dataf(A[5]), + .datag(VCC)); + end + /*else + if(WIDTH == 7) begin + TODO: There's not a just 7-input function on Cyclone V, see the following note: + **Extended LUT Mode** + Use extended LUT mode to implement a specific set of 7-input functions. The set must + be a 2-to-1 multiplexer fed by two arbitrary 5-input functions sharing four inputs. + [source](Device Interfaces and Integration Basics for Cyclone V Devices). + end*/ + else + wire _TECHMAP_FAIL_ = 1; + endgenerate +endmodule // lut + + diff --git a/yosys/techlibs/intel/cyclonev/cells_sim.v b/yosys/techlibs/intel/cyclonev/cells_sim.v new file mode 100644 index 000000000..fa27c2c8e --- /dev/null +++ b/yosys/techlibs/intel/cyclonev/cells_sim.v @@ -0,0 +1,144 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +module VCC (output V); + assign V = 1'b1; +endmodule // VCC + +module GND (output G); + assign G = 1'b0; +endmodule // GND + +/* Altera Cyclone V devices Input Buffer Primitive */ +module cyclonev_io_ibuf + (output o, input i, input ibar); + assign ibar = ibar; + assign o = i; +endmodule // cyclonev_io_ibuf + +/* Altera Cyclone V devices Output Buffer Primitive */ +module cyclonev_io_obuf + (output o, input i, input oe); + assign o = i; + assign oe = oe; +endmodule // cyclonev_io_obuf + +/* Altera Cyclone V LUT Primitive */ +module cyclonev_lcell_comb + (output combout, cout, sumout, shareout, + input dataa, datab, datac, datad, + input datae, dataf, datag, cin, + input sharein); + + parameter lut_mask = 64'hFFFFFFFFFFFFFFFF; + parameter dont_touch = "off"; + parameter lpm_type = "cyclonev_lcell_comb"; + parameter shared_arith = "off"; + parameter extended_lut = "off"; + + // Internal variables + // Sub mask for fragmented LUTs + wire [15:0] mask_a, mask_b, mask_c, mask_d; + // Independent output for fragmented LUTs + wire output_0, output_1, output_2, output_3; + // Extended mode uses mux to define the output + wire mux_0, mux_1; + // Input for hold the shared LUT mode value + wire shared_lut_alm; + + // Simulation model of 4-input LUT + function lut4; + input [15:0] mask; + input dataa, datab, datac, datad; + reg [7:0] s3; + reg [3:0] s2; + reg [1:0] s1; + begin + s3 = datad ? mask[15:8] : mask[7:0]; + s2 = datac ? s3[7:4] : s3[3:0]; + s1 = datab ? s2[3:2] : s2[1:0]; + lut4 = dataa ? s1[1] : s1[0]; + end + endfunction // lut4 + + // Simulation model of 5-input LUT + function lut5; + input [31:0] mask; // wp-01003.pdf, page 3: "a 5-LUT can be built with two 4-LUTs and a multiplexer. + input dataa, datab, datac, datad, datae; + reg upper_lut_value; + reg lower_lut_value; + begin + upper_lut_value = lut4(mask[31:16], dataa, datab, datac, datad); + lower_lut_value = lut4(mask[15:0], dataa, datab, datac, datad); + lut5 = (datae) ? upper_mask_value : lower_mask_value; + end + endfunction // lut5 + + // Simulation model of 6-input LUT + function lut6; + input [63:0] mask; + input dataa, datab, datac, datad, datae, dataf; + reg upper_lut_value; + reg lower_lut_value; + begin + upper_lut_value = lut5(mask[63:32], dataa, datab, datac, datad, datae); + lower_lut_value = lut5(mask[31:0], dataa, datab, datac, datad, datae); + lut6 = (dataf) ? upper_mask_value : lower_mask_value; + end + endfunction // lut6 + + assign {mask_a, mask_b, mask_c, mask_d} = {lut_mask[15:0], lut_mask[31:16], lut_mask[47:32], lut_mask[63:48]}; + + always @(*) begin + if(extended_lut == "on") + shared_lut_alm = datag; + else + shared_lut_alm = datac; + // Build the ALM behaviour + out_0 = lut4(mask_a, dataa, datab, datac, datad); + out_1 = lut4(mask_b, dataa, datab, shared_lut_alm, datad); + out_2 = lut4(mask_c, dataa, datab, datac, datad); + out_3 = lut4(mask_d, dataa, datab, shared_lut_alm, datad); + end +endmodule // cyclonev_lcell_comb + + +/* Altera D Flip-Flop Primitive */ +module dffeas + (output q, + input d, clk, clrn, prn, ena, + input asdata, aload, sclr, sload); + + // Timing simulation is not covered + parameter power_up="dontcare"; + parameter is_wysiwyg="false"; + + reg q_tmp; + wire reset; + reg [7:0] debug_net; + + assign reset = (prn && sclr && ~clrn && ena); + assign q = q_tmp & 1'b1; + + always @(posedge clk, posedge aload) begin + if(reset) q_tmp <= 0; + else q_tmp <= d; + end + assign q = q_tmp; + +endmodule // dffeas diff --git a/yosys/techlibs/intel/max10/cells_arith.v b/yosys/techlibs/intel/max10/cells_arith.v new file mode 100644 index 000000000..e2194cbd7 --- /dev/null +++ b/yosys/techlibs/intel/max10/cells_arith.v @@ -0,0 +1,65 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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: This is still WIP. +(* techmap_celltype = "$alu" *) +module _80_altera_max10_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; + output CO; + + wire _TECHMAP_FAIL_ = Y_WIDTH <= 4; + + 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:0] C = {CO, CI}; + wire [Y_WIDTH+1:0] COx; + wire [Y_WIDTH+1:0] C = {COx, CI}; + + /* Start implementation */ + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b0000_0000_1010_1010), .sum_lutc_input("cin")) carry_start (.cout(COx[0]), .dataa(C[0]), .datab(1'b1), .datac(1'b1), .datad(1'b1)); + + genvar i; + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice + if(i==Y_WIDTH-1) begin + (* keep *) fiftyfivenm_lcell_comb #(.lut_mask(16'b1111_0000_1110_0000), .sum_lutc_input("cin")) carry_end (.combout(COx[Y_WIDTH]), .dataa(1'b1), .datab(1'b1), .datac(1'b1), .datad(1'b1), .cin(C[Y_WIDTH])); + assign CO = COx[Y_WIDTH]; + end + else + fiftyfivenm_lcell_comb #(.lut_mask(16'b1001_0110_1110_1000), .sum_lutc_input("cin")) arith_cell (.combout(Y[i]), .cout(COx[i+1]), .dataa(AA[i]), .datab(BB[i]), .datac(1'b1), .datad(1'b1), .cin(C[i+1])); + end: slice + endgenerate + /* End implementation */ + assign X = AA ^ BB; + +endmodule diff --git a/yosys/techlibs/intel/max10/cells_map.v b/yosys/techlibs/intel/max10/cells_map.v new file mode 100644 index 000000000..6d604e072 --- /dev/null +++ b/yosys/techlibs/intel/max10/cells_map.v @@ -0,0 +1,93 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ +// > c60k28 (Viacheslav, VT) [at] yandex [dot] com +// > Intel FPGA technology mapping. User must first simulate the generated \ +// > netlist before going to test it on board. +// > Changelog: 1) The missing power_up parameter in the techmap introduces a problem in Quartus mapper. Fixed. + +// Normal mode DFF negedge clk, negedge reset +module \$_DFF_N_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Normal mode DFF +module \$_DFF_P_ (input D, C, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(1'b1), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +// Async Active Low Reset DFF +module \$_DFF_PN0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up("power_up")) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule +// Async Active High Reset DFF +module \$_DFF_PP0_ (input D, C, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire R_i = ~ R; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R_i), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(1'b0), .sload(1'b0)); +endmodule + +module \$__DFFE_PP0 (input D, C, E, R, output Q); + parameter WYSIWYG="TRUE"; + parameter power_up=1'bx; + wire E_i = ~ E; + dffeas #(.is_wysiwyg(WYSIWYG), .power_up(power_up)) _TECHMAP_REPLACE_ (.d(D), .q(Q), .clk(C), .clrn(R), .prn(1'b1), .ena(1'b1), .asdata(1'b0), .aload(1'b0), .sclr(E_i), .sload(1'b0)); +endmodule + +// Input buffer map +module \$__inpad (input I, output O); + fiftyfivenm_io_ibuf _TECHMAP_REPLACE_ (.o(O), .i(I), .ibar(1'b0)); +endmodule + +// Output buffer map +module \$__outpad (input I, output O); + fiftyfivenm_io_obuf _TECHMAP_REPLACE_ (.o(O), .i(I), .oe(1'b1)); +endmodule + +// LUT Map +/* 0 -> datac + 1 -> cin */ +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + input [WIDTH-1:0] A; + output Y; + generate + if (WIDTH == 1) begin + assign Y = ~A[0]; // Not need to spend 1 logic cell for such an easy function + end else + if (WIDTH == 2) begin + fiftyfivenm_lcell_comb #(.lut_mask({4{LUT}}), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(1'b1),.datad(1'b1)); + end else + if(WIDTH == 3) begin + fiftyfivenm_lcell_comb #(.lut_mask({2{LUT}}), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(A[2]),.datad(1'b1)); + end else + if(WIDTH == 4) begin + fiftyfivenm_lcell_comb #(.lut_mask(LUT), .sum_lutc_input("datac")) _TECHMAP_REPLACE_ (.combout(Y), .dataa(A[0]), .datab(A[1]), .datac(A[2]),.datad(A[3])); + end else + wire _TECHMAP_FAIL_ = 1; + endgenerate +endmodule // + + diff --git a/yosys/techlibs/intel/max10/cells_sim.v b/yosys/techlibs/intel/max10/cells_sim.v new file mode 100644 index 000000000..98673596b --- /dev/null +++ b/yosys/techlibs/intel/max10/cells_sim.v @@ -0,0 +1,292 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ + +module VCC (output V); + assign V = 1'b1; +endmodule // VCC + +module GND (output G); + assign G = 1'b0; +endmodule // GND + +/* Altera MAX10 devices Input Buffer Primitive */ +module fiftyfivenm_io_ibuf + (output o, input i, input ibar); + assign ibar = ibar; + assign o = i; +endmodule // fiftyfivenm_io_ibuf + +/* Altera MAX10 devices Output Buffer Primitive */ +module fiftyfivenm_io_obuf + (output o, input i, input oe); + assign o = i; + assign oe = oe; +endmodule // fiftyfivenm_io_obuf + +/* Altera MAX10 4-input non-fracturable LUT Primitive */ +module fiftyfivenm_lcell_comb + (output combout, cout, + input dataa, datab, datac, datad, cin); + + /* Internal parameters which define the behaviour + of the LUT primitive. + lut_mask define the lut function, can be expressed in 16-digit bin or hex. + sum_lutc_input define the type of LUT (combinational | arithmetic). + dont_touch for retiming || carry options. + lpm_type for WYSIWYG */ + + parameter lut_mask = 16'hFFFF; + parameter dont_touch = "off"; + parameter lpm_type = "fiftyfivenm_lcell_comb"; + parameter sum_lutc_input = "datac"; + + reg [1:0] lut_type; + reg cout_rt; + reg combout_rt; + wire dataa_w; + wire datab_w; + wire datac_w; + wire datad_w; + wire cin_w; + + assign dataa_w = dataa; + assign datab_w = datab; + assign datac_w = datac; + assign datad_w = datad; + + function lut_data; + input [15:0] mask; + input dataa, datab, datac, datad; + reg [7:0] s3; + reg [3:0] s2; + reg [1:0] s1; + begin + s3 = datad ? mask[15:8] : mask[7:0]; + s2 = datac ? s3[7:4] : s3[3:0]; + s1 = datab ? s2[3:2] : s2[1:0]; + lut_data = dataa ? s1[1] : s1[0]; + end + + endfunction + + initial begin + if (sum_lutc_input == "datac") lut_type = 0; + else + if (sum_lutc_input == "cin") lut_type = 1; + else begin + $error("Error in sum_lutc_input. Parameter %s is not a valid value.\n", sum_lutc_input); + $finish(); + end + end + + always @(dataa_w or datab_w or datac_w or datad_w or cin_w) begin + if (lut_type == 0) begin // logic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + datac_w, datad_w); + end + else if (lut_type == 1) begin // arithmetic function + combout_rt = lut_data(lut_mask, dataa_w, datab_w, + cin_w, datad_w); + end + cout_rt = lut_data(lut_mask, dataa_w, datab_w, cin_w, 'b0); + end + + assign combout = combout_rt & 1'b1; + assign cout = cout_rt & 1'b1; + +endmodule // fiftyfivenm_lcell_comb + +/* Altera D Flip-Flop Primitive */ +module dffeas + (output q, + input d, clk, clrn, prn, ena, + input asdata, aload, sclr, sload); + + // Timing simulation is not covered + parameter power_up="dontcare"; + parameter is_wysiwyg="false"; + + reg q_tmp; + wire reset; + reg [7:0] debug_net; + + assign reset = (prn && sclr && ~clrn && ena); + assign q = q_tmp & 1'b1; + + always @(posedge clk, posedge aload) begin + if(reset) q_tmp <= 0; + else q_tmp <= d; + end + assign q = q_tmp; + +endmodule // dffeas + +/* MAX10 altpll clearbox model */ +(* blackbox *) +module fiftyfivenm_pll + (inclk, + fbin, + fbout, + clkswitch, + areset, + pfdena, + scanclk, + scandata, + scanclkena, + configupdate, + clk, + phasecounterselect, + phaseupdown, + phasestep, + clkbad, + activeclock, + locked, + scandataout, + scandone, + phasedone, + vcooverrange, + vcounderrange); + + parameter operation_mode = "normal"; + parameter pll_type = "auto"; + parameter compensate_clock = "clock0"; + parameter inclk0_input_frequency = 0; + parameter inclk1_input_frequency = 0; + parameter self_reset_on_loss_lock = "off"; + parameter switch_over_type = "auto"; + parameter switch_over_counter = 1; + parameter enable_switch_over_counter = "off"; + parameter bandwidth = 0; + parameter bandwidth_type = "auto"; + parameter use_dc_coupling = "false"; + parameter lock_high = 0; + parameter lock_low = 0; + parameter lock_window_ui = "0.05"; + parameter test_bypass_lock_detect = "off"; + parameter clk0_output_frequency = 0; + parameter clk0_multiply_by = 0; + parameter clk0_divide_by = 0; + parameter clk0_phase_shift = "0"; + parameter clk0_duty_cycle = 50; + parameter clk1_output_frequency = 0; + parameter clk1_multiply_by = 0; + parameter clk1_divide_by = 0; + parameter clk1_phase_shift = "0"; + parameter clk1_duty_cycle = 50; + parameter clk2_output_frequency = 0; + parameter clk2_multiply_by = 0; + parameter clk2_divide_by = 0; + parameter clk2_phase_shift = "0"; + parameter clk2_duty_cycle = 50; + parameter clk3_output_frequency = 0; + parameter clk3_multiply_by = 0; + parameter clk3_divide_by = 0; + parameter clk3_phase_shift = "0"; + parameter clk3_duty_cycle = 50; + parameter clk4_output_frequency = 0; + parameter clk4_multiply_by = 0; + parameter clk4_divide_by = 0; + parameter clk4_phase_shift = "0"; + parameter clk4_duty_cycle = 50; + parameter pfd_min = 0; + parameter pfd_max = 0; + parameter vco_min = 0; + parameter vco_max = 0; + parameter vco_center = 0; + // Advanced user parameters + parameter m_initial = 1; + parameter m = 0; + parameter n = 1; + parameter c0_high = 1; + parameter c0_low = 1; + parameter c0_initial = 1; + parameter c0_mode = "bypass"; + parameter c0_ph = 0; + parameter c1_high = 1; + parameter c1_low = 1; + parameter c1_initial = 1; + parameter c1_mode = "bypass"; + parameter c1_ph = 0; + parameter c2_high = 1; + parameter c2_low = 1; + parameter c2_initial = 1; + parameter c2_mode = "bypass"; + parameter c2_ph = 0; + parameter c3_high = 1; + parameter c3_low = 1; + parameter c3_initial = 1; + parameter c3_mode = "bypass"; + parameter c3_ph = 0; + parameter c4_high = 1; + parameter c4_low = 1; + parameter c4_initial = 1; + parameter c4_mode = "bypass"; + parameter c4_ph = 0; + parameter m_ph = 0; + parameter clk0_counter = "unused"; + parameter clk1_counter = "unused"; + parameter clk2_counter = "unused"; + parameter clk3_counter = "unused"; + parameter clk4_counter = "unused"; + parameter c1_use_casc_in = "off"; + parameter c2_use_casc_in = "off"; + parameter c3_use_casc_in = "off"; + parameter c4_use_casc_in = "off"; + parameter m_test_source = -1; + parameter c0_test_source = -1; + parameter c1_test_source = -1; + parameter c2_test_source = -1; + parameter c3_test_source = -1; + parameter c4_test_source = -1; + parameter vco_multiply_by = 0; + parameter vco_divide_by = 0; + parameter vco_post_scale = 1; + parameter vco_frequency_control = "auto"; + parameter vco_phase_shift_step = 0; + parameter charge_pump_current = 10; + parameter loop_filter_r = "1.0"; + parameter loop_filter_c = 0; + parameter pll_compensation_delay = 0; + parameter lpm_type = "fiftyfivenm_pll"; + parameter phase_counter_select_width = 3; + + input [1:0] inclk; + input fbin; + input clkswitch; + input areset; + input pfdena; + input [phase_counter_select_width - 1:0] phasecounterselect; + input phaseupdown; + input phasestep; + input scanclk; + input scanclkena; + input scandata; + input configupdate; + output [4:0] clk; + output [1:0] clkbad; + output activeclock; + output locked; + output scandataout; + output scandone; + output fbout; + output phasedone; + output vcooverrange; + output vcounderrange; + +endmodule // cycloneive_pll diff --git a/yosys/techlibs/intel/synth_intel.cc b/yosys/techlibs/intel/synth_intel.cc new file mode 100644 index 000000000..0f1d7a7b5 --- /dev/null +++ b/yosys/techlibs/intel/synth_intel.cc @@ -0,0 +1,270 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 SynthIntelPass : public ScriptPass { + SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_intel [options]\n"); + log("\n"); + log("This command runs synthesis for Intel FPGAs.\n"); + log("\n"); + log(" -family < max10 | a10gx | cyclone10 | cyclonev | cycloneiv | cycloneive>\n"); + log(" generate the synthesis netlist for the specified family.\n"); + log(" MAX10 is the default target if not family argument specified.\n"); + log(" For Cyclone GX devices, use cycloneiv argument; For Cyclone E, use cycloneive.\n"); + log(" Cyclone V and Arria 10 GX devices are experimental, use it with a10gx argument.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module (default='top')\n"); + log("\n"); + log(" -vqm \n"); + log(" write the design to the specified Verilog Quartus Mapping File. Writing of an\n"); + log(" output file is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -vpr \n"); + log(" write BLIF files for VPR flow experiments. The synthesized BLIF output file is not\n"); + log(" compatible with the Quartus flow. Writing of an\n"); + log(" output file is omitted if this parameter is not specified.\n"); + log("\n"); + log(" -run :\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(" -noiopads\n"); + log(" do not use altsyncram cells in output netlist\n"); + log("\n"); + log(" -nobram\n"); + log(" do not use altsyncram cells in output netlist\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("The following commands are executed by this synthesis command:\n"); + help_script(); + log("\n"); + } + + string top_opt, family_opt, vout_file, blif_file; + bool retime, flatten, nobram, noiopads; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + family_opt = "max10"; + vout_file = ""; + blif_file = ""; + retime = false; + flatten = true; + nobram = false; + noiopads = false; + } + + void execute(std::vector 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] == "-family" && argidx+1 < args.size()) { + family_opt = args[++argidx]; + continue; + } + if (args[argidx] == "-top" && argidx+1 < args.size()) { + top_opt = "-top " + args[++argidx]; + continue; + } + if (args[argidx] == "-vqm" && argidx+1 < args.size()) { + vout_file = args[++argidx]; + continue; + } + if (args[argidx] == "-vpr" && argidx+1 < args.size()) { + blif_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] == "-noiopads") { + noiopads = true; + continue; + } + if (args[argidx] == "-nobram") { + nobram = true; + 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 command only operates on fully selected designs!\n"); + if (family_opt != "max10" && family_opt !="a10gx" && family_opt != "cyclonev" && family_opt !="cycloneiv" && family_opt !="cycloneive" && family_opt != "cyclone10") + log_cmd_error("Invalid or not family specified: '%s'\n", family_opt.c_str()); + + log_header(design, "Executing SYNTH_INTEL pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + if(check_label("family") && family_opt=="max10") + run("read_verilog -sv -lib +/intel/max10/cells_sim.v"); + else if(check_label("family") && family_opt=="a10gx") + run("read_verilog -sv -lib +/intel/a10gx/cells_sim.v"); + else if(check_label("family") && family_opt=="cyclonev") + run("read_verilog -sv -lib +/intel/cyclonev/cells_sim.v"); + else if(check_label("family") && family_opt=="cyclone10") + run("read_verilog -sv -lib +/intel/cyclone10/cells_sim.v"); + else if(check_label("family") && family_opt=="cycloneiv") + run("read_verilog -sv -lib +/intel/cycloneiv/cells_sim.v"); + else + run("read_verilog -sv -lib +/intel/cycloneive/cells_sim.v"); + // Misc and common cells + run("read_verilog -sv -lib +/intel/common/m9k_bb.v"); + run("read_verilog -sv -lib +/intel/common/altpll_bb.v"); + run(stringf("hierarchy -check %s", help_mode ? "-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 +/intel/common/brams.txt"); + run("techmap -map +/intel/common/brams_map.v"); + } + + if (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine -full"); + run("memory_map"); + run("opt -undriven -fine"); + run("dffsr2dff"); + run("dff2dffe -direct-match $_DFF_*"); + run("opt -fine"); + run("techmap -map +/techmap.v"); + run("opt -full"); + run("clean -purge"); + run("setundef -undriven -zero"); + if (retime || help_mode) + run("abc -markgroups -dff", "(only if -retime)"); + } + + if (check_label("map_luts")) + { + if(family_opt=="a10gx" || family_opt=="cyclonev") + run("abc -luts 2:2,3,6:5" + string(retime ? " -dff" : "")); + else + run("abc -lut 4" + string(retime ? " -dff" : "")); + run("clean"); + } + + if (check_label("map_cells")) + { + if (!noiopads) + run("iopadmap -bits -outpad $__outpad I:O -inpad $__inpad O:I", "(unless -noiopads)"); + if(family_opt=="max10") + run("techmap -map +/intel/max10/cells_map.v"); + else if(family_opt=="a10gx") + run("techmap -map +/intel/a10gx/cells_map.v"); + else if(family_opt=="cyclonev") + run("techmap -map +/intel/cyclonev/cells_map.v"); + else if(family_opt=="cyclone10") + run("techmap -map +/intel/cyclone10/cells_map.v"); + else if(family_opt=="cycloneiv") + run("techmap -map +/intel/cycloneiv/cells_map.v"); + else + run("techmap -map +/intel/cycloneive/cells_map.v"); + run("dffinit -highlow -ff dffeas q power_up"); + run("clean -purge"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("vqm")) + { + if (!vout_file.empty() || help_mode) + run(stringf("write_verilog -attr2comment -defparam -nohex -decimal -renameprefix syn_ %s", + help_mode ? "" : vout_file.c_str())); + } + + if (check_label("vpr")) + { + if (!blif_file.empty() || help_mode) + { + run(stringf("opt_clean -purge")); + run(stringf("write_blif %s", help_mode ? "" : blif_file.c_str())); + } + } + } +} SynthIntelPass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/sf2/Makefile.inc b/yosys/techlibs/sf2/Makefile.inc new file mode 100644 index 000000000..cade49f37 --- /dev/null +++ b/yosys/techlibs/sf2/Makefile.inc @@ -0,0 +1,7 @@ + +OBJS += techlibs/sf2/synth_sf2.o + +$(eval $(call add_share_file,share/sf2,techlibs/sf2/arith_map.v)) +$(eval $(call add_share_file,share/sf2,techlibs/sf2/cells_map.v)) +$(eval $(call add_share_file,share/sf2,techlibs/sf2/cells_sim.v)) + diff --git a/yosys/techlibs/sf2/arith_map.v b/yosys/techlibs/sf2/arith_map.v new file mode 100644 index 000000000..462d3ce50 --- /dev/null +++ b/yosys/techlibs/sf2/arith_map.v @@ -0,0 +1,21 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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. + * + */ + + +// nothing here yet diff --git a/yosys/techlibs/sf2/cells_map.v b/yosys/techlibs/sf2/cells_map.v new file mode 100644 index 000000000..5b8888294 --- /dev/null +++ b/yosys/techlibs/sf2/cells_map.v @@ -0,0 +1,68 @@ +// 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); + SLE _TECHMAP_REPLACE_ ( + .D(D), + .CLK(C), + .EN(1'b1), + .ALn(1'b1), + .ADn(1'b1), + .SLn(1'b1), + .SD(1'b0), + .LAT(1'b0), + .Q(Q) + ); +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 + +`ifndef NO_LUT +module \$lut (A, Y); + parameter WIDTH = 0; + parameter LUT = 0; + + input [WIDTH-1:0] A; + output Y; + + generate + if (WIDTH == 1) begin + CFG1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0])); + end else + if (WIDTH == 2) begin + CFG2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1])); + end else + if (WIDTH == 3) begin + CFG3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]), .C(A[2])); + end else + if (WIDTH == 4) begin + CFG4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + end else begin + wire _TECHMAP_FAIL_ = 1; + end + endgenerate +endmodule +`endif diff --git a/yosys/techlibs/sf2/cells_sim.v b/yosys/techlibs/sf2/cells_sim.v new file mode 100644 index 000000000..b03b2c750 --- /dev/null +++ b/yosys/techlibs/sf2/cells_sim.v @@ -0,0 +1,75 @@ +module SLE ( + output Q, + input ADn, + input ALn, + input CLK, + input D, + input LAT, + input SD, + input EN, + input SLn +); + reg q_latch, q_ff; + + always @(posedge CLK, negedge ALn) begin + if (!ALn) begin + q_ff <= !ADn; + end else if (EN) begin + if (!SLn) + q_ff <= SD; + else + q_ff <= D; + end + end + + always @* begin + if (!ALn) begin + q_latch <= !ADn; + end else if (CLK && EN) begin + if (!SLn) + q_ff <= SD; + else + q_ff <= D; + end + end + + assign Q = LAT ? q_latch : q_ff; +endmodule + +module CFG1 ( + output Y, + input A +); + parameter [1:0] INIT = 2'h0; + assign Y = INIT >> A; +endmodule + +module CFG2 ( + output Y, + input A, + input B +); + parameter [3:0] INIT = 4'h0; + assign Y = INIT >> {B, A}; +endmodule + +module CFG3 ( + output Y, + input A, + input B, + input C +); + parameter [7:0] INIT = 8'h0; + assign Y = INIT >> {C, B, A}; +endmodule + +module CFG4 ( + output Y, + input A, + input B, + input C, + input D +); + parameter [15:0] INIT = 16'h0; + assign Y = INIT >> {D, C, B, A}; +endmodule diff --git a/yosys/techlibs/sf2/synth_sf2.cc b/yosys/techlibs/sf2/synth_sf2.cc new file mode 100644 index 000000000..62b3cd0e2 --- /dev/null +++ b/yosys/techlibs/sf2/synth_sf2.cc @@ -0,0 +1,206 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf + * + * 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 SynthSf2Pass : public ScriptPass +{ + SynthSf2Pass() : ScriptPass("synth_sf2", "synthesis for SmartFusion2 and IGLOO2 FPGAs") { } + + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" synth_sf2 [options]\n"); + log("\n"); + log("This command runs synthesis for SmartFusion2 and IGLOO2 FPGAs.\n"); + log("\n"); + log(" -top \n"); + log(" use the specified module as top module\n"); + log("\n"); + log(" -edif \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(" -json \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 :\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, edif_file, json_file; + bool flatten, retime; + + void clear_flags() YS_OVERRIDE + { + top_opt = "-auto-top"; + edif_file = ""; + json_file = ""; + flatten = true; + retime = false; + } + + void execute(std::vector 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] == "-edif" && argidx+1 < args.size()) { + edif_file = args[++argidx]; + continue; + } + if (args[argidx] == "-json" && argidx+1 < args.size()) { + json_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] == "-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 command only operates on fully selected designs!\n"); + + log_header(design, "Executing SYNTH_SF2 pass.\n"); + log_push(); + + run_script(design, run_from, run_to); + + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("begin")) + { + run("read_verilog -lib +/sf2/cells_sim.v"); + run(stringf("hierarchy -check %s", help_mode ? "-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 (check_label("fine")) + { + run("opt -fast -mux_undef -undriven -fine"); + run("memory_map"); + run("opt -undriven -fine"); + run("techmap -map +/techmap.v -map +/sf2/arith_map.v"); + if (retime || help_mode) + run("abc -dff", "(only if -retime)"); + } + + if (check_label("map_ffs")) + { + run("dffsr2dff"); + run("techmap -D NO_LUT -map +/sf2/cells_map.v"); + run("opt_expr -mux_undef"); + run("simplemap"); + // run("sf2_ffinit"); + // run("sf2_ffssr"); + // run("sf2_opt -full"); + } + + if (check_label("map_luts")) + { + run("abc -lut 4"); + run("clean"); + } + + if (check_label("map_cells")) + { + run("techmap -map +/sf2/cells_map.v"); + run("clean"); + } + + if (check_label("check")) + { + run("hierarchy -check"); + run("stat"); + run("check -noinit"); + } + + if (check_label("edif")) + { + if (!edif_file.empty() || help_mode) + run(stringf("write_edif %s", help_mode ? "" : edif_file.c_str())); + } + + if (check_label("json")) + { + if (!json_file.empty() || help_mode) + run(stringf("write_json %s", help_mode ? "" : json_file.c_str())); + } + } +} SynthSf2Pass; + +PRIVATE_NAMESPACE_END diff --git a/yosys/techlibs/xilinx/Makefile.inc b/yosys/techlibs/xilinx/Makefile.inc index 5f09ffb02..887ea27d9 100644 --- a/yosys/techlibs/xilinx/Makefile.inc +++ b/yosys/techlibs/xilinx/Makefile.inc @@ -27,8 +27,8 @@ $(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_share_file,share/xilinx,techlibs/xilinx/lut2lut.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)) diff --git a/yosys/techlibs/xilinx/brams_init.py b/yosys/techlibs/xilinx/brams_init.py index e787b1f76..d46a2b4f7 100644 --- a/yosys/techlibs/xilinx/brams_init.py +++ b/yosys/techlibs/xilinx/brams_init.py @@ -2,27 +2,27 @@ 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)] + 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) + 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)] + 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) + 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)] + 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) + 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)] + 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) + 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): diff --git a/yosys/techlibs/xilinx/cells_map.v b/yosys/techlibs/xilinx/cells_map.v index 8e5a83ce5..0771be0b9 100644 --- a/yosys/techlibs/xilinx/cells_map.v +++ b/yosys/techlibs/xilinx/cells_map.v @@ -15,6 +15,7 @@ module \$_DFF_NP1_ (input D, C, R, output Q); FDPE #(.INIT(|0), .IS_C_INVERTED( 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 +`ifndef NO_LUT module \$lut (A, Y); parameter WIDTH = 0; parameter LUT = 0; @@ -82,3 +83,4 @@ module \$lut (A, Y); end endgenerate endmodule +`endif diff --git a/yosys/techlibs/xilinx/cells_sim.v b/yosys/techlibs/xilinx/cells_sim.v index 1f114a22c..eba17ac9c 100644 --- a/yosys/techlibs/xilinx/cells_sim.v +++ b/yosys/techlibs/xilinx/cells_sim.v @@ -156,3 +156,33 @@ module FDPE (output reg Q, input C, CE, D, PRE); endcase endgenerate endmodule +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; + wire [5:0] a = {A5, A4, A3, A2, A1, A0}; + wire [5:0] dpra = {DPRA5, DPRA4, DPRA3, DPRA2, DPRA1, DPRA0}; + reg [63:0] mem = INIT; + assign SPO = mem[a]; + assign DPO = mem[dpra]; + wire clk = WCLK ^ IS_WCLK_INVERTED; + always @(posedge clk) if (WE) mem[a] <= D; +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; + reg [127:0] mem = INIT; + assign SPO = mem[A]; + assign DPO = mem[DPRA]; + wire clk = WCLK ^ IS_WCLK_INVERTED; + always @(posedge clk) if (WE) mem[A] <= D; +endmodule diff --git a/yosys/techlibs/xilinx/cells_xtra.sh b/yosys/techlibs/xilinx/cells_xtra.sh index c7ad16043..0480410f5 100644 --- a/yosys/techlibs/xilinx/cells_xtra.sh +++ b/yosys/techlibs/xilinx/cells_xtra.sh @@ -1,13 +1,13 @@ #!/bin/bash set -e -libdir="/opt/Xilinx/Vivado/2015.4/data/verilog/src" +libdir="/opt/Xilinx/Vivado/2018.1/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 | + egrep '^\s*((end)?module|parameter|input|inout|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; @@ -112,6 +112,7 @@ function xtract_cell_decl() xtract_cell_decl PHY_CONTROL xtract_cell_decl PLLE2_ADV xtract_cell_decl PLLE2_BASE + xtract_cell_decl PS7 xtract_cell_decl PULLDOWN xtract_cell_decl PULLUP # xtract_cell_decl RAM128X1D diff --git a/yosys/techlibs/xilinx/cells_xtra.v b/yosys/techlibs/xilinx/cells_xtra.v index a2dd01ad5..8d8b91ddc 100644 --- a/yosys/techlibs/xilinx/cells_xtra.v +++ b/yosys/techlibs/xilinx/cells_xtra.v @@ -2225,6 +2225,7 @@ module IOBUF (...); parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; output O; + inout IO; input I, T; endmodule @@ -2236,6 +2237,7 @@ module IOBUF_DCIEN (...); parameter SLEW = "SLOW"; parameter USE_IBUFDISABLE = "TRUE"; output O; + inout IO; input DCITERMDISABLE; input I; input IBUFDISABLE; @@ -2250,6 +2252,7 @@ module IOBUF_INTERMDISABLE (...); parameter SLEW = "SLOW"; parameter USE_IBUFDISABLE = "TRUE"; output O; + inout IO; input I; input IBUFDISABLE; input INTERMDISABLE; @@ -2263,6 +2266,7 @@ module IOBUFDS (...); parameter IOSTANDARD = "DEFAULT"; parameter SLEW = "SLOW"; output O; + inout IO, IOB; input I, T; endmodule @@ -2275,6 +2279,8 @@ module IOBUFDS_DCIEN (...); parameter SLEW = "SLOW"; parameter USE_IBUFDISABLE = "TRUE"; output O; + inout IO; + inout IOB; input DCITERMDISABLE; input I; input IBUFDISABLE; @@ -2288,6 +2294,8 @@ module IOBUFDS_DIFF_OUT (...); parameter IOSTANDARD = "DEFAULT"; output O; output OB; + inout IO; + inout IOB; input I; input TM; input TS; @@ -2302,6 +2310,8 @@ module IOBUFDS_DIFF_OUT_DCIEN (...); parameter USE_IBUFDISABLE = "TRUE"; output O; output OB; + inout IO; + inout IOB; input DCITERMDISABLE; input I; input IBUFDISABLE; @@ -2318,6 +2328,8 @@ module IOBUFDS_DIFF_OUT_INTERMDISABLE (...); parameter USE_IBUFDISABLE = "TRUE"; output O; output OB; + inout IO; + inout IOB; input I; input IBUFDISABLE; input INTERMDISABLE; @@ -2381,6 +2393,7 @@ module ISERDESE2 (...); endmodule module KEEPER (...); + inout O; endmodule module LDCE (...); @@ -3044,6 +3057,629 @@ module PLLE2_BASE (...); input RST; endmodule +module PS7 (...); + output DMA0DAVALID; + output DMA0DRREADY; + output DMA0RSTN; + output DMA1DAVALID; + output DMA1DRREADY; + output DMA1RSTN; + output DMA2DAVALID; + output DMA2DRREADY; + output DMA2RSTN; + output DMA3DAVALID; + output DMA3DRREADY; + output DMA3RSTN; + output EMIOCAN0PHYTX; + output EMIOCAN1PHYTX; + output EMIOENET0GMIITXEN; + output EMIOENET0GMIITXER; + output EMIOENET0MDIOMDC; + output EMIOENET0MDIOO; + output EMIOENET0MDIOTN; + output EMIOENET0PTPDELAYREQRX; + output EMIOENET0PTPDELAYREQTX; + output EMIOENET0PTPPDELAYREQRX; + output EMIOENET0PTPPDELAYREQTX; + output EMIOENET0PTPPDELAYRESPRX; + output EMIOENET0PTPPDELAYRESPTX; + output EMIOENET0PTPSYNCFRAMERX; + output EMIOENET0PTPSYNCFRAMETX; + output EMIOENET0SOFRX; + output EMIOENET0SOFTX; + output EMIOENET1GMIITXEN; + output EMIOENET1GMIITXER; + output EMIOENET1MDIOMDC; + output EMIOENET1MDIOO; + output EMIOENET1MDIOTN; + output EMIOENET1PTPDELAYREQRX; + output EMIOENET1PTPDELAYREQTX; + output EMIOENET1PTPPDELAYREQRX; + output EMIOENET1PTPPDELAYREQTX; + output EMIOENET1PTPPDELAYRESPRX; + output EMIOENET1PTPPDELAYRESPTX; + output EMIOENET1PTPSYNCFRAMERX; + output EMIOENET1PTPSYNCFRAMETX; + output EMIOENET1SOFRX; + output EMIOENET1SOFTX; + output EMIOI2C0SCLO; + output EMIOI2C0SCLTN; + output EMIOI2C0SDAO; + output EMIOI2C0SDATN; + output EMIOI2C1SCLO; + output EMIOI2C1SCLTN; + output EMIOI2C1SDAO; + output EMIOI2C1SDATN; + output EMIOPJTAGTDO; + output EMIOPJTAGTDTN; + output EMIOSDIO0BUSPOW; + output EMIOSDIO0CLK; + output EMIOSDIO0CMDO; + output EMIOSDIO0CMDTN; + output EMIOSDIO0LED; + output EMIOSDIO1BUSPOW; + output EMIOSDIO1CLK; + output EMIOSDIO1CMDO; + output EMIOSDIO1CMDTN; + output EMIOSDIO1LED; + output EMIOSPI0MO; + output EMIOSPI0MOTN; + output EMIOSPI0SCLKO; + output EMIOSPI0SCLKTN; + output EMIOSPI0SO; + output EMIOSPI0SSNTN; + output EMIOSPI0STN; + output EMIOSPI1MO; + output EMIOSPI1MOTN; + output EMIOSPI1SCLKO; + output EMIOSPI1SCLKTN; + output EMIOSPI1SO; + output EMIOSPI1SSNTN; + output EMIOSPI1STN; + output EMIOTRACECTL; + output EMIOUART0DTRN; + output EMIOUART0RTSN; + output EMIOUART0TX; + output EMIOUART1DTRN; + output EMIOUART1RTSN; + output EMIOUART1TX; + output EMIOUSB0VBUSPWRSELECT; + output EMIOUSB1VBUSPWRSELECT; + output EMIOWDTRSTO; + output EVENTEVENTO; + output MAXIGP0ARESETN; + output MAXIGP0ARVALID; + output MAXIGP0AWVALID; + output MAXIGP0BREADY; + output MAXIGP0RREADY; + output MAXIGP0WLAST; + output MAXIGP0WVALID; + output MAXIGP1ARESETN; + output MAXIGP1ARVALID; + output MAXIGP1AWVALID; + output MAXIGP1BREADY; + output MAXIGP1RREADY; + output MAXIGP1WLAST; + output MAXIGP1WVALID; + output SAXIACPARESETN; + output SAXIACPARREADY; + output SAXIACPAWREADY; + output SAXIACPBVALID; + output SAXIACPRLAST; + output SAXIACPRVALID; + output SAXIACPWREADY; + output SAXIGP0ARESETN; + output SAXIGP0ARREADY; + output SAXIGP0AWREADY; + output SAXIGP0BVALID; + output SAXIGP0RLAST; + output SAXIGP0RVALID; + output SAXIGP0WREADY; + output SAXIGP1ARESETN; + output SAXIGP1ARREADY; + output SAXIGP1AWREADY; + output SAXIGP1BVALID; + output SAXIGP1RLAST; + output SAXIGP1RVALID; + output SAXIGP1WREADY; + output SAXIHP0ARESETN; + output SAXIHP0ARREADY; + output SAXIHP0AWREADY; + output SAXIHP0BVALID; + output SAXIHP0RLAST; + output SAXIHP0RVALID; + output SAXIHP0WREADY; + output SAXIHP1ARESETN; + output SAXIHP1ARREADY; + output SAXIHP1AWREADY; + output SAXIHP1BVALID; + output SAXIHP1RLAST; + output SAXIHP1RVALID; + output SAXIHP1WREADY; + output SAXIHP2ARESETN; + output SAXIHP2ARREADY; + output SAXIHP2AWREADY; + output SAXIHP2BVALID; + output SAXIHP2RLAST; + output SAXIHP2RVALID; + output SAXIHP2WREADY; + output SAXIHP3ARESETN; + output SAXIHP3ARREADY; + output SAXIHP3AWREADY; + output SAXIHP3BVALID; + output SAXIHP3RLAST; + output SAXIHP3RVALID; + output SAXIHP3WREADY; + output [11:0] MAXIGP0ARID; + output [11:0] MAXIGP0AWID; + output [11:0] MAXIGP0WID; + output [11:0] MAXIGP1ARID; + output [11:0] MAXIGP1AWID; + output [11:0] MAXIGP1WID; + output [1:0] DMA0DATYPE; + output [1:0] DMA1DATYPE; + output [1:0] DMA2DATYPE; + output [1:0] DMA3DATYPE; + output [1:0] EMIOUSB0PORTINDCTL; + output [1:0] EMIOUSB1PORTINDCTL; + output [1:0] EVENTSTANDBYWFE; + output [1:0] EVENTSTANDBYWFI; + output [1:0] MAXIGP0ARBURST; + output [1:0] MAXIGP0ARLOCK; + output [1:0] MAXIGP0ARSIZE; + output [1:0] MAXIGP0AWBURST; + output [1:0] MAXIGP0AWLOCK; + output [1:0] MAXIGP0AWSIZE; + output [1:0] MAXIGP1ARBURST; + output [1:0] MAXIGP1ARLOCK; + output [1:0] MAXIGP1ARSIZE; + output [1:0] MAXIGP1AWBURST; + output [1:0] MAXIGP1AWLOCK; + output [1:0] MAXIGP1AWSIZE; + output [1:0] SAXIACPBRESP; + output [1:0] SAXIACPRRESP; + output [1:0] SAXIGP0BRESP; + output [1:0] SAXIGP0RRESP; + output [1:0] SAXIGP1BRESP; + output [1:0] SAXIGP1RRESP; + output [1:0] SAXIHP0BRESP; + output [1:0] SAXIHP0RRESP; + output [1:0] SAXIHP1BRESP; + output [1:0] SAXIHP1RRESP; + output [1:0] SAXIHP2BRESP; + output [1:0] SAXIHP2RRESP; + output [1:0] SAXIHP3BRESP; + output [1:0] SAXIHP3RRESP; + output [28:0] IRQP2F; + output [2:0] EMIOSDIO0BUSVOLT; + output [2:0] EMIOSDIO1BUSVOLT; + output [2:0] EMIOSPI0SSON; + output [2:0] EMIOSPI1SSON; + output [2:0] EMIOTTC0WAVEO; + output [2:0] EMIOTTC1WAVEO; + output [2:0] MAXIGP0ARPROT; + output [2:0] MAXIGP0AWPROT; + output [2:0] MAXIGP1ARPROT; + output [2:0] MAXIGP1AWPROT; + output [2:0] SAXIACPBID; + output [2:0] SAXIACPRID; + output [2:0] SAXIHP0RACOUNT; + output [2:0] SAXIHP1RACOUNT; + output [2:0] SAXIHP2RACOUNT; + output [2:0] SAXIHP3RACOUNT; + output [31:0] EMIOTRACEDATA; + output [31:0] FTMTP2FDEBUG; + output [31:0] MAXIGP0ARADDR; + output [31:0] MAXIGP0AWADDR; + output [31:0] MAXIGP0WDATA; + output [31:0] MAXIGP1ARADDR; + output [31:0] MAXIGP1AWADDR; + output [31:0] MAXIGP1WDATA; + output [31:0] SAXIGP0RDATA; + output [31:0] SAXIGP1RDATA; + output [3:0] EMIOSDIO0DATAO; + output [3:0] EMIOSDIO0DATATN; + output [3:0] EMIOSDIO1DATAO; + output [3:0] EMIOSDIO1DATATN; + output [3:0] FCLKCLK; + output [3:0] FCLKRESETN; + output [3:0] FTMTF2PTRIGACK; + output [3:0] FTMTP2FTRIG; + output [3:0] MAXIGP0ARCACHE; + output [3:0] MAXIGP0ARLEN; + output [3:0] MAXIGP0ARQOS; + output [3:0] MAXIGP0AWCACHE; + output [3:0] MAXIGP0AWLEN; + output [3:0] MAXIGP0AWQOS; + output [3:0] MAXIGP0WSTRB; + output [3:0] MAXIGP1ARCACHE; + output [3:0] MAXIGP1ARLEN; + output [3:0] MAXIGP1ARQOS; + output [3:0] MAXIGP1AWCACHE; + output [3:0] MAXIGP1AWLEN; + output [3:0] MAXIGP1AWQOS; + output [3:0] MAXIGP1WSTRB; + output [5:0] SAXIGP0BID; + output [5:0] SAXIGP0RID; + output [5:0] SAXIGP1BID; + output [5:0] SAXIGP1RID; + output [5:0] SAXIHP0BID; + output [5:0] SAXIHP0RID; + output [5:0] SAXIHP0WACOUNT; + output [5:0] SAXIHP1BID; + output [5:0] SAXIHP1RID; + output [5:0] SAXIHP1WACOUNT; + output [5:0] SAXIHP2BID; + output [5:0] SAXIHP2RID; + output [5:0] SAXIHP2WACOUNT; + output [5:0] SAXIHP3BID; + output [5:0] SAXIHP3RID; + output [5:0] SAXIHP3WACOUNT; + output [63:0] EMIOGPIOO; + output [63:0] EMIOGPIOTN; + output [63:0] SAXIACPRDATA; + output [63:0] SAXIHP0RDATA; + output [63:0] SAXIHP1RDATA; + output [63:0] SAXIHP2RDATA; + output [63:0] SAXIHP3RDATA; + output [7:0] EMIOENET0GMIITXD; + output [7:0] EMIOENET1GMIITXD; + output [7:0] SAXIHP0RCOUNT; + output [7:0] SAXIHP0WCOUNT; + output [7:0] SAXIHP1RCOUNT; + output [7:0] SAXIHP1WCOUNT; + output [7:0] SAXIHP2RCOUNT; + output [7:0] SAXIHP2WCOUNT; + output [7:0] SAXIHP3RCOUNT; + output [7:0] SAXIHP3WCOUNT; + inout DDRCASB; + inout DDRCKE; + inout DDRCKN; + inout DDRCKP; + inout DDRCSB; + inout DDRDRSTB; + inout DDRODT; + inout DDRRASB; + inout DDRVRN; + inout DDRVRP; + inout DDRWEB; + inout PSCLK; + inout PSPORB; + inout PSSRSTB; + inout [14:0] DDRA; + inout [2:0] DDRBA; + inout [31:0] DDRDQ; + inout [3:0] DDRDM; + inout [3:0] DDRDQSN; + inout [3:0] DDRDQSP; + inout [53:0] MIO; + input DMA0ACLK; + input DMA0DAREADY; + input DMA0DRLAST; + input DMA0DRVALID; + input DMA1ACLK; + input DMA1DAREADY; + input DMA1DRLAST; + input DMA1DRVALID; + input DMA2ACLK; + input DMA2DAREADY; + input DMA2DRLAST; + input DMA2DRVALID; + input DMA3ACLK; + input DMA3DAREADY; + input DMA3DRLAST; + input DMA3DRVALID; + input EMIOCAN0PHYRX; + input EMIOCAN1PHYRX; + input EMIOENET0EXTINTIN; + input EMIOENET0GMIICOL; + input EMIOENET0GMIICRS; + input EMIOENET0GMIIRXCLK; + input EMIOENET0GMIIRXDV; + input EMIOENET0GMIIRXER; + input EMIOENET0GMIITXCLK; + input EMIOENET0MDIOI; + input EMIOENET1EXTINTIN; + input EMIOENET1GMIICOL; + input EMIOENET1GMIICRS; + input EMIOENET1GMIIRXCLK; + input EMIOENET1GMIIRXDV; + input EMIOENET1GMIIRXER; + input EMIOENET1GMIITXCLK; + input EMIOENET1MDIOI; + input EMIOI2C0SCLI; + input EMIOI2C0SDAI; + input EMIOI2C1SCLI; + input EMIOI2C1SDAI; + input EMIOPJTAGTCK; + input EMIOPJTAGTDI; + input EMIOPJTAGTMS; + input EMIOSDIO0CDN; + input EMIOSDIO0CLKFB; + input EMIOSDIO0CMDI; + input EMIOSDIO0WP; + input EMIOSDIO1CDN; + input EMIOSDIO1CLKFB; + input EMIOSDIO1CMDI; + input EMIOSDIO1WP; + input EMIOSPI0MI; + input EMIOSPI0SCLKI; + input EMIOSPI0SI; + input EMIOSPI0SSIN; + input EMIOSPI1MI; + input EMIOSPI1SCLKI; + input EMIOSPI1SI; + input EMIOSPI1SSIN; + input EMIOSRAMINTIN; + input EMIOTRACECLK; + input EMIOUART0CTSN; + input EMIOUART0DCDN; + input EMIOUART0DSRN; + input EMIOUART0RIN; + input EMIOUART0RX; + input EMIOUART1CTSN; + input EMIOUART1DCDN; + input EMIOUART1DSRN; + input EMIOUART1RIN; + input EMIOUART1RX; + input EMIOUSB0VBUSPWRFAULT; + input EMIOUSB1VBUSPWRFAULT; + input EMIOWDTCLKI; + input EVENTEVENTI; + input FPGAIDLEN; + input FTMDTRACEINCLOCK; + input FTMDTRACEINVALID; + input MAXIGP0ACLK; + input MAXIGP0ARREADY; + input MAXIGP0AWREADY; + input MAXIGP0BVALID; + input MAXIGP0RLAST; + input MAXIGP0RVALID; + input MAXIGP0WREADY; + input MAXIGP1ACLK; + input MAXIGP1ARREADY; + input MAXIGP1AWREADY; + input MAXIGP1BVALID; + input MAXIGP1RLAST; + input MAXIGP1RVALID; + input MAXIGP1WREADY; + input SAXIACPACLK; + input SAXIACPARVALID; + input SAXIACPAWVALID; + input SAXIACPBREADY; + input SAXIACPRREADY; + input SAXIACPWLAST; + input SAXIACPWVALID; + input SAXIGP0ACLK; + input SAXIGP0ARVALID; + input SAXIGP0AWVALID; + input SAXIGP0BREADY; + input SAXIGP0RREADY; + input SAXIGP0WLAST; + input SAXIGP0WVALID; + input SAXIGP1ACLK; + input SAXIGP1ARVALID; + input SAXIGP1AWVALID; + input SAXIGP1BREADY; + input SAXIGP1RREADY; + input SAXIGP1WLAST; + input SAXIGP1WVALID; + input SAXIHP0ACLK; + input SAXIHP0ARVALID; + input SAXIHP0AWVALID; + input SAXIHP0BREADY; + input SAXIHP0RDISSUECAP1EN; + input SAXIHP0RREADY; + input SAXIHP0WLAST; + input SAXIHP0WRISSUECAP1EN; + input SAXIHP0WVALID; + input SAXIHP1ACLK; + input SAXIHP1ARVALID; + input SAXIHP1AWVALID; + input SAXIHP1BREADY; + input SAXIHP1RDISSUECAP1EN; + input SAXIHP1RREADY; + input SAXIHP1WLAST; + input SAXIHP1WRISSUECAP1EN; + input SAXIHP1WVALID; + input SAXIHP2ACLK; + input SAXIHP2ARVALID; + input SAXIHP2AWVALID; + input SAXIHP2BREADY; + input SAXIHP2RDISSUECAP1EN; + input SAXIHP2RREADY; + input SAXIHP2WLAST; + input SAXIHP2WRISSUECAP1EN; + input SAXIHP2WVALID; + input SAXIHP3ACLK; + input SAXIHP3ARVALID; + input SAXIHP3AWVALID; + input SAXIHP3BREADY; + input SAXIHP3RDISSUECAP1EN; + input SAXIHP3RREADY; + input SAXIHP3WLAST; + input SAXIHP3WRISSUECAP1EN; + input SAXIHP3WVALID; + input [11:0] MAXIGP0BID; + input [11:0] MAXIGP0RID; + input [11:0] MAXIGP1BID; + input [11:0] MAXIGP1RID; + input [19:0] IRQF2P; + input [1:0] DMA0DRTYPE; + input [1:0] DMA1DRTYPE; + input [1:0] DMA2DRTYPE; + input [1:0] DMA3DRTYPE; + input [1:0] MAXIGP0BRESP; + input [1:0] MAXIGP0RRESP; + input [1:0] MAXIGP1BRESP; + input [1:0] MAXIGP1RRESP; + input [1:0] SAXIACPARBURST; + input [1:0] SAXIACPARLOCK; + input [1:0] SAXIACPARSIZE; + input [1:0] SAXIACPAWBURST; + input [1:0] SAXIACPAWLOCK; + input [1:0] SAXIACPAWSIZE; + input [1:0] SAXIGP0ARBURST; + input [1:0] SAXIGP0ARLOCK; + input [1:0] SAXIGP0ARSIZE; + input [1:0] SAXIGP0AWBURST; + input [1:0] SAXIGP0AWLOCK; + input [1:0] SAXIGP0AWSIZE; + input [1:0] SAXIGP1ARBURST; + input [1:0] SAXIGP1ARLOCK; + input [1:0] SAXIGP1ARSIZE; + input [1:0] SAXIGP1AWBURST; + input [1:0] SAXIGP1AWLOCK; + input [1:0] SAXIGP1AWSIZE; + input [1:0] SAXIHP0ARBURST; + input [1:0] SAXIHP0ARLOCK; + input [1:0] SAXIHP0ARSIZE; + input [1:0] SAXIHP0AWBURST; + input [1:0] SAXIHP0AWLOCK; + input [1:0] SAXIHP0AWSIZE; + input [1:0] SAXIHP1ARBURST; + input [1:0] SAXIHP1ARLOCK; + input [1:0] SAXIHP1ARSIZE; + input [1:0] SAXIHP1AWBURST; + input [1:0] SAXIHP1AWLOCK; + input [1:0] SAXIHP1AWSIZE; + input [1:0] SAXIHP2ARBURST; + input [1:0] SAXIHP2ARLOCK; + input [1:0] SAXIHP2ARSIZE; + input [1:0] SAXIHP2AWBURST; + input [1:0] SAXIHP2AWLOCK; + input [1:0] SAXIHP2AWSIZE; + input [1:0] SAXIHP3ARBURST; + input [1:0] SAXIHP3ARLOCK; + input [1:0] SAXIHP3ARSIZE; + input [1:0] SAXIHP3AWBURST; + input [1:0] SAXIHP3AWLOCK; + input [1:0] SAXIHP3AWSIZE; + input [2:0] EMIOTTC0CLKI; + input [2:0] EMIOTTC1CLKI; + input [2:0] SAXIACPARID; + input [2:0] SAXIACPARPROT; + input [2:0] SAXIACPAWID; + input [2:0] SAXIACPAWPROT; + input [2:0] SAXIACPWID; + input [2:0] SAXIGP0ARPROT; + input [2:0] SAXIGP0AWPROT; + input [2:0] SAXIGP1ARPROT; + input [2:0] SAXIGP1AWPROT; + input [2:0] SAXIHP0ARPROT; + input [2:0] SAXIHP0AWPROT; + input [2:0] SAXIHP1ARPROT; + input [2:0] SAXIHP1AWPROT; + input [2:0] SAXIHP2ARPROT; + input [2:0] SAXIHP2AWPROT; + input [2:0] SAXIHP3ARPROT; + input [2:0] SAXIHP3AWPROT; + input [31:0] FTMDTRACEINDATA; + input [31:0] FTMTF2PDEBUG; + input [31:0] MAXIGP0RDATA; + input [31:0] MAXIGP1RDATA; + input [31:0] SAXIACPARADDR; + input [31:0] SAXIACPAWADDR; + input [31:0] SAXIGP0ARADDR; + input [31:0] SAXIGP0AWADDR; + input [31:0] SAXIGP0WDATA; + input [31:0] SAXIGP1ARADDR; + input [31:0] SAXIGP1AWADDR; + input [31:0] SAXIGP1WDATA; + input [31:0] SAXIHP0ARADDR; + input [31:0] SAXIHP0AWADDR; + input [31:0] SAXIHP1ARADDR; + input [31:0] SAXIHP1AWADDR; + input [31:0] SAXIHP2ARADDR; + input [31:0] SAXIHP2AWADDR; + input [31:0] SAXIHP3ARADDR; + input [31:0] SAXIHP3AWADDR; + input [3:0] DDRARB; + input [3:0] EMIOSDIO0DATAI; + input [3:0] EMIOSDIO1DATAI; + input [3:0] FCLKCLKTRIGN; + input [3:0] FTMDTRACEINATID; + input [3:0] FTMTF2PTRIG; + input [3:0] FTMTP2FTRIGACK; + input [3:0] SAXIACPARCACHE; + input [3:0] SAXIACPARLEN; + input [3:0] SAXIACPARQOS; + input [3:0] SAXIACPAWCACHE; + input [3:0] SAXIACPAWLEN; + input [3:0] SAXIACPAWQOS; + input [3:0] SAXIGP0ARCACHE; + input [3:0] SAXIGP0ARLEN; + input [3:0] SAXIGP0ARQOS; + input [3:0] SAXIGP0AWCACHE; + input [3:0] SAXIGP0AWLEN; + input [3:0] SAXIGP0AWQOS; + input [3:0] SAXIGP0WSTRB; + input [3:0] SAXIGP1ARCACHE; + input [3:0] SAXIGP1ARLEN; + input [3:0] SAXIGP1ARQOS; + input [3:0] SAXIGP1AWCACHE; + input [3:0] SAXIGP1AWLEN; + input [3:0] SAXIGP1AWQOS; + input [3:0] SAXIGP1WSTRB; + input [3:0] SAXIHP0ARCACHE; + input [3:0] SAXIHP0ARLEN; + input [3:0] SAXIHP0ARQOS; + input [3:0] SAXIHP0AWCACHE; + input [3:0] SAXIHP0AWLEN; + input [3:0] SAXIHP0AWQOS; + input [3:0] SAXIHP1ARCACHE; + input [3:0] SAXIHP1ARLEN; + input [3:0] SAXIHP1ARQOS; + input [3:0] SAXIHP1AWCACHE; + input [3:0] SAXIHP1AWLEN; + input [3:0] SAXIHP1AWQOS; + input [3:0] SAXIHP2ARCACHE; + input [3:0] SAXIHP2ARLEN; + input [3:0] SAXIHP2ARQOS; + input [3:0] SAXIHP2AWCACHE; + input [3:0] SAXIHP2AWLEN; + input [3:0] SAXIHP2AWQOS; + input [3:0] SAXIHP3ARCACHE; + input [3:0] SAXIHP3ARLEN; + input [3:0] SAXIHP3ARQOS; + input [3:0] SAXIHP3AWCACHE; + input [3:0] SAXIHP3AWLEN; + input [3:0] SAXIHP3AWQOS; + input [4:0] SAXIACPARUSER; + input [4:0] SAXIACPAWUSER; + input [5:0] SAXIGP0ARID; + input [5:0] SAXIGP0AWID; + input [5:0] SAXIGP0WID; + input [5:0] SAXIGP1ARID; + input [5:0] SAXIGP1AWID; + input [5:0] SAXIGP1WID; + input [5:0] SAXIHP0ARID; + input [5:0] SAXIHP0AWID; + input [5:0] SAXIHP0WID; + input [5:0] SAXIHP1ARID; + input [5:0] SAXIHP1AWID; + input [5:0] SAXIHP1WID; + input [5:0] SAXIHP2ARID; + input [5:0] SAXIHP2AWID; + input [5:0] SAXIHP2WID; + input [5:0] SAXIHP3ARID; + input [5:0] SAXIHP3AWID; + input [5:0] SAXIHP3WID; + input [63:0] EMIOGPIOI; + input [63:0] SAXIACPWDATA; + input [63:0] SAXIHP0WDATA; + input [63:0] SAXIHP1WDATA; + input [63:0] SAXIHP2WDATA; + input [63:0] SAXIHP3WDATA; + input [7:0] EMIOENET0GMIIRXD; + input [7:0] EMIOENET1GMIIRXD; + input [7:0] SAXIACPWSTRB; + input [7:0] SAXIHP0WSTRB; + input [7:0] SAXIHP1WSTRB; + input [7:0] SAXIHP2WSTRB; + input [7:0] SAXIHP3WSTRB; +endmodule + module PULLDOWN (...); output O; endmodule diff --git a/yosys/techlibs/xilinx/drams_bb.v b/yosys/techlibs/xilinx/drams_bb.v deleted file mode 100644 index 11168fe13..000000000 --- a/yosys/techlibs/xilinx/drams_bb.v +++ /dev/null @@ -1,20 +0,0 @@ - -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/yosys/techlibs/xilinx/lut2lut.v b/yosys/techlibs/xilinx/lut2lut.v new file mode 100644 index 000000000..061ad2041 --- /dev/null +++ b/yosys/techlibs/xilinx/lut2lut.v @@ -0,0 +1,65 @@ +module LUT1(output O, input I0); + parameter [1:0] INIT = 0; + \$lut #( + .WIDTH(1), + .LUT(INIT) + ) _TECHMAP_REPLACE_ ( + .A(I0), + .Y(O) + ); +endmodule + +module LUT2(output O, input I0, I1); + parameter [3:0] INIT = 0; + \$lut #( + .WIDTH(2), + .LUT(INIT) + ) _TECHMAP_REPLACE_ ( + .A({I1, I0}), + .Y(O) + ); +endmodule + +module LUT3(output O, input I0, I1, I2); + parameter [7:0] INIT = 0; + \$lut #( + .WIDTH(3), + .LUT(INIT) + ) _TECHMAP_REPLACE_ ( + .A({I2, I1, I0}), + .Y(O) + ); +endmodule + +module LUT4(output O, input I0, I1, I2, I3); + parameter [15:0] INIT = 0; + \$lut #( + .WIDTH(4), + .LUT(INIT) + ) _TECHMAP_REPLACE_ ( + .A({I3, I2, I1, I0}), + .Y(O) + ); +endmodule + +module LUT5(output O, input I0, I1, I2, I3, I4); + parameter [31:0] INIT = 0; + \$lut #( + .WIDTH(5), + .LUT(INIT) + ) _TECHMAP_REPLACE_ ( + .A({I4, I3, I2, I1, I0}), + .Y(O) + ); +endmodule + +module LUT6(output O, input I0, I1, I2, I3, I4, I5); + parameter [63:0] INIT = 0; + \$lut #( + .WIDTH(6), + .LUT(INIT) + ) _TECHMAP_REPLACE_ ( + .A({I5, I4, I3, I2, I1, I0}), + .Y(O) + ); +endmodule diff --git a/yosys/techlibs/xilinx/synth_xilinx.cc b/yosys/techlibs/xilinx/synth_xilinx.cc index e7ec1e6e8..6c11d885d 100644 --- a/yosys/techlibs/xilinx/synth_xilinx.cc +++ b/yosys/techlibs/xilinx/synth_xilinx.cc @@ -34,9 +34,11 @@ bool check_label(bool &active, std::string run_from, std::string run_to, std::st return active; } -struct SynthXilinxPass : public Pass { +struct SynthXilinxPass : public Pass +{ SynthXilinxPass() : Pass("synth_xilinx", "synthesis for Xilinx FPGAs") { } - virtual void help() + + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -53,6 +55,14 @@ struct SynthXilinxPass : public Pass { 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(" -blif \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(" -vpr\n"); + log(" generate an output netlist (and BLIF file) suitable for VPR\n"); + log(" (this feature is experimental and incomplete)\n"); + log("\n"); log(" -run :\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"); @@ -71,7 +81,6 @@ struct SynthXilinxPass : public Pass { 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 \n"); log("\n"); log(" flatten: (only if -flatten)\n"); @@ -103,7 +112,7 @@ struct SynthXilinxPass : public Pass { log(" clean\n"); log("\n"); log(" map_cells:\n"); - log(" techmap -map +/xilinx/cells_map.v\n"); + log(" techmap -map +/xilinx/cells_map.v (with -D NO_LUT in vpr mode)\n"); log(" dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT\n"); log(" clean\n"); log("\n"); @@ -115,14 +124,19 @@ struct SynthXilinxPass : public Pass { log(" edif: (only if -edif)\n"); log(" write_edif \n"); log("\n"); + log(" blif: (only if -blif)\n"); + log(" write_blif \n"); + log("\n"); } - virtual void execute(std::vector args, RTLIL::Design *design) + void execute(std::vector args, RTLIL::Design *design) YS_OVERRIDE { std::string top_opt = "-auto-top"; std::string edif_file; + std::string blif_file; std::string run_from, run_to; bool flatten = false; bool retime = false; + bool vpr = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -135,6 +149,10 @@ struct SynthXilinxPass : public Pass { edif_file = args[++argidx]; continue; } + if (args[argidx] == "-blif" && argidx+1 < args.size()) { + blif_file = args[++argidx]; + continue; + } if (args[argidx] == "-run" && argidx+1 < args.size()) { size_t pos = args[argidx+1].find(':'); if (pos == std::string::npos) @@ -151,12 +169,16 @@ struct SynthXilinxPass : public Pass { retime = true; continue; } + if (args[argidx] == "-vpr") { + vpr = 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_cmd_error("This command only operates on fully selected designs!\n"); bool active = run_from.empty(); @@ -168,7 +190,6 @@ struct SynthXilinxPass : public Pass { 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())); } @@ -215,6 +236,8 @@ struct SynthXilinxPass : public Pass { if (check_label(active, run_from, run_to, "map_cells")) { Pass::call(design, "techmap -map +/xilinx/cells_map.v"); + if (vpr) + Pass::call(design, "techmap -map +/xilinx/lut2lut.v"); Pass::call(design, "dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT"); Pass::call(design, "clean"); } @@ -231,6 +254,11 @@ struct SynthXilinxPass : public Pass { if (!edif_file.empty()) Pass::call(design, stringf("write_edif %s", edif_file.c_str())); } + if (check_label(active, run_from, run_to, "blif")) + { + if (!blif_file.empty()) + Pass::call(design, stringf("write_blif %s", edif_file.c_str())); + } log_pop(); } diff --git a/yosys/tests/errors/syntax_err01.v b/yosys/tests/errors/syntax_err01.v new file mode 100644 index 000000000..68e9b1d50 --- /dev/null +++ b/yosys/tests/errors/syntax_err01.v @@ -0,0 +1,4 @@ +module a; +integer [31:0]w; +endmodule + diff --git a/yosys/tests/errors/syntax_err02.v b/yosys/tests/errors/syntax_err02.v new file mode 100644 index 000000000..c72e976a8 --- /dev/null +++ b/yosys/tests/errors/syntax_err02.v @@ -0,0 +1,7 @@ +module a; +task to ( + input integer [3:0]x +); +endtask +endmodule + diff --git a/yosys/tests/errors/syntax_err03.v b/yosys/tests/errors/syntax_err03.v new file mode 100644 index 000000000..6eec44ade --- /dev/null +++ b/yosys/tests/errors/syntax_err03.v @@ -0,0 +1,7 @@ +module a; +task to ( + input [3]x +); +endtask +endmodule + diff --git a/yosys/tests/errors/syntax_err04.v b/yosys/tests/errors/syntax_err04.v new file mode 100644 index 000000000..d488e5dbb --- /dev/null +++ b/yosys/tests/errors/syntax_err04.v @@ -0,0 +1,4 @@ +module a; +wire [3]x; +endmodule + diff --git a/yosys/tests/errors/syntax_err05.v b/yosys/tests/errors/syntax_err05.v new file mode 100644 index 000000000..8a1f11532 --- /dev/null +++ b/yosys/tests/errors/syntax_err05.v @@ -0,0 +1,4 @@ +module a; +input x[2:0]; +endmodule + diff --git a/yosys/tests/errors/syntax_err06.v b/yosys/tests/errors/syntax_err06.v new file mode 100644 index 000000000..b35a1dea2 --- /dev/null +++ b/yosys/tests/errors/syntax_err06.v @@ -0,0 +1,6 @@ +module a; +initial +begin : label1 +end: label2 +endmodule + diff --git a/yosys/tests/errors/syntax_err07.v b/yosys/tests/errors/syntax_err07.v new file mode 100644 index 000000000..62bcc6b3e --- /dev/null +++ b/yosys/tests/errors/syntax_err07.v @@ -0,0 +1,6 @@ +module a; +wire [5:0]x; +wire [3:0]y; +assign y = (4)55; +endmodule + diff --git a/yosys/tests/errors/syntax_err08.v b/yosys/tests/errors/syntax_err08.v new file mode 100644 index 000000000..d41bfd6c9 --- /dev/null +++ b/yosys/tests/errors/syntax_err08.v @@ -0,0 +1,6 @@ +module a; +wire [5:0]x; +wire [3:0]y; +assign y = x 55; +endmodule + diff --git a/yosys/tests/errors/syntax_err09.v b/yosys/tests/errors/syntax_err09.v new file mode 100644 index 000000000..1e472eb94 --- /dev/null +++ b/yosys/tests/errors/syntax_err09.v @@ -0,0 +1,3 @@ +module a(input wire x = 1'b0); +endmodule + diff --git a/yosys/tests/errors/syntax_err10.v b/yosys/tests/errors/syntax_err10.v new file mode 100644 index 000000000..d3280405c --- /dev/null +++ b/yosys/tests/errors/syntax_err10.v @@ -0,0 +1,3 @@ +module a; +parameter integer [2:0]x=0; +endmodule diff --git a/yosys/tests/errors/syntax_err11.v b/yosys/tests/errors/syntax_err11.v new file mode 100644 index 000000000..f3cde9dfc --- /dev/null +++ b/yosys/tests/errors/syntax_err11.v @@ -0,0 +1,3 @@ +module a; +parameter integer real x=0; +endmodule diff --git a/yosys/tests/errors/syntax_err12.v b/yosys/tests/errors/syntax_err12.v new file mode 100644 index 000000000..f9b5d5b0b --- /dev/null +++ b/yosys/tests/errors/syntax_err12.v @@ -0,0 +1,7 @@ +interface iface; +endinterface + +module a ( + iface x = 1'b0 +); +endmodule diff --git a/yosys/tests/errors/syntax_err13.v b/yosys/tests/errors/syntax_err13.v new file mode 100644 index 000000000..b5c942fca --- /dev/null +++ b/yosys/tests/errors/syntax_err13.v @@ -0,0 +1,4 @@ +module a #(p = 0) +(); +endmodule + diff --git a/yosys/tests/lut/.gitignore b/yosys/tests/lut/.gitignore new file mode 100644 index 000000000..397b4a762 --- /dev/null +++ b/yosys/tests/lut/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/yosys/tests/lut/check_map.ys b/yosys/tests/lut/check_map.ys new file mode 100644 index 000000000..46854e82e --- /dev/null +++ b/yosys/tests/lut/check_map.ys @@ -0,0 +1,6 @@ +simplemap +equiv_opt -assert techmap -D LUT_WIDTH=4 -map +/cmp2lut.v +design -load postopt +equiv_opt -assert techmap -D LUT_WIDTH=4 -map +/gate2lut.v +design -load postopt +select -assert-count 0 t:* t:$lut %d diff --git a/yosys/tests/lut/map_and.v b/yosys/tests/lut/map_and.v new file mode 100644 index 000000000..68ae33fd6 --- /dev/null +++ b/yosys/tests/lut/map_and.v @@ -0,0 +1,5 @@ +module top(...); + input a, b; + output y; + assign y = a&b; +endmodule diff --git a/yosys/tests/lut/map_cmp.v b/yosys/tests/lut/map_cmp.v new file mode 100644 index 000000000..5e413f894 --- /dev/null +++ b/yosys/tests/lut/map_cmp.v @@ -0,0 +1,29 @@ +module top(...); + input [3:0] a; + + output o1_1 = 4'b1010 <= a; + output o1_2 = 4'b1010 < a; + output o1_3 = 4'b1010 >= a; + output o1_4 = 4'b1010 > a; + output o1_5 = 4'b1010 == a; + output o1_6 = 4'b1010 != a; + + output o2_1 = a <= 4'b1010; + output o2_2 = a < 4'b1010; + output o2_3 = a >= 4'b1010; + output o2_4 = a > 4'b1010; + output o2_5 = a == 4'b1010; + output o2_6 = a != 4'b1010; + + output o3_1 = 4'sb0101 <= $signed(a); + output o3_2 = 4'sb0101 < $signed(a); + output o3_3 = 4'sb0101 >= $signed(a); + output o3_4 = 4'sb0101 > $signed(a); + output o3_5 = 4'sb0101 == $signed(a); + output o3_6 = 4'sb0101 != $signed(a); + + output o4_1 = $signed(a) <= 4'sb0000; + output o4_2 = $signed(a) < 4'sb0000; + output o4_3 = $signed(a) >= 4'sb0000; + output o4_4 = $signed(a) > 4'sb0000; +endmodule diff --git a/yosys/tests/lut/map_mux.v b/yosys/tests/lut/map_mux.v new file mode 100644 index 000000000..ccecf3023 --- /dev/null +++ b/yosys/tests/lut/map_mux.v @@ -0,0 +1,5 @@ +module top(...); + input a, b, s; + output y; + assign y = s?a:b; +endmodule diff --git a/yosys/tests/lut/map_not.v b/yosys/tests/lut/map_not.v new file mode 100644 index 000000000..385997414 --- /dev/null +++ b/yosys/tests/lut/map_not.v @@ -0,0 +1,5 @@ +module top(...); + input a; + output y; + assign y = ~a; +endmodule diff --git a/yosys/tests/lut/map_or.v b/yosys/tests/lut/map_or.v new file mode 100644 index 000000000..8b8c55188 --- /dev/null +++ b/yosys/tests/lut/map_or.v @@ -0,0 +1,5 @@ +module top(...); + input a, b; + output y; + assign y = a|b; +endmodule diff --git a/yosys/tests/lut/map_xor.v b/yosys/tests/lut/map_xor.v new file mode 100644 index 000000000..708a05789 --- /dev/null +++ b/yosys/tests/lut/map_xor.v @@ -0,0 +1,5 @@ +module top(...); + input a, b; + output y; + assign y = a^b; +endmodule diff --git a/yosys/tests/lut/run-test.sh b/yosys/tests/lut/run-test.sh new file mode 100755 index 000000000..207417fa6 --- /dev/null +++ b/yosys/tests/lut/run-test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +for x in *.v; do + echo "Running $x.." + ../../yosys -q -s check_map.ys -l ${x%.v}.log $x +done diff --git a/yosys/tests/opt/.gitignore b/yosys/tests/opt/.gitignore new file mode 100644 index 000000000..397b4a762 --- /dev/null +++ b/yosys/tests/opt/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/yosys/tests/opt/opt_expr_cmp.v b/yosys/tests/opt/opt_expr_cmp.v new file mode 100644 index 000000000..5aff4b80f --- /dev/null +++ b/yosys/tests/opt/opt_expr_cmp.v @@ -0,0 +1,40 @@ +module top(...); + input [3:0] a; + + output o1_1 = 4'b0000 > a; + output o1_2 = 4'b0000 <= a; + output o1_3 = 4'b1111 < a; + output o1_4 = 4'b1111 >= a; + output o1_5 = a < 4'b0000; + output o1_6 = a >= 4'b0000; + output o1_7 = a > 4'b1111; + output o1_8 = a <= 4'b1111; + + output o2_1 = 4'sb0000 > $signed(a); + output o2_2 = 4'sb0000 <= $signed(a); + output o2_3 = $signed(a) < 4'sb0000; + output o2_4 = $signed(a) >= 4'sb0000; + + output o3_1 = 4'b0100 > a; + output o3_2 = 4'b0100 <= a; + output o3_3 = a < 4'b0100; + output o3_4 = a >= 4'b0100; + + output o4_1 = 5'b10000 > a; + output o4_2 = 5'b10000 >= a; + output o4_3 = 5'b10000 < a; + output o4_4 = 5'b10000 <= a; + output o4_5 = a < 5'b10000; + output o4_6 = a <= 5'b10000; + output o4_7 = a > 5'b10000; + output o4_8 = a >= 5'b10000; + + output o5_1 = 5'b10100 > a; + output o5_2 = 5'b10100 >= a; + output o5_3 = 5'b10100 < a; + output o5_4 = 5'b10100 <= a; + output o5_5 = a < 5'b10100; + output o5_6 = a <= 5'b10100; + output o5_7 = a > 5'b10100; + output o5_8 = a >= 5'b10100; +endmodule diff --git a/yosys/tests/opt/opt_expr_cmp.ys b/yosys/tests/opt/opt_expr_cmp.ys new file mode 100644 index 000000000..214ce8b11 --- /dev/null +++ b/yosys/tests/opt/opt_expr_cmp.ys @@ -0,0 +1,4 @@ +read_verilog opt_expr_cmp.v +equiv_opt -assert opt_expr -fine +design -load postopt +select -assert-count 0 t:$gt t:$ge t:$lt t:$le diff --git a/yosys/tests/opt/opt_lut.v b/yosys/tests/opt/opt_lut.v new file mode 100644 index 000000000..b13db367d --- /dev/null +++ b/yosys/tests/opt/opt_lut.v @@ -0,0 +1,18 @@ +module top( + input [8:0] a, + input [8:0] b, + output [8:0] o1, + output [2:0] o2, + input [2:0] c, + input [2:0] d, + output [2:0] o3, + output [2:0] o4, + input s +); + +assign o1 = (s ? 0 : a + b); +assign o2 = (s ? a : a - b); +assign o3 = (s ? 4'b1111 : d + c); +assign o4 = (s ? d : c - d); + +endmodule diff --git a/yosys/tests/opt/opt_lut.ys b/yosys/tests/opt/opt_lut.ys new file mode 100644 index 000000000..59b12c351 --- /dev/null +++ b/yosys/tests/opt/opt_lut.ys @@ -0,0 +1,4 @@ +read_verilog opt_lut.v +synth_ice40 +ice40_unlut +equiv_opt -map +/ice40/cells_sim.v -assert opt_lut -dlogic SB_CARRY:I0=1:I1=2:CI=3 diff --git a/yosys/tests/opt/opt_lut_elim.il b/yosys/tests/opt/opt_lut_elim.il new file mode 100644 index 000000000..75675d983 --- /dev/null +++ b/yosys/tests/opt/opt_lut_elim.il @@ -0,0 +1,19 @@ +module \test + wire input 1 \i + + wire output 2 \o1 + cell $lut $1 + parameter \LUT 16'0110100110010110 + parameter \WIDTH 4 + connect \A { \i 3'000 } + connect \Y \o1 + end + + wire output 2 \o2 + cell $lut $2 + parameter \LUT 16'0110100010010110 + parameter \WIDTH 4 + connect \A { \i 3'000 } + connect \Y \o2 + end +end diff --git a/yosys/tests/opt/opt_lut_elim.ys b/yosys/tests/opt/opt_lut_elim.ys new file mode 100644 index 000000000..8e5e23aea --- /dev/null +++ b/yosys/tests/opt/opt_lut_elim.ys @@ -0,0 +1,3 @@ +read_ilang opt_lut_elim.il +opt_lut +select -assert-count 0 t:$lut diff --git a/yosys/tests/opt/opt_lut_port.il b/yosys/tests/opt/opt_lut_port.il new file mode 100644 index 000000000..7eb71890f --- /dev/null +++ b/yosys/tests/opt/opt_lut_port.il @@ -0,0 +1,18 @@ +module $1 + wire width 4 input 2 \_0_ + wire output 4 \_1_ + wire input 3 \_2_ + wire output 1 \o + cell $lut \_3_ + parameter \LUT 16'0011000000000011 + parameter \WIDTH 4 + connect \A { \_0_ [3] \o 2'00 } + connect \Y \_1_ + end + cell $lut \_4_ + parameter \LUT 4'0001 + parameter \WIDTH 4 + connect \A { 3'000 \_2_ } + connect \Y \o + end +end diff --git a/yosys/tests/opt/opt_lut_port.ys b/yosys/tests/opt/opt_lut_port.ys new file mode 100644 index 000000000..3cb4ecb23 --- /dev/null +++ b/yosys/tests/opt/opt_lut_port.ys @@ -0,0 +1,3 @@ +read_ilang opt_lut_port.il +opt_lut +select -assert-count 2 t:$lut diff --git a/yosys/tests/opt/run-test.sh b/yosys/tests/opt/run-test.sh new file mode 100755 index 000000000..44ce7e674 --- /dev/null +++ b/yosys/tests/opt/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/yosys/tests/sat/sizebits.sv b/yosys/tests/sat/sizebits.sv new file mode 100644 index 000000000..d7ce2326e --- /dev/null +++ b/yosys/tests/sat/sizebits.sv @@ -0,0 +1,32 @@ +module functions01; + +wire [5:2]x; +wire [3:0]y[2:7]; +wire [3:0]z[7:2][2:9]; + +//wire [$size(x)-1:0]x_size; +//wire [$size({x, x})-1:0]xx_size; +//wire [$size(y)-1:0]y_size; +//wire [$size(z)-1:0]z_size; + +assert property ($size(x) == 4); +assert property ($size({3{x}}) == 3*4); +assert property ($size(y) == 6); +assert property ($size(y, 1) == 6); +assert property ($size(y, (1+1)) == 4); + +assert property ($size(z) == 6); +assert property ($size(z, 1) == 6); +assert property ($size(z, 2) == 8); +assert property ($size(z, 3) == 4); +// This should trigger an error if enabled (it does). +//assert property ($size(z, 4) == 4); + +//wire [$bits(x)-1:0]x_bits; +//wire [$bits({x, x})-1:0]xx_bits; + +assert property ($bits(x) == 4); +assert property ($bits(y) == 4*6); +assert property ($bits(z) == 4*6*8); + +endmodule diff --git a/yosys/tests/sat/sizebits.ys b/yosys/tests/sat/sizebits.ys new file mode 100644 index 000000000..689227a41 --- /dev/null +++ b/yosys/tests/sat/sizebits.ys @@ -0,0 +1,2 @@ +read_verilog -sv sizebits.sv +prep; sat -verify -prove-asserts diff --git a/yosys/tests/simple/arraycells.v b/yosys/tests/simple/arraycells.v index 704ca3fda..fd85a1195 100644 --- a/yosys/tests/simple/arraycells.v +++ b/yosys/tests/simple/arraycells.v @@ -2,7 +2,7 @@ module array_test001(a, b, c, y); input a; input [31:0] b, c; - input [31:0] y; + output [31:0] y; aoi12 p [31:0] (a, b, c, y); endmodule diff --git a/yosys/tests/simple/graphtest.v b/yosys/tests/simple/graphtest.v index 74788dbbe..9b16b61ca 100644 --- a/yosys/tests/simple/graphtest.v +++ b/yosys/tests/simple/graphtest.v @@ -25,7 +25,7 @@ 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 + end else begin X = 4'b0000; // All-Const at port (mux) end diff --git a/yosys/tests/simple/hierdefparam.v b/yosys/tests/simple/hierdefparam.v new file mode 100644 index 000000000..ff92c38bd --- /dev/null +++ b/yosys/tests/simple/hierdefparam.v @@ -0,0 +1,23 @@ +module hierdefparam_top(input [7:0] A, output [7:0] Y); + generate begin:foo + hierdefparam_a mod_a(.A(A), .Y(Y)); + end endgenerate + defparam foo.mod_a.bar[0].mod_b.addvalue = 42; + defparam foo.mod_a.bar[1].mod_b.addvalue = 43; +endmodule + +module hierdefparam_a(input [7:0] A, output [7:0] Y); + genvar i; + generate + for (i = 0; i < 2; i=i+1) begin:bar + wire [7:0] a, y; + hierdefparam_b mod_b(.A(a), .Y(y)); + end + endgenerate + assign bar[0].a = A, bar[1].a = bar[0].y, Y = bar[1].y; +endmodule + +module hierdefparam_b(input [7:0] A, output [7:0] Y); + parameter [7:0] addvalue = 44; + assign Y = A + addvalue; +endmodule diff --git a/yosys/tests/simple/specify.v b/yosys/tests/simple/specify.v new file mode 100644 index 000000000..f19418d90 --- /dev/null +++ b/yosys/tests/simple/specify.v @@ -0,0 +1,31 @@ +module test_specify; + +specparam a=1; + +specify +endspecify + +specify +(A => B) = ( 1 ) ; +(A- => B) = ( 1,2 ) ; +(A+ => B) = ( 1,2,3 ) ; +(A => B) = ( + 1.1, 2, 3, + 4, 5.5, 6.6 +) ; +(A => B) = ( + 1.1, 2, 3, + 4, 5.5, 6.6 , + 7.7, 8.8, 9, + 10.1, 11, 12 +) ; +specparam b=1; +specparam [1:2] asasa=1; +endspecify + +specify +specparam c=1:2:3; +endspecify + +endmodule + diff --git a/yosys/tests/sva/.gitignore b/yosys/tests/sva/.gitignore new file mode 100644 index 000000000..cc254049a --- /dev/null +++ b/yosys/tests/sva/.gitignore @@ -0,0 +1,7 @@ +/*_pass.sby +/*_fail.sby +/*_pass +/*_fail +/*.ok +/vhdlpsl[0-9][0-9] +/vhdlpsl[0-9][0-9].sby diff --git a/yosys/tests/sva/Makefile b/yosys/tests/sva/Makefile new file mode 100644 index 000000000..1b217f746 --- /dev/null +++ b/yosys/tests/sva/Makefile @@ -0,0 +1,13 @@ + +TESTS = $(sort $(basename $(wildcard *.sv)) $(basename $(wildcard *.vhd))) + +all: $(addsuffix .ok,$(TESTS)) + +%.ok: + bash runtest.sh $@ + +clean: + rm -rf $(addsuffix .ok,$(TESTS)) $(addsuffix .sby,$(TESTS)) $(TESTS) + rm -rf $(addsuffix _pass.sby,$(TESTS)) $(addsuffix _pass,$(TESTS)) + rm -rf $(addsuffix _fail.sby,$(TESTS)) $(addsuffix _fail,$(TESTS)) + diff --git a/yosys/tests/sva/basic00.sv b/yosys/tests/sva/basic00.sv new file mode 100644 index 000000000..30c37f5f1 --- /dev/null +++ b/yosys/tests/sva/basic00.sv @@ -0,0 +1,12 @@ +module top (input clk, reset, antecedent, output reg consequent); + always @(posedge clk) + consequent <= reset ? 0 : antecedent; + +`ifdef FAIL + test_assert: assert property ( @(posedge clk) disable iff (reset) antecedent |-> consequent ) + else $error("Failed with consequent = ", $sampled(consequent)); +`else + test_assert: assert property ( @(posedge clk) disable iff (reset) antecedent |=> consequent ) + else $error("Failed with consequent = ", $sampled(consequent)); +`endif +endmodule diff --git a/yosys/tests/sva/basic01.sv b/yosys/tests/sva/basic01.sv new file mode 100644 index 000000000..d5ad497dd --- /dev/null +++ b/yosys/tests/sva/basic01.sv @@ -0,0 +1,16 @@ +module top (input logic clock, ctrl); + logic read = 0, write = 0, ready = 0; + + always @(posedge clock) begin + read <= !ctrl; + write <= ctrl; + ready <= write; + end + + a_rw: assert property ( @(posedge clock) !(read && write) ); +`ifdef FAIL + a_wr: assert property ( @(posedge clock) write |-> ready ); +`else + a_wr: assert property ( @(posedge clock) write |=> ready ); +`endif +endmodule diff --git a/yosys/tests/sva/basic02.sv b/yosys/tests/sva/basic02.sv new file mode 100644 index 000000000..b34f3aff3 --- /dev/null +++ b/yosys/tests/sva/basic02.sv @@ -0,0 +1,20 @@ +module top (input logic clock, ctrl); + logic read = 0, write = 0, ready = 0; + + always @(posedge clock) begin + read <= !ctrl; + write <= ctrl; + ready <= write; + end +endmodule + +module top_properties (input logic clock, read, write, ready); + a_rw: assert property ( @(posedge clock) !(read && write) ); +`ifdef FAIL + a_wr: assert property ( @(posedge clock) write |-> ready ); +`else + a_wr: assert property ( @(posedge clock) write |=> ready ); +`endif +endmodule + +bind top top_properties properties_inst (.*); diff --git a/yosys/tests/sva/basic03.sv b/yosys/tests/sva/basic03.sv new file mode 100644 index 000000000..8018de4ca --- /dev/null +++ b/yosys/tests/sva/basic03.sv @@ -0,0 +1,12 @@ +module top (input logic clk, input logic selA, selB, QA, QB, output logic Q); + always @(posedge clk) begin + if (selA) Q <= QA; + if (selB) Q <= QB; + end + + check_selA: assert property ( @(posedge clk) selA |=> Q == $past(QA) ); + check_selB: assert property ( @(posedge clk) selB |=> Q == $past(QB) ); +`ifndef FAIL + assume_not_11: assume property ( @(posedge clk) !(selA & selB) ); +`endif +endmodule diff --git a/yosys/tests/sva/basic04.sv b/yosys/tests/sva/basic04.sv new file mode 100644 index 000000000..bc46be9f6 --- /dev/null +++ b/yosys/tests/sva/basic04.sv @@ -0,0 +1,10 @@ +module top_properties (input logic clock, read, write, ready); + a_rw: assert property ( @(posedge clock) !(read && write) ); +`ifdef FAIL + a_wr: assert property ( @(posedge clock) write |-> ready ); +`else + a_wr: assert property ( @(posedge clock) write |=> ready ); +`endif +endmodule + +bind top top_properties properties_inst (.*); diff --git a/yosys/tests/sva/basic04.vhd b/yosys/tests/sva/basic04.vhd new file mode 100644 index 000000000..f2ec305ec --- /dev/null +++ b/yosys/tests/sva/basic04.vhd @@ -0,0 +1,26 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity top is + port ( + clock : in std_logic; + ctrl : in std_logic; + x : out std_logic + ); +end entity; + +architecture rtl of top is + signal read : std_logic := '0'; + signal write : std_logic := '0'; + signal ready : std_logic := '0'; +begin + process (clock) begin + if (rising_edge(clock)) then + read <= not ctrl; + write <= ctrl; + ready <= write; + end if; + end process; + + x <= read xor write xor ready; +end architecture; diff --git a/yosys/tests/sva/basic05.sv b/yosys/tests/sva/basic05.sv new file mode 100644 index 000000000..816ee1da7 --- /dev/null +++ b/yosys/tests/sva/basic05.sv @@ -0,0 +1,19 @@ +module top (input logic clock, ctrl); + logic read, write, ready; + + demo uut ( + .clock(clock), + .ctrl(ctrl) + ); + + assign read = uut.read; + assign write = uut.write; + assign ready = uut.ready; + + a_rw: assert property ( @(posedge clock) !(read && write) ); +`ifdef FAIL + a_wr: assert property ( @(posedge clock) write |-> ready ); +`else + a_wr: assert property ( @(posedge clock) write |=> ready ); +`endif +endmodule diff --git a/yosys/tests/sva/basic05.vhd b/yosys/tests/sva/basic05.vhd new file mode 100644 index 000000000..8d42f71e8 --- /dev/null +++ b/yosys/tests/sva/basic05.vhd @@ -0,0 +1,26 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity demo is + port ( + clock : in std_logic; + ctrl : in std_logic; + x : out std_logic + ); +end entity; + +architecture rtl of demo is + signal read : std_logic := '0'; + signal write : std_logic := '0'; + signal ready : std_logic := '0'; +begin + process (clock) begin + if (rising_edge(clock)) then + read <= not ctrl; + write <= ctrl; + ready <= write; + end if; + end process; + + x <= read xor write xor ready; +end architecture; diff --git a/yosys/tests/sva/counter.sv b/yosys/tests/sva/counter.sv new file mode 100644 index 000000000..4f949115e --- /dev/null +++ b/yosys/tests/sva/counter.sv @@ -0,0 +1,30 @@ +module top (input clk, reset, up, down, output reg [7:0] cnt); + always @(posedge clk) begin + if (reset) + cnt <= 0; + else if (up) + cnt <= cnt + 1; + else if (down) + cnt <= cnt - 1; + end + + default clocking @(posedge clk); endclocking + default disable iff (reset); + + assert property (up |=> cnt == $past(cnt) + 8'd 1); + assert property (up [*2] |=> cnt == $past(cnt, 2) + 8'd 2); + assert property (up ##1 up |=> cnt == $past(cnt, 2) + 8'd 2); + +`ifndef FAIL + assume property (down |-> !up); +`endif + assert property (up ##1 down |=> cnt == $past(cnt, 2)); + assert property (down |=> cnt == $past(cnt) - 8'd 1); + + property down_n(n); + down [*n] |=> cnt == $past(cnt, n) - n; + endproperty + + assert property (down_n(8'd 3)); + assert property (down_n(8'd 5)); +endmodule diff --git a/yosys/tests/sva/runtest.sh b/yosys/tests/sva/runtest.sh new file mode 100644 index 000000000..1b65ca9c9 --- /dev/null +++ b/yosys/tests/sva/runtest.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +set -ex + +prefix=${1%.ok} +prefix=${prefix%.sv} +prefix=${prefix%.vhd} +test -f $prefix.sv -o -f $prefix.vhd + +generate_sby() { + cat <<- EOT + [options] + mode bmc + depth 10 + expect $1 + + [engines] + smtbmc yices + + [script] + EOT + + if [ -f $prefix.sv ]; then + if [ "$1" = "fail" ]; then + echo "verific -sv ${prefix}_fail.sv" + else + echo "verific -sv $prefix.sv" + fi + fi + + if [ -f $prefix.vhd ]; then + echo "verific -vhdl $prefix.vhd" + fi + + cat <<- EOT + verific -import -extnets -all top + prep -top top + chformal -early -assume + + [files] + EOT + + if [ -f $prefix.sv ]; then + echo "$prefix.sv" + fi + + if [ -f $prefix.vhd ]; then + echo "$prefix.vhd" + fi + + if [ "$1" = "fail" ]; then + cat <<- EOT + + [file ${prefix}_fail.sv] + \`define FAIL + \`include "$prefix.sv" + EOT + fi +} + +if [ -f $prefix.sv ]; then + generate_sby pass > ${prefix}_pass.sby + generate_sby fail > ${prefix}_fail.sby + sby --yosys $PWD/../../yosys -f ${prefix}_pass.sby + sby --yosys $PWD/../../yosys -f ${prefix}_fail.sby +else + generate_sby pass > ${prefix}.sby + sby --yosys $PWD/../../yosys -f ${prefix}.sby +fi + +touch $prefix.ok + diff --git a/yosys/tests/sva/sva_not.sv b/yosys/tests/sva/sva_not.sv new file mode 100644 index 000000000..d81a48653 --- /dev/null +++ b/yosys/tests/sva/sva_not.sv @@ -0,0 +1,34 @@ +module top ( + input clk, + input reset, + input ping, + input [1:0] cfg, + output reg pong +); + reg [2:0] cnt; + localparam integer maxdelay = 8; + + always @(posedge clk) begin + if (reset) begin + cnt <= 0; + pong <= 0; + end else begin + cnt <= cnt - |cnt; + pong <= cnt == 1; + if (ping) cnt <= 4 + cfg; + end + end + + assert property ( + @(posedge clk) + disable iff (reset) + not (ping ##1 !pong [*maxdelay]) + ); + +`ifndef FAIL + assume property ( + @(posedge clk) + not (cnt && ping) + ); +`endif +endmodule diff --git a/yosys/tests/sva/sva_range.sv b/yosys/tests/sva/sva_range.sv new file mode 100644 index 000000000..d1569fc83 --- /dev/null +++ b/yosys/tests/sva/sva_range.sv @@ -0,0 +1,19 @@ +module top ( + input clk, + input a, b, c, d +); + default clocking @(posedge clk); endclocking + + assert property ( + a ##[*] b |=> c until d + ); + +`ifndef FAIL + assume property ( + b |=> ##5 d + ); + assume property ( + b || (c && !d) |=> c + ); +`endif +endmodule diff --git a/yosys/tests/sva/sva_throughout.sv b/yosys/tests/sva/sva_throughout.sv new file mode 100644 index 000000000..7e036a066 --- /dev/null +++ b/yosys/tests/sva/sva_throughout.sv @@ -0,0 +1,19 @@ +module top ( + input clk, + input a, b, c, d +); + default clocking @(posedge clk); endclocking + + assert property ( + a |=> b throughout (c ##1 d) + ); + +`ifndef FAIL + assume property ( + a |=> b && c + ); + assume property ( + b && c |=> b && d + ); +`endif +endmodule diff --git a/yosys/tests/svinterfaces/.gitignore b/yosys/tests/svinterfaces/.gitignore new file mode 100644 index 000000000..a5b7927d1 --- /dev/null +++ b/yosys/tests/svinterfaces/.gitignore @@ -0,0 +1,8 @@ +/a.out +/dut_result.txt +/reference_result.txt +/*.diff +/*.log_stderr +/*.log_stdout +/*_ref_syn.v +/*_syn.v diff --git a/yosys/tests/svinterfaces/run-test.sh b/yosys/tests/svinterfaces/run-test.sh new file mode 100755 index 000000000..86567d1c1 --- /dev/null +++ b/yosys/tests/svinterfaces/run-test.sh @@ -0,0 +1,6 @@ +#/bin/bash -e + + + +./runone.sh svinterface1 +./runone.sh svinterface_at_top diff --git a/yosys/tests/svinterfaces/runone.sh b/yosys/tests/svinterfaces/runone.sh new file mode 100755 index 000000000..0adecc797 --- /dev/null +++ b/yosys/tests/svinterfaces/runone.sh @@ -0,0 +1,44 @@ +#!/bin/bash + + +TESTNAME=$1 + +STDOUTFILE=${TESTNAME}.log_stdout +STDERRFILE=${TESTNAME}.log_stderr + +echo "" > $STDOUTFILE +echo "" > $STDERRFILE + +echo -n "Test: ${TESTNAME} -> " + +$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}.sv ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_syn.v" >> $STDOUTFILE >> $STDERRFILE +$PWD/../../yosys -p "read_verilog -sv ${TESTNAME}_ref.v ; hierarchy -check -top TopModule ; synth ; write_verilog ${TESTNAME}_ref_syn.v" >> $STDOUTFILE >> $STDERRFILE + +rm -f a.out reference_result.txt dut_result.txt + +set -e + +iverilog -g2012 ${TESTNAME}_syn.v +iverilog -g2012 ${TESTNAME}_ref_syn.v + +set +e +iverilog -g2012 ${TESTNAME}_tb.v ${TESTNAME}_ref_syn.v +./a.out +mv output.txt reference_result.txt +if [ -f ${TESTNAME}_wrapper.v ] ; then + iverilog -g2012 ${TESTNAME}_tb_wrapper.v ${TESTNAME}_syn.v +else + iverilog -g2012 ${TESTNAME}_tb.v ${TESTNAME}_syn.v +fi +./a.out +mv output.txt dut_result.txt + +diff reference_result.txt dut_result.txt > ${TESTNAME}.diff +RET=$? +if [ "$RET" != "0" ] ; then + echo "ERROR!" + exit -1 +fi + +echo "ok" +exit 0 diff --git a/yosys/tests/svinterfaces/svinterface1.sv b/yosys/tests/svinterfaces/svinterface1.sv new file mode 100644 index 000000000..6d60b4880 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface1.sv @@ -0,0 +1,117 @@ + + +module TopModule( + input logic clk, + input logic rst, + output logic [21:0] outOther, + input logic [1:0] sig, + input logic flip, + output logic [1:0] sig_out, + output logic [15:0] passThrough); + + MyInterface #(.WIDTH(4)) MyInterfaceInstance(); + + SubModule1 u_SubModule1 ( + .clk(clk), + .rst(rst), + .u_MyInterface(MyInterfaceInstance), + .outOther(outOther), + .sig (sig) + ); + + assign sig_out = MyInterfaceInstance.mysig_out; + + + assign MyInterfaceInstance.setting = flip; + + assign passThrough = MyInterfaceInstance.passThrough; + +endmodule + +interface MyInterface #( + parameter WIDTH = 3)( + ); + + logic setting; + logic [WIDTH-1:0] other_setting; + + logic [1:0] mysig_out; + + logic [15:0] passThrough; + + modport submodule1 ( + input setting, + output other_setting, + output mysig_out, + output passThrough + ); + + modport submodule2 ( + input setting, + output other_setting, + input mysig_out, + output passThrough + ); + +endinterface + + +module SubModule1( + input logic clk, + input logic rst, + MyInterface.submodule1 u_MyInterface, + input logic [1:0] sig, + output logic [21:0] outOther + + ); + + always_ff @(posedge clk or posedge rst) + if(rst) + u_MyInterface.mysig_out <= 0; + else begin + if(u_MyInterface.setting) + u_MyInterface.mysig_out <= sig; + else + u_MyInterface.mysig_out <= ~sig; + end + + MyInterface #(.WIDTH(22)) MyInterfaceInstanceInSub(); + + SubModule2 u_SubModule2 ( + .clk(clk), + .rst(rst), + .u_MyInterfaceInSub2(u_MyInterface), + .u_MyInterfaceInSub3(MyInterfaceInstanceInSub) + ); + + assign outOther = MyInterfaceInstanceInSub.other_setting; + + assign MyInterfaceInstanceInSub.setting = 0; + assign MyInterfaceInstanceInSub.mysig_out = sig; + +endmodule + +module SubModule2( + + input logic clk, + input logic rst, + MyInterface.submodule2 u_MyInterfaceInSub2, + MyInterface.submodule2 u_MyInterfaceInSub3 + + ); + + always_comb begin + if (u_MyInterfaceInSub3.mysig_out == 2'b00) + u_MyInterfaceInSub3.other_setting[21:0] = 1000; + else if (u_MyInterfaceInSub3.mysig_out == 2'b01) + u_MyInterfaceInSub3.other_setting[21:0] = 2000; + else if (u_MyInterfaceInSub3.mysig_out == 2'b10) + u_MyInterfaceInSub3.other_setting[21:0] = 3000; + else + u_MyInterfaceInSub3.other_setting[21:0] = 4000; + end + + assign u_MyInterfaceInSub2.passThrough[7:0] = 124; + assign u_MyInterfaceInSub2.passThrough[15:8] = 200; + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface1_ref.v b/yosys/tests/svinterfaces/svinterface1_ref.v new file mode 100644 index 000000000..b119f4037 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface1_ref.v @@ -0,0 +1,107 @@ + +module TopModule( + input logic clk, + input logic rst, + input logic [1:0] sig, + input logic flip, + output logic [15:0] passThrough, + output logic [21:0] outOther, + output logic [1:0] sig_out); + + + logic MyInterfaceInstance_setting; + logic [3:0] MyInterfaceInstance_other_setting; + logic [1:0] MyInterfaceInstance_mysig_out; + + SubModule1 u_SubModule1 ( + .clk(clk), + .rst(rst), + .u_MyInterface_setting(MyInterfaceInstance_setting), + .u_MyInterface_mysig_out(MyInterfaceInstance_mysig_out), + .u_MyInterface_other_setting(MyInterfaceInstance_other_setting), + .outOther(outOther), + .passThrough (passThrough), + .sig (sig) + ); + + assign sig_out = MyInterfaceInstance_mysig_out; + + + assign MyInterfaceInstance_setting = flip; + +endmodule + + +module SubModule1( + input logic clk, + input logic rst, + input logic u_MyInterface_setting, + output logic [3:0] u_MyInterface_other_setting, + output logic [1:0] u_MyInterface_mysig_out, + output logic [21:0] outOther, + input logic [1:0] sig, + output logic [15:0] passThrough + ); + + always @(posedge clk or posedge rst) + if(rst) + u_MyInterface_mysig_out <= 0; + else begin + if(u_MyInterface_setting) + u_MyInterface_mysig_out <= sig; + else + u_MyInterface_mysig_out <= ~sig; + end + + logic MyInterfaceInstanceInSub_setting; + logic [21:0] MyInterfaceInstanceInSub_other_setting; + logic [1:0] MyInterfaceInstanceInSub_mysig_out; + + + SubModule2 u_SubModule2 ( + .clk(clk), + .rst(rst), + .u_MyInterfaceInSub2_setting(u_MyInterface_setting), + .u_MyInterfaceInSub2_mysig_out(u_MyInterface_mysig_out), + .u_MyInterfaceInSub2_other_setting(u_MyInterface_other_setting), + .u_MyInterfaceInSub3_setting(MyInterfaceInstanceInSub_setting), + .u_MyInterfaceInSub3_mysig_out(MyInterfaceInstanceInSub_mysig_out), + .u_MyInterfaceInSub3_other_setting(MyInterfaceInstanceInSub_other_setting), + .passThrough (passThrough) + ); + assign outOther = MyInterfaceInstanceInSub_other_setting; + + assign MyInterfaceInstanceInSub_setting = 0; + assign MyInterfaceInstanceInSub_mysig_out = sig; + +endmodule + +module SubModule2( + + input logic clk, + input logic rst, + input logic u_MyInterfaceInSub2_setting, + output logic [3:0] u_MyInterfaceInSub2_other_setting, + input logic [1:0] u_MyInterfaceInSub2_mysig_out, + input logic u_MyInterfaceInSub3_setting, + output logic [21:0] u_MyInterfaceInSub3_other_setting, + input logic [1:0] u_MyInterfaceInSub3_mysig_out, + output logic [15:0] passThrough + + ); + + always @(u_MyInterfaceInSub3_mysig_out) begin + if (u_MyInterfaceInSub3_mysig_out == 2'b00) + u_MyInterfaceInSub3_other_setting[21:0] = 1000; + else if (u_MyInterfaceInSub3_mysig_out == 2'b01) + u_MyInterfaceInSub3_other_setting[21:0] = 2000; + else if (u_MyInterfaceInSub3_mysig_out == 2'b10) + u_MyInterfaceInSub3_other_setting[21:0] = 3000; + else + u_MyInterfaceInSub3_other_setting[21:0] = 4000; + end + + assign passThrough[7:0] = 124; + assign passThrough[15:8] = 200; + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface1_tb.v b/yosys/tests/svinterfaces/svinterface1_tb.v new file mode 100644 index 000000000..44c3b5f68 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface1_tb.v @@ -0,0 +1,57 @@ +`timescale 1ns/10ps + +module svinterface1_tb; + + + logic clk; + logic rst; + logic [21:0] outOther; + logic [1:0] sig; + logic [1:0] sig_out; + logic flip; + logic [15:0] passThrough; + integer outfile; + + TopModule u_dut ( + .clk(clk), + .rst(rst), + .outOther(outOther), + .sig(sig), + .flip(flip), + .passThrough(passThrough), + .sig_out(sig_out) + ); + + initial begin + clk = 0; + while(1) begin + clk = ~clk; + #50; + end + end + + initial begin + outfile = $fopen("output.txt"); + rst = 1; + sig = 0; + flip = 0; + @(posedge clk); + #(2); + rst = 0; + @(posedge clk); + for(int j=0;j<2;j++) begin + for(int i=0;i<20;i++) begin + #(2); + flip = j; + sig = i; + @(posedge clk); + end + end + $finish; + end + + always @(negedge clk) begin + $fdisplay(outfile, "%d %d %d", outOther, sig_out, passThrough); + end + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface_at_top.sv b/yosys/tests/svinterfaces/svinterface_at_top.sv new file mode 100644 index 000000000..b5aa8c8f5 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface_at_top.sv @@ -0,0 +1,125 @@ + + +module TopModule( + input logic clk, + input logic rst, + output logic [21:0] outOther, + input logic [1:0] sig, + input logic flip, + output logic [1:0] sig_out, + MyInterface.submodule1 interfaceInstanceAtTop, + output logic [15:0] passThrough); + + MyInterface #(.WIDTH(4)) MyInterfaceInstance(); + + SubModule1 u_SubModule1 ( + .clk(clk), + .rst(rst), + .u_MyInterface(MyInterfaceInstance), + .u_MyInterfaceFromTop(interfaceInstanceAtTop), + .outOther(outOther), + .sig (sig) + ); + + assign sig_out = MyInterfaceInstance.mysig_out; + + + assign MyInterfaceInstance.setting = flip; + + assign passThrough = MyInterfaceInstance.passThrough; + +endmodule + +interface MyInterface #( + parameter WIDTH = 3)( + ); + + logic setting; + logic [WIDTH-1:0] other_setting; + + logic [1:0] mysig_out; + + logic [15:0] passThrough; + + modport submodule1 ( + input setting, + output other_setting, + output mysig_out, + output passThrough + ); + + modport submodule2 ( + input setting, + output other_setting, + input mysig_out, + output passThrough + ); + +endinterface + + +module SubModule1( + input logic clk, + input logic rst, + MyInterface.submodule1 u_MyInterface, + MyInterface.submodule1 u_MyInterfaceFromTop, + input logic [1:0] sig, + output logic [21:0] outOther + + ); + + + always_ff @(posedge clk or posedge rst) + if(rst) + u_MyInterface.mysig_out <= 0; + else begin + if(u_MyInterface.setting) + u_MyInterface.mysig_out <= sig; + else + u_MyInterface.mysig_out <= ~sig; + end + + MyInterface #(.WIDTH(22)) MyInterfaceInstanceInSub(); + + SubModule2 u_SubModule2 ( + .clk(clk), + .rst(rst), + .u_MyInterfaceFromTopDown(u_MyInterfaceFromTop), + .u_MyInterfaceInSub2(u_MyInterface), + .u_MyInterfaceInSub3(MyInterfaceInstanceInSub) + ); + + assign outOther = MyInterfaceInstanceInSub.other_setting; + + assign MyInterfaceInstanceInSub.setting = 0; + assign MyInterfaceInstanceInSub.mysig_out = sig; + +endmodule + +module SubModule2( + + input logic clk, + input logic rst, + MyInterface.submodule2 u_MyInterfaceInSub2, + MyInterface.submodule1 u_MyInterfaceFromTopDown, + MyInterface.submodule2 u_MyInterfaceInSub3 + + ); + + assign u_MyInterfaceFromTopDown.mysig_out = u_MyInterfaceFromTop.setting ? 10 : 20; + + always_comb begin + if (u_MyInterfaceInSub3.mysig_out == 2'b00) + u_MyInterfaceInSub3.other_setting[21:0] = 1000; + else if (u_MyInterfaceInSub3.mysig_out == 2'b01) + u_MyInterfaceInSub3.other_setting[21:0] = 2000; + else if (u_MyInterfaceInSub3.mysig_out == 2'b10) + u_MyInterfaceInSub3.other_setting[21:0] = 3000; + else + u_MyInterfaceInSub3.other_setting[21:0] = 4000; + end + + assign u_MyInterfaceInSub2.passThrough[7:0] = 124; + assign u_MyInterfaceInSub2.passThrough[15:8] = 200; + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface_at_top_ref.v b/yosys/tests/svinterfaces/svinterface_at_top_ref.v new file mode 100644 index 000000000..7b54a26d6 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface_at_top_ref.v @@ -0,0 +1,120 @@ + +module TopModule( + input logic clk, + input logic rst, + input logic [1:0] sig, + input logic flip, + output logic [15:0] passThrough, + output logic [21:0] outOther, + input logic interfaceInstanceAtTop_setting, + output logic [2:0] interfaceInstanceAtTop_other_setting, + output logic [1:0] interfaceInstanceAtTop_mysig_out, + output logic [15:0] interfaceInstanceAtTop_passThrough, + output logic [1:0] sig_out); + + + logic MyInterfaceInstance_setting; + logic [3:0] MyInterfaceInstance_other_setting; + logic [1:0] MyInterfaceInstance_mysig_out; + + SubModule1 u_SubModule1 ( + .clk(clk), + .rst(rst), + .u_MyInterface_setting(MyInterfaceInstance_setting), + .u_MyInterface_mysig_out(MyInterfaceInstance_mysig_out), + .u_MyInterface_other_setting(MyInterfaceInstance_other_setting), + .u_MyInterfaceFromTop_setting(interfaceInstanceAtTop_setting), + .u_MyInterfaceFromTop_other_setting(interfaceInstanceAtTop_other_setting), + .u_MyInterfaceFromTop_mysig_out(interfaceInstanceAtTop_mysig_out), + .u_MyInterfaceFromTop_passThrough(interfaceInstanceAtTop_passThrough), + .outOther(outOther), + .passThrough (passThrough), + .sig (sig) + ); + + assign sig_out = MyInterfaceInstance_mysig_out; + + + assign MyInterfaceInstance_setting = flip; + +endmodule + + +module SubModule1( + input logic clk, + input logic rst, + input logic u_MyInterface_setting, + output logic [3:0] u_MyInterface_other_setting, + output logic [1:0] u_MyInterface_mysig_out, + output logic [21:0] outOther, + input logic [1:0] sig, + input logic u_MyInterfaceFromTop_setting, + output logic [2:0] u_MyInterfaceFromTop_other_setting, + output logic [1:0] u_MyInterfaceFromTop_mysig_out, + output logic [14:0] u_MyInterfaceFromTop_passThrough, + output logic [15:0] passThrough + ); + + always @(posedge clk or posedge rst) + if(rst) + u_MyInterface_mysig_out <= 0; + else begin + if(u_MyInterface_setting) + u_MyInterface_mysig_out <= sig; + else + u_MyInterface_mysig_out <= ~sig; + end + + logic MyInterfaceInstanceInSub_setting; + logic [21:0] MyInterfaceInstanceInSub_other_setting; + logic [1:0] MyInterfaceInstanceInSub_mysig_out; + + assign u_MyInterfaceFromTop_mysig_out = u_MyInterfaceFromTop_setting ? 10 : 20; + + SubModule2 u_SubModule2 ( + .clk(clk), + .rst(rst), + .u_MyInterfaceInSub2_setting(u_MyInterface_setting), + .u_MyInterfaceInSub2_mysig_out(u_MyInterface_mysig_out), + .u_MyInterfaceInSub2_other_setting(u_MyInterface_other_setting), + .u_MyInterfaceInSub3_setting(MyInterfaceInstanceInSub_setting), + .u_MyInterfaceInSub3_mysig_out(MyInterfaceInstanceInSub_mysig_out), + .u_MyInterfaceInSub3_other_setting(MyInterfaceInstanceInSub_other_setting), + .passThrough (passThrough) + ); + assign outOther = MyInterfaceInstanceInSub_other_setting; + + assign MyInterfaceInstanceInSub_setting = 0; + assign MyInterfaceInstanceInSub_mysig_out = sig; + +endmodule + +module SubModule2( + + input logic clk, + input logic rst, + input logic u_MyInterfaceInSub2_setting, + output logic [3:0] u_MyInterfaceInSub2_other_setting, + input logic [1:0] u_MyInterfaceInSub2_mysig_out, + input logic u_MyInterfaceInSub3_setting, + output logic [21:0] u_MyInterfaceInSub3_other_setting, + input logic [1:0] u_MyInterfaceInSub3_mysig_out, + output logic [15:0] passThrough + + ); + + always @(u_MyInterfaceInSub3_mysig_out) begin + if (u_MyInterfaceInSub3_mysig_out == 2'b00) + u_MyInterfaceInSub3_other_setting[21:0] = 1000; + else if (u_MyInterfaceInSub3_mysig_out == 2'b01) + u_MyInterfaceInSub3_other_setting[21:0] = 2000; + else if (u_MyInterfaceInSub3_mysig_out == 2'b10) + u_MyInterfaceInSub3_other_setting[21:0] = 3000; + else + u_MyInterfaceInSub3_other_setting[21:0] = 4000; + end + + assign passThrough[7:0] = 124; + assign passThrough[15:8] = 200; + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface_at_top_tb.v b/yosys/tests/svinterfaces/svinterface_at_top_tb.v new file mode 100644 index 000000000..bf37a148d --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface_at_top_tb.v @@ -0,0 +1,68 @@ +`timescale 1ns/10ps + +module svinterface_at_top_tb; + + + logic clk; + logic rst; + logic [21:0] outOther; + logic [1:0] sig; + logic [1:0] sig_out; + logic flip; + logic [15:0] passThrough; + integer outfile; + + logic interfaceInstanceAtTop_setting; + logic [2:0] interfaceInstanceAtTop_other_setting; + logic [1:0] interfaceInstanceAtTop_mysig_out; + logic [15:0] interfaceInstanceAtTop_passThrough; + + + TopModule u_dut ( + .clk(clk), + .rst(rst), + .outOther(outOther), + .sig(sig), + .flip(flip), + .passThrough(passThrough), + .interfaceInstanceAtTop_setting(interfaceInstanceAtTop_setting), + .interfaceInstanceAtTop_other_setting(interfaceInstanceAtTop_other_setting), + .interfaceInstanceAtTop_mysig_out(interfaceInstanceAtTop_mysig_out), + .interfaceInstanceAtTop_passThrough(interfaceInstanceAtTop_passThrough), + .sig_out(sig_out) + ); + + initial begin + clk = 0; + while(1) begin + clk = ~clk; + #50; + end + end + + initial begin + outfile = $fopen("output.txt"); + rst = 1; + interfaceInstanceAtTop_setting = 0; + sig = 0; + flip = 0; + @(posedge clk); + #(2); + rst = 0; + @(posedge clk); + for(int j=0;j<2;j++) begin + for(int i=0;i<20;i++) begin + #(2); + flip = j; + sig = i; + @(posedge clk); + end + end + $finish; + end + + always @(negedge clk) begin + $fdisplay(outfile, "%d %d %d %d", outOther, sig_out, passThrough, interfaceInstanceAtTop_mysig_out); + end + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface_at_top_tb_wrapper.v b/yosys/tests/svinterfaces/svinterface_at_top_tb_wrapper.v new file mode 100644 index 000000000..b344a7b88 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface_at_top_tb_wrapper.v @@ -0,0 +1,68 @@ +`timescale 1ns/10ps + +module svinterface_at_top_tb_wrapper; + + + logic clk; + logic rst; + logic [21:0] outOther; + logic [1:0] sig; + logic [1:0] sig_out; + logic flip; + logic [15:0] passThrough; + integer outfile; + + logic interfaceInstanceAtTop_setting; + logic [2:0] interfaceInstanceAtTop_other_setting; + logic [1:0] interfaceInstanceAtTop_mysig_out; + logic [15:0] interfaceInstanceAtTop_passThrough; + + + TopModule u_dut ( + .clk(clk), + .rst(rst), + .outOther(outOther), + .sig(sig), + .flip(flip), + .passThrough(passThrough), + .\interfaceInstanceAtTop.setting (interfaceInstanceAtTop_setting), + .\interfaceInstanceAtTop.other_setting (interfaceInstanceAtTop_other_setting), + .\interfaceInstanceAtTop.mysig_out (interfaceInstanceAtTop_mysig_out), + .\interfaceInstanceAtTop.passThrough (interfaceInstanceAtTop_passThrough), + .sig_out(sig_out) + ); + + initial begin + clk = 0; + while(1) begin + clk = ~clk; + #50; + end + end + + initial begin + outfile = $fopen("output.txt"); + rst = 1; + sig = 0; + interfaceInstanceAtTop_setting = 0; + flip = 0; + @(posedge clk); + #(2); + rst = 0; + @(posedge clk); + for(int j=0;j<2;j++) begin + for(int i=0;i<20;i++) begin + #(2); + flip = j; + sig = i; + @(posedge clk); + end + end + $finish; + end + + always @(negedge clk) begin + $fdisplay(outfile, "%d %d %d %d", outOther, sig_out, passThrough, interfaceInstanceAtTop_mysig_out); + end + +endmodule diff --git a/yosys/tests/svinterfaces/svinterface_at_top_wrapper.v b/yosys/tests/svinterfaces/svinterface_at_top_wrapper.v new file mode 100644 index 000000000..64f906c07 --- /dev/null +++ b/yosys/tests/svinterfaces/svinterface_at_top_wrapper.v @@ -0,0 +1,33 @@ +`timescale 1ns/10ps + +module svinterface_at_top_wrapper( + input logic clk, + input logic rst, + output logic [21:0] outOther, + input logic [1:0] sig, + output logic [1:0] sig_out, + input logic flip, + output logic [15:0] passThrough, + + input logic interfaceInstanceAtTop_setting, + output logic [2:0] interfaceInstanceAtTop_other_setting, + output logic [1:0] interfaceInstanceAtTop_mysig_out, + output logic [15:0] interfaceInstanceAtTop_passThrough, + ); + + + TopModule u_dut ( + .clk(clk), + .rst(rst), + .outOther(outOther), + .sig(sig), + .flip(flip), + .passThrough(passThrough), + .\interfaceInstanceAtTop.setting(interfaceInstanceAtTop_setting), + .\interfaceInstanceAtTop.other_setting(interfaceInstanceAtTop_other_setting), + .\interfaceInstanceAtTop.mysig_out(interfaceInstanceAtTop_mysig_out), + .\interfaceInstanceAtTop.passThrough(interfaceInstanceAtTop_passThrough), + .sig_out(sig_out) + ); + +endmodule diff --git a/yosys/tests/tools/autotest.sh b/yosys/tests/tools/autotest.sh index d0b0a89d7..d6216244f 100755 --- a/yosys/tests/tools/autotest.sh +++ b/yosys/tests/tools/autotest.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash libs="" genvcd=false @@ -100,7 +100,7 @@ do echo -n "Test: $bn " fi - rm -f ${bn}.{err,log,sikp} + rm -f ${bn}.{err,log,skip} mkdir -p ${bn}.out rm -rf ${bn}.out/* diff --git a/yosys/tests/unit/Makefile b/yosys/tests/unit/Makefile new file mode 100644 index 000000000..9f1e5c99e --- /dev/null +++ b/yosys/tests/unit/Makefile @@ -0,0 +1,35 @@ +GTESTFLAG := -lgtest -lgtest_main +RPATH := -Wl,-rpath +EXTRAFLAGS := -lyosys -pthreads + +OBJTEST := objtest +BINTEST := bintest + +ALLTESTFILE := $(shell find -name '*Test.cc' -printf '%P ') +TESTDIRS := $(sort $(dir $(ALLTESTFILE))) +TESTS := $(addprefix $(BINTEST)/, $(basename $(ALLTESTFILE:%Test.cc=%Test.o))) + +# Prevent make from removing our .o files +.SECONDARY: + +all: prepare $(TESTS) run-tests + +$(BINTEST)/%: $(OBJTEST)/%.o + $(CXX) -L$(ROOTPATH) $(RPATH)=$(ROOTPATH) -o $@ $^ $(LDLIBS) \ + $(GTESTFLAG) $(EXTRAFLAGS) + +$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc + $(CXX) -o $@ -c -I$(ROOTPATH) $(CPPFLAGS) $(CXXFLAGS) $^ + +.PHONY: prepare run-tests clean + +run-tests: $(TESTS) + $(subst Test ,Test; ,$^) + +prepare: + mkdir -p $(addprefix $(BINTEST)/,$(TESTDIRS)) + mkdir -p $(addprefix $(OBJTEST)/,$(TESTDIRS)) + +clean: + rm -rf $(OBJTEST) + rm -rf $(BINTEST) diff --git a/yosys/tests/unit/kernel/logTest.cc b/yosys/tests/unit/kernel/logTest.cc new file mode 100644 index 000000000..62b4f3b98 --- /dev/null +++ b/yosys/tests/unit/kernel/logTest.cc @@ -0,0 +1,14 @@ +#include + +#include "kernel/yosys.h" +#include "kernel/log.h" + +YOSYS_NAMESPACE_BEGIN + +TEST(KernelLogTest, logvValidValues) +{ + //TODO: Implement log test + EXPECT_EQ(7, 7); +} + +YOSYS_NAMESPACE_END diff --git a/yosys/tests/unit/kernel/rtlilTest.cc b/yosys/tests/unit/kernel/rtlilTest.cc new file mode 100644 index 000000000..d9eeed555 --- /dev/null +++ b/yosys/tests/unit/kernel/rtlilTest.cc @@ -0,0 +1,14 @@ +#include + +#include "kernel/yosys.h" +#include "kernel/rtlil.h" + +YOSYS_NAMESPACE_BEGIN + +TEST(KernelRtlilTest, getReferenceValid) +{ + //TODO: Implement rtlil test + EXPECT_EQ(33, 33); +} + +YOSYS_NAMESPACE_END diff --git a/yosys/tests/various/reg_wire_error.sv b/yosys/tests/various/reg_wire_error.sv new file mode 100644 index 000000000..fe5ff3abd --- /dev/null +++ b/yosys/tests/various/reg_wire_error.sv @@ -0,0 +1,74 @@ +module sub_mod(input i_in, output o_out); +assign o_out = i_in; +endmodule + +module test(i_clk, i, i_reg, o_reg, o_wire, o_mr, o_mw, o_ml); +input i_clk; +input i; +input i_reg; +output o_reg; +output o_wire; +output o_mr, o_mw, o_ml; + +// Enable this to see how it doesn't fail on yosys although it should +//reg o_wire; +// Enable this instead of the above to see how logic can be mapped to a wire +logic o_wire; +// Enable this to see how it doesn't fail on yosys although it should +//reg i_reg; +// Disable this to see how it doesn't fail on yosys although it should +//reg o_reg; + +logic l_reg; + +// Enable this to tst if logic-turne-reg will catch assignments even if done before it turned into a reg +assign l_reg = !o_reg; +initial o_reg = 1'b0; +always @(posedge i_clk) +begin + o_reg <= !o_reg; + l_reg <= !o_reg; +end + +assign o_wire = !o_reg; +// Uncomment this to see how a logic already turned intoa reg can be freely assigned on yosys +assign l_reg = !o_reg; + +sub_mod sm_inst ( + .i_in(1'b1), + .o_out(o_reg) +); + +wire mw1[0:1]; +wire mw2[0:1]; +wire mw3[0:1]; +reg mr1[0:1]; +reg mr2[0:1]; +reg mr3[0:1]; +logic ml1[0:1]; +logic ml2[0:1]; +logic ml3[0:1]; + +assign o_mw = mw1[i]; +assign o_mr = mr1[i]; +assign o_ml = ml1[i]; + +assign mw1[1] = 1'b1; +//assign mr1[1] = 1'b1; +assign ml1[1] = 1'b1; +always @(posedge i_clk) +begin + mr2[0] = 1'b0; + mw2[0] = 1'b0; + ml2[0] = 1'b0; +end + +always @(posedge i_clk) +begin + mr3[0] <= 1'b0; + mw3[0] <= 1'b0; + ml3[0] <= 1'b0; +end + +endmodule + diff --git a/yosys/tests/various/reg_wire_error.ys b/yosys/tests/various/reg_wire_error.ys new file mode 100644 index 000000000..b9d03155d --- /dev/null +++ b/yosys/tests/various/reg_wire_error.ys @@ -0,0 +1 @@ +read_verilog -sv reg_wire_error.sv