aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Brewfile1
-rw-r--r--CHANGELOG130
-rw-r--r--COPYING2
-rw-r--r--CodingReadme1
-rw-r--r--Makefile16
-rw-r--r--README.md43
-rw-r--r--backends/aiger/xaiger.cc49
-rw-r--r--examples/gowin/demo.cst51
-rw-r--r--examples/gowin/demo.v6
-rw-r--r--examples/gowin/device.cfg16
-rw-r--r--examples/gowin/run.sh3
-rw-r--r--examples/mimas2/run_yosys.ys3
-rw-r--r--frontends/aiger/aigerparse.cc4
-rw-r--r--frontends/ast/ast.cc5
-rw-r--r--frontends/ast/simplify.cc5
-rw-r--r--kernel/yosys.cc2
-rw-r--r--misc/py_wrap_generator.py26
-rw-r--r--passes/cmds/select.cc4
-rw-r--r--passes/equiv/equiv_make.cc4
-rw-r--r--passes/opt/opt_expr.cc30
-rw-r--r--passes/opt/opt_share.cc60
-rw-r--r--passes/pmgen/.gitignore3
-rw-r--r--passes/pmgen/Makefile.inc35
-rw-r--r--passes/pmgen/README.md162
-rw-r--r--passes/pmgen/ice40_dsp.cc5
-rw-r--r--passes/pmgen/ice40_dsp.pmg1
-rw-r--r--passes/pmgen/peepopt_muldiv.pmg2
-rw-r--r--passes/pmgen/peepopt_shiftmul.pmg4
-rw-r--r--passes/pmgen/pmgen.py359
-rw-r--r--passes/pmgen/test_pmgen.cc385
-rw-r--r--passes/pmgen/test_pmgen.pmg189
-rw-r--r--passes/pmgen/xilinx_srl.cc258
-rw-r--r--passes/pmgen/xilinx_srl.pmg326
-rw-r--r--passes/proc/proc_clean.cc3
-rw-r--r--passes/sat/async2sync.cc37
-rw-r--r--passes/sat/sat.cc2
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc9.cc149
-rw-r--r--passes/techmap/attrmap.cc187
-rw-r--r--passes/techmap/clkbufmap.cc298
-rw-r--r--passes/techmap/dff2dffe.cc2
-rw-r--r--passes/techmap/iopadmap.cc58
-rw-r--r--passes/techmap/shregmap.cc181
-rw-r--r--passes/techmap/techmap.cc10
-rw-r--r--techlibs/anlogic/Makefile.inc2
-rw-r--r--techlibs/anlogic/anlogic_determine_init.cc72
-rw-r--r--techlibs/anlogic/anlogic_fixcarry.cc130
-rw-r--r--techlibs/anlogic/arith_map.v44
-rw-r--r--techlibs/anlogic/synth_anlogic.cc7
-rw-r--r--techlibs/common/synth.cc2
-rw-r--r--techlibs/ecp5/Makefile.inc5
-rw-r--r--techlibs/ecp5/brams_map.v10
-rw-r--r--techlibs/ecp5/cells_bb.v20
-rw-r--r--techlibs/ecp5/cells_ff.vh40
-rw-r--r--techlibs/ecp5/cells_io.vh14
-rw-r--r--techlibs/ecp5/cells_map.v153
-rw-r--r--techlibs/ecp5/cells_sim.v99
-rw-r--r--techlibs/ecp5/ecp5_gsr.cc135
-rw-r--r--techlibs/ecp5/synth_ecp5.cc2
-rw-r--r--techlibs/ecp5/tests/.gitignore1
-rw-r--r--techlibs/ecp5/tests/test_diamond_ffs.py82
-rw-r--r--techlibs/efinix/Makefile.inc10
-rw-r--r--techlibs/efinix/arith_map.v79
-rw-r--r--techlibs/efinix/bram.txt32
-rw-r--r--techlibs/efinix/brams_map.v65
-rw-r--r--techlibs/efinix/cells_map.v45
-rw-r--r--techlibs/efinix/cells_sim.v107
-rw-r--r--techlibs/efinix/efinix_fixcarry.cc122
-rw-r--r--techlibs/efinix/efinix_gbuf.cc119
-rw-r--r--techlibs/efinix/synth_efinix.cc219
-rw-r--r--techlibs/gowin/synth_gowin.cc1
-rw-r--r--techlibs/ice40/abc_hx.box8
-rw-r--r--techlibs/ice40/abc_lp.box8
-rw-r--r--techlibs/ice40/abc_u.box8
-rw-r--r--techlibs/ice40/cells_sim.v23
-rw-r--r--techlibs/ice40/ice40_opt.cc9
-rw-r--r--techlibs/xilinx/Makefile.inc4
-rw-r--r--techlibs/xilinx/cells_sim.v113
-rw-r--r--techlibs/xilinx/cells_xtra.py257
-rw-r--r--techlibs/xilinx/cells_xtra.sh147
-rw-r--r--techlibs/xilinx/cells_xtra.v289
-rw-r--r--techlibs/xilinx/lutrams.txt (renamed from techlibs/xilinx/drams.txt)0
-rw-r--r--techlibs/xilinx/lutrams_map.v (renamed from techlibs/xilinx/drams_map.v)0
-rw-r--r--techlibs/xilinx/synth_xilinx.cc120
-rw-r--r--techlibs/xilinx/xc6s_brams_bb.v4
-rw-r--r--techlibs/xilinx/xc7_brams_bb.v4
-rwxr-xr-xtests/fsm/run-test.sh2
-rw-r--r--tests/ice40/.gitignore4
-rw-r--r--tests/ice40/add_sub.v13
-rw-r--r--tests/ice40/add_sub.ys9
-rw-r--r--tests/ice40/adffs.v91
-rw-r--r--tests/ice40/adffs.ys12
-rw-r--r--tests/ice40/alu.v19
-rw-r--r--tests/ice40/alu.ys11
-rw-r--r--tests/ice40/counter.v17
-rw-r--r--tests/ice40/counter.ys11
-rw-r--r--tests/ice40/dffs.v37
-rw-r--r--tests/ice40/dffs.ys10
-rw-r--r--tests/ice40/div_mod.v13
-rw-r--r--tests/ice40/div_mod.ys9
-rw-r--r--tests/ice40/dpram.v23
-rw-r--r--tests/ice40/dpram.ys15
-rw-r--r--tests/ice40/fsm.v73
-rw-r--r--tests/ice40/fsm.ys13
-rw-r--r--tests/ice40/ice40_opt.ys26
-rw-r--r--tests/ice40/latches.v58
-rw-r--r--tests/ice40/latches.ys15
-rw-r--r--tests/ice40/logic.v18
-rw-r--r--tests/ice40/logic.ys7
-rw-r--r--tests/ice40/macc.v25
-rw-r--r--tests/ice40/macc.ys13
-rw-r--r--tests/ice40/memory.v21
-rw-r--r--tests/ice40/memory.ys15
-rw-r--r--tests/ice40/mul.v11
-rw-r--r--tests/ice40/mul.ys7
-rw-r--r--tests/ice40/mux.v100
-rw-r--r--tests/ice40/mux.ys8
-rw-r--r--tests/ice40/rom.v18
-rw-r--r--tests/ice40/rom.ys8
-rwxr-xr-xtests/ice40/run-test.sh20
-rw-r--r--tests/ice40/shifter.v22
-rw-r--r--tests/ice40/shifter.ys9
-rw-r--r--tests/ice40/tribuf.v23
-rw-r--r--tests/ice40/tribuf.ys9
-rw-r--r--tests/opt/opt_expr.ys70
-rw-r--r--tests/opt/opt_rmdff.ys10
-rw-r--r--tests/proc/.gitignore1
-rw-r--r--tests/proc/bug_1268.v23
-rw-r--r--tests/proc/bug_1268.ys5
-rwxr-xr-xtests/proc/run-test.sh6
-rw-r--r--tests/sat/initval.v8
-rw-r--r--tests/simple/realexpr.v11
-rwxr-xr-xtests/simple/run-test.sh2
-rw-r--r--tests/simple_abc9/.gitignore1
-rwxr-xr-xtests/simple_abc9/run-test.sh11
-rw-r--r--tests/techmap/.gitignore1
-rw-r--r--tests/techmap/clkbufmap.ys96
-rw-r--r--tests/techmap/recursive.v8
-rw-r--r--tests/techmap/recursive_map.v4
-rw-r--r--tests/techmap/recursive_runtest.sh3
-rwxr-xr-xtests/techmap/run-test.sh24
-rw-r--r--tests/various/.gitignore1
-rw-r--r--tests/various/mem2reg.ys14
-rw-r--r--tests/various/muxpack.ys30
-rw-r--r--tests/various/pmgen_reduce.ys21
-rwxr-xr-xtests/various/run-test.sh16
-rw-r--r--tests/various/shregmap.ys43
-rw-r--r--tests/xilinx/.gitignore3
-rw-r--r--tests/xilinx/pmgen_xilinx_srl.ys57
-rwxr-xr-xtests/xilinx/run-test.sh20
-rw-r--r--tests/xilinx/xilinx_srl.v40
-rw-r--r--tests/xilinx/xilinx_srl.ys67
152 files changed, 5927 insertions, 1213 deletions
diff --git a/Brewfile b/Brewfile
index 0c58ce161..4ffe50e86 100644
--- a/Brewfile
+++ b/Brewfile
@@ -6,3 +6,4 @@ brew "git"
brew "graphviz"
brew "pkg-config"
brew "python3"
+brew "tcl-tk"
diff --git a/CHANGELOG b/CHANGELOG
index 291c8d249..c29429295 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,43 +12,151 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "synth_xilinx -abc9" (experimental)
- Added "synth_ice40 -abc9" (experimental)
- Added "synth -abc9" (experimental)
- - Added "script -scriptwire
+ - Added "script -scriptwire"
+ - Added "synth_xilinx -nocarry"
+ - Added "synth_xilinx -nowidelut"
+ - Added "synth_ecp5 -nowidelut"
- "synth_xilinx" to now infer wide multiplexers (-widemux <min> to enable)
+ - Renamed labels/options in synth_ice40 (e.g. dram -> map_lutram; -nodram -> -nolutram)
+ - Renamed labels/options in synth_ecp5 (e.g. dram -> map_lutram; -nodram -> -nolutram)
+ - Renamed labels in synth_intel (e.g. bram -> map_bram)
+ - Renamed labels/options in synth_xilinx (e.g. dram -> map_lutram; -nodram -> -nolutram)
- Added automatic gzip decompression for frontends
- Added $_NMUX_ cell type
- Added automatic gzip compression (based on filename extension) for backends
- Improve attribute and parameter encoding in JSON to avoid ambiguities between
bit vectors and strings containing [01xz]*
+ - Added "clkbufmap" pass
+ - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental)
+ - Added "synth_xilinx -ise" (experimental)
+ - Added "synth_xilinx -iopad"
+ - "synth_xilinx" now automatically inserts clock buffers (add -noclkbuf to disable)
+ - Improvements in pmgen: subpattern and recursive matches
- Added "opt_share" pass, run as part of "opt -full"
- Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping
- Removed "ice40_unlut"
+ - Improvements in pmgen: slices, choices, define, generate
+ - Added "xilinx_srl" for Xilinx shift register extraction
+ - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl")
-Yosys 0.8 .. Yosys 0.8-dev
---------------------------
+Yosys 0.8 .. Yosys 0.9
+----------------------
* Various
- - Added $changed support to read_verilog
+ - Many bugfixes and small improvements
+ - Added support for SystemVerilog interfaces and modports
- Added "write_edif -attrprop"
- - Added "ice40_unlut" pass
- Added "opt_lut" pass
- - Added "synth_ice40 -relut"
- - Added "synth_ice40 -noabc"
- Added "gate2lut.v" techmap rule
- Added "rename -src"
- Added "equiv_opt" pass
- - Added "shregmap -tech xilinx"
+ - Added "flowmap" LUT mapping pass
+ - Added "rename -wire" to rename cells based on the wires they drive
+ - Added "bugpoint" for creating minimised testcases
+ - Added "write_edif -gndvccy"
+ - "write_verilog" to escape Verilog keywords
+ - Fixed sign handling of real constants
+ - "write_verilog" to write initial statement for initial flop state
+ - Added pmgen pattern matcher generator
+ - Fixed opt_rmdff handling of $_DFFSR_???_ and $_DLATCHSR_???_
+ - Added "setundef -params" to replace undefined cell parameters
+ - Renamed "yosys -D" to "yosys -U", added "yosys -D" to set Verilog defines
+ - Fixed handling of defparam when default_nettype is none
+ - Fixed "wreduce" flipflop handling
+ - Fixed FIRRTL to Verilog process instance subfield assignment
+ - Added "write_verilog -siminit"
+ - Several fixes and improvements for mem2reg memories
+ - Fixed handling of task output ports in clocked always blocks
+ - Improved handling of and-with-1 and or-with-0 in "opt_expr"
- Added "read_aiger" frontend
+ - Added "mutate" pass
+ - Added "hdlname" attribute
+ - Added "rename -output"
+ - Added "read_ilang -lib"
+ - Improved "proc" full_case detection and handling
+ - Added "whitebox" and "lib_whitebox" attributes
+ - Added "read_verilog -nowb", "flatten -wb" and "wbflip"
+ - Added Python bindings and support for Python plug-ins
+ - Added "pmux2shiftx"
+ - Added log_debug framework for reduced default verbosity
+ - Improved "opt_expr" and "opt_clean" handling of (partially) undriven and/or unused wires
+ - Added "peepopt" peephole optimisation pass using pmgen
+ - Added approximate support for SystemVerilog "var" keyword
+ - Added parsing of "specify" blocks into $specrule and $specify[23]
+ - Added support for attributes on parameters and localparams
+ - Added support for parsing attributes on port connections
+ - Added "wreduce -keepdc"
+ - Added support for optimising $dffe and $_DFFE_* cells in "opt_rmdff"
+ - Added Verilog wand/wor wire type support
+ - Added support for elaboration system tasks
- Added "muxcover -mux{4,8,16}=<cost>"
- Added "muxcover -dmux=<cost>"
- Added "muxcover -nopartial"
- Added "muxpack" pass
- Added "pmux2shiftx -norange"
+ - Added support for "~" in filename parsing
+ - Added "read_verilog -pwires" feature to turn parameters into wires
+ - Fixed sign extension of unsized constants with 'bx and 'bz MSB
+ - Fixed genvar to be a signed type
+ - Added support for attributes on case rules
+ - Added "upto" and "offset" to JSON frontend and backend
+ - Several liberty file parser improvements
+ - Fixed handling of more complex BRAM patterns
+ - Add "write_aiger -I -O -B"
+
+ * Formal Verification
+ - Added $changed support to read_verilog
+ - Added "read_verilog -noassert -noassume -assert-assumes"
+ - Added btor ops for $mul, $div, $mod and $concat
+ - Added yosys-smtbmc support for btor witnesses
+ - Added "supercover" pass
+ - Fixed $global_clock handling vs autowire
+ - Added $dffsr support to "async2sync"
+ - Added "fmcombine" pass
+ - Added memory init support in "write_btor"
+ - Added "cutpoint" pass
+ - Changed "ne" to "neq" in btor2 output
+ - Added support for SVA "final" keyword
+ - Added "fmcombine -initeq -anyeq"
+ - Added timescale and generated-by header to yosys-smtbmc vcd output
+ - Improved BTOR2 handling of undriven wires
+
+ * Verific support
+ - Enabled Verific flags vhdl_support_variable_slice and veri_elaborate_top_level_modules_having_interface_ports
+ - Improved support for asymmetric memories
+ - Added "verific -chparam"
+ - Fixed "verific -extnets" for more complex situations
+ - Added "read -verific" and "read -noverific"
+ - Added "hierarchy -chparam"
+
+ * New back-ends
+ - Added initial Anlogic support
+ - Added initial SmartFusion2 and IGLOO2 support
+
+ * ECP5 support
+ - Added "synth_ecp5 -nowidelut"
+ - Added BRAM inference support to "synth_ecp5"
+ - Added support for transforming Diamond IO and flipflop primitives
+
+ * iCE40 support
+ - Added "ice40_unlut" pass
+ - Added "synth_ice40 -relut"
+ - Added "synth_ice40 -noabc"
+ - Added "synth_ice40 -dffe_min_ce_use"
+ - Added DSP inference support using pmgen
+ - Added support for initialising BRAM primitives from a file
+ - Added iCE40 Ultra RGB LED driver cells
+
+ * Xilinx support
+ - Use "write_edif -pvector bra" for Xilinx EDIF files
+ - Fixes for VPR place and route support with "synth_xilinx"
+ - Added more cell simulation models
+ - Added "synth_xilinx -family"
+ - Added "stat -tech xilinx" to estimate logic cell usage
- Added "synth_xilinx -nocarry"
- Added "synth_xilinx -nowidelut"
- - Added "synth_ecp5 -nowidelut"
- "synth_xilinx" to now infer hard shift registers (-nosrl to disable)
- - Fixed sign extension of unsized constants with 'bx and 'bz MSB
-
+ - Added support for mapping RAM32X1D
Yosys 0.7 .. Yosys 0.8
----------------------
diff --git a/COPYING b/COPYING
index a121cdfe9..0839088c3 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
+Copyright (C) 2012 - 2019 Clifford Wolf <clifford@clifford.at>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/CodingReadme b/CodingReadme
index b64e79178..8212436e5 100644
--- a/CodingReadme
+++ b/CodingReadme
@@ -390,6 +390,7 @@ Finally run all tests with "make config-{clang,gcc,gcc-4.8}":
Release:
- set YOSYS_VER to x.y.z in Makefile
+ - remove "bumpversion" target from Makefile
- update version string in CHANGELOG
git commit -am "Yosys x.y.z"
diff --git a/Makefile b/Makefile
index 2efa5e72c..2cac80f0f 100644
--- a/Makefile
+++ b/Makefile
@@ -91,8 +91,10 @@ PLUGIN_LDFLAGS += -undefined dynamic_lookup
ifneq ($(shell which brew),)
BREW_PREFIX := $(shell brew --prefix)/opt
$(info $$BREW_PREFIX is [${BREW_PREFIX}])
+ifeq ($(ENABLE_PYOSYS),1)
CXXFLAGS += -I$(BREW_PREFIX)/boost/include/boost
LDFLAGS += -L$(BREW_PREFIX)/boost/lib
+endif
CXXFLAGS += -I$(BREW_PREFIX)/readline/include
LDFLAGS += -L$(BREW_PREFIX)/readline/lib
PKG_CONFIG_PATH := $(BREW_PREFIX)/libffi/lib/pkgconfig:$(PKG_CONFIG_PATH)
@@ -113,10 +115,13 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt
endif
-YOSYS_VER := 0.8+$(shell cd $(YOSYS_SRC) && test -e .git && { git log --author=clifford@clifford.at --oneline 4d4665b.. 2> /dev/null | wc -l; })
+YOSYS_VER := 0.9+36
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o
+bumpversion:
+ sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8a4c6e6.. | wc -l`/;" Makefile
+
# set 'ABCREV = default' to use abc/ as it is
#
# Note: If you do ABC development, make sure that 'abc' in this directory
@@ -487,6 +492,11 @@ define add_include_file
$(eval $(call add_share_file,$(dir share/include/$(1)),$(1)))
endef
+define add_extra_objs
+EXTRA_OBJS += $(1)
+.SECONDARY: $(1)
+endef
+
ifeq ($(PRETTY), 1)
P_STATUS = 0
P_OFFSET = 0
@@ -682,6 +692,7 @@ endif
test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/simple && bash run-test.sh $(SEEDOPT)
+ +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT)
+cd tests/hana && bash run-test.sh $(SEEDOPT)
+cd tests/asicworld && bash run-test.sh $(SEEDOPT)
# +cd tests/realmath && bash run-test.sh $(SEEDOPT)
@@ -694,10 +705,11 @@ test: $(TARGETS) $(EXTRA_TARGETS)
+cd tests/various && bash run-test.sh
+cd tests/sat && bash run-test.sh
+cd tests/svinterfaces && bash run-test.sh $(SEEDOPT)
+ +cd tests/proc && bash run-test.sh
+cd tests/opt && bash run-test.sh
+cd tests/aiger && bash run-test.sh $(ABCOPT)
+cd tests/arch && bash run-test.sh
- +cd tests/simple_abc9 && bash run-test.sh $(SEEDOPT)
+ +cd tests/ice40 && bash run-test.sh $(SEEDOPT)
@echo ""
@echo " Passed \"make test\"."
@echo ""
diff --git a/README.md b/README.md
index d9989eb29..e0a95a9d5 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
```
yosys -- Yosys Open SYnthesis Suite
-Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at>
+Copyright (C) 2012 - 2019 Clifford Wolf <clifford@clifford.at>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -69,11 +69,14 @@ prerequisites for building yosys:
graphviz xdot pkg-config python3 libboost-system-dev \
libboost-python-dev libboost-filesystem-dev zlib1g-dev
-Similarily, on Mac OS X MacPorts or Homebrew can be used to install dependencies:
+Similarily, on Mac OS X Homebrew can be used to install dependencies:
$ brew tap Homebrew/bundle && brew bundle
+
+or MacPorts:
+
$ sudo port install bison flex readline gawk libffi \
- git graphviz pkgconfig python36 boost zlib
+ git graphviz pkgconfig python36 boost zlib tcl
On FreeBSD use the following command to install all prerequisites:
@@ -327,7 +330,39 @@ Verilog Attributes and non-standard features
- The ``parameter`` and ``localparam`` attributes are used to mark wires
that represent module parameters or localparams (when the HDL front-end
- is run in -pwires mode).
+ is run in ``-pwires`` mode).
+
+- The ``clkbuf_driver`` attribute can be set on an output port of a blackbox
+ module to mark it as a clock buffer output, and thus prevent ``clkbufmap``
+ from inserting another clock buffer on a net driven by such output.
+
+- The ``clkbuf_sink`` attribute can be set on an input port of a module to
+ request clock buffer insertion by the ``clkbufmap`` pass.
+
+- The ``clkbuf_inhibit`` is the default attribute to set on a wire to prevent
+ automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
+ overridden by providing a custom selection to ``clkbufmap``.
+
+- The ``iopad_external_pin`` attribute on a blackbox module's port marks
+ it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
+ from inserting another pad cell on it.
+
+- The module attribute ``abc_box_id`` specifies a positive integer linking a
+ blackbox or whitebox definition to a corresponding entry in a `abc9`
+ box-file.
+
+- The port attribute ``abc_scc_break`` indicates a module input port that will
+ be treated as a primary output during `abc9` techmapping. Doing so eliminates
+ the possibility of a strongly-connected component (i.e. a combinatorial loop)
+ existing. Typically, this is specified for sequential inputs on otherwise
+ combinatorial boxes -- for example, applying ``abc_scc_break`` onto the `D`
+ port of a LUTRAM cell prevents `abc9` from interpreting any `Q` -> `D` paths
+ as a combinatorial loop.
+
+- The port attribute ``abc_carry`` marks the carry-in (if an input port) and
+ carry-out (if output port) ports of a box. This information is necessary for
+ `abc9` to preserve the integrity of carry-chains. Specifying this attribute
+ onto a bus port will affect only its most significant bit.
- In addition to the ``(* ... *)`` attribute syntax, Yosys supports
the non-standard ``{* ... *}`` attribute syntax to set default attributes
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 5e12e9a34..e1b84236d 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -261,11 +261,12 @@ struct XAigerWriter
}
}
else {
- bool cell_known = cell->known();
+ bool cell_known = inst_module || cell->known();
for (const auto &c : cell->connections()) {
if (c.second.is_fully_const()) continue;
- auto is_input = !cell_known || cell->input(c.first);
- auto is_output = !cell_known || cell->output(c.first);
+ auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
+ auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
+ auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
if (!is_input && !is_output)
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
@@ -326,7 +327,6 @@ struct XAigerWriter
#endif
log_assert(no_loops);
- pool<IdString> seen_boxes;
for (auto cell_name : toposort.sorted) {
RTLIL::Cell *cell = module->cell(cell_name);
log_assert(cell);
@@ -335,47 +335,6 @@ struct XAigerWriter
if (!box_module || !box_module->attributes.count("\\abc_box_id"))
continue;
- if (seen_boxes.insert(cell->type).second) {
- auto it = box_module->attributes.find("\\abc_carry");
- if (it != box_module->attributes.end()) {
- RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
- auto carry_in_out = it->second.decode_string();
- auto pos = carry_in_out.find(',');
- if (pos == std::string::npos)
- log_error("'abc_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type));
- auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos));
- carry_in = box_module->wire(carry_in_name);
- if (!carry_in || !carry_in->port_input)
- log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str());
-
- auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1));
- carry_out = box_module->wire(carry_out_name);
- if (!carry_out || !carry_out->port_output)
- log_error("'abc_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
-
- auto &ports = box_module->ports;
- for (auto jt = ports.begin(); jt != ports.end(); ) {
- RTLIL::Wire* w = box_module->wire(*jt);
- log_assert(w);
- if (w == carry_in || w == carry_out) {
- jt = ports.erase(jt);
- continue;
- }
- if (w->port_id > carry_in->port_id)
- --w->port_id;
- if (w->port_id > carry_out->port_id)
- --w->port_id;
- log_assert(w->port_input || w->port_output);
- log_assert(ports[w->port_id-1] == w->name);
- ++jt;
- }
- ports.push_back(carry_in->name);
- carry_in->port_id = ports.size();
- ports.push_back(carry_out->name);
- carry_out->port_id = ports.size();
- }
- }
-
// Fully pad all unused input connections of this box cell with S0
// Fully pad all undriven output connections of this box cell with anonymous wires
// NB: Assume box_module->ports are sorted alphabetically
diff --git a/examples/gowin/demo.cst b/examples/gowin/demo.cst
index 22d7eb668..c8f89dcf8 100644
--- a/examples/gowin/demo.cst
+++ b/examples/gowin/demo.cst
@@ -1,41 +1,10 @@
-// 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;
+IO_LOC "clk" 35;
+//IO_LOC "rst_n" 77;
+IO_LOC "leds[0]" 79;
+IO_LOC "leds[1]" 80;
+IO_LOC "leds[2]" 81;
+IO_LOC "leds[3]" 82;
+IO_LOC "leds[4]" 83;
+IO_LOC "leds[5]" 84;
+IO_LOC "leds[6]" 85;
+IO_LOC "leds[7]" 86; \ No newline at end of file
diff --git a/examples/gowin/demo.v b/examples/gowin/demo.v
index 6ea108384..f5c001893 100644
--- a/examples/gowin/demo.v
+++ b/examples/gowin/demo.v
@@ -1,9 +1,7 @@
module demo (
input clk,
- input [3:0] sw,
- output [15:0] leds,
- output [7:0] seg7dig,
- output [3:0] seg7sel
+ output [15:0] leds
+ //,output unused
);
localparam PRESCALE = 20;
reg [PRESCALE+3:0] counter = 0;
diff --git a/examples/gowin/device.cfg b/examples/gowin/device.cfg
new file mode 100644
index 000000000..f6ab82159
--- /dev/null
+++ b/examples/gowin/device.cfg
@@ -0,0 +1,16 @@
+set JTAG regular_io = false
+set SSPI regular_io = false
+set MSPI regular_io = false
+set READY regular_io = false
+set DONE regular_io = false
+set RECONFIG_N regular_io = false
+set MODE regular_io = false
+set CRC_check = true
+set compress = false
+set encryption = false
+set security_bit_enable = true
+set bsram_init_fuse_print = true
+set download_speed = 250/100
+set spi_flash_address = 0x00FFF000
+set format = txt
+set background_programming = false
diff --git a/examples/gowin/run.sh b/examples/gowin/run.sh
index 33a7b5c37..cd260101e 100644
--- a/examples/gowin/run.sh
+++ b/examples/gowin/run.sh
@@ -1,8 +1,7 @@
#!/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
+$GOWIN_HOME/bin/gowin -d demo_syn.v -cst demo.cst -sdc demo.sdc -p GW1NR-9-QFN88-6 -pn GW1NR-LV9QN88C6/I5 -cfg device.cfg -bit -tr -ph -timing -gpa -rpt -warning_all
# post place&route simulation (icarus verilog)
if false; then
diff --git a/examples/mimas2/run_yosys.ys b/examples/mimas2/run_yosys.ys
index b3204b1ca..b48877811 100644
--- a/examples/mimas2/run_yosys.ys
+++ b/examples/mimas2/run_yosys.ys
@@ -1,4 +1,3 @@
read_verilog example.v
-synth_xilinx -top example -family xc6s
-iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I
+synth_xilinx -top example -family xc6s -ise
write_edif -pvector bra example.edif
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index ac9e31f70..2e1fb8fad 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -451,7 +451,7 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("poNum = %u\n", poNum);
uint32_t boxNum = parse_xaiger_literal(f);
- log_debug("boxNum = %u\n", poNum);
+ log_debug("boxNum = %u\n", boxNum);
for (unsigned i = 0; i < boxNum; i++) {
f.ignore(2*sizeof(uint32_t));
uint32_t boxUniqueId = parse_xaiger_literal(f);
@@ -974,7 +974,7 @@ void AigerReader::post_process()
// operate (and run checks on) this one module
RTLIL::Design *mapped_design = new RTLIL::Design;
mapped_design->add(module);
- Pass::call(mapped_design, "clean");
+ Pass::call(mapped_design, "clean -purge");
mapped_design->modules_.erase(module->name);
delete mapped_design;
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 0d6626b19..82283fb5b 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -1502,7 +1502,10 @@ std::string AstModule::derive_common(RTLIL::Design *design, dict<RTLIL::IdString
rewrite_parameter:
para_info += stringf("%s=%s", child->str.c_str(), log_signal(RTLIL::SigSpec(parameters[para_id])));
delete child->children.at(0);
- if ((parameters[para_id].flags & RTLIL::CONST_FLAG_STRING) != 0)
+ if ((parameters[para_id].flags & RTLIL::CONST_FLAG_REAL) != 0) {
+ child->children[0] = new AstNode(AST_REALVALUE);
+ child->children[0]->realvalue = std::stod(parameters[para_id].decode_string());
+ } else 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);
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 54b9efaad..86dd80c65 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -150,6 +150,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
reg->str = stringf("%s[%d]", node->str.c_str(), i);
reg->is_reg = true;
reg->is_signed = node->is_signed;
+ for (auto &it : node->attributes)
+ if (it.first != ID(mem2reg))
+ reg->attributes.emplace(it.first, it.second->clone());
+ reg->filename = node->filename;
+ reg->linenum = node->linenum;
children.push_back(reg);
while (reg->simplify(true, false, false, 1, -1, false, false)) { }
}
diff --git a/kernel/yosys.cc b/kernel/yosys.cc
index 747f2d739..5018a4888 100644
--- a/kernel/yosys.cc
+++ b/kernel/yosys.cc
@@ -129,7 +129,7 @@ void yosys_banner()
log(" | |\n");
log(" | yosys -- Yosys Open SYnthesis Suite |\n");
log(" | |\n");
- log(" | Copyright (C) 2012 - 2018 Clifford Wolf <clifford@clifford.at> |\n");
+ log(" | Copyright (C) 2012 - 2019 Clifford Wolf <clifford@clifford.at> |\n");
log(" | |\n");
log(" | Permission to use, copy, modify, and/or distribute this software for any |\n");
log(" | purpose with or without fee is hereby granted, provided that the above |\n");
diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py
index 66d661fa1..2bf364470 100644
--- a/misc/py_wrap_generator.py
+++ b/misc/py_wrap_generator.py
@@ -508,23 +508,17 @@ class TupleTranslator(PythonDictTranslator):
#Generate c++ code to translate to a boost::python::tuple
@classmethod
def translate_cpp(c, varname, types, prefix, ref):
- text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + varname + ".first, " + varname + ".second);"
- return text
- tmp_name = "tmp_" + str(Translator.tmp_cntr)
- Translator.tmp_cntr = Translator.tmp_cntr + 1
- if ref:
- text += prefix + "for(auto " + tmp_name + " : *" + varname + ")"
+ # if the tuple is a pair of SigSpecs (aka SigSig), then we need
+ # to call get_py_obj() on each item in the tuple
+ if types[0].name in classnames:
+ first_var = types[0].name + "::get_py_obj(" + varname + ".first)"
else:
- text += prefix + "for(auto " + tmp_name + " : " + varname + ")"
- text += prefix + "{"
- if types[0].name.split(" ")[-1] in primitive_types or types[0].name in enum_names:
- text += prefix + "\t" + varname + "___tmp.append(" + tmp_name + ");"
- elif types[0].name in known_containers:
- text += known_containers[types[0].name].translate_cpp(tmp_name, types[0].cont.args, prefix + "\t", types[1].attr_type == attr_types.star)
- text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "___tmp);"
- elif types[0].name in classnames:
- text += prefix + "\t" + varname + "___tmp.append(" + types[0].name + "::get_py_obj(" + tmp_name + "));"
- text += prefix + "}"
+ first_var = varname + ".first"
+ if types[1].name in classnames:
+ second_var = types[1].name + "::get_py_obj(" + varname + ".second)"
+ else:
+ second_var = varname + ".second"
+ text = prefix + TupleTranslator.typename + " " + varname + "___tmp = boost::python::make_tuple(" + first_var + ", " + second_var + ");"
return text
#Associate the Translators with their c++ type
diff --git a/passes/cmds/select.cc b/passes/cmds/select.cc
index 59d10a1b8..0f1f05ccb 100644
--- a/passes/cmds/select.cc
+++ b/passes/cmds/select.cc
@@ -664,7 +664,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
} else
if (arg == "%D") {
if (work_stack.size() < 2)
- log_cmd_error("Must have at least two elements on the stack for operator %%d.\n");
+ log_cmd_error("Must have at least two elements on the stack for operator %%D.\n");
select_op_diff(design, work_stack[work_stack.size()-1], work_stack[work_stack.size()-2]);
work_stack[work_stack.size()-2] = work_stack[work_stack.size()-1];
work_stack.pop_back();
@@ -693,7 +693,7 @@ static void select_stmt(RTLIL::Design *design, std::string arg)
} else
if (arg == "%C") {
if (work_stack.size() < 1)
- log_cmd_error("Must have at least one element on the stack for operator %%M.\n");
+ log_cmd_error("Must have at least one element on the stack for operator %%C.\n");
select_op_module_to_cells(design, work_stack[work_stack.size()-1]);
} else
if (arg == "%c") {
diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc
index dbd8682e6..4855ce29e 100644
--- a/passes/equiv/equiv_make.cc
+++ b/passes/equiv/equiv_make.cc
@@ -532,10 +532,10 @@ struct EquivMakePass : public Pass {
log_cmd_error("Equiv module %s already exists.\n", args[argidx+2].c_str());
if (worker.gold_mod->has_memories() || worker.gold_mod->has_processes())
- log_cmd_error("Gold module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+ log_cmd_error("Gold module contains memories or processes. Run 'memory' or 'proc' respectively.\n");
if (worker.gate_mod->has_memories() || worker.gate_mod->has_processes())
- log_cmd_error("Gate module contains memories or procresses. Run 'memory' or 'proc' respectively.\n");
+ log_cmd_error("Gate module contains memories or processes. Run 'memory' or 'proc' respectively.\n");
worker.read_blacklists();
worker.read_encfiles();
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 858b3560c..00d7d6063 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -369,7 +369,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
for (auto cell : module->cells())
if (design->selected(module, cell) && cell->type[0] == '$') {
if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) &&
- cell->getPort(ID::A).size() == 1 && cell->getPort(ID::Y).size() == 1)
+ GetSize(cell->getPort(ID::A)) == 1 && GetSize(cell->getPort(ID::Y)) == 1)
invert_map[assign_map(cell->getPort(ID::Y))] = assign_map(cell->getPort(ID::A));
if (cell->type.in(ID($mux), ID($_MUX_)) &&
cell->getPort(ID::A) == SigSpec(State::S1) && cell->getPort(ID::B) == SigSpec(State::S0))
@@ -740,12 +740,34 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
else
- replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::SigSpec(RTLIL::State::Sx, cell->getPort(ID::Y).size()));
+ replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::SigSpec(RTLIL::State::Sx, GetSize(cell->getPort(ID::Y))));
goto next_cell;
}
}
- if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && cell->getPort(ID::Y).size() == 1 &&
+ if (cell->type.in(ID($shiftx), ID($shift))) {
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
+ int width;
+ bool trim_x = cell->type == ID($shiftx) || !keepdc;
+ bool trim_0 = cell->type == ID($shift);
+ for (width = GetSize(sig_a); width > 1; width--) {
+ if ((trim_x && sig_a[width-1] == State::Sx) ||
+ (trim_0 && sig_a[width-1] == State::S0))
+ continue;
+ break;
+ }
+
+ if (width < GetSize(sig_a)) {
+ cover_list("opt.opt_expr.trim", "$shiftx", "$shift", cell->type.str());
+ sig_a.remove(width, GetSize(sig_a)-width);
+ cell->setPort(ID::A, sig_a);
+ cell->setParam(ID(A_WIDTH), width);
+ did_something = true;
+ goto next_cell;
+ }
+ }
+
+ if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && GetSize(cell->getPort(ID::Y)) == 1 &&
invert_map.count(assign_map(cell->getPort(ID::A))) != 0) {
cover_list("opt.opt_expr.invert.double", "$_NOT_", "$not", "$logic_not", cell->type.str());
replace_cell(assign_map, module, cell, "double_invert", ID::Y, invert_map.at(assign_map(cell->getPort(ID::A))));
@@ -1142,7 +1164,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (mux_undef && cell->type.in(ID($mux), ID($pmux))) {
RTLIL::SigSpec new_a, new_b, new_s;
- int width = cell->getPort(ID::A).size();
+ int width = GetSize(cell->getPort(ID::A));
if ((cell->getPort(ID::A).is_fully_undef() && cell->getPort(ID::B).is_fully_undef()) ||
cell->getPort(ID(S)).is_fully_undef()) {
cover_list("opt.opt_expr.mux_undef", "$mux", "$pmux", cell->type.str());
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index 734cbcf81..c53fb3113 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -127,7 +127,7 @@ bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_name)
{
- if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($concat), SHIFT_OPS) && port_name == ID(B))
+ if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($concat), SHIFT_OPS) && port_name == ID::B)
return port_name;
return "";
@@ -135,9 +135,9 @@ RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_na
RTLIL::SigSpec decode_port_sign(RTLIL::Cell *cell, RTLIL::IdString port_name) {
- if (cell->type == ID($alu) && port_name == ID(B))
+ if (cell->type == ID($alu) && port_name == ID::B)
return cell->getPort(ID(BI));
- else if (cell->type == ID($sub) && port_name == ID(B))
+ else if (cell->type == ID($sub) && port_name == ID::B)
return RTLIL::Const(1, 1);
return RTLIL::Const(0, 1);
@@ -173,9 +173,9 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
for (const auto& p : ports) {
auto op = p.op;
- RTLIL::IdString muxed_port_name = ID(A);
- if (decode_port(op, ID(A), &assign_map) == operand)
- muxed_port_name = ID(B);
+ RTLIL::IdString muxed_port_name = ID::A;
+ if (decode_port(op, ID::A, &assign_map) == operand)
+ muxed_port_name = ID::B;
auto operand = decode_port(op, muxed_port_name, &assign_map);
if (operand.sig.size() > max_width)
@@ -204,9 +204,9 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
if (muxed_op.sign != muxed_operands[0].sign)
muxed_op = ExtSigSpec(module->Neg(NEW_ID, muxed_op.sig, muxed_op.is_signed));
- RTLIL::SigSpec mux_y = mux->getPort(ID(Y));
- RTLIL::SigSpec mux_a = mux->getPort(ID(A));
- RTLIL::SigSpec mux_b = mux->getPort(ID(B));
+ RTLIL::SigSpec mux_y = mux->getPort(ID::Y);
+ RTLIL::SigSpec mux_a = mux->getPort(ID::A);
+ RTLIL::SigSpec mux_b = mux->getPort(ID::B);
RTLIL::SigSpec mux_s = mux->getPort(ID(S));
RTLIL::SigSpec shared_pmux_a = RTLIL::Const(RTLIL::State::Sx, max_width);
@@ -216,24 +216,24 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
int conn_width = ports[0].sig.size();
int conn_offset = ports[0].mux_port_offset;
- shared_op->setPort(ID(Y), shared_op->getPort(ID(Y)).extract(0, conn_width));
+ shared_op->setPort(ID::Y, shared_op->getPort(ID::Y).extract(0, conn_width));
if (mux->type == ID($pmux)) {
shared_pmux_s = RTLIL::SigSpec();
for (const auto &p : ports) {
shared_pmux_s.append(mux_s[p.mux_port_id]);
- mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID(Y)));
+ mux_b.replace(p.mux_port_id * mux_a.size() + conn_offset, shared_op->getPort(ID::Y));
}
} else {
shared_pmux_s = RTLIL::SigSpec{mux_s, module->Not(NEW_ID, mux_s)};
- mux_a.replace(conn_offset, shared_op->getPort(ID(Y)));
- mux_b.replace(conn_offset, shared_op->getPort(ID(Y)));
+ mux_a.replace(conn_offset, shared_op->getPort(ID::Y));
+ mux_b.replace(conn_offset, shared_op->getPort(ID::Y));
}
- mux->setPort(ID(A), mux_a);
- mux->setPort(ID(B), mux_b);
- mux->setPort(ID(Y), mux_y);
+ mux->setPort(ID::A, mux_a);
+ mux->setPort(ID::B, mux_b);
+ mux->setPort(ID::Y, mux_y);
mux->setPort(ID(S), mux_s);
for (const auto &op : muxed_operands)
@@ -251,11 +251,11 @@ void merge_operators(RTLIL::Module *module, RTLIL::Cell *mux, const std::vector<
shared_op->setParam(ID(Y_WIDTH), conn_width);
- if (decode_port(shared_op, ID(A), &assign_map) == operand) {
- shared_op->setPort(ID(B), mux_to_oper);
+ if (decode_port(shared_op, ID::A, &assign_map) == operand) {
+ shared_op->setPort(ID::B, mux_to_oper);
shared_op->setParam(ID(B_WIDTH), max_width);
} else {
- shared_op->setPort(ID(A), mux_to_oper);
+ shared_op->setPort(ID::A, mux_to_oper);
shared_op->setParam(ID(A_WIDTH), max_width);
}
}
@@ -286,9 +286,9 @@ void check_muxed_operands(std::vector<const OpMuxConn *> &ports, const ExtSigSpe
auto p = *it;
auto op = p->op;
- RTLIL::IdString muxed_port_name = ID(A);
- if (decode_port(op, ID(A), &assign_map) == shared_operand) {
- muxed_port_name = ID(B);
+ RTLIL::IdString muxed_port_name = ID::A;
+ if (decode_port(op, ID::A, &assign_map) == shared_operand) {
+ muxed_port_name = ID::B;
}
auto operand = decode_port(op, muxed_port_name, &assign_map);
@@ -315,7 +315,7 @@ ExtSigSpec find_shared_operand(const OpMuxConn* seed, std::vector<const OpMuxCon
auto op_a = seed->op;
- for (RTLIL::IdString port_name : {ID(A), ID(B)}) {
+ for (RTLIL::IdString port_name : {ID::A, ID::B}) {
oper = decode_port(op_a, port_name, &assign_map);
auto operand_users = operand_to_users.at(oper);
@@ -355,7 +355,7 @@ dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, d
std::function<void(RTLIL::SigBit)> remove_outsig_from_aux_bit = [&](RTLIL::SigBit auxbit) {
auto aux_outsig = op_aux_to_outsig.at(auxbit);
auto op = outsig_to_operator.at(aux_outsig);
- auto op_outsig = assign_map(op->getPort(ID(Y)));
+ auto op_outsig = assign_map(op->getPort(ID::Y));
remove_outsig(op_outsig);
for (auto aux_outbit : aux_outsig)
@@ -367,11 +367,11 @@ dict<RTLIL::SigSpec, OpMuxConn> find_valid_op_mux_conns(RTLIL::Module *module, d
int mux_port_size;
if (mux->type.in(ID($mux), ID($_MUX_))) {
- mux_port_size = mux->getPort(ID(A)).size();
- sig = RTLIL::SigSpec{mux->getPort(ID(B)), mux->getPort(ID(A))};
+ mux_port_size = mux->getPort(ID::A).size();
+ sig = RTLIL::SigSpec{mux->getPort(ID::B), mux->getPort(ID::A)};
} else {
- mux_port_size = mux->getPort(ID(A)).size();
- sig = mux->getPort(ID(B));
+ mux_port_size = mux->getPort(ID::A).size();
+ sig = mux->getPort(ID::B);
}
auto mux_insig = assign_map(sig);
@@ -510,12 +510,12 @@ struct OptSharePass : public Pass {
}
}
- auto mux_insig = assign_map(cell->getPort(ID(Y)));
+ auto mux_insig = assign_map(cell->getPort(ID::Y));
outsig_to_operator[mux_insig] = cell;
for (auto outbit : mux_insig)
op_outbit_to_outsig[outbit] = mux_insig;
- for (RTLIL::IdString port_name : {ID(A), ID(B)}) {
+ for (RTLIL::IdString port_name : {ID::A, ID::B}) {
auto op_insig = decode_port(cell, port_name, &assign_map);
op_insigs.push_back(op_insig);
operand_to_users[op_insig].insert(cell);
diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore
index 0ad36ea2c..6b319b8c3 100644
--- a/passes/pmgen/.gitignore
+++ b/passes/pmgen/.gitignore
@@ -1,2 +1 @@
-/ice40_dsp_pm.h
-/peepopt_pm.h
+/*_pm.h \ No newline at end of file
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc
index c03606152..4989c582a 100644
--- a/passes/pmgen/Makefile.inc
+++ b/passes/pmgen/Makefile.inc
@@ -1,33 +1,38 @@
-OBJS += passes/pmgen/ice40_dsp.o
-OBJS += passes/pmgen/ice40_wrapcarry.o
-OBJS += passes/pmgen/peepopt.o
+%_pm.h: passes/pmgen/pmgen.py %.pmg
+ $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
# --------------------------------------
-passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
-EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h
-.SECONDARY: passes/pmgen/ice40_dsp_pm.h
+OBJS += passes/pmgen/test_pmgen.o
+passes/pmgen/test_pmgen.o: passes/pmgen/test_pmgen_pm.h passes/pmgen/ice40_dsp_pm.h passes/pmgen/peepopt_pm.h passes/pmgen/xilinx_srl_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/test_pmgen_pm.h))
-passes/pmgen/ice40_dsp_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_dsp.pmg
- $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_dsp $(filter-out $<,$^)
+# --------------------------------------
+
+OBJS += passes/pmgen/ice40_dsp.o
+passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/ice40_dsp_pm.h))
# --------------------------------------
+OBJS += passes/pmgen/ice40_wrapcarry.o
passes/pmgen/ice40_wrapcarry.o: passes/pmgen/ice40_wrapcarry_pm.h
-EXTRA_OBJS += passes/pmgen/ice40_wrapcarry_pm.h
-.SECONDARY: passes/pmgen/ice40_wrapcarry_pm.h
-
-passes/pmgen/ice40_wrapcarry_pm.h: passes/pmgen/pmgen.py passes/pmgen/ice40_wrapcarry.pmg
- $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p ice40_wrapcarry $(filter-out $<,$^)
+$(eval $(call add_extra_objs,passes/pmgen/ice40_wrapcarry_pm.h))
# --------------------------------------
+OBJS += passes/pmgen/peepopt.o
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h
-EXTRA_OBJS += passes/pmgen/peepopt_pm.h
-.SECONDARY: passes/pmgen/peepopt_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul.pmg
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
+
+# --------------------------------------
+
+OBJS += passes/pmgen/xilinx_srl.o
+passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h
+$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h))
diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md
index 2f0b1fd5a..0856c9ba3 100644
--- a/passes/pmgen/README.md
+++ b/passes/pmgen/README.md
@@ -45,9 +45,9 @@ of type `foobar_pm::state_<pattern_name>_t`.)
Similarly the `.pmg` file declares user data variables that become members of
`.ud_<pattern_name>`, a struct of type `foobar_pm::udata_<pattern_name>_t`.
-There are four versions of the `run_<pattern_name>()` method: Without callback,
-callback without arguments, callback with reference to `pm`, and callback with
-reference to `pm.st_<pattern_name>`.
+There are three versions of the `run_<pattern_name>()` method: Without callback,
+callback without arguments, and callback with reference to `pm`. All versions
+of the `run_<pattern_name>()` method return the number of found matches.
The .pmg File Format
@@ -118,8 +118,8 @@ write matchers:
connected to any of the given signal bits, plus one if any of the signal
bits is also a primary input or primary output.
-- In `code..endcode` blocks there exist `accept`, `reject`, and `branch`
- statements.
+- In `code..endcode` blocks there exist `accept`, `reject`, `branch`,
+ `finish`, and `subpattern` statements.
- In `index` statements there is a special `===` operator for the index
lookup.
@@ -175,6 +175,48 @@ explore the case where `mul` is set to `nullptr`. Without the `optional`
statement a match may only be assigned nullptr when one of the `if` expressions
evaluates to `false`.
+The `semioptional` statement marks matches that must match if at least one
+matching cell exists, but if no matching cell exists it is set to `nullptr`.
+
+Slices and choices
+------------------
+
+Cell matches can contain "slices" and "choices". Slices can be used to
+create matches for different sections of a cell. For example:
+
+ state <int> pmux_slice
+
+ match pmux
+ select pmux->type == $pmux
+ slice idx GetSize(port(pmux, \S))
+ index <SigBit> port(pmux, \S)[idx] === port(eq, \Y)
+ set pmux_slice idx
+ endmatch
+
+The first argument to `slice` is the local variable name used to identify the
+slice. The second argument is the number of slices that should be created for
+this cell. The `set` statement can be used to copy that index into a state
+variable so that later matches and/or code blocks can refer to it.
+
+A similar mechanism is "choices", where a list of options is given as
+second argument, and the matcher will iterate over those options:
+
+ state <SigSpec> foo bar
+ state <IdString> eq_ab eq_ba
+
+ match eq
+ select eq->type == $eq
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(eq, AB) === foo
+ index <SigSpec> port(eq, BA) === bar
+ set eq_ab AB
+ set eq_ba BA
+ generate
+
+Notice how `define` can be used to define additional local variables similar
+to the loop variables defined by `slice` and `choice`.
+
Additional code
---------------
@@ -232,5 +274,111 @@ But in some cases it is more natural to utilize the implicit branch statement:
portAB = \B;
endcode
-There is an implicit `code..endcode` block at the end of each `.pmg` file
-that just accepts everything that gets all the way there.
+There is an implicit `code..endcode` block at the end of each (sub)pattern
+that just rejects.
+
+A `code..finally..endcode` block executes the code after `finally` during
+back-tracking. This is useful for maintaining user data state or printing
+debug messages. For example:
+
+ udata <vector<Cell*>> stack
+
+ code
+ stack.push_back(addAB);
+ ...
+ finally
+ stack.pop_back();
+ endcode
+
+`accept` and `finish` statements can be used inside the `finally` section,
+but not `reject`, `branch`, or `subpattern`.
+
+Declaring a subpattern
+----------------------
+
+A subpattern starts with a line containing the `subpattern` keyword followed
+by the name of the subpattern. Subpatterns can be called from a `code` block
+using a `subpattern(<subpattern_name>);` C statement.
+
+Arguments may be passed to subpattern via state variables. The `subpattern`
+line must be followed by a `arg <arg1> <arg2> ...` line that lists the
+state variables used to pass arguments.
+
+ state <IdString> foobar_type
+ state <bool> foobar_state
+
+ code foobar_type foobar_state
+ foobar_state = false;
+ foobar_type = $add;
+ subpattern(foo);
+ foobar_type = $sub;
+ subpattern(bar);
+ endcode
+
+ subpattern foo
+ arg foobar_type foobar_state
+
+ match addsub
+ index <IdString> addsub->type === foobar_type
+ ...
+ endmatch
+
+ code
+ if (foobar_state) {
+ subpattern(tail);
+ } else {
+ foobar_state = true;
+ subpattern(bar);
+ }
+ endcode
+
+ subpattern bar
+ arg foobar_type foobar_state
+
+ match addsub
+ index <IdString> addsub->type === foobar_type
+ ...
+ endmatch
+
+ code
+ if (foobar_state) {
+ subpattern(tail);
+ } else {
+ foobar_state = true;
+ subpattern(foo);
+ }
+ endcode
+
+ subpattern tail
+ ...
+
+Subpatterns cann be called recursively.
+
+If a `subpattern` statement is preceded by a `fallthrough` statement, this is
+equivalent to calling the subpattern at the end of the preceding block.
+
+Generate Blocks
+---------------
+
+Match blocks may contain an optional `generate` section that is used for automatic
+test-case generation. For example:
+
+ match mul
+ ...
+ generate 10 0
+ SigSpec Y = port(ff, \D);
+ SigSpec A = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
+ SigSpec B = module->addWire(NEW_ID, GetSize(Y) - rng(GetSize(Y)/2));
+ module->addMul(NEW_ID, A, B, Y, rng(2));
+ endmatch
+
+The expression `rng(n)` returns a non-negative integer less than `n`.
+
+The first argument to `generate` is the chance of this generate block being
+executed when the match block did not match anything, in percent.
+
+The second argument to `generate` is the chance of this generate block being
+executed when the match block did match something, in percent.
+
+The special statement `finish` can be used within generate blocks to terminate
+the current pattern matcher run.
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index 39d033a04..16bfe537f 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -64,11 +64,6 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
bool mul_signed = st.mul->getParam("\\A_SIGNED").as_bool();
- if (mul_signed) {
- log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n");
- return;
- }
-
log(" replacing $mul with SB_MAC16 cell.\n");
Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 1f3590d4e..7003092bb 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -159,4 +159,5 @@ code clock clock_pol clock_vld
clock_pol = cp;
clock_vld = true;
}
+ accept;
endcode
diff --git a/passes/pmgen/peepopt_muldiv.pmg b/passes/pmgen/peepopt_muldiv.pmg
index 06c275834..7cad759d0 100644
--- a/passes/pmgen/peepopt_muldiv.pmg
+++ b/passes/pmgen/peepopt_muldiv.pmg
@@ -32,5 +32,5 @@ code
log("muldiv pattern in %s: mul=%s, div=%s\n", log_id(module), log_id(mul), log_id(div));
module->connect(div_y, val_y);
autoremove(div);
- reject;
+ accept;
endcode
diff --git a/passes/pmgen/peepopt_shiftmul.pmg b/passes/pmgen/peepopt_shiftmul.pmg
index 6adab4e5f..d4748ae19 100644
--- a/passes/pmgen/peepopt_shiftmul.pmg
+++ b/passes/pmgen/peepopt_shiftmul.pmg
@@ -34,6 +34,7 @@ match mul
endmatch
code
+{
IdString const_factor_port = port(mul, \A).is_fully_const() ? \A : \B;
IdString const_factor_signed = const_factor_port == \A ? \A_SIGNED : \B_SIGNED;
Const const_factor_cnst = port(mul, const_factor_port).as_const();
@@ -90,5 +91,6 @@ code
shift->setParam(\B_WIDTH, GetSize(new_b));
blacklist(shift);
- reject;
+ accept;
+}
endcode
diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py
index 81052afce..573722d68 100644
--- a/passes/pmgen/pmgen.py
+++ b/passes/pmgen/pmgen.py
@@ -38,7 +38,10 @@ for a in args:
assert prefix is not None
current_pattern = None
+current_subpattern = None
patterns = dict()
+subpatterns = dict()
+subpattern_args = dict()
state_types = dict()
udata_types = dict()
blocks = list()
@@ -104,9 +107,12 @@ def rewrite_cpp(s):
return "".join(t)
-def process_pmgfile(f):
+def process_pmgfile(f, filename):
+ linenr = 0
global current_pattern
+ global current_subpattern
while True:
+ linenr += 1
line = f.readline()
if line == "": break
line = line.strip()
@@ -119,19 +125,52 @@ def process_pmgfile(f):
if current_pattern is not None:
block = dict()
block["type"] = "final"
- block["pattern"] = current_pattern
+ block["pattern"] = (current_pattern, current_subpattern)
blocks.append(block)
line = line.split()
assert len(line) == 2
assert line[1] not in patterns
current_pattern = line[1]
+ current_subpattern = ""
patterns[current_pattern] = len(blocks)
+ subpatterns[(current_pattern, current_subpattern)] = len(blocks)
+ subpattern_args[(current_pattern, current_subpattern)] = list()
state_types[current_pattern] = dict()
udata_types[current_pattern] = dict()
continue
assert current_pattern is not None
+ if cmd == "fallthrough":
+ block = dict()
+ block["type"] = "fallthrough"
+ blocks.append(block)
+ line = line.split()
+ assert len(line) == 1
+ continue
+
+ if cmd == "subpattern":
+ if len(blocks) == 0 or blocks[-1]["type"] != "fallthrough":
+ block = dict()
+ block["type"] = "final"
+ block["pattern"] = (current_pattern, current_subpattern)
+ blocks.append(block)
+ elif len(blocks) and blocks[-1]["type"] == "fallthrough":
+ del blocks[-1]
+ line = line.split()
+ assert len(line) == 2
+ current_subpattern = line[1]
+ subpattern_args[(current_pattern, current_subpattern)] = list()
+ assert (current_pattern, current_subpattern) not in subpatterns
+ subpatterns[(current_pattern, current_subpattern)] = len(blocks)
+ continue
+
+ if cmd == "arg":
+ line = line.split()
+ assert len(line) > 1
+ subpattern_args[(current_pattern, current_subpattern)] += line[1:]
+ continue
+
if cmd == "state":
m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line)
assert m
@@ -155,21 +194,28 @@ def process_pmgfile(f):
if cmd == "match":
block = dict()
block["type"] = "match"
- block["pattern"] = current_pattern
+ block["src"] = "%s:%d" % (filename, linenr)
+ block["pattern"] = (current_pattern, current_subpattern)
+
+ block["genargs"] = None
+ block["gencode"] = None
line = line.split()
assert len(line) == 2
- assert line[1] not in state_types[current_pattern]
+ assert (line[1] not in state_types[current_pattern]) or (state_types[current_pattern][line[1]] == "Cell*")
block["cell"] = line[1]
state_types[current_pattern][line[1]] = "Cell*";
block["if"] = list()
- block["select"] = list()
+ block["setup"] = list()
block["index"] = list()
block["filter"] = list()
+ block["sets"] = list()
block["optional"] = False
+ block["semioptional"] = False
while True:
+ linenr += 1
l = f.readline()
assert l != ""
a = l.split()
@@ -183,7 +229,22 @@ def process_pmgfile(f):
if a[0] == "select":
b = l.lstrip()[6:]
- block["select"].append(rewrite_cpp(b.strip()))
+ block["setup"].append(("select", rewrite_cpp(b.strip())))
+ continue
+
+ if a[0] == "slice":
+ m = re.match(r"^\s*slice\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("slice", m.group(1), rewrite_cpp(m.group(2))))
+ continue
+
+ if a[0] == "choice":
+ m = re.match(r"^\s*choice\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("choice", m.group(1), m.group(2), rewrite_cpp(m.group(3))))
+ continue
+
+ if a[0] == "define":
+ m = re.match(r"^\s*define\s+<(.*?)>\s+(\S+)\s+(.*?)\s*$", l)
+ block["setup"].append(("define", m.group(1), m.group(2), rewrite_cpp(m.group(3))))
continue
if a[0] == "index":
@@ -197,35 +258,71 @@ def process_pmgfile(f):
block["filter"].append(rewrite_cpp(b.strip()))
continue
+ if a[0] == "set":
+ m = re.match(r"^\s*set\s+(\S+)\s+(.*?)\s*$", l)
+ block["sets"].append((m.group(1), rewrite_cpp(m.group(2))))
+ continue
+
if a[0] == "optional":
block["optional"] = True
continue
+ if a[0] == "semioptional":
+ block["semioptional"] = True
+ continue
+
+ if a[0] == "generate":
+ block["genargs"] = list([int(s) for s in a[1:]])
+ if len(block["genargs"]) == 0: block["genargs"].append(100)
+ if len(block["genargs"]) == 1: block["genargs"].append(0)
+ assert len(block["genargs"]) == 2
+ block["gencode"] = list()
+ while True:
+ linenr += 1
+ l = f.readline()
+ assert l != ""
+ a = l.split()
+ if len(a) == 1 and a[0] == "endmatch": break
+ block["gencode"].append(rewrite_cpp(l.rstrip()))
+ break
+
assert False
+ if block["optional"]:
+ assert not block["semioptional"]
+
blocks.append(block)
continue
if cmd == "code":
block = dict()
block["type"] = "code"
- block["pattern"] = current_pattern
+ block["src"] = "%s:%d" % (filename, linenr)
+ block["pattern"] = (current_pattern, current_subpattern)
block["code"] = list()
+ block["fcode"] = list()
block["states"] = set()
for s in line.split()[1:]:
assert s in state_types[current_pattern]
block["states"].add(s)
+ codetype = "code"
+
while True:
+ linenr += 1
l = f.readline()
assert l != ""
a = l.split()
if len(a) == 0: continue
if a[0] == "endcode": break
- block["code"].append(rewrite_cpp(l.rstrip()))
+ if a[0] == "finally":
+ codetype = "fcode"
+ continue
+
+ block[codetype].append(rewrite_cpp(l.rstrip()))
blocks.append(block)
continue
@@ -234,15 +331,16 @@ def process_pmgfile(f):
for fn in pmgfiles:
with open(fn, "r") as f:
- process_pmgfile(f)
+ process_pmgfile(f, fn)
if current_pattern is not None:
block = dict()
block["type"] = "final"
- block["pattern"] = current_pattern
+ block["pattern"] = (current_pattern, current_subpattern)
blocks.append(block)
current_pattern = None
+current_subpattern = None
if debug:
pp.pprint(blocks)
@@ -262,7 +360,18 @@ with open(outfile, "w") as f:
print("struct {}_pm {{".format(prefix), file=f)
print(" Module *module;", file=f)
print(" SigMap sigmap;", file=f)
- print(" std::function<void()> on_accept;".format(prefix), file=f)
+ print(" std::function<void()> on_accept;", file=f)
+ print(" bool generate_mode;", file=f)
+ print(" int accept_cnt;", file=f)
+ print("", file=f)
+
+ print(" uint32_t rngseed;", file=f)
+ print(" int rng(unsigned int n) {", file=f)
+ print(" rngseed ^= rngseed << 13;", file=f)
+ print(" rngseed ^= rngseed >> 17;", file=f)
+ print(" rngseed ^= rngseed << 5;", file=f)
+ print(" return rngseed % n;", file=f)
+ print(" }", file=f)
print("", file=f)
for index in range(len(blocks)):
@@ -271,12 +380,21 @@ with open(outfile, "w") as f:
index_types = list()
for entry in block["index"]:
index_types.append(entry[0])
+ value_types = ["Cell*"]
+ for entry in block["setup"]:
+ if entry[0] == "slice":
+ value_types.append("int")
+ if entry[0] == "choice":
+ value_types.append(entry[1])
+ if entry[0] == "define":
+ value_types.append(entry[1])
print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f)
- print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
+ print(" typedef std::tuple<{}> index_{}_value_type;".format(", ".join(value_types), index), file=f)
+ print(" dict<index_{}_key_type, vector<index_{}_value_type>> index_{};".format(index, index, index), file=f)
print(" dict<SigBit, pool<Cell*>> sigusers;", file=f)
print(" pool<Cell*> blacklist_cells;", file=f)
print(" pool<Cell*> autoremove_cells;", file=f)
- print(" bool blacklist_dirty;", file=f)
+ print(" dict<Cell*,int> rollback_cache;", file=f)
print(" int rollback;", file=f)
print("", file=f)
@@ -304,47 +422,30 @@ with open(outfile, "w") as f:
print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f)
print(" for (auto bit : sigmap(sig)) {", file=f)
print(" if (bit.wire == nullptr) continue;", file=f)
- print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f)
- print(" sigusers[bit].insert(nullptr);", file=f)
print(" sigusers[bit].insert(cell);", file=f)
print(" }", file=f)
print(" }", file=f)
print("", file=f)
print(" void blacklist(Cell *cell) {", file=f)
- print(" if (cell != nullptr) {", file=f)
- print(" if (blacklist_cells.insert(cell).second)", file=f)
- print(" blacklist_dirty = true;", file=f)
+ print(" if (cell != nullptr && blacklist_cells.insert(cell).second) {", file=f)
+ print(" auto ptr = rollback_cache.find(cell);", file=f)
+ print(" if (ptr == rollback_cache.end()) return;", file=f)
+ print(" int rb = ptr->second;", file=f)
+ print(" if (rollback == 0 || rollback > rb)", file=f)
+ print(" rollback = rb;", file=f)
print(" }", file=f)
print(" }", file=f)
print("", file=f)
print(" void autoremove(Cell *cell) {", file=f)
print(" if (cell != nullptr) {", file=f)
- print(" if (blacklist_cells.insert(cell).second)", file=f)
- print(" blacklist_dirty = true;", file=f)
print(" autoremove_cells.insert(cell);", file=f)
+ print(" blacklist(cell);", file=f)
print(" }", file=f)
print(" }", file=f)
print("", file=f)
- for current_pattern in sorted(patterns.keys()):
- print(" void check_blacklist_{}() {{".format(current_pattern), file=f)
- print(" if (!blacklist_dirty)", file=f)
- print(" return;", file=f)
- print(" blacklist_dirty = false;", file=f)
- for index in range(len(blocks)):
- block = blocks[index]
- if block["pattern"] != current_pattern:
- continue
- if block["type"] == "match":
- print(" if (st_{}.{} != nullptr && blacklist_cells.count(st_{}.{})) {{".format(current_pattern, block["cell"], current_pattern, block["cell"]), file=f)
- print(" rollback = {};".format(index+1), file=f)
- print(" return;", file=f)
- print(" }", file=f)
- print(" rollback = 0;", file=f)
- print(" }", file=f)
- print("", file=f)
current_pattern = None
print(" SigSpec port(Cell *cell, IdString portname) {", file=f)
@@ -367,7 +468,7 @@ with open(outfile, "w") as f:
print("", file=f)
print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f)
- print(" module(module), sigmap(module) {", file=f)
+ print(" module(module), sigmap(module), generate_mode(false), rngseed(12345678) {", file=f)
for current_pattern in sorted(patterns.keys()):
for s, t in sorted(udata_types[current_pattern].items()):
if t.endswith("*"):
@@ -375,10 +476,11 @@ with open(outfile, "w") as f:
else:
print(" ud_{}.{} = {}();".format(current_pattern, s, t), file=f)
current_pattern = None
- print(" for (auto cell : module->cells()) {", file=f)
+ print(" for (auto port : module->ports)", file=f)
+ print(" add_siguser(module->wire(port), nullptr);", file=f)
+ print(" for (auto cell : module->cells())", file=f)
print(" for (auto &conn : cell->connections())", file=f)
print(" add_siguser(conn.second, cell);", file=f)
- print(" }", file=f)
print(" for (auto cell : cells) {", file=f)
for index in range(len(blocks)):
@@ -386,12 +488,34 @@ with open(outfile, "w") as f:
if block["type"] == "match":
print(" do {", file=f)
print(" Cell *{} = cell;".format(block["cell"]), file=f)
- for expr in block["select"]:
- print(" if (!({})) break;".format(expr), file=f)
+ print(" index_{}_value_type value;".format(index), file=f)
+ print(" std::get<0>(value) = cell;", file=f)
+ loopcnt = 0
+ valueidx = 1
+ for item in block["setup"]:
+ if item[0] == "select":
+ print(" if (!({})) continue;".format(item[1]), file=f)
+ if item[0] == "slice":
+ print(" int &{} = std::get<{}>(value);".format(item[1], valueidx), file=f)
+ print(" for ({} = 0; {} < {}; {}++) {{".format(item[1], item[1], item[2], item[1]), file=f)
+ valueidx += 1
+ loopcnt += 1
+ if item[0] == "choice":
+ print(" vector<{}> _pmg_choices_{} = {};".format(item[1], item[2], item[3]), file=f)
+ print(" for (const {} &{} : _pmg_choices_{}) {{".format(item[1], item[2], item[2]), file=f)
+ print(" std::get<{}>(value) = {};".format(valueidx, item[2]), file=f)
+ valueidx += 1
+ loopcnt += 1
+ if item[0] == "define":
+ print(" {} &{} = std::get<{}>(value);".format(item[1], item[2], valueidx), file=f)
+ print(" {} = {};".format(item[2], item[3]), file=f)
+ valueidx += 1
print(" index_{}_key_type key;".format(index), file=f)
for field, entry in enumerate(block["index"]):
print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f)
- print(" index_{}[key].push_back(cell);".format(index), file=f)
+ print(" index_{}[key].push_back(value);".format(index), file=f)
+ for i in range(loopcnt):
+ print(" }", file=f)
print(" } while (0);", file=f)
print(" }", file=f)
@@ -405,41 +529,47 @@ with open(outfile, "w") as f:
print("", file=f)
for current_pattern in sorted(patterns.keys()):
- print(" void run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+ print(" int run_{}(std::function<void()> on_accept_f) {{".format(current_pattern), file=f)
+ print(" accept_cnt = 0;", file=f)
print(" on_accept = on_accept_f;", file=f)
print(" rollback = 0;", file=f)
- print(" blacklist_dirty = false;", file=f)
for s, t in sorted(state_types[current_pattern].items()):
if t.endswith("*"):
print(" st_{}.{} = nullptr;".format(current_pattern, s), file=f)
else:
print(" st_{}.{} = {}();".format(current_pattern, s, t), file=f)
- print(" block_{}();".format(patterns[current_pattern]), file=f)
+ print(" block_{}(1);".format(patterns[current_pattern]), file=f)
+ print(" log_assert(rollback_cache.empty());", file=f)
+ print(" return accept_cnt;", file=f)
print(" }", file=f)
print("", file=f)
- print(" void run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
- print(" run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
+ print(" int run_{}(std::function<void({}_pm&)> on_accept_f) {{".format(current_pattern, prefix), file=f)
+ print(" return run_{}([&](){{on_accept_f(*this);}});".format(current_pattern), file=f)
print(" }", file=f)
print("", file=f)
- print(" void run_{}(std::function<void(state_{}_t&)> on_accept_f) {{".format(current_pattern, current_pattern), file=f)
- print(" run_{}([&](){{on_accept_f(st_{});}});".format(current_pattern, current_pattern), file=f)
+ print(" int run_{}() {{".format(current_pattern), file=f)
+ print(" return run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
print(" }", file=f)
print("", file=f)
- print(" void run_{}() {{".format(current_pattern), file=f)
- print(" run_{}([](){{}});".format(current_pattern, current_pattern), file=f)
- print(" }", file=f)
+
+ if len(subpatterns):
+ for p, s in sorted(subpatterns.keys()):
+ print(" void block_subpattern_{}_{}(int recursion) {{ block_{}(recursion); }}".format(p, s, subpatterns[(p, s)]), file=f)
print("", file=f)
+
current_pattern = None
+ current_subpattern = None
for index in range(len(blocks)):
block = blocks[index]
- print(" void block_{}() {{".format(index), file=f)
- current_pattern = block["pattern"]
+ if block["type"] in ("match", "code"):
+ print(" // {}".format(block["src"]), file=f)
+
+ print(" void block_{}(int recursion YS_ATTRIBUTE(unused)) {{".format(index), file=f)
+ current_pattern, current_subpattern = block["pattern"]
if block["type"] == "final":
- print(" on_accept();", file=f)
- print(" check_blacklist_{}();".format(current_pattern), file=f)
print(" }", file=f)
if index+1 != len(blocks):
print("", file=f)
@@ -449,12 +579,17 @@ with open(outfile, "w") as f:
nonconst_st = set()
restore_st = set()
- for i in range(patterns[current_pattern], index):
+ for s in subpattern_args[(current_pattern, current_subpattern)]:
+ const_st.add(s)
+
+ for i in range(subpatterns[(current_pattern, current_subpattern)], index):
if blocks[i]["type"] == "code":
for s in blocks[i]["states"]:
const_st.add(s)
elif blocks[i]["type"] == "match":
const_st.add(blocks[i]["cell"])
+ for item in blocks[i]["sets"]:
+ const_st.add(item[0])
else:
assert False
@@ -468,6 +603,10 @@ with open(outfile, "w") as f:
s = block["cell"]
assert s not in const_st
nonconst_st.add(s)
+ for item in block["sets"]:
+ if item[0] in const_st:
+ const_st.remove(item[0])
+ nonconst_st.add(item[0])
else:
assert False
@@ -482,37 +621,55 @@ with open(outfile, "w") as f:
t = state_types[current_pattern][s]
print(" {} &{} YS_ATTRIBUTE(unused) = st_{}.{};".format(t, s, current_pattern, s), file=f)
+ for u in sorted(udata_types[current_pattern].keys()):
+ t = udata_types[current_pattern][u]
+ print(" {} &{} YS_ATTRIBUTE(unused) = ud_{}.{};".format(t, u, current_pattern, u), file=f)
+
if len(restore_st):
print("", file=f)
for s in sorted(restore_st):
t = state_types[current_pattern][s]
- print(" {} backup_{} = {};".format(t, s, s), file=f)
+ print(" {} _pmg_backup_{} = {};".format(t, s, s), file=f)
if block["type"] == "code":
print("", file=f)
- print(" do {", file=f)
- print("#define reject do {{ check_blacklist_{}(); goto rollback_label; }} while(0)".format(current_pattern), file=f)
- print("#define accept do {{ on_accept(); check_blacklist_{}(); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
- print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
+ print("#define reject do { goto rollback_label; } while(0)", file=f)
+ print("#define accept do { accept_cnt++; on_accept(); if (rollback) goto rollback_label; } while(0)", file=f)
+ print("#define finish do { rollback = -1; goto rollback_label; } while(0)", file=f)
+ print("#define branch do {{ block_{}(recursion+1); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f)
+ print("#define subpattern(pattern_name) do {{ block_subpattern_{}_ ## pattern_name (recursion+1); if (rollback) goto rollback_label; }} while(0)".format(current_pattern), file=f)
for line in block["code"]:
- print(" " + line, file=f)
+ print(" " + line, file=f)
print("", file=f)
- print(" block_{}();".format(index+1), file=f)
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+
print("#undef reject", file=f)
print("#undef accept", file=f)
+ print("#undef finish", file=f)
print("#undef branch", file=f)
- print(" } while (0);", file=f)
+ print("#undef subpattern", file=f)
+
print("", file=f)
print("rollback_label:", file=f)
print(" YS_ATTRIBUTE(unused);", file=f)
+ if len(block["fcode"]):
+ print("#define accept do { accept_cnt++; on_accept(); } while(0)", file=f)
+ print("#define finish do { rollback = -1; goto finish_label; } while(0)", file=f)
+ for line in block["fcode"]:
+ print(" " + line, file=f)
+ print("finish_label:", file=f)
+ print(" YS_ATTRIBUTE(unused);", file=f)
+ print("#undef accept", file=f)
+ print("#undef finish", file=f)
+
if len(restore_st) or len(nonconst_st):
print("", file=f)
for s in sorted(restore_st):
t = state_types[current_pattern][s]
- print(" {} = backup_{};".format(s, s), file=f)
+ print(" {} = _pmg_backup_{};".format(s, s), file=f)
for s in sorted(nonconst_st):
if s not in restore_st:
t = state_types[current_pattern][s]
@@ -524,12 +681,15 @@ with open(outfile, "w") as f:
elif block["type"] == "match":
assert len(restore_st) == 0
+ print(" Cell* _pmg_backup_{} = {};".format(block["cell"], block["cell"]), file=f)
+
if len(block["if"]):
for expr in block["if"]:
print("", file=f)
print(" if (!({})) {{".format(expr), file=f)
print(" {} = nullptr;".format(block["cell"]), file=f)
- print(" block_{}();".format(index+1), file=f)
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
print(" return;", file=f)
print(" }", file=f)
@@ -537,21 +697,48 @@ with open(outfile, "w") as f:
print(" index_{}_key_type key;".format(index), file=f)
for field, entry in enumerate(block["index"]):
print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f)
- print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f)
+ print(" auto cells_ptr = index_{}.find(key);".format(index), file=f)
+
+ if block["semioptional"] or block["genargs"] is not None:
+ print(" bool found_any_match = false;", file=f)
print("", file=f)
- print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f)
- print(" {} = cells[idx];".format(block["cell"]), file=f)
- print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
+ print(" if (cells_ptr != index_{}.end()) {{".format(index), file=f)
+ print(" const vector<index_{}_value_type> &cells = cells_ptr->second;".format(index), file=f)
+ print(" for (int _pmg_idx = 0; _pmg_idx < GetSize(cells); _pmg_idx++) {", file=f)
+ print(" {} = std::get<0>(cells[_pmg_idx]);".format(block["cell"]), file=f)
+ valueidx = 1
+ for item in block["setup"]:
+ if item[0] == "slice":
+ print(" const int &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], valueidx), file=f)
+ valueidx += 1
+ if item[0] == "choice":
+ print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f)
+ valueidx += 1
+ if item[0] == "define":
+ print(" const {} &{} YS_ATTRIBUTE(unused) = std::get<{}>(cells[_pmg_idx]);".format(item[1], item[2], valueidx), file=f)
+ valueidx += 1
+ print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f)
for expr in block["filter"]:
- print(" if (!({})) continue;".format(expr), file=f)
- print(" block_{}();".format(index+1), file=f)
- print(" if (rollback) {", file=f)
- print(" if (rollback != {}) {{".format(index+1), file=f)
- print(" {} = nullptr;".format(block["cell"]), file=f)
- print(" return;", file=f)
+ print(" if (!({})) continue;".format(expr), file=f)
+ if block["semioptional"] or block["genargs"] is not None:
+ print(" found_any_match = true;", file=f)
+ for item in block["sets"]:
+ print(" auto _pmg_backup_{} = {};".format(item[0], item[0]), file=f)
+ print(" {} = {};".format(item[0], item[1]), file=f)
+ print(" auto rollback_ptr = rollback_cache.insert(make_pair(std::get<0>(cells[_pmg_idx]), recursion));", file=f)
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+ for item in block["sets"]:
+ print(" {} = _pmg_backup_{};".format(item[0], item[0]), file=f)
+ print(" if (rollback_ptr.second)", file=f)
+ print(" rollback_cache.erase(rollback_ptr.first);", file=f)
+ print(" if (rollback) {", file=f)
+ print(" if (rollback != recursion) {{".format(index+1), file=f)
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
+ print(" return;", file=f)
+ print(" }", file=f)
+ print(" rollback = 0;", file=f)
print(" }", file=f)
- print(" rollback = 0;", file=f)
print(" }", file=f)
print(" }", file=f)
@@ -559,8 +746,20 @@ with open(outfile, "w") as f:
print(" {} = nullptr;".format(block["cell"]), file=f)
if block["optional"]:
- print(" block_{}();".format(index+1), file=f)
+ print(" block_{}(recursion+1);".format(index+1), file=f)
+ if block["semioptional"]:
+ print(" if (!found_any_match) block_{}(recursion+1);".format(index+1), file=f)
+
+ print(" {} = _pmg_backup_{};".format(block["cell"], block["cell"]), file=f)
+
+ if block["genargs"] is not None:
+ print("#define finish do { rollback = -1; return; } while(0)", file=f)
+ print(" if (generate_mode && rng(100) < (found_any_match ? {} : {})) {{".format(block["genargs"][1], block["genargs"][0]), file=f)
+ for line in block["gencode"]:
+ print(" " + line, file=f)
+ print(" }", file=f)
+ print("#undef finish", file=f)
else:
assert False
diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc
new file mode 100644
index 000000000..4f3eec935
--- /dev/null
+++ b/passes/pmgen/test_pmgen.cc
@@ -0,0 +1,385 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+// for peepopt_pm
+bool did_something;
+
+#include "passes/pmgen/test_pmgen_pm.h"
+#include "passes/pmgen/ice40_dsp_pm.h"
+#include "passes/pmgen/xilinx_srl_pm.h"
+#include "passes/pmgen/peepopt_pm.h"
+
+void reduce_chain(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_reduce;
+ auto &ud = pm.ud_reduce;
+
+ if (ud.longest_chain.empty())
+ return;
+
+ log("Found chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));
+
+ SigSpec A;
+ SigSpec Y = ud.longest_chain.front().first->getPort(ID(Y));
+ auto last_cell = ud.longest_chain.back().first;
+
+ for (auto it : ud.longest_chain) {
+ auto cell = it.first;
+ if (cell == last_cell) {
+ A.append(cell->getPort(ID(A)));
+ A.append(cell->getPort(ID(B)));
+ } else {
+ A.append(cell->getPort(it.second == ID(A) ? ID(B) : ID(A)));
+ }
+ log(" %s\n", log_id(cell));
+ pm.autoremove(cell);
+ }
+
+ Cell *c;
+
+ if (last_cell->type == ID($_AND_))
+ c = pm.module->addReduceAnd(NEW_ID, A, Y);
+ else if (last_cell->type == ID($_OR_))
+ c = pm.module->addReduceOr(NEW_ID, A, Y);
+ else if (last_cell->type == ID($_XOR_))
+ c = pm.module->addReduceXor(NEW_ID, A, Y);
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void reduce_tree(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_reduce;
+ auto &ud = pm.ud_reduce;
+
+ if (ud.longest_chain.empty())
+ return;
+
+ SigSpec A = ud.leaves;
+ SigSpec Y = st.first->getPort(ID(Y));
+ pm.autoremove(st.first);
+
+ log("Found %s tree with %d leaves for %s (%s).\n", log_id(st.first->type),
+ GetSize(A), log_signal(Y), log_id(st.first));
+
+ Cell *c;
+
+ if (st.first->type == ID($_AND_))
+ c = pm.module->addReduceAnd(NEW_ID, A, Y);
+ else if (st.first->type == ID($_OR_))
+ c = pm.module->addReduceOr(NEW_ID, A, Y);
+ else if (st.first->type == ID($_XOR_))
+ c = pm.module->addReduceXor(NEW_ID, A, Y);
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void opt_eqpmux(test_pmgen_pm &pm)
+{
+ auto &st = pm.st_eqpmux;
+
+ SigSpec Y = st.pmux->getPort(ID::Y);
+ int width = GetSize(Y);
+
+ SigSpec EQ = st.pmux->getPort(ID::B).extract(st.pmux_slice_eq*width, width);
+ SigSpec NE = st.pmux->getPort(ID::B).extract(st.pmux_slice_ne*width, width);
+
+ log("Found eqpmux circuit driving %s (eq=%s, ne=%s, pmux=%s).\n",
+ log_signal(Y), log_id(st.eq), log_id(st.ne), log_id(st.pmux));
+
+ pm.autoremove(st.pmux);
+ Cell *c = pm.module->addMux(NEW_ID, NE, EQ, st.eq->getPort(ID::Y), Y);
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+#define GENERATE_PATTERN(pmclass, pattern) \
+ generate_pattern<pmclass>([](pmclass &pm, std::function<void()> f){ return pm.run_ ## pattern(f); }, #pmclass, #pattern, design)
+
+void pmtest_addports(Module *module)
+{
+ pool<SigBit> driven_bits, used_bits;
+ SigMap sigmap(module);
+ int icnt = 0, ocnt = 0;
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ {
+ if (cell->input(conn.first))
+ for (auto bit : sigmap(conn.second))
+ used_bits.insert(bit);
+ if (cell->output(conn.first))
+ for (auto bit : sigmap(conn.second))
+ driven_bits.insert(bit);
+ }
+
+ for (auto wire : vector<Wire*>(module->wires()))
+ {
+ SigSpec ibits, obits;
+ for (auto bit : sigmap(wire)) {
+ if (!used_bits.count(bit))
+ obits.append(bit);
+ if (!driven_bits.count(bit))
+ ibits.append(bit);
+ }
+ if (!ibits.empty()) {
+ Wire *w = module->addWire(stringf("\\i%d", icnt++), GetSize(ibits));
+ w->port_input = true;
+ module->connect(ibits, w);
+ }
+ if (!obits.empty()) {
+ Wire *w = module->addWire(stringf("\\o%d", ocnt++), GetSize(obits));
+ w->port_output = true;
+ module->connect(w, obits);
+ }
+ }
+
+ module->fixup_ports();
+}
+
+template <class pm>
+void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const char *pmclass, const char *pattern, Design *design)
+{
+ log("Generating \"%s\" patterns for pattern matcher \"%s\".\n", pattern, pmclass);
+
+ int modcnt = 0;
+ int maxmodcnt = 100;
+ int maxsubcnt = 4;
+ int timeout = 0;
+ vector<Module*> mods;
+
+ while (modcnt < maxmodcnt)
+ {
+ int submodcnt = 0, itercnt = 0, cellcnt = 0;
+ Module *mod = design->addModule(NEW_ID);
+
+ while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
+ {
+ if (timeout++ > 10000)
+ log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
+
+ pm matcher(mod, mod->cells());
+
+ matcher.rng(1);
+ matcher.rngseed += modcnt;
+ matcher.rng(1);
+ matcher.rngseed += submodcnt;
+ matcher.rng(1);
+ matcher.rngseed += itercnt;
+ matcher.rng(1);
+ matcher.rngseed += cellcnt;
+ matcher.rng(1);
+
+ if (GetSize(mod->cells()) != cellcnt)
+ {
+ bool found_match = false;
+ run(matcher, [&](){ found_match = true; });
+ cellcnt = GetSize(mod->cells());
+
+ if (found_match) {
+ Module *m = design->addModule(stringf("\\pmtest_%s_%s_%05d",
+ pmclass, pattern, modcnt++));
+ log("Creating module %s with %d cells.\n", log_id(m), cellcnt);
+ mod->cloneInto(m);
+ pmtest_addports(m);
+ mods.push_back(m);
+ submodcnt++;
+ timeout = 0;
+ }
+ }
+
+ matcher.generate_mode = true;
+ run(matcher, [](){});
+ }
+
+ if (submodcnt && maxsubcnt < (1 << 16))
+ maxsubcnt *= 2;
+
+ design->remove(mod);
+ }
+
+ Module *m = design->addModule(stringf("\\pmtest_%s_%s", pmclass, pattern));
+ log("Creating module %s with %d cells.\n", log_id(m), GetSize(mods));
+ for (auto mod : mods) {
+ Cell *c = m->addCell(mod->name, mod->name);
+ for (auto port : mod->ports) {
+ Wire *w = m->addWire(NEW_ID, GetSize(mod->wire(port)));
+ c->setPort(port, w);
+ }
+ }
+ pmtest_addports(m);
+}
+
+struct TestPmgenPass : public Pass {
+ TestPmgenPass() : Pass("test_pmgen", "test pass for pmgen") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" test_pmgen -reduce_chain [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Map chains of AND/OR/XOR to $reduce_*.\n");
+ log("\n");
+
+ log("\n");
+ log(" test_pmgen -reduce_tree [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Map trees of AND/OR/XOR to $reduce_*.\n");
+ log("\n");
+
+ log("\n");
+ log(" test_pmgen -eqpmux [options] [selection]\n");
+ log("\n");
+ log("Demo for recursive pmgen patterns. Optimize EQ/NE/PMUX circuits.\n");
+ log("\n");
+
+ log("\n");
+ log(" test_pmgen -generate [options] <pattern_name>\n");
+ log("\n");
+ log("Create modules that match the specified pattern.\n");
+ log("\n");
+ }
+
+ void execute_reduce_chain(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-reduce_chain).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {}
+ }
+
+ void execute_reduce_tree(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-reduce_tree).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree);
+ }
+
+ void execute_eqpmux(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-eqpmux).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ test_pmgen_pm(module, module->selected_cells()).run_eqpmux(opt_eqpmux);
+ }
+
+ void execute_generate(std::vector<std::string> args, RTLIL::Design *design)
+ {
+ log_header(design, "Executing TEST_PMGEN pass (-generate).\n");
+
+ size_t argidx;
+ for (argidx = 2; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+
+ if (argidx+1 != args.size())
+ log_cmd_error("Expected exactly one pattern.\n");
+
+ string pattern = args[argidx];
+
+ if (pattern == "reduce")
+ return GENERATE_PATTERN(test_pmgen_pm, reduce);
+
+ if (pattern == "eqpmux")
+ return GENERATE_PATTERN(test_pmgen_pm, eqpmux);
+
+ if (pattern == "ice40_dsp")
+ return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
+
+ if (pattern == "xilinx_srl.fixed")
+ return GENERATE_PATTERN(xilinx_srl_pm, fixed);
+ if (pattern == "xilinx_srl.variable")
+ return GENERATE_PATTERN(xilinx_srl_pm, variable);
+
+ if (pattern == "peepopt-muldiv")
+ return GENERATE_PATTERN(peepopt_pm, muldiv);
+
+ if (pattern == "peepopt-shiftmul")
+ return GENERATE_PATTERN(peepopt_pm, shiftmul);
+
+ log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ if (GetSize(args) > 1)
+ {
+ if (args[1] == "-reduce_chain")
+ return execute_reduce_chain(args, design);
+ if (args[1] == "-reduce_tree")
+ return execute_reduce_tree(args, design);
+ if (args[1] == "-eqpmux")
+ return execute_eqpmux(args, design);
+ if (args[1] == "-generate")
+ return execute_generate(args, design);
+ }
+ help();
+ log_cmd_error("Missing or unsupported mode parameter.\n");
+ }
+} TestPmgenPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/test_pmgen.pmg b/passes/pmgen/test_pmgen.pmg
new file mode 100644
index 000000000..287ed97d8
--- /dev/null
+++ b/passes/pmgen/test_pmgen.pmg
@@ -0,0 +1,189 @@
+pattern reduce
+
+state <IdString> portname
+udata <vector<pair<Cell*, IdString>>> chain longest_chain
+udata <pool<Cell*>> non_first_cells
+udata <SigSpec> leaves
+
+code
+ non_first_cells.clear();
+ subpattern(setup);
+endcode
+
+match first
+ select first->type.in($_AND_, $_OR_, $_XOR_)
+ filter !non_first_cells.count(first)
+generate
+ SigSpec A = module->addWire(NEW_ID);
+ SigSpec B = module->addWire(NEW_ID);
+ SigSpec Y = module->addWire(NEW_ID);
+ switch (rng(3))
+ {
+ case 0:
+ module->addAndGate(NEW_ID, A, B, Y);
+ break;
+ case 1:
+ module->addOrGate(NEW_ID, A, B, Y);
+ break;
+ case 2:
+ module->addXorGate(NEW_ID, A, B, Y);
+ break;
+ }
+endmatch
+
+code
+ leaves = SigSpec();
+ longest_chain.clear();
+ chain.push_back(make_pair(first, \A));
+ subpattern(tail);
+ chain.back().second = \B;
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) > 1)
+ accept;
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern setup
+
+match first
+ select first->type.in($_AND_, $_OR_, $_XOR_)
+endmatch
+
+code portname
+ portname = \A;
+ branch;
+ portname = \B;
+endcode
+
+match next
+ select next->type.in($_AND_, $_OR_, $_XOR_)
+ select nusers(port(next, \Y)) == 2
+ index <IdString> next->type === first->type
+ index <SigSpec> port(next, \Y) === port(first, portname)
+endmatch
+
+code
+ non_first_cells.insert(next);
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+
+match next
+ semioptional
+ select next->type.in($_AND_, $_OR_, $_XOR_)
+ select nusers(port(next, \Y)) == 2
+ index <IdString> next->type === chain.back().first->type
+ index <SigSpec> port(next, \Y) === port(chain.back().first, chain.back().second)
+generate 10
+ SigSpec A = module->addWire(NEW_ID);
+ SigSpec B = module->addWire(NEW_ID);
+ SigSpec Y = port(chain.back().first, chain.back().second);
+ Cell *c = module->addAndGate(NEW_ID, A, B, Y);
+ c->type = chain.back().first->type;
+endmatch
+
+code
+ if (next) {
+ chain.push_back(make_pair(next, \A));
+ subpattern(tail);
+ chain.back().second = \B;
+ subpattern(tail);
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ leaves.append(port(chain.back().first, chain.back().second));
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// ==================================================================
+
+pattern eqpmux
+
+state <bool> eq_ne_signed
+state <SigSpec> eq_inA eq_inB
+state <int> pmux_slice_eq pmux_slice_ne
+
+match eq
+ select eq->type == $eq
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA AB == \A ? \B : \A
+ set eq_inA port(eq, \A)
+ set eq_inB port(eq, \B)
+ set eq_ne_signed param(eq, \A_SIGNED).as_bool()
+generate 100 10
+ SigSpec A = module->addWire(NEW_ID, rng(7)+1);
+ SigSpec B = module->addWire(NEW_ID, rng(7)+1);
+ SigSpec Y = module->addWire(NEW_ID);
+ module->addEq(NEW_ID, A, B, Y, rng(2));
+endmatch
+
+match pmux
+ select pmux->type == $pmux
+ slice idx GetSize(port(pmux, \S))
+ index <SigBit> port(pmux, \S)[idx] === port(eq, \Y)
+ set pmux_slice_eq idx
+generate 100 10
+ int width = rng(7) + 1;
+ int numsel = rng(4) + 1;
+ int idx = rng(numsel);
+
+ SigSpec A = module->addWire(NEW_ID, width);
+ SigSpec Y = module->addWire(NEW_ID, width);
+
+ SigSpec B, S;
+ for (int i = 0; i < numsel; i++) {
+ B.append(module->addWire(NEW_ID, width));
+ S.append(i == idx ? port(eq, \Y) : module->addWire(NEW_ID));
+ }
+
+ module->addPmux(NEW_ID, A, B, S, Y);
+endmatch
+
+match ne
+ select ne->type == $ne
+ choice <IdString> AB {\A, \B}
+ define <IdString> BA (AB == \A ? \B : \A)
+ index <SigSpec> port(ne, AB) === eq_inA
+ index <SigSpec> port(ne, BA) === eq_inB
+ index <int> param(ne, \A_SIGNED).as_bool() === eq_ne_signed
+generate 100 10
+ SigSpec A = eq_inA, B = eq_inB, Y;
+ if (rng(2)) {
+ std::swap(A, B);
+ }
+ if (rng(2)) {
+ for (auto bit : port(pmux, \S)) {
+ if (nusers(bit) < 2)
+ Y.append(bit);
+ }
+ if (GetSize(Y))
+ Y = Y[rng(GetSize(Y))];
+ else
+ Y = module->addWire(NEW_ID);
+ } else {
+ Y = module->addWire(NEW_ID);
+ }
+ module->addNe(NEW_ID, A, B, Y, rng(2));
+endmatch
+
+match pmux2
+ select pmux2->type == $pmux
+ slice idx GetSize(port(pmux2, \S))
+ index <Cell*> pmux2 === pmux
+ index <SigBit> port(pmux2, \S)[idx] === port(ne, \Y)
+ set pmux_slice_ne idx
+endmatch
+
+code
+ accept;
+endcode
diff --git a/passes/pmgen/xilinx_srl.cc b/passes/pmgen/xilinx_srl.cc
new file mode 100644
index 000000000..3d264e8d4
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.cc
@@ -0,0 +1,258 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * (C) 2019 Eddie Hung <eddie@fpgeh.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+#include "passes/pmgen/xilinx_srl_pm.h"
+
+void run_fixed(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_fixed;
+ auto &ud = pm.ud_fixed;
+ log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (auto cell : ud.longest_chain) {
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) {
+ SigBit Q = cell->getPort(ID(Q));
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else if (cell->type.in(ID(FDRE), ID(FDRE_1))) {
+ if (cell->parameters.at(ID(INIT), State::S0).as_bool())
+ initval.append(State::S1);
+ else
+ initval.append(State::S0);
+ }
+ else
+ log_abort();
+ pm.autoremove(cell);
+ }
+
+ auto first_cell = ud.longest_chain.back();
+ auto last_cell = ud.longest_chain.front();
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) {
+ c->setParam(ID(DEPTH), GetSize(ud.longest_chain));
+ c->setParam(ID(INIT), initval.as_const());
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setParam(ID(CLKPOL), 1);
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1)))
+ c->setParam(ID(CLKPOL), 0);
+ else if (first_cell->type.in(ID(FDRE))) {
+ if (!first_cell->parameters.at(ID(IS_C_INVERTED), State::S0).as_bool())
+ c->setParam(ID(CLKPOL), 1);
+ else
+ c->setParam(ID(CLKPOL), 0);
+ }
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ c->setParam(ID(ENPOL), 1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ c->setParam(ID(ENPOL), 0);
+ else
+ c->setParam(ID(ENPOL), 2);
+
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ c->setPort(ID(D), first_cell->getPort(ID(D)));
+ c->setPort(ID(Q), last_cell->getPort(ID(Q)));
+ c->setPort(ID(L), GetSize(ud.longest_chain)-1);
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID(FDRE), ID(FDRE_1)))
+ c->setPort(ID(E), first_cell->getPort(ID(CE)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+void run_variable(xilinx_srl_pm &pm)
+{
+ auto &st = pm.st_variable;
+ auto &ud = pm.ud_variable;
+
+ log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type));
+
+ SigSpec initval;
+ for (const auto &i : ud.chain) {
+ auto cell = i.first;
+ auto slice = i.second;
+ log_debug(" %s\n", log_id(cell));
+ if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ SigBit Q = cell->getPort(ID(Q))[slice];
+ log_assert(Q.wire);
+ auto it = Q.wire->attributes.find(ID(init));
+ if (it != Q.wire->attributes.end()) {
+ auto &i = it->second[Q.offset];
+ initval.append(i);
+ i = State::Sx;
+ }
+ else
+ initval.append(State::Sx);
+ }
+ else
+ log_abort();
+ }
+ pm.autoremove(st.shiftx);
+
+ auto first_cell = ud.chain.back().first;
+ auto first_slice = ud.chain.back().second;
+
+ Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_));
+ pm.module->swap_names(c, first_cell);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) {
+ c->setParam(ID(DEPTH), GetSize(ud.chain));
+ c->setParam(ID(INIT), initval.as_const());
+ Const clkpol, enpol;
+ if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ clkpol = 1;
+ else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_)))
+ clkpol = 0;
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ clkpol = first_cell->getParam(ID(CLK_POLARITY));
+ else
+ log_abort();
+ if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_)))
+ enpol = 1;
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_)))
+ enpol = 0;
+ else if (first_cell->type.in(ID($dffe)))
+ enpol = first_cell->getParam(ID(EN_POLARITY));
+ else
+ enpol = 2;
+ c->setParam(ID(CLKPOL), clkpol);
+ c->setParam(ID(ENPOL), enpol);
+
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(C), first_cell->getPort(ID(C)));
+ else if (first_cell->type.in(ID($dff), ID($dffe)))
+ c->setPort(ID(C), first_cell->getPort(ID(CLK)));
+ else
+ log_abort();
+ c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]);
+ c->setPort(ID(Q), st.shiftx->getPort(ID(Y)));
+ c->setPort(ID(L), st.shiftx->getPort(ID(B)));
+ if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff)))
+ c->setPort(ID(E), State::S1);
+ else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
+ c->setPort(ID(E), first_cell->getPort(ID(E)));
+ else if (first_cell->type.in(ID($dffe)))
+ c->setPort(ID(E), first_cell->getPort(ID(EN)));
+ else
+ log_abort();
+ }
+ else
+ log_abort();
+
+ log(" -> %s (%s)\n", log_id(c), log_id(c->type));
+}
+
+struct XilinxSrlPass : public Pass {
+ XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" xilinx_srl [options] [selection]\n");
+ log("\n");
+ log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n");
+ log("and word-level: $dff, $dffe) as well as Xilinx flops (FDRE, FDRE_1) into a\n");
+ log("$__XILINX_SHREG cell. Chains must be of the same cell type, clock, clock polarity,\n");
+ log("enable, and enable polarity (where relevant).\n");
+ log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred.");
+ log("\n");
+ log(" -minlen N\n");
+ log(" min length of shift register (default = 3)\n");
+ log("\n");
+ log(" -fixed\n");
+ log(" infer fixed-length shift registers.\n");
+ log("\n");
+ log(" -variable\n");
+ log(" infer variable-length shift registers (i.e. fixed-length shifts where\n");
+ log(" each element also fans-out to a $shiftx cell).\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing XILINX_SRL pass (Xilinx shift register extraction).\n");
+
+ bool fixed = false;
+ bool variable = false;
+ int minlen = 3;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-minlen" && argidx+1 < args.size()) {
+ minlen = atoi(args[++argidx].c_str());
+ continue;
+ }
+ if (args[argidx] == "-fixed") {
+ fixed = true;
+ continue;
+ }
+ if (args[argidx] == "-variable") {
+ variable = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (!fixed && !variable)
+ log_cmd_error("'-fixed' and/or '-variable' must be specified.\n");
+
+ for (auto module : design->selected_modules()) {
+ auto pm = xilinx_srl_pm(module, module->selected_cells());
+ pm.ud_fixed.minlen = minlen;
+ pm.ud_variable.minlen = minlen;
+
+ if (fixed)
+ pm.run_fixed(run_fixed);
+ if (variable)
+ pm.run_variable(run_variable);
+ }
+ }
+} XilinxSrlPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/pmgen/xilinx_srl.pmg b/passes/pmgen/xilinx_srl.pmg
new file mode 100644
index 000000000..b18119b87
--- /dev/null
+++ b/passes/pmgen/xilinx_srl.pmg
@@ -0,0 +1,326 @@
+pattern fixed
+
+state <IdString> clk_port en_port
+udata <vector<Cell*>> chain longest_chain
+udata <pool<Cell*>> non_first_cells
+udata <int> minlen
+
+code
+ non_first_cells.clear();
+ subpattern(setup);
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
+ filter !non_first_cells.count(first)
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ SigSpec D = module->addWire(NEW_ID);
+ SigSpec Q = module->addWire(NEW_ID);
+ auto r = rng(8);
+ Cell* cell;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addCell(NEW_ID, \FDRE);
+ cell->setPort(\C, C);
+ cell->setPort(\D, D);
+ cell->setPort(\Q, Q);
+ cell->setPort(\CE, module->addWire(NEW_ID));
+ if (r & 1)
+ cell->setPort(\R, module->addWire(NEW_ID));
+ else {
+ if (rng(2) == 0)
+ cell->setPort(\R, State::S0);
+ }
+ break;
+ case 2:
+ case 3:
+ cell = module->addDffGate(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2);
+ break;
+ default: log_abort();
+ }
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+
+ longest_chain.clear();
+ chain.push_back(first);
+ subpattern(tail);
+finally
+ chain.pop_back();
+ log_assert(chain.empty());
+ if (GetSize(longest_chain) >= minlen)
+ accept;
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern setup
+arg clk_port
+arg en_port
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !first->has_keep_attr()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
+ clk_port = \C;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in(\FDRE, \FDRE_1))
+ en_port = \CE;
+ else log_abort();
+endcode
+
+match next
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === first->type
+ index <SigBit> port(next, \Q) === port(first, \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
+endmatch
+
+code
+ non_first_cells.insert(next);
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ select nusers(port(next, \Q)) == 2
+ index <IdString> next->type === chain.back()->type
+ index <SigBit> port(next, \Q) === port(chain.back(), \D)
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
+ filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
+generate
+ Cell *cell = module->addCell(NEW_ID, chain.back()->type);
+ cell->setPort(\C, chain.back()->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, chain.back()->getPort(\D));
+ if (cell->type == \FDRE) {
+ if (rng(2) == 0)
+ cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
+ cell->setPort(\CE, chain.back()->getPort(\CE));
+ }
+ else if (cell->type.begins_with("$_DFFE_"))
+ cell->setPort(\E, chain.back()->getPort(\E));
+endmatch
+
+code
+ if (next) {
+ chain.push_back(next);
+ subpattern(tail);
+ } else {
+ if (GetSize(chain) > GetSize(longest_chain))
+ longest_chain = chain;
+ }
+finally
+ if (next)
+ chain.pop_back();
+endcode
+
+// -----------
+
+pattern variable
+
+state <IdString> clk_port en_port
+state <int> shiftx_width
+state <int> slice
+udata <int> minlen
+udata <vector<pair<Cell*,int>>> chain
+udata <pool<SigBit>> chain_bits
+
+code
+ chain_bits.clear();
+endcode
+
+match shiftx
+ select shiftx->type.in($shiftx)
+ select !shiftx->has_keep_attr()
+ select param(shiftx, \Y_WIDTH).as_int() == 1
+ filter param(shiftx, \A_WIDTH).as_int() >= minlen
+generate
+ minlen = 3;
+ module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID));
+endmatch
+
+code shiftx_width
+ shiftx_width = param(shiftx, \A_WIDTH).as_int();
+endcode
+
+match first
+ select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !first->has_keep_attr()
+ select port(first, \Q)[0].wire && !port(first, \Q)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(first, \Q))
+ select nusers(port(first, \Q)[idx]) <= 2
+ index <SigBit> port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1]
+ set slice idx
+generate
+ SigSpec C = module->addWire(NEW_ID);
+ auto WIDTH = rng(3)+1;
+ SigSpec D = module->addWire(NEW_ID, WIDTH);
+ SigSpec Q = module->addWire(NEW_ID, WIDTH);
+ auto r = rng(8);
+ Cell *cell = nullptr;
+ switch (r)
+ {
+ case 0:
+ case 1:
+ cell = module->addDff(NEW_ID, C, D, Q, r & 1);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ //cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4);
+ //break;
+ case 6:
+ case 7:
+ WIDTH = 1;
+ cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1);
+ break;
+ default: log_abort();
+ }
+ shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)];
+endmatch
+
+code clk_port en_port
+ if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ clk_port = \C;
+ else if (first->type.in($dff, $dffe))
+ clk_port = \CLK;
+ else log_abort();
+ if (first->type.in($_DFF_N_, $_DFF_P_, $dff))
+ en_port = IdString();
+ else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
+ en_port = \E;
+ else if (first->type.in($dffe))
+ en_port = \EN;
+ else log_abort();
+
+ chain_bits.insert(port(first, \Q)[slice]);
+ chain.emplace_back(first, slice);
+ subpattern(tail);
+finally
+ if (GetSize(chain) == shiftx_width)
+ accept;
+ chain.clear();
+endcode
+
+// ------------------------------------------------------------------
+
+subpattern tail
+arg first
+arg shiftx
+arg shiftx_width
+arg slice
+arg clk_port
+arg en_port
+
+match next
+ semioptional
+ select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
+ select !next->has_keep_attr()
+ select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
+ slice idx GetSize(port(next, \Q))
+ select nusers(port(next, \Q)[idx]) <= 3
+ index <IdString> next->type === chain.back().first->type
+ index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second]
+ index <SigBit> port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)]
+ filter port(next, clk_port) == port(first, clk_port)
+ filter en_port == IdString() || port(next, en_port) == port(first, en_port)
+ filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool()
+ filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool()
+ filter !chain_bits.count(port(next, \D)[idx])
+ set slice idx
+generate
+ if (GetSize(chain) < shiftx_width) {
+ auto back = chain.back().first;
+ auto slice = chain.back().second;
+ if (back->type.in($dff, $dffe)) {
+ auto WIDTH = GetSize(port(back, \D));
+ if (rng(2) == 0 && slice < WIDTH-1) {
+ auto new_slice = slice + rng(WIDTH-1-slice);
+ back->connections_.at(\D)[slice] = port(back, \Q)[new_slice];
+ }
+ else {
+ auto D = module->addWire(NEW_ID, WIDTH);
+ if (back->type == $dff)
+ module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool());
+ else if (back->type == $dffe)
+ module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool());
+ else
+ log_abort();
+ }
+ }
+ else if (back->type.begins_with("$_DFF_")) {
+ Cell *cell = module->addCell(NEW_ID, back->type);
+ cell->setPort(\C, back->getPort(\C));
+ cell->setPort(\D, module->addWire(NEW_ID));
+ cell->setPort(\Q, back->getPort(\D));
+ }
+ else
+ log_abort();
+ shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice];
+ }
+endmatch
+
+code
+ if (next) {
+ chain_bits.insert(port(next, \Q)[slice]);
+ chain.emplace_back(next, slice);
+ if (GetSize(chain) < shiftx_width)
+ subpattern(tail);
+ }
+endcode
diff --git a/passes/proc/proc_clean.cc b/passes/proc/proc_clean.cc
index 97f4c6573..114c6ab03 100644
--- a/passes/proc/proc_clean.cc
+++ b/passes/proc/proc_clean.cc
@@ -69,8 +69,7 @@ void proc_clean_switch(RTLIL::SwitchRule *sw, RTLIL::CaseRule *parent, bool &did
did_something = true;
for (auto &action : sw->cases[0]->actions)
parent->actions.push_back(action);
- for (auto sw2 : sw->cases[0]->switches)
- parent->switches.push_back(sw2);
+ parent->switches.insert(parent->switches.begin(), sw->cases[0]->switches.begin(), sw->cases[0]->switches.end());
sw->cases[0]->switches.clear();
delete sw->cases[0];
sw->cases.clear();
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index d045d0dcb..24ae6e448 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -39,7 +39,7 @@ struct Async2syncPass : public Pass {
log("reset value in the next cycle regardless of the data-in value at the time of\n");
log("the clock edge.\n");
log("\n");
- log("Currently only $adff and $dffsr cells are supported by this pass.\n");
+ log("Currently only $adff, $dffsr, and $dlatch cells are supported by this pass.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -169,6 +169,41 @@ struct Async2syncPass : public Pass {
cell->type = "$dff";
continue;
}
+
+ if (cell->type.in("$dlatch"))
+ {
+ bool en_pol = cell->parameters["\\EN_POLARITY"].as_bool();
+
+ SigSpec sig_en = cell->getPort("\\EN");
+ SigSpec sig_d = cell->getPort("\\D");
+ SigSpec sig_q = cell->getPort("\\Q");
+
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(sig_en), log_signal(sig_d), log_signal(sig_q));
+
+ 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_q = module->addWire(NEW_ID, GetSize(sig_q));
+ new_q->attributes["\\init"] = init_val;
+
+ if (en_pol) {
+ module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q);
+ } else {
+ module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
+ }
+
+ cell->setPort("\\Q", new_q);
+ cell->unsetPort("\\EN");
+ cell->unsetParam("\\EN_POLARITY");
+ cell->type = "$ff";
+ continue;
+ }
}
for (auto wire : module->wires())
diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc
index dd56d8c71..430bba1e8 100644
--- a/passes/sat/sat.cc
+++ b/passes/sat/sat.cc
@@ -268,7 +268,7 @@ struct SatHelper
RTLIL::SigSpec removed_bits;
for (int i = 0; i < lhs.size(); i++) {
RTLIL::SigSpec bit = lhs.extract(i, 1);
- if (!satgen.initial_state.check_all(bit)) {
+ if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) {
removed_bits.append(bit);
lhs.remove(i, 1);
rhs.remove(i, 1);
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 56f05eca4..631a80aa5 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -16,6 +16,7 @@ endif
ifneq ($(SMALL),1)
OBJS += passes/techmap/iopadmap.o
+OBJS += passes/techmap/clkbufmap.o
OBJS += passes/techmap/hilomap.o
OBJS += passes/techmap/extract.o
OBJS += passes/techmap/extract_fa.o
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index c1d4fff2d..6fdf987f0 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -76,12 +76,11 @@ inline std::string remap_name(RTLIL::IdString abc_name)
return stringf("$abc$%d$%s", map_autoidx, abc_name.c_str()+1);
}
-void handle_loops(RTLIL::Design *design)
+void handle_loops(RTLIL::Design *design,
+ const dict<IdString,pool<IdString>> &scc_break_inputs)
{
Pass::call(design, "scc -set_attr abc_scc_id {}");
- dict<IdString, vector<IdString>> abc_scc_break;
-
// For every unique SCC found, (arbitrarily) find the first
// cell in the component, and select (and mark) all its output
// wires
@@ -116,44 +115,29 @@ void handle_loops(RTLIL::Design *design)
cell->attributes.erase(it);
}
- auto jt = abc_scc_break.find(cell->type);
- if (jt == abc_scc_break.end()) {
- std::vector<IdString> ports;
- RTLIL::Module* box_module = design->module(cell->type);
- if (box_module) {
- auto ports_csv = box_module->attributes.at(ID(abc_scc_break), RTLIL::Const::from_string("")).decode_string();
- for (const auto &port_name : split_tokens(ports_csv, ",")) {
- auto port_id = RTLIL::escape_id(port_name);
- auto kt = cell->connections_.find(port_id);
- if (kt == cell->connections_.end())
- log_error("abc_scc_break attribute value '%s' does not exist as port on module '%s'\n", port_name.c_str(), log_id(box_module));
- ports.push_back(port_id);
- }
- }
- jt = abc_scc_break.insert(std::make_pair(cell->type, std::move(ports))).first;
- }
-
- for (auto port_name : jt->second) {
- RTLIL::SigSpec sig;
- auto &rhs = cell->connections_.at(port_name);
- for (auto b : rhs) {
- Wire *w = b.wire;
- if (!w) continue;
- w->port_output = true;
- w->set_bool_attribute(ID(abc_scc_break));
- w = module->wire(stringf("%s.abci", w->name.c_str()));
- if (!w) {
- w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
- w->port_input = true;
- }
- else {
- log_assert(b.offset < GetSize(w));
- log_assert(w->port_input);
+ auto jt = scc_break_inputs.find(cell->type);
+ if (jt != scc_break_inputs.end())
+ for (auto port_name : jt->second) {
+ RTLIL::SigSpec sig;
+ auto &rhs = cell->connections_.at(port_name);
+ for (auto b : rhs) {
+ Wire *w = b.wire;
+ if (!w) continue;
+ w->port_output = true;
+ w->set_bool_attribute(ID(abc_scc_break));
+ w = module->wire(stringf("%s.abci", w->name.c_str()));
+ if (!w) {
+ w = module->addWire(stringf("%s.abci", b.wire->name.c_str()), GetSize(b.wire));
+ w->port_input = true;
+ }
+ else {
+ log_assert(b.offset < GetSize(w));
+ log_assert(w->port_input);
+ }
+ sig.append(RTLIL::SigBit(w, b.offset));
}
- sig.append(RTLIL::SigBit(w, b.offset));
+ rhs = sig;
}
- rhs = sig;
- }
}
module->fixup_ports();
@@ -288,7 +272,9 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
- std::string wire_delay, const dict<int,IdString> &box_lookup)
+ std::string wire_delay, const dict<int,IdString> &box_lookup,
+ const dict<IdString,pool<IdString>> &scc_break_inputs
+)
{
module = current_module;
map_autoidx = autoidx++;
@@ -427,7 +413,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
RTLIL::Selection& sel = design->selection_stack.back();
sel.select(module);
- handle_loops(design);
+ handle_loops(design, scc_break_inputs);
Pass::call(design, "aigmap");
@@ -708,30 +694,27 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
int in_wires = 0, out_wires = 0;
// Stitch in mapped_mod's inputs/outputs into module
- for (auto &it : mapped_mod->wires_) {
- RTLIL::Wire *w = it.second;
- if (!w->port_input && !w->port_output)
- continue;
- RTLIL::Wire *wire = module->wire(w->name);
+ for (auto port : mapped_mod->ports) {
+ RTLIL::Wire *w = mapped_mod->wire(port);
+ RTLIL::Wire *wire = module->wire(port);
log_assert(wire);
- RTLIL::Wire *remap_wire = module->wire(remap_name(w->name));
+ RTLIL::Wire *remap_wire = module->wire(remap_name(port));
RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
log_assert(GetSize(signal) >= GetSize(remap_wire));
- log_assert(w->port_input || w->port_output);
RTLIL::SigSig conn;
- if (w->port_input) {
- conn.first = remap_wire;
- conn.second = signal;
- in_wires++;
- module->connect(conn);
- }
if (w->port_output) {
conn.first = signal;
conn.second = remap_wire;
out_wires++;
module->connect(conn);
}
+ else if (w->port_input) {
+ conn.first = remap_wire;
+ conn.second = signal;
+ in_wires++;
+ module->connect(conn);
+ }
}
for (auto &it : bit_users)
@@ -1081,6 +1064,7 @@ struct Abc9Pass : public Pass {
extra_args(args, argidx, design);
dict<int,IdString> box_lookup;
+ dict<IdString,pool<IdString>> scc_break_inputs;
for (auto m : design->modules()) {
auto it = m->attributes.find(ID(abc_box_id));
if (it == m->attributes.end())
@@ -1093,6 +1077,56 @@ struct Abc9Pass : public Pass {
log_error("Module '%s' has the same abc_box_id = %d value as '%s'.\n",
log_id(m), id, log_id(r.first->second));
log_assert(r.second);
+
+ RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
+ for (auto p : m->ports) {
+ auto w = m->wire(p);
+ log_assert(w);
+ if (w->port_input) {
+ if (w->attributes.count(ID(abc_scc_break)))
+ scc_break_inputs[m->name].insert(p);
+ if (w->attributes.count(ID(abc_carry))) {
+ if (carry_in)
+ log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
+ carry_in = w;
+ }
+ }
+ if (w->port_output) {
+ if (w->attributes.count(ID(abc_carry))) {
+ if (carry_out)
+ log_error("Module '%s' contains more than one 'abc_carry' input port.\n", log_id(m));
+ carry_out = w;
+ }
+ }
+ }
+ if (carry_in || carry_out) {
+ if (carry_in && !carry_out)
+ log_error("Module '%s' contains an 'abc_carry' input port but no output port.\n", log_id(m));
+ if (!carry_in && carry_out)
+ log_error("Module '%s' contains an 'abc_carry' output port but no input port.\n", log_id(m));
+ // Make carry_in the last PI, and carry_out the last PO
+ // since ABC requires it this way
+ auto &ports = m->ports;
+ for (auto it = ports.begin(); it != ports.end(); ) {
+ RTLIL::Wire* w = m->wire(*it);
+ log_assert(w);
+ if (w == carry_in || w == carry_out) {
+ it = ports.erase(it);
+ continue;
+ }
+ if (w->port_id > carry_in->port_id)
+ --w->port_id;
+ if (w->port_id > carry_out->port_id)
+ --w->port_id;
+ log_assert(w->port_input || w->port_output);
+ log_assert(ports[w->port_id-1] == w->name);
+ ++it;
+ }
+ ports.push_back(carry_in->name);
+ carry_in->port_id = ports.size();
+ ports.push_back(carry_out->name);
+ carry_out->port_id = ports.size();
+ }
}
for (auto mod : design->selected_modules())
@@ -1110,7 +1144,7 @@ struct Abc9Pass : public Pass {
if (!dff_mode || !clk_str.empty()) {
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup);
+ box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
continue;
}
@@ -1256,16 +1290,13 @@ struct Abc9Pass : public Pass {
en_sig = assign_map(std::get<3>(it.first));
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
- box_file, lut_file, wire_delay, box_lookup);
+ box_file, lut_file, wire_delay, box_lookup, scc_break_inputs);
assign_map.set(mod);
}
}
assign_map.clear();
- // The "clean" pass also contains a design->check() call
- Pass::call(design, "clean");
-
log_pop();
}
} Abc9Pass;
diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc
index a38638e0b..5f30817d4 100644
--- a/passes/techmap/attrmap.cc
+++ b/passes/techmap/attrmap.cc
@@ -143,6 +143,82 @@ void attrmap_apply(string objname, vector<std::unique_ptr<AttrmapAction>> &actio
attributes.swap(new_attributes);
}
+void log_attrmap_paramap_options()
+{
+ log(" -tocase <name>\n");
+ log(" Match attribute names case-insensitively and set it to the specified\n");
+ log(" name.\n");
+ log("\n");
+ log(" -rename <old_name> <new_name>\n");
+ log(" Rename attributes as specified\n");
+ log("\n");
+ log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Map key/value pairs as indicated.\n");
+ log("\n");
+ log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
+ log(" Like -map, but use case-insensitive match for <old_value> when\n");
+ log(" it is a string value.\n");
+ log("\n");
+ log(" -remove <name>=<value>\n");
+ log(" Remove attributes matching this pattern.\n");
+}
+
+bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &args, vector<std::unique_ptr<AttrmapAction>> &actions)
+{
+ std::string arg = args[argidx];
+ if (arg == "-tocase" && argidx+1 < args.size()) {
+ auto action = new AttrmapTocase;
+ action->name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-rename" && argidx+2 < args.size()) {
+ auto action = new AttrmapRename;
+ action->old_name = args[++argidx];
+ action->new_name = args[++argidx];
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
+ string arg1 = args[++argidx];
+ string arg2 = args[++argidx];
+ string val1, val2;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ p = arg2.find("=");
+ if (p != string::npos) {
+ val2 = arg2.substr(p+1);
+ arg2 = arg2.substr(0, p);
+ }
+ auto action = new AttrmapMap;
+ action->imap = (arg == "-map");
+ action->old_name = arg1;
+ action->new_name = arg2;
+ action->old_value = val1;
+ action->new_value = val2;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ if (arg == "-remove" && argidx+1 < args.size()) {
+ string arg1 = args[++argidx], val1;
+ size_t p = arg1.find("=");
+ if (p != string::npos) {
+ val1 = arg1.substr(p+1);
+ arg1 = arg1.substr(0, p);
+ }
+ auto action = new AttrmapRemove;
+ action->name = arg1;
+ action->has_value = (p != string::npos);
+ action->value = val1;
+ actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ return true;
+ }
+ return false;
+}
+
struct AttrmapPass : public Pass {
AttrmapPass() : Pass("attrmap", "renaming attributes") { }
void help() YS_OVERRIDE
@@ -151,25 +227,10 @@ struct AttrmapPass : public Pass {
log("\n");
log(" attrmap [options] [selection]\n");
log("\n");
- log("This command renames attributes and/or mapps key/value pairs to\n");
+ log("This command renames attributes and/or maps key/value pairs to\n");
log("other key/value pairs.\n");
log("\n");
- log(" -tocase <name>\n");
- log(" Match attribute names case-insensitively and set it to the specified\n");
- log(" name.\n");
- log("\n");
- log(" -rename <old_name> <new_name>\n");
- log(" Rename attributes as specified\n");
- log("\n");
- log(" -map <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Map key/value pairs as indicated.\n");
- log("\n");
- log(" -imap <old_name>=<old_value> <new_name>=<new_value>\n");
- log(" Like -map, but use case-insensitive match for <old_value> when\n");
- log(" it is a string value.\n");
- log("\n");
- log(" -remove <name>=<value>\n");
- log(" Remove attributes matching this pattern.\n");
+ log_attrmap_paramap_options();
log("\n");
log(" -modattr\n");
log(" Operate on module attributes instead of attributes on wires and cells.\n");
@@ -190,58 +251,9 @@ struct AttrmapPass : public Pass {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
- std::string arg = args[argidx];
- if (arg == "-tocase" && argidx+1 < args.size()) {
- auto action = new AttrmapTocase;
- action->name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-rename" && argidx+2 < args.size()) {
- auto action = new AttrmapRename;
- action->old_name = args[++argidx];
- action->new_name = args[++argidx];
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
+ if (parse_attrmap_paramap_options(argidx, args, actions))
continue;
- }
- if ((arg == "-map" || arg == "-imap") && argidx+2 < args.size()) {
- string arg1 = args[++argidx];
- string arg2 = args[++argidx];
- string val1, val2;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- p = arg2.find("=");
- if (p != string::npos) {
- val2 = arg2.substr(p+1);
- arg2 = arg2.substr(0, p);
- }
- auto action = new AttrmapMap;
- action->imap = (arg == "-map");
- action->old_name = arg1;
- action->new_name = arg2;
- action->old_value = val1;
- action->new_value = val2;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-remove" && argidx+1 < args.size()) {
- string arg1 = args[++argidx], val1;
- size_t p = arg1.find("=");
- if (p != string::npos) {
- val1 = arg1.substr(p+1);
- arg1 = arg1.substr(0, p);
- }
- auto action = new AttrmapRemove;
- action->name = arg1;
- action->has_value = (p != string::npos);
- action->value = val1;
- actions.push_back(std::unique_ptr<AttrmapAction>(action));
- continue;
- }
- if (arg == "-modattr") {
+ if (args[argidx] == "-modattr") {
modattr_mode = true;
continue;
}
@@ -287,4 +299,43 @@ struct AttrmapPass : public Pass {
}
} AttrmapPass;
+struct ParamapPass : public Pass {
+ ParamapPass() : Pass("paramap", "renaming cell parameters") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" paramap [options] [selection]\n");
+ log("\n");
+ log("This command renames cell parameters and/or maps key/value pairs to\n");
+ log("other key/value pairs.\n");
+ log("\n");
+ log_attrmap_paramap_options();
+ log("\n");
+ log("For example, mapping Diamond-style ECP5 \"init\" attributes to Yosys-style:\n");
+ log("\n");
+ log(" paramap -tocase INIT t:LUT4\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n");
+
+ vector<std::unique_ptr<AttrmapAction>> actions;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (parse_attrmap_paramap_options(argidx, args, actions))
+ continue;
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ for (auto cell : module->selected_cells())
+ attrmap_apply(stringf("%s.%s", log_id(module), log_id(cell)), actions, cell->parameters);
+ }
+} ParamapPass;
+
PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc
new file mode 100644
index 000000000..246932d81
--- /dev/null
+++ b/passes/techmap/clkbufmap.cc
@@ -0,0 +1,298 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Marcin Kościelnicki <mwk@0x04.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+ size_t pos = port1.find_first_of(':');
+ if (pos != std::string::npos) {
+ port2 = port1.substr(pos+1);
+ port1 = port1.substr(0, pos);
+ }
+}
+
+struct ClkbufmapPass : public Pass {
+ ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" clkbufmap [options] [selection]\n");
+ log("\n");
+ log("Inserts global buffers between nets connected to clock inputs and their drivers.\n");
+ log("\n");
+ log("In the absence of any selection, all wires without the 'clkbuf_inhibit'\n");
+ log("attribute will be considered for global buffer insertion.\n");
+ log("Alternatively, to consider all wires without the 'buffer_type' attribute set to\n");
+ log("'none' or 'bufr' one would specify:\n");
+ log(" 'w:* a:buffer_type=none a:buffer_type=bufr %%u %%d'\n");
+ log("as the selection.\n");
+ log("\n");
+ log(" -buf <celltype> <portname_out>:<portname_in>\n");
+ log(" Specifies the cell type to use for the global buffers\n");
+ log(" and its port names. The first port will be connected to\n");
+ log(" the clock network sinks, and the second will be connected\n");
+ log(" to the actual clock source. This option is required.\n");
+ log("\n");
+ log(" -inpad <celltype> <portname_out>:<portname_in>\n");
+ log(" If specified, a PAD cell of the given type is inserted on\n");
+ log(" clock nets that are also top module's inputs (in addition\n");
+ log(" to the global buffer).\n");
+ log("\n");
+ }
+
+ void module_queue(Design *design, Module *module, std::vector<Module *> &modules_sorted, pool<Module *> &modules_processed) {
+ if (modules_processed.count(module))
+ return;
+ for (auto cell : module->cells()) {
+ Module *submodule = design->module(cell->type);
+ if (!submodule)
+ continue;
+ module_queue(design, submodule, modules_sorted, modules_processed);
+ }
+ modules_sorted.push_back(module);
+ modules_processed.insert(module);
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n");
+
+ std::string buf_celltype, buf_portname, buf_portname2;
+ std::string inpad_celltype, inpad_portname, inpad_portname2;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ std::string arg = args[argidx];
+ if (arg == "-buf" && argidx+2 < args.size()) {
+ buf_celltype = args[++argidx];
+ buf_portname = args[++argidx];
+ split_portname_pair(buf_portname, buf_portname2);
+ continue;
+ }
+ if (arg == "-inpad" && argidx+2 < args.size()) {
+ inpad_celltype = args[++argidx];
+ inpad_portname = args[++argidx];
+ split_portname_pair(inpad_portname, inpad_portname2);
+ continue;
+ }
+ break;
+ }
+
+ bool select = false;
+ if (argidx < args.size()) {
+ if (args[argidx].compare(0, 1, "-") != 0)
+ select = true;
+ extra_args(args, argidx, design);
+ }
+
+ if (buf_celltype.empty())
+ log_error("The -buf option is required.\n");
+
+ // Cell type, port name, bit index.
+ pool<pair<IdString, pair<IdString, int>>> sink_ports;
+ pool<pair<IdString, pair<IdString, int>>> buf_ports;
+
+ // Process submodules before module using them.
+ std::vector<Module *> modules_sorted;
+ pool<Module *> modules_processed;
+ for (auto module : design->selected_modules())
+ module_queue(design, module, modules_sorted, modules_processed);
+
+ for (auto module : modules_sorted)
+ {
+ if (module->get_blackbox_attribute()) {
+ for (auto port : module->ports) {
+ auto wire = module->wire(port);
+ if (wire->get_bool_attribute("\\clkbuf_driver"))
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ if (wire->get_bool_attribute("\\clkbuf_sink"))
+ for (int i = 0; i < GetSize(wire); i++)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ continue;
+ }
+ pool<SigBit> sink_wire_bits;
+ pool<SigBit> buf_wire_bits;
+ pool<SigBit> driven_wire_bits;
+ SigMap sigmap(module);
+ // bit -> (buffer, buffer's input)
+ dict<SigBit, pair<Cell *, Wire *>> buffered_bits;
+
+ // First, collect nets that could use a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (sink_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ sink_wire_bits.insert(sigmap(port.second[i]));
+
+ // Second, collect ones that already have a clock buffer.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ for (int i = 0; i < port.second.size(); i++)
+ if (buf_ports.count(make_pair(cell->type, make_pair(port.first, i))))
+ buf_wire_bits.insert(sigmap(port.second[i]));
+
+ // Collect all driven bits.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections())
+ if (cell->output(port.first))
+ for (int i = 0; i < port.second.size(); i++)
+ driven_wire_bits.insert(port.second[i]);
+
+ // Insert buffers.
+ std::vector<pair<Wire *, Wire *>> input_queue;
+ // Copy current wire list, as we will be adding new ones during iteration.
+ std::vector<Wire *> wires(module->wires());
+ for (auto wire : wires)
+ {
+ // Should not happen.
+ if (wire->port_input && wire->port_output)
+ continue;
+ bool process_wire = module->selected(wire);
+ if (!select && wire->get_bool_attribute("\\clkbuf_inhibit"))
+ process_wire = false;
+ if (!process_wire) {
+ // This wire is supposed to be bypassed, so make sure we don't buffer it in
+ // some buffer higher up in the hierarchy.
+ if (wire->port_output)
+ for (int i = 0; i < GetSize(wire); i++)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ continue;
+ }
+
+ pool<int> input_bits;
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buf_wire_bits.count(mapped_wire_bit)) {
+ // Already buffered downstream. If this is an output, mark it.
+ if (wire->port_output)
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ } else if (!sink_wire_bits.count(mapped_wire_bit)) {
+ // Nothing to do.
+ } else if (driven_wire_bits.count(wire_bit) || (wire->port_input && module->get_bool_attribute("\\top"))) {
+ // Clock network not yet buffered, driven by one of
+ // our cells or a top-level input -- buffer it.
+
+ log("Inserting %s on %s.%s[%d].\n", buf_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell = module->addCell(NEW_ID, RTLIL::escape_id(buf_celltype));
+ Wire *iwire = module->addWire(NEW_ID);
+ cell->setPort(RTLIL::escape_id(buf_portname), mapped_wire_bit);
+ cell->setPort(RTLIL::escape_id(buf_portname2), iwire);
+ if (wire->port_input && !inpad_celltype.empty() && module->get_bool_attribute("\\top")) {
+ log("Inserting %s on %s.%s[%d].\n", inpad_celltype.c_str(), log_id(module), log_id(wire), i);
+ RTLIL::Cell *cell2 = module->addCell(NEW_ID, RTLIL::escape_id(inpad_celltype));
+ cell2->setPort(RTLIL::escape_id(inpad_portname), iwire);
+ iwire = module->addWire(NEW_ID);
+ cell2->setPort(RTLIL::escape_id(inpad_portname2), iwire);
+ }
+ buffered_bits[mapped_wire_bit] = make_pair(cell, iwire);
+
+ if (wire->port_input) {
+ input_bits.insert(i);
+ }
+ } else if (wire->port_input) {
+ // A clock input in a submodule -- mark it, let higher level
+ // worry about it.
+ if (wire->port_input)
+ sink_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+ if (!input_bits.empty()) {
+ // This is an input port and some buffers were inserted -- we need
+ // to create a new input wire and transfer attributes.
+ Wire *new_wire = module->addWire(NEW_ID, wire);
+
+ for (int i = 0; i < wire->width; i++) {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ auto it = buffered_bits.find(mapped_wire_bit);
+ if (it != buffered_bits.end()) {
+
+ module->connect(it->second.second, SigSpec(new_wire, i));
+ } else {
+ module->connect(SigSpec(wire, i), SigSpec(new_wire, i));
+ }
+ }
+ input_queue.push_back(make_pair(wire, new_wire));
+ }
+ }
+
+ // Mark any newly-buffered output ports as such.
+ for (auto wire : module->selected_wires()) {
+ if (wire->port_input || !wire->port_output)
+ continue;
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wire_bit(wire, i);
+ SigBit mapped_wire_bit = sigmap(wire_bit);
+ if (buffered_bits.count(mapped_wire_bit))
+ buf_ports.insert(make_pair(module->name, make_pair(wire->name, i)));
+ }
+ }
+
+ // Reconnect the drivers to buffer inputs.
+ for (auto cell : module->cells())
+ for (auto port : cell->connections()) {
+ if (!cell->output(port.first))
+ continue;
+ SigSpec sig = port.second;
+ bool newsig = false;
+ for (auto &bit : sig) {
+ const auto it = buffered_bits.find(sigmap(bit));
+ if (it == buffered_bits.end())
+ continue;
+ // Avoid substituting buffer's own output pin.
+ if (cell == it->second.first)
+ continue;
+ bit = it->second.second;
+ newsig = true;
+ }
+ if (newsig)
+ cell->setPort(port.first, sig);
+ }
+
+ // This has to be done last, to avoid upsetting sigmap before the port reconnections.
+ for (auto &it : input_queue) {
+ Wire *wire = it.first;
+ Wire *new_wire = it.second;
+ module->swap_names(new_wire, wire);
+ wire->attributes.clear();
+ wire->port_id = 0;
+ wire->port_input = false;
+ wire->port_output = false;
+ }
+
+ module->fixup_ports();
+ }
+ }
+} ClkbufmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc
index e766f2cf6..0242256e5 100644
--- a/passes/techmap/dff2dffe.cc
+++ b/passes/techmap/dff2dffe.cc
@@ -265,7 +265,7 @@ struct Dff2dffePass : public Pass {
log("\n");
log(" -unmap\n");
log(" operate in the opposite direction: replace $dffe cells with combinations\n");
- log(" of $dff and $mux cells. the options below are ignore in unmap mode.\n");
+ log(" of $dff and $mux cells. the options below are ignored 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");
diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc
index a2551316f..c868b9a87 100644
--- a/passes/techmap/iopadmap.cc
+++ b/passes/techmap/iopadmap.cc
@@ -64,6 +64,11 @@ struct IopadmapPass : public Pass {
log(" of the tristate driver and the 2nd portname is the internal output\n");
log(" buffering the external signal.\n");
log("\n");
+ log(" -ignore <celltype> <portname>[:<portname>]*\n");
+ log(" Skips mapping inputs/outputs that are already connected to given\n");
+ log(" ports of the given cell. Can be used multiple times. This is in\n");
+ log(" addition to the cells specified as mapping targets.\n");
+ log("\n");
log(" -widthparam <param_name>\n");
log(" Use the specified parameter name to set the port width.\n");
log("\n");
@@ -88,6 +93,7 @@ struct IopadmapPass : public Pass {
std::string toutpad_celltype, toutpad_portname, toutpad_portname2, toutpad_portname3;
std::string tinoutpad_celltype, tinoutpad_portname, tinoutpad_portname2, tinoutpad_portname3, tinoutpad_portname4;
std::string widthparam, nameparam;
+ pool<pair<IdString, IdString>> ignore;
bool flag_bits = false;
size_t argidx;
@@ -127,6 +133,18 @@ struct IopadmapPass : public Pass {
split_portname_pair(tinoutpad_portname3, tinoutpad_portname4);
continue;
}
+ if (arg == "-ignore" && argidx+2 < args.size()) {
+ std::string ignore_celltype = args[++argidx];
+ std::string ignore_portname = args[++argidx];
+ std::string ignore_portname2;
+ while (!ignore_portname.empty()) {
+ split_portname_pair(ignore_portname, ignore_portname2);
+ ignore.insert(make_pair(RTLIL::escape_id(ignore_celltype), RTLIL::escape_id(ignore_portname)));
+
+ ignore_portname = ignore_portname2;
+ }
+ continue;
+ }
if (arg == "-widthparam" && argidx+1 < args.size()) {
widthparam = args[++argidx];
continue;
@@ -143,6 +161,23 @@ struct IopadmapPass : public Pass {
}
extra_args(args, argidx, design);
+ if (!inpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname2)));
+ if (!outpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(outpad_celltype), RTLIL::escape_id(outpad_portname2)));
+ if (!inoutpad_portname2.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(inoutpad_celltype), RTLIL::escape_id(inoutpad_portname2)));
+ if (!toutpad_portname3.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(toutpad_celltype), RTLIL::escape_id(toutpad_portname3)));
+ if (!tinoutpad_portname4.empty())
+ ignore.insert(make_pair(RTLIL::escape_id(tinoutpad_celltype), RTLIL::escape_id(tinoutpad_portname4)));
+
+ for (auto module : design->modules())
+ if (module->get_blackbox_attribute())
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute("\\iopad_external_pin"))
+ ignore.insert(make_pair(module->name, wire->name));
+
for (auto module : design->selected_modules())
{
dict<IdString, pool<int>> skip_wires;
@@ -150,28 +185,11 @@ struct IopadmapPass : public Pass {
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))))
+ for (auto port : cell->connections())
+ if (ignore.count(make_pair(cell->type, port.first)))
+ for (auto bit : sigmap(port.second))
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())
{
dict<SigBit, pair<IdString, pool<IdString>>> tbuf_bits;
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 5e298d8dd..be00e5030 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
- virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
+ virtual bool analyze(vector<int> &taps) = 0;
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
@@ -56,7 +54,7 @@ struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
+ bool analyze(vector<int> &taps)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
@@ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech
}
};
-struct ShregmapTechXilinx7 : ShregmapTech
-{
- dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
- const ShregmapOptions &opts;
-
- ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
-
- virtual void init(const Module* module, const SigMap &sigmap) override
- {
- for (const auto &i : module->cells_) {
- auto cell = i.second;
- if (cell->type == ID($shiftx)) {
- if (cell->getParam(ID(Y_WIDTH)) != 1) continue;
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
- log_assert(j == cell->getParam(ID(A_WIDTH)).as_int());
- }
- else if (cell->type == ID($mux)) {
- int j = 0;
- for (auto bit : sigmap(cell->getPort(ID::A)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
- j = 0;
- for (auto bit : sigmap(cell->getPort(ID::B)))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
- }
- }
- }
-
- virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
- {
- auto it = sigbit_to_shiftx_offset.find(bit);
- if (it == sigbit_to_shiftx_offset.end())
- return;
- if (cell) {
- if (cell->type == ID($shiftx) && port == ID::A)
- return;
- if (cell->type == ID($mux) && port.in(ID::A, ID::B))
- return;
- }
- sigbit_to_shiftx_offset.erase(it);
- }
-
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
- {
- if (GetSize(taps) == 1)
- return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
-
- if (taps.back() < opts.minlen-1)
- return false;
-
- Cell *shiftx = nullptr;
- int group = 0;
- for (int i = 0; i < GetSize(taps); ++i) {
- auto it = sigbit_to_shiftx_offset.find(qbits[i]);
- if (it == sigbit_to_shiftx_offset.end())
- return false;
-
- // Check taps are sequential
- if (i != taps[i])
- return false;
- // Check taps are not connected to a shift register,
- // or sequential to the same shift register
- if (i == 0) {
- int offset;
- std::tie(shiftx,offset,group) = it->second;
- if (offset != i)
- return false;
- }
- else {
- Cell *shiftx_ = std::get<0>(it->second);
- if (shiftx_ != shiftx)
- return false;
- int offset = std::get<1>(it->second);
- if (offset != i)
- return false;
- int group_ = std::get<2>(it->second);
- if (group_ != group)
- return false;
- }
- }
- log_assert(shiftx);
-
- // Only map if $shiftx exclusively covers the shift register
- if (shiftx->type == ID($shiftx)) {
- if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- // Due to padding the most significant bits of A may be 1'bx,
- // and if so, discount them
- if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) {
- const SigSpec A = shiftx->getPort(ID::A);
- const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int();
- for (int i = GetSize(taps); i < A_width; ++i)
- if (A[i] != RTLIL::Sx) return false;
- }
- else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int())
- return false;
- }
- else if (shiftx->type == ID($mux)) {
- if (GetSize(taps) != 2)
- return false;
- }
- else log_abort();
-
- return true;
- }
-
- virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
- {
- const auto &tap = *taps.begin();
- auto bit = tap.second;
-
- auto it = sigbit_to_shiftx_offset.find(bit);
- log_assert(it != sigbit_to_shiftx_offset.end());
-
- auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_));
- newcell->set_src_attribute(cell->get_src_attribute());
- newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH)));
- newcell->setParam(ID(INIT), cell->getParam(ID(INIT)));
- newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL)));
- newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL)));
-
- newcell->setPort(ID(C), cell->getPort(ID(C)));
- newcell->setPort(ID(D), cell->getPort(ID(D)));
- if (cell->hasPort(ID(E)))
- newcell->setPort(ID(E), cell->getPort(ID(E)));
-
- Cell* shiftx = std::get<0>(it->second);
- RTLIL::SigSpec l_wire, q_wire;
- if (shiftx->type == ID($shiftx)) {
- l_wire = shiftx->getPort(ID::B);
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else if (shiftx->type == ID($mux)) {
- l_wire = shiftx->getPort(ID(S));
- q_wire = shiftx->getPort(ID::Y);
- shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
- }
- else log_abort();
-
- newcell->setPort(ID(Q), q_wire);
- newcell->setPort(ID(L), l_wire);
-
- return false;
- }
-};
-
-
struct ShregmapWorker
{
Module *module;
@@ -264,10 +113,8 @@ struct ShregmapWorker
for (auto wire : module->wires())
{
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
- for (auto bit : sigmap(wire)) {
+ for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
- }
}
if (wire->attributes.count(ID(init))) {
@@ -317,10 +164,8 @@ struct ShregmapWorker
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second)) {
+ for (auto bit : sigmap(conn.second))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
- }
}
}
@@ -346,7 +191,7 @@ struct ShregmapWorker
IdString q_port = opts.ffcells.at(c1->type).second;
auto c1_conn = c1->connections();
- auto c2_conn = c1->connections();
+ auto c2_conn = c2->connections();
c1_conn.erase(d_port);
c1_conn.erase(q_port);
@@ -425,7 +270,7 @@ struct ShregmapWorker
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps, qbits))
+ if (opts.tech->analyze(taps))
break;
taps.pop_back();
@@ -544,9 +389,6 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
- if (opts.tech)
- opts.tech->init(module, sigmap);
-
make_sigbit_chain_next_prev();
find_chain_start_cells();
@@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {
log("\n");
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
- log(" this option also implies -clkpol pos -zinit\n");
- log("\n");
- log(" -tech xilinx\n");
- log(" map to xilinx dynamic-length shift registers.\n");
- log(" this option also implies -params -init\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -676,12 +513,6 @@ struct ShregmapPass : public Pass {
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
- }
- else if (tech == "xilinx") {
- opts.init = true;
- opts.params = true;
- enpol = "any_or_none";
- opts.tech = new ShregmapTechXilinx7(opts);
} else {
argidx--;
break;
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index b271c8781..c4496f76f 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -943,7 +943,8 @@ struct TechmapPass : public Pass {
log(" instead of inlining them.\n");
log("\n");
log(" -max_iter <number>\n");
- log(" only run the specified number of iterations.\n");
+ log(" only run the specified number of iterations on each module.\n");
+ log(" default: unlimited\n");
log("\n");
log(" -recursive\n");
log(" instead of the iterative breadth-first algorithm use a recursive\n");
@@ -1157,15 +1158,16 @@ struct TechmapPass : public Pass {
RTLIL::Module *module = *worker.module_queue.begin();
worker.module_queue.erase(module);
+ int module_max_iter = max_iter;
bool did_something = true;
std::set<RTLIL::Cell*> handled_cells;
while (did_something) {
did_something = false;
- if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
- did_something = true;
+ if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
+ did_something = true;
if (did_something)
module->check();
- if (max_iter > 0 && --max_iter == 0)
+ if (module_max_iter > 0 && --module_max_iter == 0)
break;
}
}
diff --git a/techlibs/anlogic/Makefile.inc b/techlibs/anlogic/Makefile.inc
index 67cf9cf10..9426b5ca5 100644
--- a/techlibs/anlogic/Makefile.inc
+++ b/techlibs/anlogic/Makefile.inc
@@ -1,7 +1,7 @@
OBJS += techlibs/anlogic/synth_anlogic.o
OBJS += techlibs/anlogic/anlogic_eqn.o
-OBJS += techlibs/anlogic/anlogic_determine_init.o
+OBJS += techlibs/anlogic/anlogic_fixcarry.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))
diff --git a/techlibs/anlogic/anlogic_determine_init.cc b/techlibs/anlogic/anlogic_determine_init.cc
deleted file mode 100644
index c4089dac2..000000000
--- a/techlibs/anlogic/anlogic_determine_init.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2018 Icenowy Zheng <icenowy@aosc.io>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "kernel/yosys.h"
-#include "kernel/sigtools.h"
-
-USING_YOSYS_NAMESPACE
-PRIVATE_NAMESPACE_BEGIN
-
-struct 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<std::string> 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);
-
- int 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 %d cells with determined init value.\n", cnt);
- }
-} AnlogicDetermineInitPass;
-
-PRIVATE_NAMESPACE_END
diff --git a/techlibs/anlogic/anlogic_fixcarry.cc b/techlibs/anlogic/anlogic_fixcarry.cc
new file mode 100644
index 000000000..87164d375
--- /dev/null
+++ b/techlibs/anlogic/anlogic_fixcarry.cc
@@ -0,0 +1,130 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+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 fix_carry_chain(Module *module)
+{
+ SigMap sigmap(module);
+
+ pool<SigBit> ci_bits;
+ dict<SigBit, SigBit> mapping_bits;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "\\AL_MAP_ADDER") {
+ if (cell->getParam("\\ALUTYPE") != Const("ADD")) continue;
+ SigBit bit_i0 = get_bit_or_zero(cell->getPort("\\a"));
+ SigBit bit_i1 = get_bit_or_zero(cell->getPort("\\b"));
+ if (bit_i0 == State::S0 && bit_i1== State::S0) {
+ SigBit bit_ci = get_bit_or_zero(cell->getPort("\\c"));
+ SigSpec o = cell->getPort("\\o");
+ if (GetSize(o) == 2) {
+ SigBit bit_o = o[0];
+ ci_bits.insert(bit_ci);
+ mapping_bits[bit_ci] = bit_o;
+ }
+ }
+ }
+ }
+ vector<Cell*> adders_to_fix_cells;
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "\\AL_MAP_ADDER") {
+ if (cell->getParam("\\ALUTYPE") != Const("ADD")) continue;
+ SigBit bit_ci = get_bit_or_zero(cell->getPort("\\c"));
+ SigBit bit_i0 = get_bit_or_zero(cell->getPort("\\a"));
+ SigBit bit_i1 = get_bit_or_zero(cell->getPort("\\b"));
+ SigBit canonical_bit = sigmap(bit_ci);
+ if (!ci_bits.count(canonical_bit))
+ continue;
+ if (bit_i0 == State::S0 && bit_i1== State::S0)
+ continue;
+
+ adders_to_fix_cells.push_back(cell);
+ log("Found %s cell named %s with invalid 'c' signal.\n", log_id(cell->type), log_id(cell));
+ }
+ }
+
+ for (auto cell : adders_to_fix_cells)
+ {
+ SigBit bit_ci = get_bit_or_zero(cell->getPort("\\c"));
+ SigBit canonical_bit = sigmap(bit_ci);
+ auto bit = mapping_bits.at(canonical_bit);
+ log("Fixing %s cell named %s breaking carry chain.\n", log_id(cell->type), log_id(cell));
+ Cell *c = module->addCell(NEW_ID, "\\AL_MAP_ADDER");
+ SigBit new_bit = module->addWire(NEW_ID);
+ SigBit dummy_bit = module->addWire(NEW_ID);
+ SigSpec bits;
+ bits.append(dummy_bit);
+ bits.append(new_bit);
+ c->setParam("\\ALUTYPE", Const("ADD_CARRY"));
+ c->setPort("\\a", bit);
+ c->setPort("\\b", State::S0);
+ c->setPort("\\c", State::S0);
+ c->setPort("\\o", bits);
+
+ cell->setPort("\\c", new_bit);
+ }
+
+}
+
+struct AnlogicCarryFixPass : public Pass {
+ AnlogicCarryFixPass() : Pass("anlogic_fixcarry", "Anlogic: fix carry chain") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" anlogic_fixcarry [options] [selection]\n");
+ log("\n");
+ log("Add Anlogic adders to fix carry chain if needed.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing anlogic_fixcarry pass (fix invalid carry chain).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Module *module = design->top_module();
+
+ if (module == nullptr)
+ log_cmd_error("No top module found.\n");
+
+ fix_carry_chain(module);
+ }
+} AnlogicCarryFixPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/anlogic/arith_map.v b/techlibs/anlogic/arith_map.v
index 6d6a7ca37..1186543da 100644
--- a/techlibs/anlogic/arith_map.v
+++ b/techlibs/anlogic/arith_map.v
@@ -31,7 +31,10 @@ module _80_anlogic_alu (A, B, CI, BI, X, Y, CO);
output [Y_WIDTH-1:0] X, Y;
input CI, BI;
- output CO;
+ output [Y_WIDTH-1:0] CO;
+
+ wire CIx;
+ wire [Y_WIDTH-1:0] COx;
wire _TECHMAP_FAIL_ = Y_WIDTH <= 2;
@@ -41,15 +44,16 @@ module _80_anlogic_alu (A, B, CI, BI, X, Y, CO);
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+2:0] C = {COx, CI};
+ wire [Y_WIDTH-1:0] C = { COx, CIx };
wire dummy;
AL_MAP_ADDER #(
.ALUTYPE("ADD_CARRY"))
adder_cin (
- .a(C[0]),
- .o({COx[0], dummy})
+ .a(CI),
+ .b(1'b0),
+ .c(1'b0),
+ .o({CIx, dummy})
);
genvar i;
@@ -59,18 +63,22 @@ module _80_anlogic_alu (A, B, CI, BI, X, Y, CO);
) adder_i (
.a(AA[i]),
.b(BB[i]),
- .c(C[i+1]),
- .o({COx[i+1],Y[i]})
+ .c(C[i]),
+ .o({COx[i],Y[i]})
);
- end: slice
+
+ wire cout;
+ AL_MAP_ADDER #(
+ .ALUTYPE("ADD"))
+ adder_cout (
+ .a(1'b0),
+ .b(1'b0),
+ .c(COx[i]),
+ .o({cout, CO[i]})
+ );
+ end: slice
endgenerate
- /* End implementation */
- AL_MAP_ADDER #(
- .ALUTYPE("ADD"))
- adder_cout (
- .c(C[Y_WIDTH+1]),
- .o(COx[Y_WIDTH+1])
- );
- assign CO = COx[Y_WIDTH+1];
- assign X = AA ^ BB;
-endmodule \ No newline at end of file
+
+ /* End implementation */
+ assign X = AA ^ BB;
+endmodule
diff --git a/techlibs/anlogic/synth_anlogic.cc b/techlibs/anlogic/synth_anlogic.cc
index 620bf3965..b87fc8566 100644
--- a/techlibs/anlogic/synth_anlogic.cc
+++ b/techlibs/anlogic/synth_anlogic.cc
@@ -154,7 +154,7 @@ struct SynthAnlogicPass : public ScriptPass
{
run("memory_bram -rules +/anlogic/drams.txt");
run("techmap -map +/anlogic/drams_map.v");
- run("anlogic_determine_init");
+ run("setundef -zero -params t:EG_LOGIC_DRAM16X4");
}
if (check_label("fine"))
@@ -186,6 +186,11 @@ struct SynthAnlogicPass : public ScriptPass
{
run("techmap -map +/anlogic/cells_map.v");
run("clean");
+ }
+
+ if (check_label("map_anlogic"))
+ {
+ run("anlogic_fixcarry");
run("anlogic_eqn");
}
diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc
index 555de9fba..a176357a7 100644
--- a/techlibs/common/synth.cc
+++ b/techlibs/common/synth.cc
@@ -175,7 +175,7 @@ struct SynthPass : public ScriptPass
log_cmd_error("This command only operates on fully selected designs!\n");
if (abc == "abc9" && !lut)
- log_cmd_error("ABC9 flow only supported for FPGA synthesis (using '-lut' option)");
+ log_cmd_error("ABC9 flow only supported for FPGA synthesis (using '-lut' option)\n");
log_header(design, "Executing SYNTH pass.\n");
log_push();
diff --git a/techlibs/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc
index 73e18112f..2143acae6 100644
--- a/techlibs/ecp5/Makefile.inc
+++ b/techlibs/ecp5/Makefile.inc
@@ -1,6 +1,9 @@
-OBJS += techlibs/ecp5/synth_ecp5.o techlibs/ecp5/ecp5_ffinit.o
+OBJS += techlibs/ecp5/synth_ecp5.o techlibs/ecp5/ecp5_ffinit.o \
+ techlibs/ecp5/ecp5_gsr.o
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_ff.vh))
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/cells_io.vh))
$(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))
diff --git a/techlibs/ecp5/brams_map.v b/techlibs/ecp5/brams_map.v
index b2c136863..0353cbadb 100644
--- a/techlibs/ecp5/brams_map.v
+++ b/techlibs/ecp5/brams_map.v
@@ -33,7 +33,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
.CLKBMUX(CLKBMUX),
.WRITEMODE_A(WRITEMODE_A),
.WRITEMODE_B("READBEFOREWRITE"),
- .GSR("DISABLED")
+ .GSR("AUTO")
) _TECHMAP_REPLACE_ (
`include "bram_conn_1.vh"
.CLKA(CLK2), .CLKB(CLK3),
@@ -50,7 +50,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
.CLKBMUX(CLKBMUX),
.WRITEMODE_A(WRITEMODE_A),
.WRITEMODE_B("READBEFOREWRITE"),
- .GSR("DISABLED")
+ .GSR("AUTO")
) _TECHMAP_REPLACE_ (
`include "bram_conn_2.vh"
.CLKA(CLK2), .CLKB(CLK3),
@@ -67,7 +67,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
.CLKBMUX(CLKBMUX),
.WRITEMODE_A(WRITEMODE_A),
.WRITEMODE_B("READBEFOREWRITE"),
- .GSR("DISABLED")
+ .GSR("AUTO")
) _TECHMAP_REPLACE_ (
`include "bram_conn_4.vh"
.CLKA(CLK2), .CLKB(CLK3),
@@ -84,7 +84,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
.CLKBMUX(CLKBMUX),
.WRITEMODE_A(WRITEMODE_A),
.WRITEMODE_B("READBEFOREWRITE"),
- .GSR("DISABLED")
+ .GSR("AUTO")
) _TECHMAP_REPLACE_ (
`include "bram_conn_9.vh"
.CLKA(CLK2), .CLKB(CLK3),
@@ -101,7 +101,7 @@ module \$__ECP5_DP16KD (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
.CLKBMUX(CLKBMUX),
.WRITEMODE_A(WRITEMODE_A),
.WRITEMODE_B("READBEFOREWRITE"),
- .GSR("DISABLED")
+ .GSR("AUTO")
) _TECHMAP_REPLACE_ (
`include "bram_conn_18.vh"
.CLKA(CLK2), .CLKB(CLK3),
diff --git a/techlibs/ecp5/cells_bb.v b/techlibs/ecp5/cells_bb.v
index 223e19b9e..8557053b6 100644
--- a/techlibs/ecp5/cells_bb.v
+++ b/techlibs/ecp5/cells_bb.v
@@ -664,3 +664,23 @@ module PCSCLKDIV (
);
parameter GSR = "DISABLED";
endmodule
+
+// Note: this module is not marked keep as we want it swept away in synth (sim use only)
+(* blackbox *)
+module PUR (
+ input PUR
+);
+ parameter RST_PULSE = 1;
+endmodule
+
+(* blackbox, keep *)
+module GSR (
+ input GSR
+);
+endmodule
+
+(* blackbox, keep *)
+module SGSR (
+ input GSR, CLK
+);
+endmodule \ No newline at end of file
diff --git a/techlibs/ecp5/cells_ff.vh b/techlibs/ecp5/cells_ff.vh
new file mode 100644
index 000000000..0c9689ebd
--- /dev/null
+++ b/techlibs/ecp5/cells_ff.vh
@@ -0,0 +1,40 @@
+// Diamond flip-flops
+module FD1P3AX(input D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .CE(SP), .DI(D), .Q(Q)); endmodule
+module FD1P3AY(input D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .CE(SP), .DI(D), .Q(Q)); endmodule
+module FD1P3BX(input PD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module FD1P3DX(input CD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module FD1P3IX(input CD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module FD1P3JX(input PD, D, SP, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module FD1S3AX(input D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .DI(D), .Q(Q)); endmodule
+module FD1S3AY(input D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(0), .DI(D), .Q(Q)); endmodule
+module FD1S3BX(input PD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
+module FD1S3DX(input CD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
+module FD1S3IX(input CD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
+module FD1S3JX(input PD, D, CK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
+
+// TODO: Diamond latches
+// module FL1P3AY(); endmodule
+// module FL1P3AZ(); endmodule
+// module FL1P3BX(); endmodule
+// module FL1P3DX(); endmodule
+// module FL1P3IY(); endmodule
+// module FL1P3JY(); endmodule
+// module FL1S3AX(); endmodule
+// module FL1S3AY(); endmodule
+
+// Diamond I/O registers
+module IFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module IFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+
+module OFS1P3BX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3DX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3IX(input CD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
+module OFS1P3JX(input PD, D, SP, SCLK, output Q); parameter GSR = "ENABLED"; TRELLIS_FF #(.GSR(GSR), .CEMUX("CE"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
+
+// TODO: Diamond I/O latches
+// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
+// module IFS1S1D(input CD, D, SCLK, output Q); endmodule
+// module IFS1S1I(input PD, D, SCLK, output Q); endmodule
+// module IFS1S1J(input CD, D, SCLK, output Q); endmodule
diff --git a/techlibs/ecp5/cells_io.vh b/techlibs/ecp5/cells_io.vh
new file mode 100644
index 000000000..02e66e8a5
--- /dev/null
+++ b/techlibs/ecp5/cells_io.vh
@@ -0,0 +1,14 @@
+// Diamond I/O buffers
+module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
+module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
+module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
+module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I)); endmodule
+module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
+module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
+module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
+module OBCO (input I, output OT, OC); OLVDS olvds (.A(I), .Z(OT), .ZN(OC)); endmodule
+module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
+module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
+module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
+module ILVDS(input A, AN, output Z ); TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(A), .O(Z)); endmodule
+module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(Z), .I(A)); endmodule
diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v
index 6985fbbc8..71ae9237b 100644
--- a/techlibs/ecp5/cells_map.v
+++ b/techlibs/ecp5/cells_map.v
@@ -1,105 +1,54 @@
-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
-
-// TODO: Diamond flip-flops
-// module FD1P3AX(); endmodule
-// module FD1P3AY(); endmodule
-// module FD1P3BX(); endmodule
-// module FD1P3DX(); endmodule
-// module FD1P3IX(); endmodule
-// module FD1P3JX(); endmodule
-// module FD1S3AX(); endmodule
-// module FD1S3AY(); endmodule
-module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
-module FD1S3DX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
-module FD1S3IX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
-module FD1S3JX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
-// module FL1P3AY(); endmodule
-// module FL1P3AZ(); endmodule
-// module FL1P3BX(); endmodule
-// module FL1P3DX(); endmodule
-// module FL1P3IY(); endmodule
-// module FL1P3JY(); endmodule
-// module FL1S3AX(); endmodule
-// module FL1S3AY(); endmodule
-
-// Diamond I/O buffers
-module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
-module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
-module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(I), .O(O)); endmodule
-module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I)); endmodule
-module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
-module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
-module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(O), .I(I), .T(T)); endmodule
-module OBCO (input I, output OT, OC); OLVDS _TECHMAP_REPLACE_ (.A(I), .Z(OT), .ZN(OC)); endmodule
-module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
-module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
-module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) _TECHMAP_REPLACE_ (.B(B), .I(I), .O(O), .T(T)); endmodule
-module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) _TECHMAP_REPLACE_ (.B(A), .O(Z)); endmodule
-module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) _TECHMAP_REPLACE_ (.B(Z), .I(A)); endmodule
-
-// Diamond I/O registers
-module IFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-
-module OFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) _TECHMAP_REPLACE_ (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-
-// TODO: Diamond I/O latches
-// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
-// module IFS1S1D(input CD, D, SCLK, output Q); endmodule
-// module IFS1S1I(input PD, D, SCLK, output Q); endmodule
-// module IFS1S1J(input CD, D, SCLK, output Q); endmodule
+module \$_DFF_N_ (input D, C, output Q); TRELLIS_FF #(.GSR("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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("AUTO"), .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
+
+`include "cells_ff.vh"
+`include "cells_io.vh"
`ifndef NO_LUT
module \$lut (A, Y);
diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v
index 3d343b315..5bdb8395e 100644
--- a/techlibs/ecp5/cells_sim.v
+++ b/techlibs/ecp5/cells_sim.v
@@ -15,10 +15,15 @@ module L6MUX21 (input D0, D1, SD, output Z);
endmodule
// ---------------------------------------
-(* abc_box_id=1, abc_carry="CIN,COUT", lib_whitebox *)
-module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1,
- output S0, S1, COUT);
-
+(* abc_box_id=1, lib_whitebox *)
+module CCU2C(
+ (* abc_carry *)
+ input CIN,
+ input A0, B0, C0, D0, A1, B1, C1, D1,
+ output S0, S1,
+ (* abc_carry *)
+ output COUT
+);
parameter [15:0] INIT0 = 16'h0000;
parameter [15:0] INIT1 = 16'h0000;
parameter INJECT1_0 = "YES";
@@ -104,12 +109,16 @@ module PFUMX (input ALUT, BLUT, C0, output Z);
endmodule
// ---------------------------------------
-//(* abc_box_id=2, abc_scc_break="DI,WAD,WRE" *)
+//(* abc_box_id=2 *)
module TRELLIS_DPR16X4 (
- input [3:0] DI,
- input [3:0] WAD,
- input WRE, WCK,
- input [3:0] RAD,
+ (* abc_scc_break *)
+ input [3:0] DI,
+ (* abc_scc_break *)
+ input [3:0] WAD,
+ (* abc_scc_break *)
+ input WRE,
+ input WCK,
+ input [3:0] RAD,
output [3:0] DO
);
parameter WCKMUX = "WCK";
@@ -220,14 +229,15 @@ module TRELLIS_FF(input CLK, LSR, CE, DI, M, output reg Q);
parameter REGSET = "RESET";
parameter [127:0] LSRMODE = "LSR";
- reg muxce;
- always @(*)
+ wire muxce;
+ generate
case (CEMUX)
- "1": muxce = 1'b1;
- "0": muxce = 1'b0;
- "INV": muxce = ~CE;
- default: muxce = CE;
+ "1": assign muxce = 1'b1;
+ "0": assign muxce = 1'b0;
+ "INV": assign muxce = ~CE;
+ default: assign muxce = CE;
endcase
+ endgenerate
wire muxlsr = (LSRMUX == "INV") ? ~LSR : LSR;
wire muxclk = (CLKMUX == "INV") ? ~CLK : CLK;
@@ -684,56 +694,9 @@ module DP16KD(
parameter INITVAL_3F = 320'h00000000000000000000000000000000000000000000000000000000000000000000000000000000;
endmodule
-// TODO: Diamond flip-flops
-// module FD1P3AX(); endmodule
-// module FD1P3AY(); endmodule
-// module FD1P3BX(); endmodule
-// module FD1P3DX(); endmodule
-// module FD1P3IX(); endmodule
-// module FD1P3JX(); endmodule
-// module FD1S3AX(); endmodule
-// module FD1S3AY(); endmodule
-module FD1S3BX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
-module FD1S3DX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
-module FD1S3IX(input CD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(CK), .LSR(CD), .DI(D), .Q(Q)); endmodule
-module FD1S3JX(input PD, D, CK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(CK), .LSR(PD), .DI(D), .Q(Q)); endmodule
-// module FL1P3AY(); endmodule
-// module FL1P3AZ(); endmodule
-// module FL1P3BX(); endmodule
-// module FL1P3DX(); endmodule
-// module FL1P3IY(); endmodule
-// module FL1P3JY(); endmodule
-// module FL1S3AX(); endmodule
-// module FL1S3AY(); endmodule
-
-// Diamond I/O buffers
-module IB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule
-module IBPU (input I, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule
-module IBPD (input I, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("INPUT")) tio (.B(I), .O(O)); endmodule
-module OB (input I, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I)); endmodule
-module OBZ (input I, T, output O); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule
-module OBZPU(input I, T, output O); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule
-module OBZPD(input I, T, output O); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(O), .I(I), .T(T)); endmodule
-module OBCO (input I, output OT, OC); OLVDS olvds (.A(I), .Z(OT), .ZN(OC)); endmodule
-module BB (input I, T, output O, inout B); (* PULLMODE="NONE" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule
-module BBPU (input I, T, output O, inout B); (* PULLMODE="UP" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule
-module BBPD (input I, T, output O, inout B); (* PULLMODE="DOWN" *) TRELLIS_IO #(.DIR("BIDIR")) tio (.B(B), .I(I), .O(O), .T(T)); endmodule
-module ILVDS(input A, AN, output Z); TRELLIS_IO #(.DIR("INPUT")) tio (.B(A), .O(Z)); endmodule
-module OLVDS(input A, output Z, ZN); TRELLIS_IO #(.DIR("OUTPUT")) tio (.B(Z), .I(A)); endmodule
-
-// Diamond I/O registers
-module IFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module IFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-
-module OFS1P3BX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3DX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("ASYNC")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3IX(input CD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("RESET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(CD), .CE(SP), .DI(D), .Q(Q)); endmodule
-module OFS1P3JX(input PD, D, SP, SCLK, output Q); TRELLIS_FF #(.GSR("DISABLED"), .CEMUX("1"), .CLKMUX("CLK"), .LSRMUX("LSR"), .REGSET("SET"), .SRMODE("LSR_OVER_CE")) tff (.CLK(SCLK), .LSR(PD), .CE(SP), .DI(D), .Q(Q)); endmodule
-
-// TODO: Diamond I/O latches
-// module IFS1S1B(input PD, D, SCLK, output Q); endmodule
-// module IFS1S1D(input CD, D, SCLK, output Q); endmodule
-// module IFS1S1I(input PD, D, SCLK, output Q); endmodule
-// module IFS1S1J(input CD, D, SCLK, output Q); endmodule
+`ifndef NO_INCLUDES
+
+`include "cells_ff.vh"
+`include "cells_io.vh"
+
+`endif
diff --git a/techlibs/ecp5/ecp5_gsr.cc b/techlibs/ecp5/ecp5_gsr.cc
new file mode 100644
index 000000000..2bc714b6f
--- /dev/null
+++ b/techlibs/ecp5/ecp5_gsr.cc
@@ -0,0 +1,135 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct Ecp5GsrPass : public Pass {
+ Ecp5GsrPass() : Pass("ecp5_gsr", "ECP5: handle GSR") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" ecp5_gsr [options] [selection]\n");
+ log("\n");
+ log("Trim active low async resets connected to GSR and resolve GSR parameter,\n");
+ log("if a GSR or SGSR primitive is used in the design.\n");
+ log("\n");
+ log("If any cell has the GSR parameter set to \"AUTO\", this will be resolved\n");
+ log("to \"ENABLED\" if a GSR primitive is present and the (* nogsr *) attribute\n");
+ log("is not set, otherwise it will be resolved to \"DISABLED\".\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ECP5_GSR pass (implement FF init values).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-singleton") {
+ // singleton_mode = true;
+ // continue;
+ // }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto module : design->selected_modules())
+ {
+ log("Handling GSR in %s.\n", log_id(module));
+
+ SigMap sigmap(module);
+
+ SigBit gsr;
+ bool found_gsr = false;
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID(GSR) && cell->type != ID(SGSR))
+ continue;
+ if (found_gsr)
+ log_error("Found more than one GSR or SGSR cell in module %s.\n", log_id(module));
+ found_gsr = true;
+ SigSpec sig_gsr = cell->getPort(ID(GSR));
+ if (GetSize(sig_gsr) < 1)
+ log_error("GSR cell %s has disconnected GSR input.\n", log_id(cell));
+ gsr = sigmap(sig_gsr[0]);
+ }
+
+ // Resolve GSR parameter
+
+ for (auto cell : module->selected_cells())
+ {
+ if (!cell->hasParam(ID(GSR)) || cell->getParam(ID(GSR)).decode_string() != "AUTO")
+ continue;
+
+ bool gsren = found_gsr;
+ if (cell->get_bool_attribute("\\nogsr"))
+ gsren = false;
+ cell->setParam(ID(GSR), gsren ? Const("ENABLED") : Const("DISABLED"));
+
+ }
+
+ if (!found_gsr)
+ continue;
+
+ // For finding active low FF inputs
+ pool<SigBit> inverted_gsr;
+
+ log_debug("GSR net in module %s is %s.\n", log_id(module), log_signal(gsr));
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID($_NOT_))
+ continue;
+ SigSpec sig_a = cell->getPort(ID(A)), sig_y = cell->getPort(ID(Y));
+ if (GetSize(sig_a) < 1 || GetSize(sig_y) < 1)
+ continue;
+ SigBit a = sigmap(sig_a[0]);
+ if (a == gsr)
+ inverted_gsr.insert(sigmap(sig_y[0]));
+ }
+
+ for (auto cell : module->selected_cells())
+ {
+ if (cell->type != ID(TRELLIS_FF))
+ continue;
+ if (!cell->hasParam(ID(GSR)) || cell->getParam(ID(GSR)).decode_string() != "ENABLED")
+ continue;
+ if (!cell->hasParam(ID(SRMODE)) || cell->getParam(ID(SRMODE)).decode_string() != "ASYNC")
+ continue;
+ SigSpec sig_lsr = cell->getPort(ID(LSR));
+ if (GetSize(sig_lsr) < 1)
+ continue;
+ SigBit lsr = sigmap(sig_lsr[0]);
+ if (!inverted_gsr.count(lsr))
+ continue;
+ cell->setParam(ID(SRMODE), Const("LSR_OVER_CE"));
+ cell->unsetPort(ID(LSR));
+ }
+
+ }
+ }
+} Ecp5GsrPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index 143d1f95c..a8075e86e 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -271,6 +271,8 @@ struct SynthEcp5Pass : public ScriptPass
run("opt_expr -undriven -mux_undef");
run("simplemap");
run("ecp5_ffinit");
+ run("ecp5_gsr");
+ run("opt_clean");
}
if (check_label("map_luts"))
diff --git a/techlibs/ecp5/tests/.gitignore b/techlibs/ecp5/tests/.gitignore
new file mode 100644
index 000000000..0e18132cc
--- /dev/null
+++ b/techlibs/ecp5/tests/.gitignore
@@ -0,0 +1 @@
+work_*
diff --git a/techlibs/ecp5/tests/test_diamond_ffs.py b/techlibs/ecp5/tests/test_diamond_ffs.py
new file mode 100644
index 000000000..1ed85ce8b
--- /dev/null
+++ b/techlibs/ecp5/tests/test_diamond_ffs.py
@@ -0,0 +1,82 @@
+import os
+import subprocess
+
+if not os.path.exists("work_ff"):
+ os.mkdir("work_ff")
+
+modules = []
+
+with open("../cells_ff.vh", "r") as f:
+ with open("work_ff/cells_ff_gate.v", "w") as g:
+ for line in f:
+ if not line.startswith("module"):
+ g.write(line)
+ continue
+ else:
+ spidx = line.find(" ")
+ bridx = line.find("(")
+ modname = line[spidx+1 : bridx]
+ g.write("module %s_gate" % modname)
+ g.write(line[bridx:])
+ inpidx = line.find("input ")
+ outpidx = line.find(", output")
+ modules.append((modname, [x.strip() for x in line[inpidx+6:outpidx].split(",")]))
+
+with open("work_ff/testbench.v", "w") as f:
+ print("""
+`timescale 1ns/ 1ps
+
+module testbench;
+reg pur = 0, clk, rst, cen, d;
+
+// Needed for Diamond sim models
+GSR GSR_INST (.GSR(1'b1));
+PUR PUR_INST (.PUR(pur));
+
+
+initial begin
+ $dumpfile("work_ff/ffs.vcd");
+ $dumpvars(0, testbench);
+ #5;
+ pur = 1;
+ #95;
+ repeat (2500) begin
+ {clk, rst, cen, d} = $random;
+ #10;
+ check_outputs;
+ #1;
+ end
+ $finish;
+end
+ """, file=f)
+
+ for modname, inputs in modules:
+ print(" wire %s_gold_q, %s_gate_q;" % (modname, modname), file=f)
+ portconns = []
+ for inp in inputs:
+ if inp in ("SCLK", "CK"):
+ portconns.append(".%s(clk)" % inp)
+ elif inp in ("CD", "PD"):
+ portconns.append(".%s(rst)" % inp)
+ elif inp == "SP":
+ portconns.append(".%s(cen)" % inp)
+ elif inp == "D":
+ portconns.append(".%s(d)" % inp)
+ else:
+ assert False
+ portconns.append(".Q(%s_gold_q)" % modname)
+ print(" %s %s_gold_i (%s);" % (modname, modname, ", ".join(portconns)), file=f)
+ portconns[-1] = (".Q(%s_gate_q)" % modname)
+ print(" %s_gate %s_gate_i (%s);" % (modname, modname, ", ".join(portconns)), file=f)
+ print("", file=f)
+ print(" task check_outputs;", file=f)
+ print(" begin", file=f)
+ print(" if (%s_gold_q != %s_gate_q) $display(\"MISMATCH at %%1t: %s_gold_q=%%b, %s_gate_q=%%b\", $time, %s_gold_q, %s_gate_q);" %
+ (modname, modname, modname, modname, modname, modname), file=f)
+ print(" end", file=f)
+ print(" endtask", file=f)
+ print("endmodule", file=f)
+
+diamond_models = "/usr/local/diamond/3.10_x64/cae_library/simulation/verilog/ecp5u"
+subprocess.call(["iverilog", "-s", "testbench", "-o", "work_ff/testbench", "-Dmixed_hdl", "-DNO_INCLUDES", "-y", diamond_models, "work_ff/cells_ff_gate.v", "../cells_sim.v", "work_ff/testbench.v"])
+subprocess.call(["vvp", "work_ff/testbench"])
diff --git a/techlibs/efinix/Makefile.inc b/techlibs/efinix/Makefile.inc
new file mode 100644
index 000000000..5013f7fc1
--- /dev/null
+++ b/techlibs/efinix/Makefile.inc
@@ -0,0 +1,10 @@
+
+OBJS += techlibs/efinix/synth_efinix.o
+OBJS += techlibs/efinix/efinix_gbuf.o
+OBJS += techlibs/efinix/efinix_fixcarry.o
+
+$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_map.v))
+$(eval $(call add_share_file,share/efinix,techlibs/efinix/arith_map.v))
+$(eval $(call add_share_file,share/efinix,techlibs/efinix/cells_sim.v))
+$(eval $(call add_share_file,share/efinix,techlibs/efinix/brams_map.v))
+$(eval $(call add_share_file,share/efinix,techlibs/efinix/bram.txt))
diff --git a/techlibs/efinix/arith_map.v b/techlibs/efinix/arith_map.v
new file mode 100644
index 000000000..178f57bc5
--- /dev/null
+++ b/techlibs/efinix/arith_map.v
@@ -0,0 +1,79 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+(* techmap_celltype = "$alu" *)
+module _80_efinix_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 CIx;
+ wire [Y_WIDTH-1:0] COx;
+
+ 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 = { COx, CIx };
+
+ EFX_ADD #(.I0_POLARITY(1'b1),.I1_POLARITY(1'b1))
+ adder_cin (
+ .I0(CI),
+ .I1(1'b1),
+ .CI(1'b0),
+ .CO(CIx)
+ );
+
+ genvar i;
+ generate for (i = 0; i < Y_WIDTH; i = i + 1) begin: slice
+ EFX_ADD #(.I0_POLARITY(1'b1),.I1_POLARITY(1'b1))
+ adder_i (
+ .I0(AA[i]),
+ .I1(BB[i]),
+ .CI(C[i]),
+ .O(Y[i]),
+ .CO(COx[i])
+ );
+ EFX_ADD #(.I0_POLARITY(1'b1),.I1_POLARITY(1'b1))
+ adder_cout (
+ .I0(1'b0),
+ .I1(1'b0),
+ .CI(COx[i]),
+ .O(CO[i])
+ );
+ end: slice
+ endgenerate
+
+ /* End implementation */
+ assign X = AA ^ BB;
+endmodule \ No newline at end of file
diff --git a/techlibs/efinix/bram.txt b/techlibs/efinix/bram.txt
new file mode 100644
index 000000000..0b3fd9308
--- /dev/null
+++ b/techlibs/efinix/bram.txt
@@ -0,0 +1,32 @@
+bram $__EFINIX_5K
+ init 1
+
+ abits 8 @a8d16
+ dbits 16 @a8d16
+ abits 9 @a9d8
+ dbits 8 @a9d8
+ abits 10 @a10d4
+ dbits 4 @a10d4
+ abits 11 @a11d2
+ dbits 2 @a11d2
+ abits 12 @a12d1
+ dbits 1 @a12d1
+ abits 8 @a8d20
+ dbits 20 @a8d20
+ abits 9 @a9d10
+ dbits 10 @a9d10
+
+ groups 2
+ ports 1 1
+ wrmode 1 0
+ enable 1 1
+ transp 0 2
+ clocks 2 3
+ clkpol 2 3
+endbram
+
+match $__EFINIX_5K
+ min bits 256
+ min efficiency 5
+ shuffle_enable B
+endmatch
diff --git a/techlibs/efinix/brams_map.v b/techlibs/efinix/brams_map.v
new file mode 100644
index 000000000..6786ae769
--- /dev/null
+++ b/techlibs/efinix/brams_map.v
@@ -0,0 +1,65 @@
+module \$__EFINIX_5K (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+ parameter CFG_ABITS = 8;
+ parameter CFG_DBITS = 20;
+ parameter CFG_ENABLE_A = 1;
+
+ parameter CLKPOL2 = 1;
+ parameter CLKPOL3 = 1;
+ parameter [5119:0] INIT = 5119'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 WRITEMODE_A = TRANSP2 ? "WRITE_FIRST" : "READ_FIRST";
+
+ EFX_RAM_5K #(
+ .READ_WIDTH(CFG_DBITS),
+ .WRITE_WIDTH(CFG_DBITS),
+ .OUTPUT_REG(1'b0),
+ .RCLK_POLARITY(1'b1),
+ .RE_POLARITY(1'b1),
+ .WCLK_POLARITY(1'b1),
+ .WE_POLARITY(1'b1),
+ .WCLKE_POLARITY(1'b1),
+ .WRITE_MODE(WRITEMODE_A),
+ .INIT_0(INIT[ 0*256 +: 256]),
+ .INIT_1(INIT[ 1*256 +: 256]),
+ .INIT_2(INIT[ 2*256 +: 256]),
+ .INIT_3(INIT[ 3*256 +: 256]),
+ .INIT_4(INIT[ 4*256 +: 256]),
+ .INIT_5(INIT[ 5*256 +: 256]),
+ .INIT_6(INIT[ 6*256 +: 256]),
+ .INIT_7(INIT[ 7*256 +: 256]),
+ .INIT_8(INIT[ 8*256 +: 256]),
+ .INIT_9(INIT[ 9*256 +: 256]),
+ .INIT_A(INIT[10*256 +: 256]),
+ .INIT_B(INIT[11*256 +: 256]),
+ .INIT_C(INIT[12*256 +: 256]),
+ .INIT_D(INIT[13*256 +: 256]),
+ .INIT_E(INIT[14*256 +: 256]),
+ .INIT_F(INIT[15*256 +: 256]),
+ .INIT_10(INIT[16*256 +: 256]),
+ .INIT_11(INIT[17*256 +: 256]),
+ .INIT_12(INIT[18*256 +: 256]),
+ .INIT_13(INIT[19*256 +: 256])
+ ) _TECHMAP_REPLACE_ (
+ .WDATA(A1DATA),
+ .WADDR(A1ADDR),
+ .WE(A1EN),
+ .WCLK(CLK2),
+ .WCLKE(1'b1),
+ .RDATA(B1DATA),
+ .RADDR(B1ADDR),
+ .RE(B1EN),
+ .RCLK(CLK3)
+ );
+endmodule
diff --git a/techlibs/efinix/cells_map.v b/techlibs/efinix/cells_map.v
new file mode 100644
index 000000000..0aeab1902
--- /dev/null
+++ b/techlibs/efinix/cells_map.v
@@ -0,0 +1,45 @@
+module \$_DFF_N_ (input D, C, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(1'b0), .Q(Q)); endmodule
+module \$_DFF_P_ (input D, C, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(1'b0), .Q(Q)); endmodule
+
+module \$_DFFE_NN_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b0), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule
+module \$_DFFE_NP_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule
+
+module \$_DFFE_PN_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b0), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule
+module \$_DFFE_PP_ (input D, C, E, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b1), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(E), .CLK(C), .SR(1'b0), .Q(Q)); endmodule
+
+module \$_DFF_NN0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+module \$_DFF_NN1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+module \$_DFF_PN0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+module \$_DFF_PN1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b0), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+
+module \$_DFF_NP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+module \$_DFF_NP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b0), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+module \$_DFF_PP0_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b0), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .Q(Q)); endmodule
+module \$_DFF_PP1_ (input D, C, R, output Q); EFX_FF #(.CLK_POLARITY(1'b1), .CE_POLARITY(1'b1), .SR_POLARITY(1'b1), .D_POLARITY(1'b1), .SR_SYNC(1'b0), .SR_VALUE(1'b1), .SR_SYNC_PRIORITY(1'b1)) _TECHMAP_REPLACE_ (.D(D), .CE(1'b1), .CLK(C), .SR(R), .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
+ EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0));
+ end else
+ if (WIDTH == 2) begin
+ EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0));
+ end else
+ if (WIDTH == 3) begin
+ EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0));
+ end else
+ if (WIDTH == 4) begin
+ EFX_LUT4 #(.LUTMASK(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
+ end else begin
+ wire _TECHMAP_FAIL_ = 1;
+ end
+ endgenerate
+endmodule
+`endif
diff --git a/techlibs/efinix/cells_sim.v b/techlibs/efinix/cells_sim.v
new file mode 100644
index 000000000..8c8f6afaa
--- /dev/null
+++ b/techlibs/efinix/cells_sim.v
@@ -0,0 +1,107 @@
+module EFX_LUT4(
+ output O,
+ input I0,
+ input I1,
+ input I2,
+ input I3
+);
+ parameter LUTMASK = 16'h0000;
+endmodule
+
+module EFX_ADD(
+ output O,
+ output CO,
+ input I0,
+ input I1,
+ input CI
+);
+ parameter I0_POLARITY = 1;
+ parameter I1_POLARITY = 1;
+endmodule
+
+module EFX_FF(
+ output Q,
+ input D,
+ input CE,
+ input CLK,
+ input SR
+);
+ parameter CLK_POLARITY = 1;
+ parameter CE_POLARITY = 1;
+ parameter SR_POLARITY = 1;
+ parameter SR_SYNC = 0;
+ parameter SR_VALUE = 0;
+ parameter SR_SYNC_PRIORITY = 0;
+ parameter D_POLARITY = 1;
+endmodule
+
+module EFX_GBUFCE(
+ input CE,
+ input I,
+ output O
+);
+ parameter CE_POLARITY = 1'b1;
+endmodule
+
+module EFX_RAM_5K(
+ input [WRITE_WIDTH-1:0] WDATA,
+ input [WRITE_ADDR_WIDTH-1:0] WADDR,
+ input WE,
+ input WCLK,
+ input WCLKE,
+ output [READ_WIDTH-1:0] RDATA,
+ input [READ_ADDR_WIDTH-1:0] RADDR,
+ input RE,
+ input RCLK
+);
+ parameter READ_WIDTH = 20;
+ parameter WRITE_WIDTH = 20;
+ parameter OUTPUT_REG = 1'b0;
+ parameter RCLK_POLARITY = 1'b1;
+ parameter RE_POLARITY = 1'b1;
+ parameter WCLK_POLARITY = 1'b1;
+ parameter WE_POLARITY = 1'b1;
+ parameter WCLKE_POLARITY = 1'b1;
+ parameter WRITE_MODE = "READ_FIRST";
+ parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+ parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
+
+ localparam READ_ADDR_WIDTH =
+ (READ_WIDTH == 16) ? 8 : // 256x16
+ (READ_WIDTH == 8) ? 9 : // 512x8
+ (READ_WIDTH == 4) ? 10 : // 1024x4
+ (READ_WIDTH == 2) ? 11 : // 2048x2
+ (READ_WIDTH == 1) ? 12 : // 4096x1
+ (READ_WIDTH == 20) ? 8 : // 256x20
+ (READ_WIDTH == 10) ? 9 : // 512x10
+ (READ_WIDTH == 5) ? 10 : -1; // 1024x5
+
+ localparam WRITE_ADDR_WIDTH =
+ (WRITE_WIDTH == 16) ? 8 : // 256x16
+ (WRITE_WIDTH == 8) ? 9 : // 512x8
+ (WRITE_WIDTH == 4) ? 10 : // 1024x4
+ (WRITE_WIDTH == 2) ? 11 : // 2048x2
+ (WRITE_WIDTH == 1) ? 12 : // 4096x1
+ (WRITE_WIDTH == 20) ? 8 : // 256x20
+ (WRITE_WIDTH == 10) ? 9 : // 512x10
+ (WRITE_WIDTH == 5) ? 10 : -1; // 1024x5
+
+endmodule \ No newline at end of file
diff --git a/techlibs/efinix/efinix_fixcarry.cc b/techlibs/efinix/efinix_fixcarry.cc
new file mode 100644
index 000000000..b7cd995b8
--- /dev/null
+++ b/techlibs/efinix/efinix_fixcarry.cc
@@ -0,0 +1,122 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+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 fix_carry_chain(Module *module)
+{
+ SigMap sigmap(module);
+
+ pool<SigBit> ci_bits;
+ dict<SigBit, SigBit> mapping_bits;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "\\EFX_ADD") {
+ SigBit bit_i0 = get_bit_or_zero(cell->getPort("\\I0"));
+ SigBit bit_i1 = get_bit_or_zero(cell->getPort("\\I1"));
+ if (bit_i0 == State::S0 && bit_i1== State::S0) {
+ SigBit bit_ci = get_bit_or_zero(cell->getPort("\\CI"));
+ SigBit bit_o = sigmap(cell->getPort("\\O"));
+ ci_bits.insert(bit_ci);
+ mapping_bits[bit_ci] = bit_o;
+ }
+ }
+ }
+
+ vector<Cell*> adders_to_fix_cells;
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "\\EFX_ADD") {
+ SigBit bit_ci = get_bit_or_zero(cell->getPort("\\CI"));
+ SigBit bit_i0 = get_bit_or_zero(cell->getPort("\\I0"));
+ SigBit bit_i1 = get_bit_or_zero(cell->getPort("\\I1"));
+ SigBit canonical_bit = sigmap(bit_ci);
+ if (!ci_bits.count(canonical_bit))
+ continue;
+ if (bit_i0 == State::S0 && bit_i1== State::S0)
+ continue;
+
+ adders_to_fix_cells.push_back(cell);
+ log("Found %s cell named %s with invalid CI signal.\n", log_id(cell->type), log_id(cell));
+ }
+ }
+
+ for (auto cell : adders_to_fix_cells)
+ {
+ SigBit bit_ci = get_bit_or_zero(cell->getPort("\\CI"));
+ SigBit canonical_bit = sigmap(bit_ci);
+ auto bit = mapping_bits.at(canonical_bit);
+ log("Fixing %s cell named %s breaking carry chain.\n", log_id(cell->type), log_id(cell));
+ Cell *c = module->addCell(NEW_ID, "\\EFX_ADD");
+ SigBit new_bit = module->addWire(NEW_ID);
+ c->setParam("\\I0_POLARITY", State::S1);
+ c->setParam("\\I1_POLARITY", State::S1);
+ c->setPort("\\I0", bit);
+ c->setPort("\\I1", State::S1);
+ c->setPort("\\CI", State::S0);
+ c->setPort("\\CO", new_bit);
+
+ cell->setPort("\\CI", new_bit);
+ }
+}
+
+struct EfinixCarryFixPass : public Pass {
+ EfinixCarryFixPass() : Pass("efinix_fixcarry", "Efinix: fix carry chain") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" efinix_fixcarry [options] [selection]\n");
+ log("\n");
+ log("Add Efinix adders to fix carry chain if needed.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing efinix_fixcarry pass (fix invalid carry chain).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Module *module = design->top_module();
+
+ if (module == nullptr)
+ log_cmd_error("No top module found.\n");
+
+ fix_carry_chain(module);
+ }
+} EfinixCarryFixPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/efinix/efinix_gbuf.cc b/techlibs/efinix/efinix_gbuf.cc
new file mode 100644
index 000000000..e75fb3f4d
--- /dev/null
+++ b/techlibs/efinix/efinix_gbuf.cc
@@ -0,0 +1,119 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com>
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+static void handle_gbufs(Module *module)
+{
+ SigMap sigmap(module);
+
+ pool<SigBit> clk_bits;
+ dict<SigBit, SigBit> rewrite_bits;
+ vector<pair<Cell*, SigBit>> pad_bits;
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "\\EFX_FF") {
+ for (auto bit : sigmap(cell->getPort("\\CLK")))
+ clk_bits.insert(bit);
+ }
+ if (cell->type == "\\EFX_RAM_5K") {
+ for (auto bit : sigmap(cell->getPort("\\RCLK")))
+ clk_bits.insert(bit);
+ for (auto bit : sigmap(cell->getPort("\\WCLK")))
+ clk_bits.insert(bit);
+ }
+ }
+
+ for (auto wire : vector<Wire*>(module->wires()))
+ {
+ if (!wire->port_input)
+ continue;
+
+ for (int index = 0; index < GetSize(wire); index++)
+ {
+ SigBit bit(wire, index);
+ SigBit canonical_bit = sigmap(bit);
+
+ if (!clk_bits.count(canonical_bit))
+ continue;
+
+ Cell *c = module->addCell(NEW_ID, "\\EFX_GBUFCE");
+ SigBit new_bit = module->addWire(NEW_ID);
+ c->setParam("\\CE_POLARITY", State::S1);
+ c->setPort("\\O", new_bit);
+ c->setPort("\\CE", State::S1);
+ pad_bits.push_back(make_pair(c, bit));
+ rewrite_bits[canonical_bit] = new_bit;
+
+ log("Added %s cell %s for port bit %s.\n", log_id(c->type), log_id(c), log_signal(bit));
+ }
+ }
+
+ auto rewrite_function = [&](SigSpec &s) {
+ for (auto &bit : s) {
+ SigBit canonical_bit = sigmap(bit);
+ if (rewrite_bits.count(canonical_bit))
+ bit = rewrite_bits.at(canonical_bit);
+ }
+ };
+
+ module->rewrite_sigspecs(rewrite_function);
+
+ for (auto &it : pad_bits)
+ it.first->setPort("\\I", it.second);
+}
+
+struct EfinixGbufPass : public Pass {
+ EfinixGbufPass() : Pass("efinix_gbuf", "Efinix: insert global clock buffers") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" efinix_gbuf [options] [selection]\n");
+ log("\n");
+ log("Add Efinix global clock buffers to top module as needed.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing efinix_gbuf pass (insert global clock buffers).\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ Module *module = design->top_module();
+
+ if (module == nullptr)
+ log_cmd_error("No top module found.\n");
+
+ handle_gbufs(module);
+ }
+} EfinixGbufPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/efinix/synth_efinix.cc b/techlibs/efinix/synth_efinix.cc
new file mode 100644
index 000000000..26a8d4eda
--- /dev/null
+++ b/techlibs/efinix/synth_efinix.cc
@@ -0,0 +1,219 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2019 Miodrag Milanovic <miodrag@symbioticeda.com>
+ * Copyright (C) 2019 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 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 SynthEfinixPass : public ScriptPass
+{
+ SynthEfinixPass() : ScriptPass("synth_efinix", "synthesis for Efinix FPGAs") { }
+
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" synth_efinix [options]\n");
+ log("\n");
+ log("This command runs synthesis for Efinix FPGAs.\n");
+ log("\n");
+ log(" -top <module>\n");
+ log(" use the specified module as top module\n");
+ log("\n");
+ log(" -edif <file>\n");
+ log(" write the design to the specified EDIF file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\n");
+ log("\n");
+ log(" -json <file>\n");
+ log(" write the design to the specified JSON file. writing of an output file\n");
+ log(" is omitted if this parameter is not specified.\n");
+ log("\n");
+ log(" -run <from_label>:<to_label>\n");
+ log(" only run the commands between the labels (see below). an empty\n");
+ log(" from label is synonymous to 'begin', and empty to label is\n");
+ log(" synonymous to the end of the command list.\n");
+ log("\n");
+ log(" -noflatten\n");
+ log(" do not flatten design before synthesis\n");
+ log("\n");
+ log(" -retime\n");
+ log(" run 'abc' with -dff option\n");
+ log("\n");
+ log("\n");
+ log("The following commands are executed by this synthesis command:\n");
+ help_script();
+ log("\n");
+ }
+
+ string top_opt, 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<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ string run_from, run_to;
+ clear_flags();
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ top_opt = "-top " + args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-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_EFINIX 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 +/efinix/cells_sim.v");
+ run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
+ }
+
+ if (flatten && check_label("flatten", "(unless -noflatten)"))
+ {
+ run("proc");
+ run("flatten");
+ run("tribuf -logic");
+ run("deminout");
+ }
+
+ if (check_label("coarse"))
+ {
+ run("synth -run coarse");
+ }
+
+ if (check_label("map_bram", "(skip if -nobram)"))
+ {
+ run("memory_bram -rules +/efinix/bram.txt");
+ run("techmap -map +/efinix/brams_map.v");
+ run("setundef -zero -params t:EFX_RAM_5K");
+ }
+
+ if (check_label("fine"))
+ {
+ run("opt -fast -mux_undef -undriven -fine");
+ run("memory_map");
+ run("opt -undriven -fine");
+ run("techmap -map +/techmap.v -map +/efinix/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 +/efinix/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");
+ run("clean");
+ }
+
+ if (check_label("map_cells"))
+ {
+ run("techmap -map +/efinix/cells_map.v");
+ run("clean");
+ }
+
+ if (check_label("map_gbuf"))
+ {
+ run("efinix_gbuf");
+ run("efinix_fixcarry");
+ 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 ? "<file-name>" : edif_file.c_str()));
+ }
+
+ if (check_label("json"))
+ {
+ if (!json_file.empty() || help_mode)
+ run(stringf("write_json %s", help_mode ? "<file-name>" : json_file.c_str()));
+ }
+ }
+} SynthEfinixPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc
index 1fc029e3c..0bfa9da1a 100644
--- a/techlibs/gowin/synth_gowin.cc
+++ b/techlibs/gowin/synth_gowin.cc
@@ -186,6 +186,7 @@ struct SynthGowinPass : public ScriptPass
run("techmap -map +/techmap.v");
if (retime || help_mode)
run("abc -dff", "(only if -retime)");
+ run("splitnets");
}
if (check_label("map_ffs"))
diff --git a/techlibs/ice40/abc_hx.box b/techlibs/ice40/abc_hx.box
index c0ea742e2..3ea70bc91 100644
--- a/techlibs/ice40/abc_hx.box
+++ b/techlibs/ice40/abc_hx.box
@@ -3,11 +3,11 @@
# NB: Inputs/Outputs must be ordered alphabetically
# (with exceptions for carry in/out)
-# Inputs: A B CI
+# Inputs: A B I0 I3 CI
# Outputs: O CO
# (NB: carry chain input/output must be last
# input/output and have been moved there
# overriding the alphabetical ordering)
-$__ICE40_FULL_ADDER 1 1 3 2
-400 379 316
-259 231 126
+$__ICE40_CARRY_WRAPPER 1 1 5 2
+400 379 449 316 316
+259 231 - - 126
diff --git a/techlibs/ice40/abc_lp.box b/techlibs/ice40/abc_lp.box
index d73b6d649..473e92fe9 100644
--- a/techlibs/ice40/abc_lp.box
+++ b/techlibs/ice40/abc_lp.box
@@ -3,11 +3,11 @@
# NB: Inputs/Outputs must be ordered alphabetically
# (with exceptions for carry in/out)
-# Inputs: A B CI
+# Inputs: A B I0 I3 CI
# Outputs: O CO
# (NB: carry chain input/output must be last
# input/output and have been moved there
# overriding the alphabetical ordering)
-$__ICE40_FULL_ADDER 1 1 3 2
-589 558 465
-675 609 186
+$__ICE40_CARRY_WRAPPER 1 1 5 2
+589 558 661 465 465
+675 609 - - 186
diff --git a/techlibs/ice40/abc_u.box b/techlibs/ice40/abc_u.box
index 42d666051..f00e247b8 100644
--- a/techlibs/ice40/abc_u.box
+++ b/techlibs/ice40/abc_u.box
@@ -3,11 +3,11 @@
# NB: Inputs/Outputs must be ordered alphabetically
# (with exceptions for carry in/out)
-# Inputs: A B CI
+# Inputs: A B I0 I3 CI
# Outputs: O CO
# (NB: carry chain input/output must be last
# input/output and have been moved there
# overriding the alphabetical ordering)
-$__ICE40_FULL_ADDER 1 1 3 2
-1231 1205 874
-675 609 278
+$__ICE40_CARRY_WRAPPER 1 1 5 2
+1231 1205 1285 874 874
+675 609 - - 278
diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v
index 2205be27d..2a7487f6b 100644
--- a/techlibs/ice40/cells_sim.v
+++ b/techlibs/ice40/cells_sim.v
@@ -141,8 +141,17 @@ module SB_CARRY (output CO, input I0, I1, CI);
assign CO = (I0 && I1) || ((I0 || I1) && CI);
endmodule
-(* abc_box_id = 1, abc_carry="CI,CO", lib_whitebox *)
-module \$__ICE40_FULL_ADDER (output CO, O, input A, B, CI);
+(* abc_box_id = 1, lib_whitebox *)
+module \$__ICE40_CARRY_WRAPPER (
+ (* abc_carry *)
+ output CO,
+ output O,
+ input A, B,
+ (* abc_carry *)
+ input CI,
+ input I0, I3
+);
+ parameter LUT = 0;
SB_CARRY carry (
.I0(A),
.I1(B),
@@ -150,16 +159,12 @@ module \$__ICE40_FULL_ADDER (output CO, O, input A, B, CI);
.CO(CO)
);
SB_LUT4 #(
- // I0: 1010 1010 1010 1010
- // I1: 1100 1100 1100 1100
- // I2: 1111 0000 1111 0000
- // I3: 1111 1111 0000 0000
- .LUT_INIT(16'b 0110_1001_1001_0110)
+ .LUT_INIT(LUT)
) adder (
- .I0(1'b0),
+ .I0(I0),
.I1(A),
.I2(B),
- .I3(CI),
+ .I3(I3),
.O(O)
);
endmodule
diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc
index d5106b805..ea56d3f4d 100644
--- a/techlibs/ice40/ice40_opt.cc
+++ b/techlibs/ice40/ice40_opt.cc
@@ -84,7 +84,7 @@ static void run_ice40_opts(Module *module)
continue;
}
- if (cell->type == "$__ICE40_FULL_ADDER")
+ if (cell->type == "$__ICE40_CARRY_WRAPPER")
{
SigSpec non_const_inputs, replacement_output;
int count_zeros = 0, count_ones = 0;
@@ -114,16 +114,17 @@ static void run_ice40_opts(Module *module)
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 $__ICE40_FULL_ADDER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n",
+ log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n",
log_id(module), log_id(cell), log_signal(replacement_output));
cell->type = "$lut";
- cell->setPort("\\A", { State::S0, inbit[0], inbit[1], inbit[2] });
+ cell->setPort("\\A", { cell->getPort("\\I0"), inbit[0], inbit[1], cell->getPort("\\I3") });
cell->setPort("\\Y", cell->getPort("\\O"));
cell->unsetPort("\\B");
cell->unsetPort("\\CI");
+ cell->unsetPort("\\I0");
+ cell->unsetPort("\\I3");
cell->unsetPort("\\CO");
cell->unsetPort("\\O");
- cell->setParam("\\LUT", RTLIL::Const::from_string("0110100110010110"));
cell->setParam("\\WIDTH", 4);
}
continue;
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
index 2c6e7432e..2b1af289c 100644
--- a/techlibs/xilinx/Makefile.inc
+++ b/techlibs/xilinx/Makefile.inc
@@ -32,8 +32,8 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_bb.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams.txt))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_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/lutrams.txt))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v))
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 05e46b4e7..e12b77c02 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -29,24 +29,35 @@ module GND(output G);
assign G = 0;
endmodule
-module IBUF(output O, input I);
+module IBUF(
+ output O,
+ (* iopad_external_pin *)
+ input I);
parameter IOSTANDARD = "default";
parameter IBUF_LOW_PWR = 0;
assign O = I;
endmodule
-module OBUF(output O, input I);
+module OBUF(
+ (* iopad_external_pin *)
+ output O,
+ input I);
parameter IOSTANDARD = "default";
parameter DRIVE = 12;
parameter SLEW = "SLOW";
assign O = I;
endmodule
-module BUFG(output O, input I);
+module BUFG(
+ (* clkbuf_driver *)
+ output O,
+ input I);
+
assign O = I;
endmodule
module BUFGCTRL(
+ (* clkbuf_driver *)
output O,
input I0, input I1,
input S0, input S1,
@@ -72,7 +83,11 @@ assign O = S0_true ? I0_internal : (S1_true ? I1_internal : INIT_OUT);
endmodule
-module BUFHCE(output O, input I, input CE);
+module BUFHCE(
+ (* clkbuf_driver *)
+ output O,
+ input I,
+ input CE);
parameter [0:0] INIT_OUT = 1'b0;
parameter CE_TYPE = "SYNC";
@@ -181,8 +196,16 @@ module XORCY(output O, input CI, LI);
assign O = CI ^ LI;
endmodule
-(* abc_box_id = 4, abc_carry="CI,CO", lib_whitebox *)
-module CARRY4(output [3:0] CO, O, input CI, CYINIT, input [3:0] DI, S);
+(* abc_box_id = 4, lib_whitebox *)
+module CARRY4(
+ (* abc_carry *)
+ output [3:0] CO,
+ output [3:0] O,
+ (* abc_carry *)
+ input CI,
+ input CYINIT,
+ input [3:0] DI, S
+);
assign O = S ^ {CO[2:0], CI | CYINIT};
assign CO[0] = S[0] ? CI | CYINIT : DI[0];
assign CO[1] = S[1] ? CO[0] : DI[1];
@@ -213,7 +236,7 @@ endmodule
`endif
-module FDRE (output reg Q, input C, CE, D, R);
+module FDRE (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -225,7 +248,7 @@ module FDRE (output reg Q, input C, CE, D, R);
endcase endgenerate
endmodule
-module FDSE (output reg Q, input C, CE, D, S);
+module FDSE (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
parameter [0:0] INIT = 1'b1;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -237,7 +260,7 @@ module FDSE (output reg Q, input C, CE, D, S);
endcase endgenerate
endmodule
-module FDCE (output reg Q, input C, CE, D, CLR);
+module FDCE (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -251,7 +274,7 @@ module FDCE (output reg Q, input C, CE, D, CLR);
endcase endgenerate
endmodule
-module FDPE (output reg Q, input C, CE, D, PRE);
+module FDPE (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
parameter [0:0] INIT = 1'b1;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -265,34 +288,39 @@ module FDPE (output reg Q, input C, CE, D, PRE);
endcase endgenerate
endmodule
-module FDRE_1 (output reg Q, input C, CE, D, R);
+module FDRE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, R);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C) if (R) Q <= 1'b0; else if(CE) Q <= D;
endmodule
-module FDSE_1 (output reg Q, input C, CE, D, S);
+module FDSE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, S);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C) if (S) Q <= 1'b1; else if(CE) Q <= D;
endmodule
-module FDCE_1 (output reg Q, input C, CE, D, CLR);
+module FDCE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, CLR);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C, posedge CLR) if (CLR) Q <= 1'b0; else if (CE) Q <= D;
endmodule
-module FDPE_1 (output reg Q, input C, CE, D, PRE);
+module FDPE_1 (output reg Q, (* clkbuf_sink *) input C, input CE, D, PRE);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
endmodule
-(* abc_box_id = 5, abc_scc_break="D,WE" *)
+(* abc_box_id = 5 *)
module RAM32X1D (
output DPO, SPO,
- input D, WCLK, WE,
+ (* abc_scc_break *)
+ input D,
+ (* clkbuf_sink *)
+ input WCLK,
+ (* abc_scc_break *)
+ input WE,
input A0, A1, A2, A3, A4,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4
);
@@ -307,10 +335,15 @@ module RAM32X1D (
always @(posedge clk) if (WE) mem[a] <= D;
endmodule
-(* abc_box_id = 6, abc_scc_break="D,WE" *)
+(* abc_box_id = 6 *)
module RAM64X1D (
output DPO, SPO,
- input D, WCLK, WE,
+ (* abc_scc_break *)
+ input D,
+ (* clkbuf_sink *)
+ input WCLK,
+ (* abc_scc_break *)
+ input WE,
input A0, A1, A2, A3, A4, A5,
input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5
);
@@ -325,10 +358,15 @@ module RAM64X1D (
always @(posedge clk) if (WE) mem[a] <= D;
endmodule
-(* abc_box_id = 7, abc_scc_break="D,WE" *)
+(* abc_box_id = 7 *)
module RAM128X1D (
output DPO, SPO,
- input D, WCLK, WE,
+ (* abc_scc_break *)
+ input D,
+ (* clkbuf_sink *)
+ input WCLK,
+ (* abc_scc_break *)
+ input WE,
input [6:0] A, DPRA
);
parameter INIT = 128'h0;
@@ -342,19 +380,45 @@ endmodule
module SRL16E (
output Q,
- input A0, A1, A2, A3, CE, CLK, D
+ input A0, A1, A2, A3, CE,
+ (* clkbuf_sink *)
+ input CLK,
+ input D
+);
+ parameter [15:0] INIT = 16'h0000;
+ parameter [0:0] IS_CLK_INVERTED = 1'b0;
+
+ reg [15:0] r = INIT;
+ assign Q = r[{A3,A2,A1,A0}];
+ generate
+ if (IS_CLK_INVERTED) begin
+ always @(negedge CLK) if (CE) r <= { r[14:0], D };
+ end
+ else
+ always @(posedge CLK) if (CE) r <= { r[14:0], D };
+ endgenerate
+endmodule
+
+module SRLC16E (
+ output Q,
+ output Q15,
+ input A0, A1, A2, A3, CE,
+ (* clkbuf_sink *)
+ input CLK,
+ input D
);
parameter [15:0] INIT = 16'h0000;
parameter [0:0] IS_CLK_INVERTED = 1'b0;
reg [15:0] r = INIT;
+ assign Q15 = r[15];
assign Q = r[{A3,A2,A1,A0}];
generate
if (IS_CLK_INVERTED) begin
always @(negedge CLK) if (CE) r <= { r[14:0], D };
end
else
- always @(posedge CLK) if (CE) r <= { r[14:0], D };
+ always @(posedge CLK) if (CE) r <= { r[14:0], D };
endgenerate
endmodule
@@ -362,7 +426,10 @@ module SRLC32E (
output Q,
output Q31,
input [4:0] A,
- input CE, CLK, D
+ input CE,
+ (* clkbuf_sink *)
+ input CLK,
+ input D
);
parameter [31:0] INIT = 32'h00000000;
parameter [0:0] IS_CLK_INVERTED = 1'b0;
diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py
new file mode 100644
index 000000000..dd4e300ae
--- /dev/null
+++ b/techlibs/xilinx/cells_xtra.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python3
+
+from argparse import ArgumentParser
+from io import StringIO
+from enum import Enum, auto
+import os.path
+import sys
+
+
+class Cell:
+ def __init__(self, name, keep=False, port_attrs={}):
+ self.name = name
+ self.keep = keep
+ self.port_attrs = port_attrs
+
+
+CELLS = [
+ # Design elements types listed in Xilinx UG953
+ Cell('BSCANE2', keep=True),
+ # Cell('BUFG', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFGCE', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFGCE_1', port_attrs={'O': ['clkbuf_driver']}),
+ #Cell('BUFGCTRL', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFGMUX', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFGMUX_1', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFGMUX_CTRL', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFH', port_attrs={'O': ['clkbuf_driver']}),
+ #Cell('BUFHCE', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFIO', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFMR', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFMRCE', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('BUFR', port_attrs={'O': ['clkbuf_driver']}),
+ Cell('CAPTUREE2', keep=True),
+ # Cell('CARRY4'),
+ Cell('CFGLUT5', port_attrs={'CLK': ['clkbuf_sink']}),
+ Cell('DCIRESET', keep=True),
+ Cell('DNA_PORT'),
+ Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}),
+ Cell('EFUSE_USR'),
+ # Cell('FDCE'),
+ # Cell('FDPE'),
+ # Cell('FDRE'),
+ # Cell('FDSE'),
+ Cell('FIFO18E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
+ Cell('FIFO36E1', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
+ Cell('FRAME_ECCE2'),
+ Cell('GTHE2_CHANNEL'),
+ Cell('GTHE2_COMMON'),
+ Cell('GTPE2_CHANNEL'),
+ Cell('GTPE2_COMMON'),
+ Cell('GTXE2_CHANNEL'),
+ Cell('GTXE2_COMMON'),
+ # Cell('IBUF', port_attrs={'I': ['iopad_external_pin']}),
+ Cell('IBUF_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin']}),
+ Cell('IBUF_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin']}),
+ Cell('IBUFDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFDS_DIFF_OUT_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFDS_GTE2', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFDS_IBUFDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFDS_INTERMDISABLE', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFG', port_attrs={'I': ['iopad_external_pin']}),
+ Cell('IBUFGDS', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('IBUFGDS_DIFF_OUT', port_attrs={'I': ['iopad_external_pin'], 'IB': ['iopad_external_pin']}),
+ Cell('ICAPE2', keep=True),
+ Cell('IDDR', port_attrs={'C': ['clkbuf_sink']}),
+ Cell('IDDR_2CLK', port_attrs={'C': ['clkbuf_sink'], 'CB': ['clkbuf_sink']}),
+ Cell('IDELAYCTRL', keep=True, port_attrs={'REFCLK': ['clkbuf_sink']}),
+ Cell('IDELAYE2', port_attrs={'C': ['clkbuf_sink']}),
+ Cell('IN_FIFO', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
+ Cell('IOBUF', port_attrs={'IO': ['iopad_external_pin']}),
+ Cell('IOBUF_DCIEN', port_attrs={'IO': ['iopad_external_pin']}),
+ Cell('IOBUF_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin']}),
+ Cell('IOBUFDS', port_attrs={'IO': ['iopad_external_pin']}),
+ Cell('IOBUFDS_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
+ Cell('IOBUFDS_DIFF_OUT', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
+ Cell('IOBUFDS_DIFF_OUT_DCIEN', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
+ Cell('IOBUFDS_DIFF_OUT_INTERMDISABLE', port_attrs={'IO': ['iopad_external_pin'], 'IOB': ['iopad_external_pin']}),
+ Cell('ISERDESE2', port_attrs={
+ 'CLK': ['clkbuf_sink'],
+ 'CLKB': ['clkbuf_sink'],
+ 'OCLK': ['clkbuf_sink'],
+ 'OCLKB': ['clkbuf_sink'],
+ 'CLKDIV': ['clkbuf_sink'],
+ 'CLKDIVP': ['clkbuf_sink'],
+ }),
+ Cell('KEEPER'),
+ Cell('LDCE'),
+ Cell('LDPE'),
+ # Cell('LUT1'),
+ # Cell('LUT2'),
+ # Cell('LUT3'),
+ # Cell('LUT4'),
+ # Cell('LUT5'),
+ # Cell('LUT6'),
+ #Cell('LUT6_2'),
+ Cell('MMCME2_ADV'),
+ Cell('MMCME2_BASE'),
+ # Cell('MUXF7'),
+ # Cell('MUXF8'),
+ # Cell('OBUF', port_attrs={'O': ['iopad_external_pin']}),
+ Cell('OBUFDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
+ Cell('OBUFT', port_attrs={'O': ['iopad_external_pin']}),
+ Cell('OBUFTDS', port_attrs={'O': ['iopad_external_pin'], 'OB': ['iopad_external_pin']}),
+ Cell('ODDR', port_attrs={'C': ['clkbuf_sink']}),
+ Cell('ODELAYE2', port_attrs={'C': ['clkbuf_sink']}),
+ Cell('OSERDESE2', port_attrs={'CLK': ['clkbuf_sink'], 'CLKDIV': ['clkbuf_sink']}),
+ Cell('OUT_FIFO', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}),
+ Cell('PHASER_IN'),
+ Cell('PHASER_IN_PHY'),
+ Cell('PHASER_OUT'),
+ Cell('PHASER_OUT_PHY'),
+ Cell('PHASER_REF'),
+ Cell('PHY_CONTROL'),
+ Cell('PLLE2_ADV'),
+ Cell('PLLE2_BASE'),
+ Cell('PS7', keep=True),
+ Cell('PULLDOWN'),
+ Cell('PULLUP'),
+ #Cell('RAM128X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM128X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM256X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM32M', port_attrs={'WCLK': ['clkbuf_sink']}),
+ #Cell('RAM32X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM32X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM32X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM32X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM64M', port_attrs={'WCLK': ['clkbuf_sink']}),
+ #Cell('RAM64X1D', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM64X1S', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM64X1S_1', port_attrs={'WCLK': ['clkbuf_sink']}),
+ Cell('RAM64X2S', port_attrs={'WCLK': ['clkbuf_sink']}),
+ # Cell('RAMB18E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
+ # Cell('RAMB36E1', port_attrs={'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink']}),
+ Cell('ROM128X1'),
+ Cell('ROM256X1'),
+ Cell('ROM32X1'),
+ Cell('ROM64X1'),
+ #Cell('SRL16E', port_attrs={'CLK': ['clkbuf_sink']}),
+ #Cell('SRLC32E', port_attrs={'CLK': ['clkbuf_sink']}),
+ Cell('STARTUPE2', keep=True),
+ Cell('USR_ACCESSE2'),
+ Cell('XADC'),
+]
+
+class State(Enum):
+ OUTSIDE = auto()
+ IN_MODULE = auto()
+ IN_OTHER_MODULE = auto()
+ IN_FUNCTION = auto()
+ IN_TASK = auto()
+
+def xtract_cell_decl(cell, dirs, outf):
+ for dir in dirs:
+ fname = os.path.join(dir, cell.name + '.v')
+ try:
+ with open(fname) as f:
+ state = State.OUTSIDE
+ found = False
+ # Probably the most horrible Verilog "parser" ever written.
+ for l in f:
+ l = l.partition('//')[0]
+ l = l.strip()
+ if l == 'module {}'.format(cell.name) or l.startswith('module {} '.format(cell.name)):
+ if found:
+ print('Multiple modules in {}.'.format(fname))
+ sys.exit(1)
+ elif state != State.OUTSIDE:
+ print('Nested modules in {}.'.format(fname))
+ sys.exit(1)
+ found = True
+ state = State.IN_MODULE
+ if cell.keep:
+ outf.write('(* keep *)\n')
+ outf.write('module {} (...);\n'.format(cell.name))
+ elif l.startswith('module '):
+ if state != State.OUTSIDE:
+ print('Nested modules in {}.'.format(fname))
+ sys.exit(1)
+ state = State.IN_OTHER_MODULE
+ elif l.startswith('task '):
+ if state == State.IN_MODULE:
+ state = State.IN_TASK
+ elif l.startswith('function '):
+ if state == State.IN_MODULE:
+ state = State.IN_FUNCTION
+ elif l == 'endtask':
+ if state == State.IN_TASK:
+ state = State.IN_MODULE
+ elif l == 'endfunction':
+ if state == State.IN_FUNCTION:
+ state = State.IN_MODULE
+ elif l == 'endmodule':
+ if state == State.IN_MODULE:
+ outf.write(l + '\n')
+ outf.write('\n')
+ elif state != State.IN_OTHER_MODULE:
+ print('endmodule in weird place in {}.'.format(cell.name, fname))
+ sys.exit(1)
+ state = State.OUTSIDE
+ elif l.startswith(('input ', 'output ', 'inout ')) and state == State.IN_MODULE:
+ if l.endswith((';', ',')):
+ l = l[:-1]
+ if ';' in l:
+ print('Weird port line in {} [{}].'.format(fname, l))
+ sys.exit(1)
+ kind, _, ports = l.partition(' ')
+ for port in ports.split(','):
+ port = port.strip()
+ for attr in cell.port_attrs.get(port, []):
+ outf.write(' (* {} *)\n'.format(attr))
+ outf.write(' {} {};\n'.format(kind, port))
+ elif l.startswith('parameter ') and state == State.IN_MODULE:
+ if 'UNPLACED' in l:
+ continue
+ if l.endswith((';', ',')):
+ l = l[:-1]
+ while ' ' in l:
+ l = l.replace(' ', ' ')
+ if ';' in l:
+ print('Weird parameter line in {} [{}].'.format(fname, l))
+ sys.exit(1)
+ outf.write(' {};\n'.format(l))
+ if state != State.OUTSIDE:
+ print('endmodule not found in {}.'.format(fname))
+ sys.exit(1)
+ if not found:
+ print('Cannot find module {} in {}.'.format(cell.name, fname))
+ sys.exit(1)
+ return
+ except FileNotFoundError:
+ continue
+ print('Cannot find {}.'.format(cell.name))
+ sys.exit(1)
+
+if __name__ == '__main__':
+ parser = ArgumentParser(description='Extract Xilinx blackbox cell definitions from Vivado.')
+ parser.add_argument('vivado_dir', nargs='?', default='/opt/Xilinx/Vivado/2018.1')
+ args = parser.parse_args()
+
+ dirs = [
+ os.path.join(args.vivado_dir, 'data/verilog/src/xeclib'),
+ os.path.join(args.vivado_dir, 'data/verilog/src/retarget'),
+ ]
+ for dir in dirs:
+ if not os.path.isdir(dir):
+ print('{} is not a directory'.format(dir))
+
+ out = StringIO()
+ for cell in CELLS:
+ xtract_cell_decl(cell, dirs, out)
+
+ with open('cells_xtra.v', 'w') as f:
+ f.write('// Created by cells_xtra.py from Xilinx models\n')
+ f.write('\n')
+ f.write(out.getvalue())
diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh
deleted file mode 100644
index 53b528820..000000000
--- a/techlibs/xilinx/cells_xtra.sh
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/bin/bash
-
-set -e
-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
- [ -z "$2" ] || echo $2
- 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;
- s/^ ((end)?module)/\1/; s/^ / /; /module.*_bb/,/endmodule/ d;'
- echo; return
- done
- echo "Can't find $1."
- exit 1
-}
-
-{
- echo "// Created by cells_xtra.sh from Xilinx models"
- echo
-
- # Design elements types listed in Xilinx UG953
- xtract_cell_decl BSCANE2
- # xtract_cell_decl BUFG
- xtract_cell_decl BUFGCE
- xtract_cell_decl BUFGCE_1
- #xtract_cell_decl BUFGCTRL
- xtract_cell_decl BUFGMUX
- xtract_cell_decl BUFGMUX_1
- xtract_cell_decl BUFGMUX_CTRL
- xtract_cell_decl BUFH
- #xtract_cell_decl BUFHCE
- xtract_cell_decl BUFIO
- xtract_cell_decl BUFMR
- xtract_cell_decl BUFMRCE
- xtract_cell_decl BUFR
- xtract_cell_decl CAPTUREE2 "(* keep *)"
- # xtract_cell_decl CARRY4
- xtract_cell_decl CFGLUT5
- xtract_cell_decl DCIRESET "(* keep *)"
- xtract_cell_decl DNA_PORT
- xtract_cell_decl DSP48E1
- xtract_cell_decl EFUSE_USR
- # xtract_cell_decl FDCE
- # xtract_cell_decl FDPE
- # xtract_cell_decl FDRE
- # xtract_cell_decl FDSE
- xtract_cell_decl FIFO18E1
- xtract_cell_decl FIFO36E1
- xtract_cell_decl FRAME_ECCE2
- xtract_cell_decl GTHE2_CHANNEL
- xtract_cell_decl GTHE2_COMMON
- xtract_cell_decl GTPE2_CHANNEL
- xtract_cell_decl GTPE2_COMMON
- xtract_cell_decl GTXE2_CHANNEL
- xtract_cell_decl GTXE2_COMMON
- # xtract_cell_decl IBUF
- xtract_cell_decl IBUF_IBUFDISABLE
- xtract_cell_decl IBUF_INTERMDISABLE
- xtract_cell_decl IBUFDS
- xtract_cell_decl IBUFDS_DIFF_OUT
- xtract_cell_decl IBUFDS_DIFF_OUT_IBUFDISABLE
- xtract_cell_decl IBUFDS_DIFF_OUT_INTERMDISABLE
- xtract_cell_decl IBUFDS_GTE2
- xtract_cell_decl IBUFDS_IBUFDISABLE
- xtract_cell_decl IBUFDS_INTERMDISABLE
- xtract_cell_decl ICAPE2 "(* keep *)"
- xtract_cell_decl IDDR
- xtract_cell_decl IDDR_2CLK
- xtract_cell_decl IDELAYCTRL "(* keep *)"
- xtract_cell_decl IDELAYE2
- xtract_cell_decl IN_FIFO
- xtract_cell_decl IOBUF
- xtract_cell_decl IOBUF_DCIEN
- xtract_cell_decl IOBUF_INTERMDISABLE
- xtract_cell_decl IOBUFDS
- xtract_cell_decl IOBUFDS_DCIEN
- xtract_cell_decl IOBUFDS_DIFF_OUT
- xtract_cell_decl IOBUFDS_DIFF_OUT_DCIEN
- xtract_cell_decl IOBUFDS_DIFF_OUT_INTERMDISABLE
- xtract_cell_decl ISERDESE2
- xtract_cell_decl KEEPER
- xtract_cell_decl LDCE
- xtract_cell_decl LDPE
- # xtract_cell_decl LUT1
- # xtract_cell_decl LUT2
- # xtract_cell_decl LUT3
- # xtract_cell_decl LUT4
- # xtract_cell_decl LUT5
- # xtract_cell_decl LUT6
- #xtract_cell_decl LUT6_2
- xtract_cell_decl MMCME2_ADV
- xtract_cell_decl MMCME2_BASE
- # xtract_cell_decl MUXF7
- # xtract_cell_decl MUXF8
- # xtract_cell_decl OBUF
- xtract_cell_decl OBUFDS
- xtract_cell_decl OBUFT
- xtract_cell_decl OBUFTDS
- xtract_cell_decl ODDR
- xtract_cell_decl ODELAYE2
- xtract_cell_decl OSERDESE2
- xtract_cell_decl OUT_FIFO
- xtract_cell_decl PHASER_IN
- xtract_cell_decl PHASER_IN_PHY
- xtract_cell_decl PHASER_OUT
- xtract_cell_decl PHASER_OUT_PHY
- xtract_cell_decl PHASER_REF
- xtract_cell_decl PHY_CONTROL
- xtract_cell_decl PLLE2_ADV
- xtract_cell_decl PLLE2_BASE
- xtract_cell_decl PS7 "(* keep *)"
- xtract_cell_decl PULLDOWN
- xtract_cell_decl PULLUP
- #xtract_cell_decl RAM128X1D
- xtract_cell_decl RAM128X1S
- xtract_cell_decl RAM256X1S
- xtract_cell_decl RAM32M
- #xtract_cell_decl RAM32X1D
- xtract_cell_decl RAM32X1S
- xtract_cell_decl RAM32X1S_1
- xtract_cell_decl RAM32X2S
- xtract_cell_decl RAM64M
- #xtract_cell_decl RAM64X1D
- xtract_cell_decl RAM64X1S
- xtract_cell_decl RAM64X1S_1
- xtract_cell_decl RAM64X2S
- # xtract_cell_decl RAMB18E1
- # xtract_cell_decl RAMB36E1
- xtract_cell_decl ROM128X1
- xtract_cell_decl ROM256X1
- xtract_cell_decl ROM32X1
- xtract_cell_decl ROM64X1
- #xtract_cell_decl SRL16E
- #xtract_cell_decl SRLC32E
- xtract_cell_decl STARTUPE2 "(* keep *)"
- xtract_cell_decl USR_ACCESSE2
- xtract_cell_decl XADC
-} > cells_xtra.new
-
-mv cells_xtra.new cells_xtra.v
-exit 0
-
diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v
index 15fa1b63a..a6669b872 100644
--- a/techlibs/xilinx/cells_xtra.v
+++ b/techlibs/xilinx/cells_xtra.v
@@ -1,5 +1,6 @@
-// Created by cells_xtra.sh from Xilinx models
+// Created by cells_xtra.py from Xilinx models
+(* keep *)
module BSCANE2 (...);
parameter DISABLE_JTAG = "FALSE";
parameter integer JTAG_CHAIN = 1;
@@ -20,29 +21,39 @@ module BUFGCE (...);
parameter CE_TYPE = "SYNC";
parameter [0:0] IS_CE_INVERTED = 1'b0;
parameter [0:0] IS_I_INVERTED = 1'b0;
+ (* clkbuf_driver *)
output O;
input CE;
input I;
endmodule
module BUFGCE_1 (...);
+ (* clkbuf_driver *)
output O;
- input CE, I;
+ input CE;
+ input I;
endmodule
module BUFGMUX (...);
parameter CLK_SEL_TYPE = "SYNC";
+ (* clkbuf_driver *)
output O;
- input I0, I1, S;
+ input I0;
+ input I1;
+ input S;
endmodule
module BUFGMUX_1 (...);
parameter CLK_SEL_TYPE = "SYNC";
+ (* clkbuf_driver *)
output O;
- input I0, I1, S;
+ input I0;
+ input I1;
+ input S;
endmodule
module BUFGMUX_CTRL (...);
+ (* clkbuf_driver *)
output O;
input I0;
input I1;
@@ -50,16 +61,19 @@ module BUFGMUX_CTRL (...);
endmodule
module BUFH (...);
+ (* clkbuf_driver *)
output O;
input I;
endmodule
module BUFIO (...);
+ (* clkbuf_driver *)
output O;
input I;
endmodule
module BUFMR (...);
+ (* clkbuf_driver *)
output O;
input I;
endmodule
@@ -68,12 +82,14 @@ module BUFMRCE (...);
parameter CE_TYPE = "SYNC";
parameter integer INIT_OUT = 0;
parameter [0:0] IS_CE_INVERTED = 1'b0;
+ (* clkbuf_driver *)
output O;
input CE;
input I;
endmodule
module BUFR (...);
+ (* clkbuf_driver *)
output O;
input CE;
input CLR;
@@ -95,8 +111,15 @@ module CFGLUT5 (...);
output CDO;
output O5;
output O6;
- input I4, I3, I2, I1, I0;
- input CDI, CE, CLK;
+ input I4;
+ input I3;
+ input I2;
+ input I1;
+ input I0;
+ input CDI;
+ input CE;
+ (* clkbuf_sink *)
+ input CLK;
endmodule
(* keep *)
@@ -108,7 +131,10 @@ endmodule
module DNA_PORT (...);
parameter [56:0] SIM_DNA_VALUE = 57'h0;
output DOUT;
- input CLK, DIN, READ, SHIFT;
+ input CLK;
+ input DIN;
+ input READ;
+ input SHIFT;
endmodule
module DSP48E1 (...);
@@ -175,6 +201,7 @@ module DSP48E1 (...);
input CEINMODE;
input CEM;
input CEP;
+ (* clkbuf_sink *)
input CLK;
input [24:0] D;
input [4:0] INMODE;
@@ -227,11 +254,13 @@ module FIFO18E1 (...);
output WRERR;
input [31:0] DI;
input [3:0] DIP;
+ (* clkbuf_sink *)
input RDCLK;
input RDEN;
input REGCE;
input RST;
input RSTREG;
+ (* clkbuf_sink *)
input WRCLK;
input WREN;
endmodule
@@ -272,11 +301,13 @@ module FIFO36E1 (...);
input [7:0] DIP;
input INJECTDBITERR;
input INJECTSBITERR;
+ (* clkbuf_sink *)
input RDCLK;
input RDEN;
input REGCE;
input RST;
input RSTREG;
+ (* clkbuf_sink *)
input WRCLK;
input WREN;
endmodule
@@ -1969,6 +2000,7 @@ module IBUF_IBUFDISABLE (...);
parameter SIM_DEVICE = "7SERIES";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
input I;
input IBUFDISABLE;
endmodule
@@ -1979,6 +2011,7 @@ module IBUF_INTERMDISABLE (...);
parameter SIM_DEVICE = "7SERIES";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
input I;
input IBUFDISABLE;
input INTERMDISABLE;
@@ -1993,7 +2026,10 @@ module IBUFDS (...);
parameter IFD_DELAY_VALUE = "AUTO";
parameter IOSTANDARD = "DEFAULT";
output O;
- input I, IB;
+ (* iopad_external_pin *)
+ input I;
+ (* iopad_external_pin *)
+ input IB;
endmodule
module IBUFDS_DIFF_OUT (...);
@@ -2001,8 +2037,12 @@ module IBUFDS_DIFF_OUT (...);
parameter DQS_BIAS = "FALSE";
parameter IBUF_LOW_PWR = "TRUE";
parameter IOSTANDARD = "DEFAULT";
- output O, OB;
- input I, IB;
+ output O;
+ output OB;
+ (* iopad_external_pin *)
+ input I;
+ (* iopad_external_pin *)
+ input IB;
endmodule
module IBUFDS_DIFF_OUT_IBUFDISABLE (...);
@@ -2014,7 +2054,9 @@ module IBUFDS_DIFF_OUT_IBUFDISABLE (...);
parameter USE_IBUFDISABLE = "TRUE";
output O;
output OB;
+ (* iopad_external_pin *)
input I;
+ (* iopad_external_pin *)
input IB;
input IBUFDISABLE;
endmodule
@@ -2028,7 +2070,9 @@ module IBUFDS_DIFF_OUT_INTERMDISABLE (...);
parameter USE_IBUFDISABLE = "TRUE";
output O;
output OB;
+ (* iopad_external_pin *)
input I;
+ (* iopad_external_pin *)
input IB;
input IBUFDISABLE;
input INTERMDISABLE;
@@ -2041,7 +2085,9 @@ module IBUFDS_GTE2 (...);
output O;
output ODIV2;
input CEB;
+ (* iopad_external_pin *)
input I;
+ (* iopad_external_pin *)
input IB;
endmodule
@@ -2053,7 +2099,9 @@ module IBUFDS_IBUFDISABLE (...);
parameter SIM_DEVICE = "7SERIES";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
input I;
+ (* iopad_external_pin *)
input IB;
input IBUFDISABLE;
endmodule
@@ -2066,12 +2114,50 @@ module IBUFDS_INTERMDISABLE (...);
parameter SIM_DEVICE = "7SERIES";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
input I;
+ (* iopad_external_pin *)
input IB;
input IBUFDISABLE;
input INTERMDISABLE;
endmodule
+module IBUFG (...);
+ parameter CAPACITANCE = "DONT_CARE";
+ parameter IBUF_DELAY_VALUE = "0";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ output O;
+ (* iopad_external_pin *)
+ input I;
+endmodule
+
+module IBUFGDS (...);
+ parameter CAPACITANCE = "DONT_CARE";
+ parameter DIFF_TERM = "FALSE";
+ parameter IBUF_DELAY_VALUE = "0";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ output O;
+ (* iopad_external_pin *)
+ input I;
+ (* iopad_external_pin *)
+ input IB;
+endmodule
+
+module IBUFGDS_DIFF_OUT (...);
+ parameter DIFF_TERM = "FALSE";
+ parameter DQS_BIAS = "FALSE";
+ parameter IBUF_LOW_PWR = "TRUE";
+ parameter IOSTANDARD = "DEFAULT";
+ output O;
+ output OB;
+ (* iopad_external_pin *)
+ input I;
+ (* iopad_external_pin *)
+ input IB;
+endmodule
+
(* keep *)
module ICAPE2 (...);
parameter [31:0] DEVICE_ID = 32'h04244093;
@@ -2095,6 +2181,7 @@ module IDDR (...);
parameter XON = "TRUE";
output Q1;
output Q2;
+ (* clkbuf_sink *)
input C;
input CE;
input D;
@@ -2112,7 +2199,9 @@ module IDDR_2CLK (...);
parameter SRTYPE = "SYNC";
output Q1;
output Q2;
+ (* clkbuf_sink *)
input C;
+ (* clkbuf_sink *)
input CB;
input CE;
input D;
@@ -2124,6 +2213,7 @@ endmodule
module IDELAYCTRL (...);
parameter SIM_DEVICE = "7SERIES";
output RDY;
+ (* clkbuf_sink *)
input REFCLK;
input RST;
endmodule
@@ -2143,6 +2233,7 @@ module IDELAYE2 (...);
parameter integer SIM_DELAY_D = 0;
output [4:0] CNTVALUEOUT;
output DATAOUT;
+ (* clkbuf_sink *)
input C;
input CE;
input CINVCTRL;
@@ -2174,9 +2265,11 @@ module IN_FIFO (...);
output [7:0] Q7;
output [7:0] Q8;
output [7:0] Q9;
+ (* clkbuf_sink *)
input RDCLK;
input RDEN;
input RESET;
+ (* clkbuf_sink *)
input WRCLK;
input WREN;
input [3:0] D0;
@@ -2197,8 +2290,10 @@ module IOBUF (...);
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
output O;
+ (* iopad_external_pin *)
inout IO;
- input I, T;
+ input I;
+ input T;
endmodule
module IOBUF_DCIEN (...);
@@ -2209,6 +2304,7 @@ module IOBUF_DCIEN (...);
parameter SLEW = "SLOW";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
inout IO;
input DCITERMDISABLE;
input I;
@@ -2224,6 +2320,7 @@ module IOBUF_INTERMDISABLE (...);
parameter SLEW = "SLOW";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
inout IO;
input I;
input IBUFDISABLE;
@@ -2238,8 +2335,11 @@ module IOBUFDS (...);
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
output O;
- inout IO, IOB;
- input I, T;
+ (* iopad_external_pin *)
+ inout IO;
+ inout IOB;
+ input I;
+ input T;
endmodule
module IOBUFDS_DCIEN (...);
@@ -2251,7 +2351,9 @@ module IOBUFDS_DCIEN (...);
parameter SLEW = "SLOW";
parameter USE_IBUFDISABLE = "TRUE";
output O;
+ (* iopad_external_pin *)
inout IO;
+ (* iopad_external_pin *)
inout IOB;
input DCITERMDISABLE;
input I;
@@ -2266,7 +2368,9 @@ module IOBUFDS_DIFF_OUT (...);
parameter IOSTANDARD = "DEFAULT";
output O;
output OB;
+ (* iopad_external_pin *)
inout IO;
+ (* iopad_external_pin *)
inout IOB;
input I;
input TM;
@@ -2282,7 +2386,9 @@ module IOBUFDS_DIFF_OUT_DCIEN (...);
parameter USE_IBUFDISABLE = "TRUE";
output O;
output OB;
+ (* iopad_external_pin *)
inout IO;
+ (* iopad_external_pin *)
inout IOB;
input DCITERMDISABLE;
input I;
@@ -2300,7 +2406,9 @@ module IOBUFDS_DIFF_OUT_INTERMDISABLE (...);
parameter USE_IBUFDISABLE = "TRUE";
output O;
output OB;
+ (* iopad_external_pin *)
inout IO;
+ (* iopad_external_pin *)
inout IOB;
input I;
input IBUFDISABLE;
@@ -2348,15 +2456,21 @@ module ISERDESE2 (...);
input BITSLIP;
input CE1;
input CE2;
+ (* clkbuf_sink *)
input CLK;
+ (* clkbuf_sink *)
input CLKB;
+ (* clkbuf_sink *)
input CLKDIV;
+ (* clkbuf_sink *)
input CLKDIVP;
input D;
input DDLY;
input DYNCLKDIVSEL;
input DYNCLKSEL;
+ (* clkbuf_sink *)
input OCLK;
+ (* clkbuf_sink *)
input OCLKB;
input OFB;
input RST;
@@ -2375,7 +2489,10 @@ module LDCE (...);
parameter MSGON = "TRUE";
parameter XON = "TRUE";
output Q;
- input CLR, D, G, GE;
+ input CLR;
+ input D;
+ input G;
+ input GE;
endmodule
module LDPE (...);
@@ -2385,7 +2502,10 @@ module LDPE (...);
parameter MSGON = "TRUE";
parameter XON = "TRUE";
output Q;
- input D, G, GE, PRE;
+ input D;
+ input G;
+ input GE;
+ input PRE;
endmodule
module MMCME2_ADV (...);
@@ -2533,7 +2653,10 @@ module OBUFDS (...);
parameter CAPACITANCE = "DONT_CARE";
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
- output O, OB;
+ (* iopad_external_pin *)
+ output O;
+ (* iopad_external_pin *)
+ output OB;
input I;
endmodule
@@ -2542,20 +2665,27 @@ module OBUFT (...);
parameter integer DRIVE = 12;
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
+ (* iopad_external_pin *)
output O;
- input I, T;
+ input I;
+ input T;
endmodule
module OBUFTDS (...);
parameter CAPACITANCE = "DONT_CARE";
parameter IOSTANDARD = "DEFAULT";
parameter SLEW = "SLOW";
- output O, OB;
- input I, T;
+ (* iopad_external_pin *)
+ output O;
+ (* iopad_external_pin *)
+ output OB;
+ input I;
+ input T;
endmodule
module ODDR (...);
output Q;
+ (* clkbuf_sink *)
input C;
input CE;
input D1;
@@ -2586,6 +2716,7 @@ module ODELAYE2 (...);
parameter integer SIM_DELAY_D = 0;
output [4:0] CNTVALUEOUT;
output DATAOUT;
+ (* clkbuf_sink *)
input C;
input CE;
input CINVCTRL;
@@ -2631,7 +2762,9 @@ module OSERDESE2 (...);
output TBYTEOUT;
output TFB;
output TQ;
+ (* clkbuf_sink *)
input CLK;
+ (* clkbuf_sink *)
input CLKDIV;
input D1;
input D2;
@@ -2673,9 +2806,11 @@ module OUT_FIFO (...);
output [3:0] Q9;
output [7:0] Q5;
output [7:0] Q6;
+ (* clkbuf_sink *)
input RDCLK;
input RDEN;
input RESET;
+ (* clkbuf_sink *)
input WRCLK;
input WREN;
input [7:0] D0;
@@ -3659,7 +3794,17 @@ module RAM128X1S (...);
parameter [127:0] INIT = 128'h00000000000000000000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
output O;
- input A0, A1, A2, A3, A4, A5, A6, D, WCLK, WE;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
+ input A6;
+ input D;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module RAM256X1S (...);
@@ -3668,6 +3813,7 @@ module RAM256X1S (...);
output O;
input [7:0] A;
input D;
+ (* clkbuf_sink *)
input WCLK;
input WE;
endmodule
@@ -3690,6 +3836,7 @@ module RAM32M (...);
input [1:0] DIB;
input [1:0] DIC;
input [1:0] DID;
+ (* clkbuf_sink *)
input WCLK;
input WE;
endmodule
@@ -3698,22 +3845,48 @@ module RAM32X1S (...);
parameter [31:0] INIT = 32'h00000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
output O;
- input A0, A1, A2, A3, A4, D, WCLK, WE;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input D;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module RAM32X1S_1 (...);
parameter [31:0] INIT = 32'h00000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
output O;
- input A0, A1, A2, A3, A4, D, WCLK, WE;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input D;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module RAM32X2S (...);
parameter [31:0] INIT_00 = 32'h00000000;
parameter [31:0] INIT_01 = 32'h00000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
- output O0, O1;
- input A0, A1, A2, A3, A4, D0, D1, WCLK, WE;
+ output O0;
+ output O1;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input D0;
+ input D1;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module RAM64M (...);
@@ -3734,6 +3907,7 @@ module RAM64M (...);
input DIB;
input DIC;
input DID;
+ (* clkbuf_sink *)
input WCLK;
input WE;
endmodule
@@ -3742,46 +3916,97 @@ module RAM64X1S (...);
parameter [63:0] INIT = 64'h0000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
output O;
- input A0, A1, A2, A3, A4, A5, D, WCLK, WE;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
+ input D;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module RAM64X1S_1 (...);
parameter [63:0] INIT = 64'h0000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
output O;
- input A0, A1, A2, A3, A4, A5, D, WCLK, WE;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
+ input D;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module RAM64X2S (...);
parameter [63:0] INIT_00 = 64'h0000000000000000;
parameter [63:0] INIT_01 = 64'h0000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
- output O0, O1;
- input A0, A1, A2, A3, A4, A5, D0, D1, WCLK, WE;
+ output O0;
+ output O1;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
+ input D0;
+ input D1;
+ (* clkbuf_sink *)
+ input WCLK;
+ input WE;
endmodule
module ROM128X1 (...);
parameter [127:0] INIT = 128'h00000000000000000000000000000000;
output O;
- input A0, A1, A2, A3, A4, A5, A6;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
+ input A6;
endmodule
module ROM256X1 (...);
parameter [255:0] INIT = 256'h0000000000000000000000000000000000000000000000000000000000000000;
output O;
- input A0, A1, A2, A3, A4, A5, A6, A7;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
+ input A6;
+ input A7;
endmodule
module ROM32X1 (...);
parameter [31:0] INIT = 32'h00000000;
output O;
- input A0, A1, A2, A3, A4;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
endmodule
module ROM64X1 (...);
parameter [63:0] INIT = 64'h0000000000000000;
output O;
- input A0, A1, A2, A3, A4, A5;
+ input A0;
+ input A1;
+ input A2;
+ input A3;
+ input A4;
+ input A5;
endmodule
(* keep *)
diff --git a/techlibs/xilinx/drams.txt b/techlibs/xilinx/lutrams.txt
index 2613c206c..2613c206c 100644
--- a/techlibs/xilinx/drams.txt
+++ b/techlibs/xilinx/lutrams.txt
diff --git a/techlibs/xilinx/drams_map.v b/techlibs/xilinx/lutrams_map.v
index 77041ca86..77041ca86 100644
--- a/techlibs/xilinx/drams_map.v
+++ b/techlibs/xilinx/lutrams_map.v
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index d143c6823..3760a1129 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -63,14 +63,17 @@ struct SynthXilinxPass : public ScriptPass
log(" generate an output netlist (and BLIF file) suitable for VPR\n");
log(" (this feature is experimental and incomplete)\n");
log("\n");
+ log(" -ise\n");
+ log(" generate an output netlist suitable for ISE (enables -iopad)\n");
+ log("\n");
log(" -nobram\n");
- log(" disable inference of block rams\n");
+ log(" do not use block RAM cells in output netlist\n");
log("\n");
- log(" -nodram\n");
- log(" disable inference of distributed rams\n");
+ log(" -nolutram\n");
+ log(" do not use distributed RAM cells in output netlist\n");
log("\n");
log(" -nosrl\n");
- log(" disable inference of shift registers\n");
+ log(" do not use distributed SRL cells in output netlist\n");
log("\n");
log(" -nocarry\n");
log(" do not use XORCY/MUXCY/CARRY4 cells in output netlist\n");
@@ -78,6 +81,15 @@ struct SynthXilinxPass : public ScriptPass
log(" -nowidelut\n");
log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n");
log("\n");
+ log(" -iopad\n");
+ log(" enable I/O buffer insertion (selected automatically by -ise)\n");
+ log("\n");
+ log(" -noiopad\n");
+ log(" disable I/O buffer insertion (only useful with -ise)\n");
+ log("\n");
+ log(" -noclkbuf\n");
+ log(" disable automatic clock buffer insertion\n");
+ log("\n");
log(" -widemux <int>\n");
log(" enable inference of hard multiplexer resources (MUXF[78]) for muxes at or\n");
log(" above this number of inputs (minimum value 2, recommended value >= 5).\n");
@@ -104,7 +116,8 @@ struct SynthXilinxPass : public ScriptPass
}
std::string top_opt, edif_file, blif_file, family;
- bool flatten, retime, vpr, nobram, nodram, nosrl, nocarry, nowidelut, abc9;
+ bool flatten, retime, vpr, ise, iopad, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, abc9;
+ bool flatten_before_abc;
int widemux;
void clear_flags() YS_OVERRIDE
@@ -116,13 +129,18 @@ struct SynthXilinxPass : public ScriptPass
flatten = false;
retime = false;
vpr = false;
+ ise = false;
+ iopad = false;
+ noiopad = false;
+ noclkbuf = false;
nocarry = false;
nobram = false;
- nodram = false;
+ nolutram = false;
nosrl = false;
nocarry = false;
nowidelut = false;
abc9 = false;
+ flatten_before_abc = false;
widemux = 0;
}
@@ -162,6 +180,10 @@ struct SynthXilinxPass : public ScriptPass
flatten = true;
continue;
}
+ if (args[argidx] == "-flatten_before_abc") {
+ flatten_before_abc = true;
+ continue;
+ }
if (args[argidx] == "-retime") {
retime = true;
continue;
@@ -178,6 +200,22 @@ struct SynthXilinxPass : public ScriptPass
vpr = true;
continue;
}
+ if (args[argidx] == "-ise") {
+ ise = true;
+ continue;
+ }
+ if (args[argidx] == "-iopad") {
+ iopad = true;
+ continue;
+ }
+ if (args[argidx] == "-noiopad") {
+ noiopad = true;
+ continue;
+ }
+ if (args[argidx] == "-noclkbuf") {
+ noclkbuf = true;
+ continue;
+ }
if (args[argidx] == "-nocarry") {
nocarry = true;
continue;
@@ -186,8 +224,8 @@ struct SynthXilinxPass : public ScriptPass
nobram = true;
continue;
}
- if (args[argidx] == "-nodram") {
- nodram = true;
+ if (args[argidx] == "-nolutram" || /*deprecated alias*/ args[argidx] == "-nodram") {
+ nolutram = true;
continue;
}
if (args[argidx] == "-nosrl") {
@@ -265,9 +303,8 @@ struct SynthXilinxPass : public ScriptPass
if (widemux > 0 || help_mode)
run("muxpack", " ('-widemux' only)");
- // shregmap -tech xilinx can cope with $shiftx and $mux
- // cells for identifying variable-length shift registers,
- // so attempt to convert $pmux-es to the former
+ // xilinx_srl looks for $shiftx cells for identifying variable-length
+ // shift registers, so attempt to convert $pmux-es to this
// Also: wide multiplexer inference benefits from this too
if (!(nosrl && widemux == 0) || help_mode) {
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
@@ -284,7 +321,7 @@ struct SynthXilinxPass : public ScriptPass
run("opt_clean");
}
- if (check_label("bram", "(skip if '-nobram')")) {
+ if (check_label("map_bram", "(skip if '-nobram')")) {
if (help_mode) {
run("memory_bram -rules +/xilinx/{family}_brams.txt");
run("techmap -map +/xilinx/{family}_brams_map.v");
@@ -301,20 +338,23 @@ struct SynthXilinxPass : public ScriptPass
}
}
- if (check_label("dram", "(skip if '-nodram')")) {
- if (!nodram || help_mode) {
- run("memory_bram -rules +/xilinx/drams.txt");
- run("techmap -map +/xilinx/drams_map.v");
+ if (check_label("map_lutram", "(skip if '-nolutram')")) {
+ if (!nolutram || help_mode) {
+ run("memory_bram -rules +/xilinx/lutrams.txt");
+ run("techmap -map +/xilinx/lutrams_map.v");
}
}
- if (check_label("fine")) {
+ if (check_label("map_ffram")) {
if (widemux > 0)
run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover
// performs less efficiently
else
run("opt -fast -full");
run("memory_map");
+ }
+
+ if (check_label("fine")) {
run("dffsr2dff");
run("dff2dffe");
if (help_mode) {
@@ -346,13 +386,8 @@ struct SynthXilinxPass : public ScriptPass
}
run("opt -full");
- if (!nosrl || help_mode) {
- // shregmap operates on bit-level flops, not word-level,
- // so break those down here
- run("simplemap t:$dff t:$dffe", " (skip if '-nosrl')");
- // shregmap with '-tech xilinx' infers variable length shift regs
- run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
- }
+ if (!nosrl || help_mode)
+ run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')");
std::string techmap_args = " -map +/techmap.v";
if (help_mode)
@@ -380,8 +415,18 @@ struct SynthXilinxPass : public ScriptPass
run("clean");
}
+ if (check_label("map_ffs")) {
+ if (abc9 || help_mode) {
+ run("techmap -map +/xilinx/ff_map.v", "('-abc9' only)");
+ run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
+ "-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "('-abc9' only)");
+ }
+ }
+
if (check_label("map_luts")) {
run("opt_expr -mux_undef");
+ if (flatten_before_abc)
+ run("flatten");
if (help_mode)
run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut', option for '-retime')");
else if (abc9) {
@@ -403,13 +448,32 @@ struct SynthXilinxPass : public ScriptPass
// This shregmap call infers fixed length shift registers after abc
// has performed any necessary retiming
if (!nosrl || help_mode)
- run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')");
- run("techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
- run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
- "-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
+ run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')");
+
+ std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v";
+ if (help_mode)
+ techmap_args += " [-map +/xilinx/ff_map.v]";
+ else if (!abc9)
+ techmap_args += " -map +/xilinx/ff_map.v";
+ run("techmap " + techmap_args);
+ if (!abc9)
+ run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
+ "-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "(without '-abc9' only)");
run("clean");
}
+ if (check_label("finalize")) {
+ bool do_iopad = iopad || (ise && !noiopad);
+ if (help_mode || !noclkbuf) {
+ if (help_mode || do_iopad)
+ run("clkbufmap -buf BUFG O:I -inpad IBUFG O:I", "(skip if '-noclkbuf', '-inpad' passed if '-iopad' or '-ise' and not '-noiopad')");
+ else
+ run("clkbufmap -buf BUFG O:I");
+ }
+ if (do_iopad)
+ run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I A:top", "(only if '-iopad' or '-ise' and not '-noiopad')");
+ }
+
if (check_label("check")) {
run("hierarchy -check");
run("stat -tech xilinx");
diff --git a/techlibs/xilinx/xc6s_brams_bb.v b/techlibs/xilinx/xc6s_brams_bb.v
index eb1a29579..041d6b54f 100644
--- a/techlibs/xilinx/xc6s_brams_bb.v
+++ b/techlibs/xilinx/xc6s_brams_bb.v
@@ -1,5 +1,7 @@
module RAMB8BWER (
+ (* clkbuf_sink *)
input CLKAWRCLK,
+ (* clkbuf_sink *)
input CLKBRDCLK,
input ENAWREN,
input ENBRDEN,
@@ -87,7 +89,9 @@ module RAMB8BWER (
endmodule
module RAMB16BWER (
+ (* clkbuf_sink *)
input CLKA,
+ (* clkbuf_sink *)
input CLKB,
input ENA,
input ENB,
diff --git a/techlibs/xilinx/xc7_brams_bb.v b/techlibs/xilinx/xc7_brams_bb.v
index a682ba4a7..a43b4b5a1 100644
--- a/techlibs/xilinx/xc7_brams_bb.v
+++ b/techlibs/xilinx/xc7_brams_bb.v
@@ -1,5 +1,7 @@
module RAMB18E1 (
+ (* clkbuf_sink *)
input CLKARDCLK,
+ (* clkbuf_sink *)
input CLKBWRCLK,
input ENARDEN,
input ENBWREN,
@@ -123,7 +125,9 @@ module RAMB18E1 (
endmodule
module RAMB36E1 (
+ (* clkbuf_sink *)
input CLKARDCLK,
+ (* clkbuf_sink *)
input CLKBWRCLK,
input ENARDEN,
input ENBWREN,
diff --git a/tests/fsm/run-test.sh b/tests/fsm/run-test.sh
index cf506470d..fbdcbf048 100755
--- a/tests/fsm/run-test.sh
+++ b/tests/fsm/run-test.sh
@@ -6,7 +6,7 @@
set -e
OPTIND=1
-count=100
+count=50
seed="" # default to no seed specified
while getopts "c:S:" opt
do
diff --git a/tests/ice40/.gitignore b/tests/ice40/.gitignore
new file mode 100644
index 000000000..9a71dca69
--- /dev/null
+++ b/tests/ice40/.gitignore
@@ -0,0 +1,4 @@
+*.log
+/run-test.mk
++*_synth.v
++*_testbench
diff --git a/tests/ice40/add_sub.v b/tests/ice40/add_sub.v
new file mode 100644
index 000000000..177c32e30
--- /dev/null
+++ b/tests/ice40/add_sub.v
@@ -0,0 +1,13 @@
+module top
+(
+ input [3:0] x,
+ input [3:0] y,
+
+ output [3:0] A,
+ output [3:0] B
+ );
+
+assign A = x + y;
+assign B = x - y;
+
+endmodule
diff --git a/tests/ice40/add_sub.ys b/tests/ice40/add_sub.ys
new file mode 100644
index 000000000..4a998d98d
--- /dev/null
+++ b/tests/ice40/add_sub.ys
@@ -0,0 +1,9 @@
+read_verilog add_sub.v
+hierarchy -top top
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 11 t:SB_LUT4
+select -assert-count 6 t:SB_CARRY
+select -assert-none t:SB_LUT4 t:SB_CARRY %% t:* %D
+
diff --git a/tests/ice40/adffs.v b/tests/ice40/adffs.v
new file mode 100644
index 000000000..93c8bf52c
--- /dev/null
+++ b/tests/ice40/adffs.v
@@ -0,0 +1,91 @@
+module adff
+ ( input d, clk, clr, output reg q );
+ initial begin
+ q = 0;
+ end
+ always @( posedge clk, posedge clr )
+ if ( clr )
+ q <= 1'b0;
+ else
+ q <= d;
+endmodule
+
+module adffn
+ ( input d, clk, clr, output reg q );
+ initial begin
+ q = 0;
+ end
+ always @( posedge clk, negedge clr )
+ if ( !clr )
+ q <= 1'b0;
+ else
+ q <= d;
+endmodule
+
+module dffsr
+ ( input d, clk, pre, clr, output reg q );
+ initial begin
+ q = 0;
+ end
+ always @( posedge clk, posedge pre, posedge clr )
+ if ( clr )
+ q <= 1'b0;
+ else if ( pre )
+ q <= 1'b1;
+ else
+ q <= d;
+endmodule
+
+module ndffnsnr
+ ( input d, clk, pre, clr, output reg q );
+ initial begin
+ q = 0;
+ end
+ always @( negedge clk, negedge pre, negedge clr )
+ if ( !clr )
+ q <= 1'b0;
+ else if ( !pre )
+ q <= 1'b1;
+ else
+ q <= d;
+endmodule
+
+module top (
+input clk,
+input clr,
+input pre,
+input a,
+output b,b1,b2,b3
+);
+
+dffsr u_dffsr (
+ .clk (clk ),
+ .clr (clr),
+ .pre (pre),
+ .d (a ),
+ .q (b )
+ );
+
+ndffnsnr u_ndffnsnr (
+ .clk (clk ),
+ .clr (clr),
+ .pre (pre),
+ .d (a ),
+ .q (b1 )
+ );
+
+adff u_adff (
+ .clk (clk ),
+ .clr (clr),
+ .d (a ),
+ .q (b2 )
+ );
+
+adffn u_adffn (
+ .clk (clk ),
+ .clr (clr),
+ .d (a ),
+ .q (b3 )
+ );
+
+endmodule
diff --git a/tests/ice40/adffs.ys b/tests/ice40/adffs.ys
new file mode 100644
index 000000000..14b251c5c
--- /dev/null
+++ b/tests/ice40/adffs.ys
@@ -0,0 +1,12 @@
+read_verilog adffs.v
+proc
+async2sync # converts async flops to a 'sync' variant clocked by a 'super'-clock
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 1 t:SB_DFF
+select -assert-count 1 t:SB_DFFN
+select -assert-count 2 t:SB_DFFSR
+select -assert-count 7 t:SB_LUT4
+select -assert-none t:SB_DFF t:SB_DFFN t:SB_DFFSR t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/alu.v b/tests/ice40/alu.v
new file mode 100644
index 000000000..f82cc2e21
--- /dev/null
+++ b/tests/ice40/alu.v
@@ -0,0 +1,19 @@
+module top (
+ input clock,
+ input [31:0] dinA, dinB,
+ input [2:0] opcode,
+ output reg [31:0] dout
+);
+ always @(posedge clock) begin
+ case (opcode)
+ 0: dout <= dinA + dinB;
+ 1: dout <= dinA - dinB;
+ 2: dout <= dinA >> dinB;
+ 3: dout <= $signed(dinA) >>> dinB;
+ 4: dout <= dinA << dinB;
+ 5: dout <= dinA & dinB;
+ 6: dout <= dinA | dinB;
+ 7: dout <= dinA ^ dinB;
+ endcase
+ end
+endmodule
diff --git a/tests/ice40/alu.ys b/tests/ice40/alu.ys
new file mode 100644
index 000000000..bd859efc4
--- /dev/null
+++ b/tests/ice40/alu.ys
@@ -0,0 +1,11 @@
+read_verilog alu.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 62 t:SB_CARRY
+select -assert-count 32 t:SB_DFF
+select -assert-count 655 t:SB_LUT4
+select -assert-none t:SB_CARRY t:SB_DFF t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/counter.v b/tests/ice40/counter.v
new file mode 100644
index 000000000..52852f8ac
--- /dev/null
+++ b/tests/ice40/counter.v
@@ -0,0 +1,17 @@
+module top (
+out,
+clk,
+reset
+);
+ output [7:0] out;
+ input clk, reset;
+ reg [7:0] out;
+
+ always @(posedge clk, posedge reset)
+ if (reset) begin
+ out <= 8'b0 ;
+ end else
+ out <= out + 1;
+
+
+endmodule
diff --git a/tests/ice40/counter.ys b/tests/ice40/counter.ys
new file mode 100644
index 000000000..c65c21622
--- /dev/null
+++ b/tests/ice40/counter.ys
@@ -0,0 +1,11 @@
+read_verilog counter.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 6 t:SB_CARRY
+select -assert-count 8 t:SB_DFFR
+select -assert-count 8 t:SB_LUT4
+select -assert-none t:SB_CARRY t:SB_DFFR t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/dffs.v b/tests/ice40/dffs.v
new file mode 100644
index 000000000..d97840c43
--- /dev/null
+++ b/tests/ice40/dffs.v
@@ -0,0 +1,37 @@
+module dff
+ ( input d, clk, output reg q );
+ always @( posedge clk )
+ q <= d;
+endmodule
+
+module dffe
+ ( input d, clk, en, output reg q );
+ initial begin
+ q = 0;
+ end
+ always @( posedge clk )
+ if ( en )
+ q <= d;
+endmodule
+
+module top (
+input clk,
+input en,
+input a,
+output b,b1,
+);
+
+dff u_dff (
+ .clk (clk ),
+ .d (a ),
+ .q (b )
+ );
+
+dffe u_ndffe (
+ .clk (clk ),
+ .en (en),
+ .d (a ),
+ .q (b1 )
+ );
+
+endmodule
diff --git a/tests/ice40/dffs.ys b/tests/ice40/dffs.ys
new file mode 100644
index 000000000..ee7f884b1
--- /dev/null
+++ b/tests/ice40/dffs.ys
@@ -0,0 +1,10 @@
+read_verilog dffs.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 1 t:SB_DFF
+select -assert-count 1 t:SB_DFFE
+select -assert-none t:SB_DFF t:SB_DFFE %% t:* %D
diff --git a/tests/ice40/div_mod.v b/tests/ice40/div_mod.v
new file mode 100644
index 000000000..64a36707d
--- /dev/null
+++ b/tests/ice40/div_mod.v
@@ -0,0 +1,13 @@
+module top
+(
+ input [3:0] x,
+ input [3:0] y,
+
+ output [3:0] A,
+ output [3:0] B
+ );
+
+assign A = x % y;
+assign B = x / y;
+
+endmodule
diff --git a/tests/ice40/div_mod.ys b/tests/ice40/div_mod.ys
new file mode 100644
index 000000000..21cac7144
--- /dev/null
+++ b/tests/ice40/div_mod.ys
@@ -0,0 +1,9 @@
+read_verilog div_mod.v
+hierarchy -top top
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 62 t:SB_LUT4
+select -assert-count 41 t:SB_CARRY
+select -assert-none t:SB_LUT4 t:SB_CARRY %% t:* %D
diff --git a/tests/ice40/dpram.v b/tests/ice40/dpram.v
new file mode 100644
index 000000000..3ea4c1f27
--- /dev/null
+++ b/tests/ice40/dpram.v
@@ -0,0 +1,23 @@
+/*
+Example from: https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/EI/iCEcube201701UserGuide.ashx?document_id=52071 [p. 72].
+*/
+module top (din, write_en, waddr, wclk, raddr, rclk, dout);
+parameter addr_width = 8;
+parameter data_width = 8;
+input [addr_width-1:0] waddr, raddr;
+input [data_width-1:0] din;
+input write_en, wclk, rclk;
+output [data_width-1:0] dout;
+reg [data_width-1:0] dout;
+reg [data_width-1:0] mem [(1<<addr_width)-1:0]
+/* synthesis syn_ramstyle = "no_rw_check" */ ;
+always @(posedge wclk) // Write memory.
+begin
+if (write_en)
+mem[waddr] <= din; // Using write address bus.
+end
+always @(posedge rclk) // Read memory.
+begin
+dout <= mem[raddr]; // Using read address bus.
+end
+endmodule
diff --git a/tests/ice40/dpram.ys b/tests/ice40/dpram.ys
new file mode 100644
index 000000000..4f6a253ea
--- /dev/null
+++ b/tests/ice40/dpram.ys
@@ -0,0 +1,15 @@
+read_verilog dpram.v
+hierarchy -top top
+proc
+memory -nomap
+equiv_opt -run :prove -map +/ice40/cells_sim.v synth_ice40
+memory
+opt -full
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -seq 3 -set-init-zero -show-inputs -show-outputs miter
+
+design -load postopt
+cd top
+select -assert-count 1 t:SB_RAM40_4K
+select -assert-none t:SB_RAM40_4K %% t:* %D
diff --git a/tests/ice40/fsm.v b/tests/ice40/fsm.v
new file mode 100644
index 000000000..0605bd102
--- /dev/null
+++ b/tests/ice40/fsm.v
@@ -0,0 +1,73 @@
+ module fsm (
+ clock,
+ reset,
+ req_0,
+ req_1,
+ gnt_0,
+ gnt_1
+ );
+ input clock,reset,req_0,req_1;
+ output gnt_0,gnt_1;
+ wire clock,reset,req_0,req_1;
+ reg gnt_0,gnt_1;
+
+ parameter SIZE = 3 ;
+ parameter IDLE = 3'b001,GNT0 = 3'b010,GNT1 = 3'b100,GNT2 = 3'b101 ;
+
+ reg [SIZE-1:0] state;
+ reg [SIZE-1:0] next_state;
+
+ always @ (posedge clock)
+ begin : FSM
+ if (reset == 1'b1) begin
+ state <= #1 IDLE;
+ gnt_0 <= 0;
+ gnt_1 <= 0;
+ end else
+ case(state)
+ IDLE : if (req_0 == 1'b1) begin
+ state <= #1 GNT0;
+ gnt_0 <= 1;
+ end else if (req_1 == 1'b1) begin
+ gnt_1 <= 1;
+ state <= #1 GNT0;
+ end else begin
+ state <= #1 IDLE;
+ end
+ GNT0 : if (req_0 == 1'b1) begin
+ state <= #1 GNT0;
+ end else begin
+ gnt_0 <= 0;
+ state <= #1 IDLE;
+ end
+ GNT1 : if (req_1 == 1'b1) begin
+ state <= #1 GNT2;
+ gnt_1 <= req_0;
+ end
+ GNT2 : if (req_0 == 1'b1) begin
+ state <= #1 GNT1;
+ gnt_1 <= req_1;
+ end
+ default : state <= #1 IDLE;
+ endcase
+ end
+
+ endmodule
+
+ module top (
+input clk,
+input rst,
+input a,
+input b,
+output g0,
+output g1
+);
+
+fsm u_fsm ( .clock(clk),
+ .reset(rst),
+ .req_0(a),
+ .req_1(b),
+ .gnt_0(g0),
+ .gnt_1(g1));
+
+endmodule
diff --git a/tests/ice40/fsm.ys b/tests/ice40/fsm.ys
new file mode 100644
index 000000000..4cc8629d6
--- /dev/null
+++ b/tests/ice40/fsm.ys
@@ -0,0 +1,13 @@
+read_verilog fsm.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+
+select -assert-count 2 t:SB_DFFESR
+select -assert-count 2 t:SB_DFFSR
+select -assert-count 1 t:SB_DFFSS
+select -assert-count 13 t:SB_LUT4
+select -assert-none t:SB_DFFESR t:SB_DFFSR t:SB_DFFSS t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/ice40_opt.ys b/tests/ice40/ice40_opt.ys
new file mode 100644
index 000000000..b17c69c91
--- /dev/null
+++ b/tests/ice40/ice40_opt.ys
@@ -0,0 +1,26 @@
+read_verilog -icells -formal <<EOT
+module top(input CI, I0, output [1:0] CO, output O);
+ wire A = 1'b0, B = 1'b0;
+ \$__ICE40_CARRY_WRAPPER #(
+ // A[0]: 1010 1010 1010 1010
+ // A[1]: 1100 1100 1100 1100
+ // A[2]: 1111 0000 1111 0000
+ // A[3]: 1111 1111 0000 0000
+ .LUT(~16'b 0110_1001_1001_0110)
+ ) u0 (
+ .A(A),
+ .B(B),
+ .CI(CI),
+ .I0(I0),
+ .I3(CI),
+ .CO(CO[0]),
+ .O(O)
+ );
+ SB_CARRY u1 (.I0(~A), .I1(~B), .CI(CI), .CO(CO[1]));
+endmodule
+EOT
+
+equiv_opt -assert -map +/ice40/cells_map.v -map +/ice40/cells_sim.v ice40_opt
+design -load postopt
+select -assert-count 1 t:*
+select -assert-count 1 t:$lut
diff --git a/tests/ice40/latches.v b/tests/ice40/latches.v
new file mode 100644
index 000000000..9dc43e4c2
--- /dev/null
+++ b/tests/ice40/latches.v
@@ -0,0 +1,58 @@
+module latchp
+ ( input d, clk, en, output reg q );
+ always @*
+ if ( en )
+ q <= d;
+endmodule
+
+module latchn
+ ( input d, clk, en, output reg q );
+ always @*
+ if ( !en )
+ q <= d;
+endmodule
+
+module latchsr
+ ( input d, clk, en, clr, pre, output reg q );
+ always @*
+ if ( clr )
+ q <= 1'b0;
+ else if ( pre )
+ q <= 1'b1;
+ else if ( en )
+ q <= d;
+endmodule
+
+
+module top (
+input clk,
+input clr,
+input pre,
+input a,
+output b,b1,b2
+);
+
+
+latchp u_latchp (
+ .en (clk ),
+ .d (a ),
+ .q (b )
+ );
+
+
+latchn u_latchn (
+ .en (clk ),
+ .d (a ),
+ .q (b1 )
+ );
+
+
+latchsr u_latchsr (
+ .en (clk ),
+ .clr (clr),
+ .pre (pre),
+ .d (a ),
+ .q (b2 )
+ );
+
+endmodule
diff --git a/tests/ice40/latches.ys b/tests/ice40/latches.ys
new file mode 100644
index 000000000..f3562559e
--- /dev/null
+++ b/tests/ice40/latches.ys
@@ -0,0 +1,15 @@
+read_verilog latches.v
+design -save read
+
+proc
+async2sync # converts latches to a 'sync' variant clocked by a 'super'-clock
+flatten
+synth_ice40
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+
+design -load read
+synth_ice40
+cd top
+select -assert-count 4 t:SB_LUT4
+select -assert-none t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/logic.v b/tests/ice40/logic.v
new file mode 100644
index 000000000..e5343cae0
--- /dev/null
+++ b/tests/ice40/logic.v
@@ -0,0 +1,18 @@
+module top
+(
+ input [0:7] in,
+ output B1,B2,B3,B4,B5,B6,B7,B8,B9,B10
+ );
+
+ assign B1 = in[0] & in[1];
+ assign B2 = in[0] | in[1];
+ assign B3 = in[0] ~& in[1];
+ assign B4 = in[0] ~| in[1];
+ assign B5 = in[0] ^ in[1];
+ assign B6 = in[0] ~^ in[1];
+ assign B7 = ~in[0];
+ assign B8 = in[0];
+ assign B9 = in[0:1] && in [2:3];
+ assign B10 = in[0:1] || in [2:3];
+
+endmodule
diff --git a/tests/ice40/logic.ys b/tests/ice40/logic.ys
new file mode 100644
index 000000000..fc5e5b1d8
--- /dev/null
+++ b/tests/ice40/logic.ys
@@ -0,0 +1,7 @@
+read_verilog logic.v
+hierarchy -top top
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 9 t:SB_LUT4
+select -assert-none t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/macc.v b/tests/ice40/macc.v
new file mode 100644
index 000000000..6c3676c83
--- /dev/null
+++ b/tests/ice40/macc.v
@@ -0,0 +1,25 @@
+/*
+Example from: https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/EI/iCEcube201701UserGuide.ashx?document_id=52071 [p. 77].
+*/
+module top(clk,a,b,c,set);
+parameter A_WIDTH = 6 /*4*/;
+parameter B_WIDTH = 6 /*3*/;
+input set;
+input clk;
+input signed [(A_WIDTH - 1):0] a;
+input signed [(B_WIDTH - 1):0] b;
+output signed [(A_WIDTH + B_WIDTH - 1):0] c;
+reg [(A_WIDTH + B_WIDTH - 1):0] reg_tmp_c;
+assign c = reg_tmp_c;
+always @(posedge clk)
+begin
+if(set)
+begin
+reg_tmp_c <= 0;
+end
+else
+begin
+reg_tmp_c <= a * b + c;
+end
+end
+endmodule
diff --git a/tests/ice40/macc.ys b/tests/ice40/macc.ys
new file mode 100644
index 000000000..0f4c19be5
--- /dev/null
+++ b/tests/ice40/macc.ys
@@ -0,0 +1,13 @@
+read_verilog macc.v
+proc
+hierarchy -top top
+#equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 -dsp # equivalency check
+
+equiv_opt -run :prove -map +/ice40/cells_sim.v synth_ice40 -dsp
+async2sync
+equiv_opt -run prove: -assert null
+
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 1 t:SB_MAC16
+select -assert-none t:SB_MAC16 %% t:* %D
diff --git a/tests/ice40/memory.v b/tests/ice40/memory.v
new file mode 100644
index 000000000..cb7753f7b
--- /dev/null
+++ b/tests/ice40/memory.v
@@ -0,0 +1,21 @@
+module top
+(
+ input [7:0] data_a,
+ input [6:1] addr_a,
+ input we_a, clk,
+ output reg [7:0] q_a
+);
+ // Declare the RAM variable
+ reg [7:0] ram[63:0];
+
+ // Port A
+ always @ (posedge clk)
+ begin
+ if (we_a)
+ begin
+ ram[addr_a] <= data_a;
+ q_a <= data_a;
+ end
+ q_a <= ram[addr_a];
+ end
+endmodule
diff --git a/tests/ice40/memory.ys b/tests/ice40/memory.ys
new file mode 100644
index 000000000..a66afbae6
--- /dev/null
+++ b/tests/ice40/memory.ys
@@ -0,0 +1,15 @@
+read_verilog memory.v
+hierarchy -top top
+proc
+memory -nomap
+equiv_opt -run :prove -map +/ice40/cells_sim.v synth_ice40
+memory
+opt -full
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -seq 5 -set-init-zero -show-inputs -show-outputs miter
+
+design -load postopt
+cd top
+select -assert-count 1 t:SB_RAM40_4K
+select -assert-none t:SB_RAM40_4K %% t:* %D
diff --git a/tests/ice40/mul.v b/tests/ice40/mul.v
new file mode 100644
index 000000000..d5b48b1d7
--- /dev/null
+++ b/tests/ice40/mul.v
@@ -0,0 +1,11 @@
+module top
+(
+ input [5:0] x,
+ input [5:0] y,
+
+ output [11:0] A,
+ );
+
+assign A = x * y;
+
+endmodule
diff --git a/tests/ice40/mul.ys b/tests/ice40/mul.ys
new file mode 100644
index 000000000..8a0822a84
--- /dev/null
+++ b/tests/ice40/mul.ys
@@ -0,0 +1,7 @@
+read_verilog mul.v
+hierarchy -top top
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 -dsp # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 1 t:SB_MAC16
+select -assert-none t:SB_MAC16 %% t:* %D
diff --git a/tests/ice40/mux.v b/tests/ice40/mux.v
new file mode 100644
index 000000000..0814b733e
--- /dev/null
+++ b/tests/ice40/mux.v
@@ -0,0 +1,100 @@
+module mux2 (S,A,B,Y);
+ input S;
+ input A,B;
+ output reg Y;
+
+ always @(*)
+ Y = (S)? B : A;
+endmodule
+
+module mux4 ( S, D, Y );
+
+input[1:0] S;
+input[3:0] D;
+output Y;
+
+reg Y;
+wire[1:0] S;
+wire[3:0] D;
+
+always @*
+begin
+ case( S )
+ 0 : Y = D[0];
+ 1 : Y = D[1];
+ 2 : Y = D[2];
+ 3 : Y = D[3];
+ endcase
+end
+
+endmodule
+
+module mux8 ( S, D, Y );
+
+input[2:0] S;
+input[7:0] D;
+output Y;
+
+reg Y;
+wire[2:0] S;
+wire[7:0] D;
+
+always @*
+begin
+ case( S )
+ 0 : Y = D[0];
+ 1 : Y = D[1];
+ 2 : Y = D[2];
+ 3 : Y = D[3];
+ 4 : Y = D[4];
+ 5 : Y = D[5];
+ 6 : Y = D[6];
+ 7 : Y = D[7];
+ endcase
+end
+
+endmodule
+
+module mux16 (D, S, Y);
+ input [15:0] D;
+ input [3:0] S;
+ output Y;
+
+assign Y = D[S];
+
+endmodule
+
+
+module top (
+input [3:0] S,
+input [15:0] D,
+output M2,M4,M8,M16
+);
+
+mux2 u_mux2 (
+ .S (S[0]),
+ .A (D[0]),
+ .B (D[1]),
+ .Y (M2)
+ );
+
+
+mux4 u_mux4 (
+ .S (S[1:0]),
+ .D (D[3:0]),
+ .Y (M4)
+ );
+
+mux8 u_mux8 (
+ .S (S[2:0]),
+ .D (D[7:0]),
+ .Y (M8)
+ );
+
+mux16 u_mux16 (
+ .S (S[3:0]),
+ .D (D[15:0]),
+ .Y (M16)
+ );
+
+endmodule
diff --git a/tests/ice40/mux.ys b/tests/ice40/mux.ys
new file mode 100644
index 000000000..182b49499
--- /dev/null
+++ b/tests/ice40/mux.ys
@@ -0,0 +1,8 @@
+read_verilog mux.v
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 19 t:SB_LUT4
+select -assert-none t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/rom.v b/tests/ice40/rom.v
new file mode 100644
index 000000000..0a0f41f37
--- /dev/null
+++ b/tests/ice40/rom.v
@@ -0,0 +1,18 @@
+/*
+Example from: https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/EI/iCEcube201701UserGuide.ashx?document_id=52071 [p. 74].
+*/
+module top(data, addr);
+output [3:0] data;
+input [4:0] addr;
+always @(addr) begin
+case (addr)
+0 : data = 'h4;
+1 : data = 'h9;
+2 : data = 'h1;
+15 : data = 'h8;
+16 : data = 'h1;
+17 : data = 'h0;
+default : data = 'h0;
+endcase
+end
+endmodule
diff --git a/tests/ice40/rom.ys b/tests/ice40/rom.ys
new file mode 100644
index 000000000..41d214e2a
--- /dev/null
+++ b/tests/ice40/rom.ys
@@ -0,0 +1,8 @@
+read_verilog rom.v
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 5 t:SB_LUT4
+select -assert-none t:SB_LUT4 %% t:* %D
diff --git a/tests/ice40/run-test.sh b/tests/ice40/run-test.sh
new file mode 100755
index 000000000..2c72ca3a9
--- /dev/null
+++ b/tests/ice40/run-test.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -e
+{
+echo "all::"
+for x in *.ys; do
+ echo "all:: run-$x"
+ echo "run-$x:"
+ echo " @echo 'Running $x..'"
+ echo " @../../yosys -ql ${x%.ys}.log $x -w 'Yosys has only limited support for tri-state logic at the moment.'"
+done
+for s in *.sh; do
+ if [ "$s" != "run-test.sh" ]; then
+ echo "all:: run-$s"
+ echo "run-$s:"
+ echo " @echo 'Running $s..'"
+ echo " @bash $s"
+ fi
+done
+} > run-test.mk
+exec ${MAKE:-make} -f run-test.mk
diff --git a/tests/ice40/shifter.v b/tests/ice40/shifter.v
new file mode 100644
index 000000000..c55632552
--- /dev/null
+++ b/tests/ice40/shifter.v
@@ -0,0 +1,22 @@
+module top (
+out,
+clk,
+in
+);
+ output [7:0] out;
+ input signed clk, in;
+ reg signed [7:0] out = 0;
+
+ always @(posedge clk)
+ begin
+`ifndef BUG
+ out <= out >> 1;
+ out[7] <= in;
+`else
+
+ out <= out << 1;
+ out[7] <= in;
+`endif
+ end
+
+endmodule
diff --git a/tests/ice40/shifter.ys b/tests/ice40/shifter.ys
new file mode 100644
index 000000000..47d95d298
--- /dev/null
+++ b/tests/ice40/shifter.ys
@@ -0,0 +1,9 @@
+read_verilog shifter.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 8 t:SB_DFF
+select -assert-none t:SB_DFF %% t:* %D
diff --git a/tests/ice40/tribuf.v b/tests/ice40/tribuf.v
new file mode 100644
index 000000000..870a02584
--- /dev/null
+++ b/tests/ice40/tribuf.v
@@ -0,0 +1,23 @@
+module tristate (en, i, o);
+ input en;
+ input i;
+ output o;
+
+ assign o = en ? i : 1'bz;
+
+endmodule
+
+
+module top (
+input en,
+input a,
+output b
+);
+
+tristate u_tri (
+ .en (en ),
+ .i (a ),
+ .o (b )
+ );
+
+endmodule
diff --git a/tests/ice40/tribuf.ys b/tests/ice40/tribuf.ys
new file mode 100644
index 000000000..d1e1b3108
--- /dev/null
+++ b/tests/ice40/tribuf.ys
@@ -0,0 +1,9 @@
+read_verilog tribuf.v
+hierarchy -top top
+proc
+flatten
+equiv_opt -assert -map +/ice40/cells_sim.v -map +/simcells.v synth_ice40 # equivalency check
+design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
+cd top # Constrain all select calls below inside the top module
+select -assert-count 1 t:$_TBUF_
+select -assert-none t:$_TBUF_ %% t:* %D
diff --git a/tests/opt/opt_expr.ys b/tests/opt/opt_expr.ys
index f0306efa1..ecc2c8da8 100644
--- a/tests/opt/opt_expr.ys
+++ b/tests/opt/opt_expr.ys
@@ -221,3 +221,73 @@ check
equiv_opt opt_expr -fine
design -load postopt
select -assert-count 1 t:$alu r:A_WIDTH=8 r:B_WIDTH=8 r:Y_WIDTH=9 %i %i %i
+
+###########
+
+design -reset
+read_verilog -icells <<EOT
+module opt_expr_shiftx_1bit(input [2:0] a, input [1:0] b, output y);
+ \$shiftx #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(4), .B_WIDTH(2), .Y_WIDTH(1)) shiftx (.A({1'bx,a}), .B(b), .Y(y));
+endmodule
+EOT
+check
+
+equiv_opt opt_expr
+design -load postopt
+select -assert-count 1 t:$shiftx r:A_WIDTH=3 %i
+
+###########
+
+design -reset
+read_verilog -icells <<EOT
+module opt_expr_shiftx_3bit(input [9:0] a, input [3:0] b, output [2:0] y);
+ \$shiftx #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(14), .B_WIDTH(4), .Y_WIDTH(3)) shiftx (.A({4'bxx00,a}), .B(b), .Y(y));
+endmodule
+EOT
+check
+
+equiv_opt opt_expr
+design -load postopt
+select -assert-count 1 t:$shiftx r:A_WIDTH=12 %i
+
+###########
+
+design -reset
+read_verilog -icells <<EOT
+module opt_expr_shift_1bit(input [2:0] a, input [1:0] b, output y);
+ \$shift #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(4), .B_WIDTH(2), .Y_WIDTH(1)) shift (.A({1'b0,a}), .B(b), .Y(y));
+endmodule
+EOT
+check
+
+equiv_opt opt_expr
+design -load postopt
+select -assert-count 1 t:$shift r:A_WIDTH=3 %i
+
+###########
+
+design -reset
+read_verilog -icells <<EOT
+module opt_expr_shift_3bit(input [9:0] a, input [3:0] b, output [2:0] y);
+ \$shift #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(14), .B_WIDTH(4), .Y_WIDTH(3)) shift (.A({4'b0x0x,a}), .B(b), .Y(y));
+endmodule
+EOT
+check
+
+equiv_opt opt_expr
+design -load postopt
+select -assert-count 1 t:$shift r:A_WIDTH=10 %i
+
+###########
+
+design -reset
+read_verilog -icells <<EOT
+module opt_expr_shift_3bit_keepdc(input [9:0] a, input [3:0] b, output [2:0] y);
+ \$shift #(.A_SIGNED(0), .B_SIGNED(0), .A_WIDTH(14), .B_WIDTH(4), .Y_WIDTH(3)) shift (.A({4'b0x0x,a}), .B(b), .Y(y));
+endmodule
+EOT
+check
+
+equiv_opt opt_expr -keepdc
+design -load postopt
+select -assert-count 1 t:$shift r:A_WIDTH=13 %i
diff --git a/tests/opt/opt_rmdff.ys b/tests/opt/opt_rmdff.ys
index 081f81782..83a162f44 100644
--- a/tests/opt/opt_rmdff.ys
+++ b/tests/opt/opt_rmdff.ys
@@ -19,8 +19,8 @@ hierarchy -top equiv
equiv_simple -undef
equiv_status -assert
-design -load gold
-stat
-
-design -load gate
-stat
+#design -load gold
+#stat
+#
+#design -load gate
+#stat
diff --git a/tests/proc/.gitignore b/tests/proc/.gitignore
new file mode 100644
index 000000000..397b4a762
--- /dev/null
+++ b/tests/proc/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/tests/proc/bug_1268.v b/tests/proc/bug_1268.v
new file mode 100644
index 000000000..698ac937a
--- /dev/null
+++ b/tests/proc/bug_1268.v
@@ -0,0 +1,23 @@
+module gold (input clock, ctrl, din, output reg dout);
+ always @(posedge clock) begin
+ if (1'b1) begin
+ if (1'b0) begin end else begin
+ dout <= 0;
+ end
+ if (ctrl)
+ dout <= din;
+ end
+ end
+endmodule
+
+module gate (input clock, ctrl, din, output reg dout);
+ always @(posedge clock) begin
+ if (1'b1) begin
+ if (1'b0) begin end else begin
+ dout <= 0;
+ end
+ end
+ if (ctrl)
+ dout <= din;
+ end
+endmodule
diff --git a/tests/proc/bug_1268.ys b/tests/proc/bug_1268.ys
new file mode 100644
index 000000000..b73e94449
--- /dev/null
+++ b/tests/proc/bug_1268.ys
@@ -0,0 +1,5 @@
+read_verilog bug_1268.v
+proc
+equiv_make gold gate equiv
+equiv_induct
+equiv_status -assert
diff --git a/tests/proc/run-test.sh b/tests/proc/run-test.sh
new file mode 100755
index 000000000..44ce7e674
--- /dev/null
+++ b/tests/proc/run-test.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+for x in *.ys; do
+ echo "Running $x.."
+ ../../yosys -ql ${x%.ys}.log $x
+done
diff --git a/tests/sat/initval.v b/tests/sat/initval.v
index 5b661f8d6..81f71b5ba 100644
--- a/tests/sat/initval.v
+++ b/tests/sat/initval.v
@@ -1,6 +1,7 @@
-module test(input clk, input [3:0] bar, output [3:0] foo);
+module test(input clk, input [3:0] bar, output [3:0] foo, asdf);
reg [3:0] foo = 0;
reg [3:0] last_bar = 0;
+ reg [3:0] asdf = 4'b1xxx;
always @*
foo[1:0] <= bar[1:0];
@@ -11,5 +12,10 @@ module test(input clk, input [3:0] bar, output [3:0] foo);
always @(posedge clk)
last_bar <= bar;
+ always @(posedge clk)
+ asdf[3] <= bar[3];
+ always @*
+ asdf[2:0] = 3'b111;
+
assert property (foo == {last_bar[3:2], bar[1:0]});
endmodule
diff --git a/tests/simple/realexpr.v b/tests/simple/realexpr.v
index 5b756e6be..74ed8faa5 100644
--- a/tests/simple/realexpr.v
+++ b/tests/simple/realexpr.v
@@ -1,4 +1,3 @@
-
module demo_001(y1, y2, y3, y4);
output [7:0] y1, y2, y3, y4;
@@ -22,3 +21,13 @@ module demo_002(y0, y1, y2, y3);
assign y3 = 1 ? -1 : 'd0;
endmodule
+module demo_003(output A, B);
+ parameter real p = 0;
+ assign A = (p==1.0);
+ assign B = (p!="1.000000");
+endmodule
+
+module demo_004(output A, B, C, D);
+ demo_003 #(1.0) demo_real (A, B);
+ demo_003 #(1) demo_int (C, D);
+endmodule
diff --git a/tests/simple/run-test.sh b/tests/simple/run-test.sh
index 967ac49f2..f20fd0d30 100755
--- a/tests/simple/run-test.sh
+++ b/tests/simple/run-test.sh
@@ -12,7 +12,7 @@ done
shift "$((OPTIND-1))"
# check for Icarus Verilog
-if ! which iverilog > /dev/null ; then
+if ! command -v iverilog > /dev/null ; then
echo "$0: Error: Icarus Verilog 'iverilog' not found."
exit 1
fi
diff --git a/tests/simple_abc9/.gitignore b/tests/simple_abc9/.gitignore
index 598951333..2355aea29 100644
--- a/tests/simple_abc9/.gitignore
+++ b/tests/simple_abc9/.gitignore
@@ -1,3 +1,4 @@
*.v
+*.sv
*.log
*.out
diff --git a/tests/simple_abc9/run-test.sh b/tests/simple_abc9/run-test.sh
index 4935d41ad..0d4262005 100755
--- a/tests/simple_abc9/run-test.sh
+++ b/tests/simple_abc9/run-test.sh
@@ -12,11 +12,18 @@ done
shift "$((OPTIND-1))"
# check for Icarus Verilog
-if ! which iverilog > /dev/null ; then
+if ! command -v iverilog > /dev/null ; then
echo "$0: Error: Icarus Verilog 'iverilog' not found."
exit 1
fi
cp ../simple/*.v .
+cp ../simple/*.sv .
DOLLAR='?'
-exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-p 'hierarchy; synth -run coarse; opt -full; techmap; abc9 -lut 4 -box ../abc.box; stat; check -assert; select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'"
+exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-n 300 -p '\
+ hierarchy; \
+ synth -run coarse; \
+ opt -full; \
+ techmap; abc9 -lut 4 -box ../abc.box; \
+ check -assert; \
+ select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'"
diff --git a/tests/techmap/.gitignore b/tests/techmap/.gitignore
index 397b4a762..cfed22fc5 100644
--- a/tests/techmap/.gitignore
+++ b/tests/techmap/.gitignore
@@ -1 +1,2 @@
*.log
+/*.mk
diff --git a/tests/techmap/clkbufmap.ys b/tests/techmap/clkbufmap.ys
new file mode 100644
index 000000000..f1277864e
--- /dev/null
+++ b/tests/techmap/clkbufmap.ys
@@ -0,0 +1,96 @@
+read_verilog <<EOT
+module clkbuf (input i, (* clkbuf_driver *) output o); endmodule
+module dff ((* clkbuf_sink *) input clk, input d, output q); endmodule
+module dffe ((* clkbuf_sink *) input c, input d, e, output q); endmodule
+module latch (input e, d, output q); endmodule
+module clkgen (output o); endmodule
+
+module top(input clk1, clk2, clk3, d, e, output [4:0] q);
+wire clk4, clk5, clk6;
+dff s0 (.clk(clk1), .d(d), .q(q[0]));
+dffe s1 (.c(clk2), .d(d), .e(e), .q(q[1]));
+latch s2 (.e(clk3), .d(d), .q(q[2]));
+sub s3 (.sclk4(clk4), .sclk5(clk5), .sclk6(clk6), .sd(d), .sq(q[3]));
+dff s4 (.clk(clk4), .d(d), .q(q[4]));
+dff s5 (.clk(clk5), .d(d), .q(q[4]));
+dff s6 (.clk(clk6), .d(d), .q(q[4]));
+endmodule
+
+module sub(output sclk4, output sclk5, output sclk6, input sd, output sq);
+wire tmp;
+clkgen s7(.o(sclk4));
+clkgen s8(.o(sclk5));
+clkgen s9(.o(tmp));
+clkbuf s10(.i(tmp), .o(sclk6));
+dff s11(.clk(sclk4), .d(sd), .q(sq));
+endmodule
+EOT
+
+hierarchy -auto-top
+design -save ref
+
+# ----------------------
+
+design -load ref
+clkbufmap -buf clkbuf o:i
+select -assert-count 3 top/t:clkbuf
+select -assert-count 2 sub/t:clkbuf
+select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf'
+select -assert-count 1 @clk1 # Check there is one such fanout
+select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout
+select -assert-count 1 @clk1 %x:+[o] %co c:s0 %i # And that one fanout is 's0'
+select -set clk2 w:clk2 %a %co t:clkbuf %i
+select -assert-count 1 @clk2
+select -assert-count 1 @clk2 %x:+[o] %co c:s* %i
+select -assert-count 1 @clk2 %x:+[o] %co c:s1 %i
+select -set clk5 w:clk5 %a %ci t:clkbuf %i
+select -assert-count 1 @clk5
+select -assert-count 1 @clk5 %x:+[o] %co c:s5 %i
+select -assert-count 1 @clk5 %x:+[i] %ci c:s3 %i
+select -set sclk4 w:sclk4 %a %ci t:clkbuf %i
+select -assert-count 1 @sclk4
+select -assert-count 1 @sclk4 %x:+[o] %co c:s11 %i
+select -assert-count 1 @sclk4 %x:+[i] %ci c:s7 %i
+
+# ----------------------
+
+design -load ref
+setattr -set clkbuf_inhibit 0 w:clk1
+setattr -set clkbuf_inhibit 1 w:clk2
+clkbufmap -buf clkbuf o:i
+select -assert-count 2 top/t:clkbuf
+select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf'
+select -assert-count 1 @clk1 # Check there is one such fanout
+select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout
+select -assert-count 1 @clk1 %x:+[o] %co c:s0 %i # And that one fanout is 's0'
+select -assert-count 0 w:clk2 %a %co t:clkbuf %i
+
+# ----------------------
+
+design -load ref
+setattr -set clkbuf_inhibit 1 w:clk1
+setattr -set buffer_type "bufg" w:clk2
+clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d
+select -assert-count 3 top/t:clkbuf
+select -assert-count 2 sub/t:clkbuf
+select -set clk1 w:clk1 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf'
+select -assert-count 1 @clk1 # Check there is one such fanout
+select -assert-count 1 @clk1 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout
+select -assert-count 1 @clk1 %x:+[o] %co c:s0 %i # And that one fanout is 's0'
+select -set clk2 w:clk2 %a %co t:clkbuf %i # Find 'clk1' fanouts that are 'clkbuf'
+select -assert-count 1 @clk2 # Check there is one such fanout
+select -assert-count 1 @clk2 %x:+[o] %co c:s* %i # Check that the 'o' of that clkbuf drives one fanout
+select -assert-count 1 @clk2 %x:+[o] %co c:s1 %i # And that one fanout is 's0'
+
+# ----------------------
+
+design -load ref
+setattr -set buffer_type "none" w:clk1
+setattr -set buffer_type "bufr" w:clk2
+setattr -set buffer_type "bufr" w:sclk4
+setattr -set buffer_type "bufr" w:sclk5
+clkbufmap -buf clkbuf o:i w:* a:buffer_type=none a:buffer_type=bufr %u %d
+select -assert-count 0 w:clk1 %a %co t:clkbuf %i
+select -assert-count 0 w:clk2 %a %co t:clkbuf %i
+select -assert-count 0 top/t:clkbuf
+select -assert-count 1 sub/t:clkbuf
diff --git a/tests/techmap/recursive.v b/tests/techmap/recursive.v
new file mode 100644
index 000000000..d281b21d8
--- /dev/null
+++ b/tests/techmap/recursive.v
@@ -0,0 +1,8 @@
+module top;
+sub s0();
+foo f0();
+endmodule
+
+module foo;
+sub s0();
+endmodule
diff --git a/tests/techmap/recursive_map.v b/tests/techmap/recursive_map.v
new file mode 100644
index 000000000..934256552
--- /dev/null
+++ b/tests/techmap/recursive_map.v
@@ -0,0 +1,4 @@
+module sub;
+ sub _TECHMAP_REPLACE_ ();
+ bar f0();
+endmodule
diff --git a/tests/techmap/recursive_runtest.sh b/tests/techmap/recursive_runtest.sh
new file mode 100644
index 000000000..30c79bf03
--- /dev/null
+++ b/tests/techmap/recursive_runtest.sh
@@ -0,0 +1,3 @@
+set -ev
+
+../../yosys -p 'hierarchy -top top; techmap -map recursive_map.v -max_iter 1; select -assert-count 2 t:sub; select -assert-count 2 t:bar' recursive.v
diff --git a/tests/techmap/run-test.sh b/tests/techmap/run-test.sh
index e2fc11e52..96489ff15 100755
--- a/tests/techmap/run-test.sh
+++ b/tests/techmap/run-test.sh
@@ -1,10 +1,20 @@
-#!/bin/bash
+#!/usr/bin/env bash
set -e
-for x in *_runtest.sh; do
- echo "Running $x.."
- if ! bash $x &> ${x%.sh}.log; then
- tail ${x%.sh}.log
- echo ERROR
- exit 1
+{
+echo "all::"
+for x in *.ys; do
+ echo "all:: run-$x"
+ echo "run-$x:"
+ echo " @echo 'Running $x..'"
+ echo " @../../yosys -ql ${x%.ys}.log $x"
+done
+for s in *.sh; do
+ if [ "$s" != "run-test.sh" ]; then
+ echo "all:: run-$s"
+ echo "run-$s:"
+ echo " @echo 'Running $s..'"
+ echo " @bash $s > ${s%.sh}.log 2>&1"
fi
done
+} > run-test.mk
+exec ${MAKE:-make} -f run-test.mk
diff --git a/tests/various/.gitignore b/tests/various/.gitignore
index 31078b298..4b286fd61 100644
--- a/tests/various/.gitignore
+++ b/tests/various/.gitignore
@@ -2,3 +2,4 @@
/*.out
/write_gzip.v
/write_gzip.v.gz
+/run-test.mk
diff --git a/tests/various/mem2reg.ys b/tests/various/mem2reg.ys
new file mode 100644
index 000000000..85d6267c5
--- /dev/null
+++ b/tests/various/mem2reg.ys
@@ -0,0 +1,14 @@
+read_verilog <<EOT
+module top;
+parameter DATADEPTH=2;
+parameter DATAWIDTH=1;
+(* keep, nomem2reg *) reg [DATAWIDTH-1:0] data1 [DATADEPTH-1:0];
+(* keep, mem2reg *) reg [DATAWIDTH-1:0] data2 [DATADEPTH-1:0];
+endmodule
+EOT
+
+proc
+cd top
+select -assert-count 1 m:data1 a:src=<<EOT:4 %i
+select -assert-count 2 w:data2[*] a:src=<<EOT:5 %i
+select -assert-none a:mem2reg
diff --git a/tests/various/muxpack.ys b/tests/various/muxpack.ys
index af23fcec8..3e90419af 100644
--- a/tests/various/muxpack.ys
+++ b/tests/various/muxpack.ys
@@ -6,7 +6,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -21,7 +21,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -52,7 +52,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 2 t:$pmux
design -stash gate
@@ -67,7 +67,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -82,7 +82,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -97,7 +97,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -112,7 +112,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -127,7 +127,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -142,7 +142,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 7 t:$mux
select -assert-count 0 t:$pmux
design -stash gate
@@ -157,7 +157,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 4 t:$mux
select -assert-count 0 t:$pmux
design -stash gate
@@ -172,7 +172,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 3 t:$mux
select -assert-count 0 t:$pmux
design -stash gate
@@ -204,7 +204,7 @@ prep
design -save gold
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 2 t:$pmux
design -stash gate
@@ -222,7 +222,7 @@ opt -fast -mux_undef
select -assert-count 2 t:$pmux
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 1 t:$pmux
design -stash gate
@@ -240,7 +240,7 @@ opt -fast -mux_undef
select -assert-count 2 t:$pmux
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 2 t:$pmux
design -stash gate
@@ -258,7 +258,7 @@ opt -fast -mux_undef
select -assert-count 2 t:$pmux
muxpack
opt
-stat
+#stat
select -assert-count 0 t:$mux
select -assert-count 2 t:$pmux
design -stash gate
diff --git a/tests/various/pmgen_reduce.ys b/tests/various/pmgen_reduce.ys
new file mode 100644
index 000000000..c214d3f25
--- /dev/null
+++ b/tests/various/pmgen_reduce.ys
@@ -0,0 +1,21 @@
+test_pmgen -generate reduce
+hierarchy -top pmtest_test_pmgen_pm_reduce
+flatten; opt_clean
+
+design -save gold
+test_pmgen -reduce_chain
+design -stash gate
+
+design -copy-from gold -as gold pmtest_test_pmgen_pm_reduce
+design -copy-from gate -as gate pmtest_test_pmgen_pm_reduce
+miter -equiv -flatten -make_assert gold gate miter
+sat -verify -prove-asserts miter
+
+design -load gold
+test_pmgen -reduce_tree
+design -stash gate
+
+design -copy-from gold -as gold pmtest_test_pmgen_pm_reduce
+design -copy-from gate -as gate pmtest_test_pmgen_pm_reduce
+miter -equiv -flatten -make_assert gold gate miter
+sat -verify -prove-asserts miter
diff --git a/tests/various/run-test.sh b/tests/various/run-test.sh
index 92b905765..ea56b70f0 100755
--- a/tests/various/run-test.sh
+++ b/tests/various/run-test.sh
@@ -1,12 +1,20 @@
#!/usr/bin/env bash
set -e
+{
+echo "all::"
for x in *.ys; do
- echo "Running $x.."
- ../../yosys -ql ${x%.ys}.log $x
+ echo "all:: run-$x"
+ echo "run-$x:"
+ echo " @echo 'Running $x..'"
+ echo " @../../yosys -ql ${x%.ys}.log $x"
done
for s in *.sh; do
if [ "$s" != "run-test.sh" ]; then
- echo "Running $s.."
- bash $s
+ echo "all:: run-$s"
+ echo "run-$s:"
+ echo " @echo 'Running $s..'"
+ echo " @bash $s"
fi
done
+} > run-test.mk
+exec ${MAKE:-make} -f run-test.mk
diff --git a/tests/various/shregmap.ys b/tests/various/shregmap.ys
index d644a88aa..16e5f40e1 100644
--- a/tests/various/shregmap.ys
+++ b/tests/various/shregmap.ys
@@ -11,7 +11,7 @@ shregmap -init
opt
-stat
+# stat
# show -width
select -assert-count 1 t:$_DFF_P_
select -assert-count 2 t:$__SHREG_DFF_P_
@@ -26,41 +26,8 @@ prep
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -verify -prove-asserts -show-ports -seq 5 miter
-design -load gold
-stat
+#design -load gold
+#stat
-design -load gate
-stat
-
-##########
-
-design -load read
-design -copy-to model $__XILINX_SHREG_
-hierarchy -top shregmap_variable_test
-prep
-design -save gold
-
-simplemap t:$dff t:$dffe
-shregmap -tech xilinx
-
-stat
-# show -width
-write_verilog -noexpr -norename
-select -assert-count 1 t:$_DFF_P_
-select -assert-count 2 t:$__XILINX_SHREG_
-
-design -stash gate
-
-design -import gold -as gold
-design -import gate -as gate
-design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
-prep
-
-miter -equiv -flatten -make_assert -make_outputs gold gate miter
-sat -verify -prove-asserts -show-ports -seq 5 miter
-
-design -load gold
-stat
-
-design -load gate
-stat
+#design -load gate
+#stat
diff --git a/tests/xilinx/.gitignore b/tests/xilinx/.gitignore
new file mode 100644
index 000000000..b48f808a1
--- /dev/null
+++ b/tests/xilinx/.gitignore
@@ -0,0 +1,3 @@
+/*.log
+/*.out
+/run-test.mk
diff --git a/tests/xilinx/pmgen_xilinx_srl.ys b/tests/xilinx/pmgen_xilinx_srl.ys
new file mode 100644
index 000000000..ea2f20487
--- /dev/null
+++ b/tests/xilinx/pmgen_xilinx_srl.ys
@@ -0,0 +1,57 @@
+read_verilog -icells <<EOT
+module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO);
+ parameter DEPTH = 1;
+ parameter [DEPTH-1:0] INIT = 0;
+ parameter CLKPOL = 1;
+ parameter ENPOL = 2;
+
+ wire pos_clk = C == CLKPOL;
+ reg pos_en;
+ always @(E)
+ if (ENPOL == 2) pos_en = 1'b1;
+ else pos_en = (E == ENPOL[0]);
+
+ reg [DEPTH-1:0] r;
+ always @(posedge pos_clk)
+ if (pos_en)
+ r <= {r[DEPTH-2:0], D};
+
+ assign Q = r[L];
+ assign SO = r[DEPTH-1];
+endmodule
+EOT
+read_verilog +/xilinx/cells_sim.v
+proc
+design -save model
+
+test_pmgen -generate xilinx_srl.fixed
+hierarchy -top pmtest_xilinx_srl_pm_fixed
+flatten; opt_clean
+
+design -save gold
+xilinx_srl -fixed
+techmap -autoproc -map %model
+design -stash gate
+
+design -copy-from gold -as gold pmtest_xilinx_srl_pm_fixed
+design -copy-from gate -as gate pmtest_xilinx_srl_pm_fixed
+dff2dffe -unmap # sat does not support flops-with-enable yet
+miter -equiv -flatten -make_assert gold gate miter
+sat -set-init-zero -seq 5 -verify -prove-asserts miter
+
+design -load model
+
+test_pmgen -generate xilinx_srl.variable
+hierarchy -top pmtest_xilinx_srl_pm_variable
+flatten; opt_clean
+
+design -save gold
+xilinx_srl -variable
+techmap -autoproc -map %model
+design -stash gate
+
+design -copy-from gold -as gold pmtest_xilinx_srl_pm_variable
+design -copy-from gate -as gate pmtest_xilinx_srl_pm_variable
+dff2dffe -unmap # sat does not support flops-with-enable yet
+miter -equiv -flatten -make_assert gold gate miter
+sat -set-init-zero -seq 5 -verify -prove-asserts miter
diff --git a/tests/xilinx/run-test.sh b/tests/xilinx/run-test.sh
new file mode 100755
index 000000000..ea56b70f0
--- /dev/null
+++ b/tests/xilinx/run-test.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+set -e
+{
+echo "all::"
+for x in *.ys; do
+ echo "all:: run-$x"
+ echo "run-$x:"
+ echo " @echo 'Running $x..'"
+ echo " @../../yosys -ql ${x%.ys}.log $x"
+done
+for s in *.sh; do
+ if [ "$s" != "run-test.sh" ]; then
+ echo "all:: run-$s"
+ echo "run-$s:"
+ echo " @echo 'Running $s..'"
+ echo " @bash $s"
+ fi
+done
+} > run-test.mk
+exec ${MAKE:-make} -f run-test.mk
diff --git a/tests/xilinx/xilinx_srl.v b/tests/xilinx/xilinx_srl.v
new file mode 100644
index 000000000..bc2a15ab2
--- /dev/null
+++ b/tests/xilinx/xilinx_srl.v
@@ -0,0 +1,40 @@
+module xilinx_srl_static_test(input i, clk, output [1:0] q);
+reg head = 1'b0;
+reg [3:0] shift1 = 4'b0000;
+reg [3:0] shift2 = 4'b0000;
+
+always @(posedge clk) begin
+ head <= i;
+ shift1 <= {shift1[2:0], head};
+ shift2 <= {shift2[2:0], head};
+end
+
+assign q = {shift2[3], shift1[3]};
+endmodule
+
+module xilinx_srl_variable_test(input i, clk, input [1:0] l1, l2, output [1:0] q);
+reg head = 1'b0;
+reg [3:0] shift1 = 4'b0000;
+reg [3:0] shift2 = 4'b0000;
+
+always @(posedge clk) begin
+ head <= i;
+ shift1 <= {shift1[2:0], head};
+ shift2 <= {shift2[2:0], head};
+end
+
+assign q = {shift2[l2], shift1[l1]};
+endmodule
+
+module $__XILINX_SHREG_(input C, D, E, input [1:0] L, output Q);
+parameter CLKPOL = 1;
+parameter ENPOL = 1;
+parameter DEPTH = 1;
+parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}};
+reg [DEPTH-1:0] r = INIT;
+wire clk = C ^ CLKPOL;
+always @(posedge C)
+ if (E)
+ r <= { r[DEPTH-2:0], D };
+assign Q = r[L];
+endmodule
diff --git a/tests/xilinx/xilinx_srl.ys b/tests/xilinx/xilinx_srl.ys
new file mode 100644
index 000000000..b8df0e55a
--- /dev/null
+++ b/tests/xilinx/xilinx_srl.ys
@@ -0,0 +1,67 @@
+read_verilog xilinx_srl.v
+design -save read
+
+design -copy-to model $__XILINX_SHREG_
+hierarchy -top xilinx_srl_static_test
+prep
+design -save gold
+
+techmap
+xilinx_srl -fixed
+opt
+
+# stat
+# show -width
+select -assert-count 1 t:$_DFF_P_
+select -assert-count 2 t:$__XILINX_SHREG_
+
+design -stash gate
+
+design -import gold -as gold
+design -import gate -as gate
+design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
+prep
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+dump gate
+sat -verify -prove-asserts -show-ports -seq 5 miter
+
+#design -load gold
+#stat
+
+#design -load gate
+#stat
+
+##########
+
+design -load read
+design -copy-to model $__XILINX_SHREG_
+hierarchy -top xilinx_srl_variable_test
+prep
+design -save gold
+
+xilinx_srl -variable
+opt
+
+#stat
+# show -width
+# write_verilog -noexpr -norename
+select -assert-count 1 t:$dff
+select -assert-count 1 t:$dff r:WIDTH=1 %i
+select -assert-count 2 t:$__XILINX_SHREG_
+
+design -stash gate
+
+design -import gold -as gold
+design -import gate -as gate
+design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
+prep
+
+miter -equiv -flatten -make_assert -make_outputs gold gate miter
+sat -verify -prove-asserts -show-ports -seq 5 miter
+
+# design -load gold
+# stat
+
+# design -load gate
+# stat