aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG5
-rw-r--r--Makefile6
-rw-r--r--backends/cxxrtl/cxxrtl.h3
-rw-r--r--backends/smt2/smt2.cc13
-rw-r--r--backends/smt2/smtbmc.py4
-rw-r--r--backends/smt2/smtio.py24
-rw-r--r--backends/verilog/verilog_backend.cc386
-rw-r--r--frontends/ast/simplify.cc64
-rw-r--r--frontends/verific/verific.cc4
-rw-r--r--kernel/celltypes.h8
-rw-r--r--kernel/constids.inc1
-rw-r--r--kernel/ff.h486
-rw-r--r--kernel/ffinit.h141
-rw-r--r--kernel/satgen.cc1239
-rw-r--r--kernel/satgen.h1178
-rw-r--r--passes/equiv/equiv_induct.cc4
-rw-r--r--passes/memory/memory_dff.cc136
-rw-r--r--passes/opt/Makefile.inc1
-rw-r--r--passes/opt/opt.cc34
-rw-r--r--passes/opt/opt_dff.cc875
-rw-r--r--passes/opt/opt_expr.cc20
-rw-r--r--passes/opt/pmux2shiftx.cc19
-rw-r--r--passes/opt/wreduce.cc44
-rw-r--r--passes/pmgen/ice40_dsp.cc38
-rw-r--r--passes/pmgen/ice40_dsp.pmg295
-rw-r--r--passes/pmgen/xilinx_dsp.cc159
-rw-r--r--passes/pmgen/xilinx_dsp.pmg333
-rw-r--r--passes/pmgen/xilinx_dsp48a.pmg322
-rw-r--r--passes/pmgen/xilinx_dsp_CREG.pmg122
-rw-r--r--passes/pmgen/xilinx_dsp_cascade.pmg125
-rw-r--r--passes/proc/proc_dlatch.cc36
-rw-r--r--passes/sat/async2sync.cc302
-rw-r--r--passes/sat/clk2fflogic.cc283
-rw-r--r--passes/sat/qbfsat.cc15
-rw-r--r--passes/sat/qbfsat.h1
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc.cc31
-rw-r--r--passes/techmap/abc9.cc2
-rw-r--r--passes/techmap/dffinit.cc48
-rw-r--r--passes/techmap/dfflegalize.cc105
-rw-r--r--passes/techmap/dffunmap.cc107
-rw-r--r--passes/techmap/shregmap.cc49
-rw-r--r--passes/techmap/techmap.cc51
-rw-r--r--passes/techmap/zinit.cc54
-rw-r--r--techlibs/common/synth.cc6
-rw-r--r--techlibs/ecp5/synth_ecp5.cc26
-rw-r--r--techlibs/gowin/synth_gowin.cc8
-rw-r--r--techlibs/greenpak4/synth_greenpak4.cc2
-rw-r--r--techlibs/ice40/Makefile.inc1
-rw-r--r--techlibs/ice40/ice40_ffssr.cc131
-rw-r--r--techlibs/ice40/ice40_opt.cc4
-rw-r--r--techlibs/ice40/synth_ice40.cc10
-rw-r--r--techlibs/intel/synth_intel.cc2
-rw-r--r--techlibs/intel_alm/Makefile.inc6
-rw-r--r--techlibs/intel_alm/common/alm_sim.v80
-rw-r--r--techlibs/intel_alm/common/bram_m10k.txt4
-rw-r--r--techlibs/intel_alm/common/bram_m10k_map.v31
-rw-r--r--techlibs/intel_alm/common/dff_sim.v55
-rw-r--r--techlibs/intel_alm/common/dsp_sim.v17
-rw-r--r--techlibs/intel_alm/common/megafunction_bb.v468
-rw-r--r--techlibs/intel_alm/common/mem_sim.v47
-rw-r--r--techlibs/intel_alm/common/quartus_rename.v51
-rw-r--r--techlibs/intel_alm/synth_intel_alm.cc10
-rw-r--r--techlibs/xilinx/arith_map.v114
-rw-r--r--techlibs/xilinx/cells_sim.v23
-rw-r--r--techlibs/xilinx/synth_xilinx.cc44
-rw-r--r--tests/arch/anlogic/dffs.ys3
-rw-r--r--tests/arch/ecp5/fsm.ys6
-rw-r--r--tests/arch/efinix/adffs.ys6
-rw-r--r--tests/arch/efinix/dffs.ys3
-rw-r--r--tests/arch/gowin/init.ys19
-rw-r--r--tests/arch/ice40/fsm.ys2
-rw-r--r--tests/arch/intel_alm/adffs.ys10
-rw-r--r--tests/arch/intel_alm/blockram.ys6
-rw-r--r--tests/arch/intel_alm/fsm.ys12
-rw-r--r--tests/arch/intel_alm/mux.ys8
-rw-r--r--tests/arch/xilinx/fsm.ys16
-rw-r--r--tests/arch/xilinx/latches.ys3
-rw-r--r--tests/arch/xilinx/pmgen_xilinx_srl.ys2
-rw-r--r--tests/opt/bug2311.ys14
-rw-r--r--tests/opt/opt_dff_arst.ys101
-rw-r--r--tests/opt/opt_dff_clk.ys45
-rw-r--r--tests/opt/opt_dff_const.ys49
-rw-r--r--tests/opt/opt_dff_en.ys157
-rw-r--r--tests/opt/opt_dff_mux.ys86
-rw-r--r--tests/opt/opt_dff_qd.ys56
-rw-r--r--tests/opt/opt_dff_sr.ys304
-rw-r--r--tests/opt/opt_dff_srst.ys113
-rw-r--r--tests/opt/opt_rmdff.ys3
-rw-r--r--tests/opt/opt_rmdff_sat.ys4
-rw-r--r--tests/sat/dff.ys21
-rw-r--r--tests/techmap/cellname.ys41
-rw-r--r--tests/techmap/dfflegalize_adff.ys8
-rw-r--r--tests/techmap/dfflegalize_adff_init.ys24
-rw-r--r--tests/techmap/dfflegalize_adlatch.ys4
-rw-r--r--tests/techmap/dfflegalize_adlatch_init.ys12
-rw-r--r--tests/techmap/dfflegalize_dff.ys18
-rw-r--r--tests/techmap/dfflegalize_dff_init.ys56
-rw-r--r--tests/techmap/dfflegalize_dffsr.ys8
-rw-r--r--tests/techmap/dfflegalize_dffsr_init.ys24
-rw-r--r--tests/techmap/dfflegalize_dlatch.ys6
-rw-r--r--tests/techmap/dfflegalize_dlatch_const.ys8
-rw-r--r--tests/techmap/dfflegalize_dlatch_init.ys16
-rw-r--r--tests/techmap/dfflegalize_dlatchsr.ys4
-rw-r--r--tests/techmap/dfflegalize_dlatchsr_init.ys12
-rw-r--r--tests/techmap/dfflegalize_inv.ys6
-rw-r--r--tests/techmap/dfflegalize_mince.ys2
-rw-r--r--tests/techmap/dfflegalize_minsrst.ys2
-rw-r--r--tests/techmap/dfflegalize_sr.ys12
-rw-r--r--tests/techmap/dfflegalize_sr_init.ys24
-rw-r--r--tests/techmap/dffunmap.ys100
-rw-r--r--tests/techmap/zinit.ys4
-rwxr-xr-xtests/tools/autotest.sh6
-rw-r--r--tests/various/const_arg_loop.v44
-rw-r--r--tests/various/const_arg_loop.ys1
-rw-r--r--tests/various/equiv_opt_undef.ys35
116 files changed, 5912 insertions, 3928 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 12fc88550..6dcd05de6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -39,7 +39,7 @@ Yosys 0.9 .. Yosys 0.9-dev
- Improvements in pmgen: slices, choices, define, generate
- Added "xilinx_srl" for Xilinx shift register extraction
- Removed "shregmap -tech xilinx" (superseded by "xilinx_srl")
- - Added "_TECHMAP_WIREINIT_*_" attribute and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass
+ - Added "_TECHMAP_WIREINIT_*_" parameter and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass
- Added "-match-init" option to "dff2dffs" pass
- Added "techmap_autopurge" support to techmap
- Added "add -mod <modname[s]>"
@@ -62,13 +62,14 @@ Yosys 0.9 .. Yosys 0.9-dev
- Improved support of $readmem[hb] Memory Content File inclusion
- Added "opt_lut_ins" pass
- Added "logger" pass
- - Removed "dffsr2dff" (use opt_rmdff instead)
- Added "design -delete"
- Added "select -unset"
- Use YosysHQ/abc instead of upstream berkeley-abc/abc
- Added $divfloor and $modfloor cells
- Added $adffe, $dffsre, $sdff, $sdffe, $sdffce, $adlatch cells
- Added "dfflegalize" pass
+ - Added "_TECHMAP_CELLNAME_" parameter for "techmap" pass
+ - Merged "dffsr2dff", "opt_rmdff", "dff2dffe", "dff2dffs", "peepopt.dffmux" passes into a new "opt_dff" pass
Yosys 0.8 .. Yosys 0.9
----------------------
diff --git a/Makefile b/Makefile
index ec8b38c48..dfdb7afd8 100644
--- a/Makefile
+++ b/Makefile
@@ -123,7 +123,7 @@ LDFLAGS += -rdynamic
LDLIBS += -lrt
endif
-YOSYS_VER := 0.9+2406
+YOSYS_VER := 0.9+3477
GIT_REV := $(shell cd $(YOSYS_SRC) && git rev-parse --short HEAD 2> /dev/null || echo UNKNOWN)
OBJS = kernel/version_$(GIT_REV).o
@@ -585,6 +585,8 @@ $(eval $(call add_include_file,kernel/modtools.h))
$(eval $(call add_include_file,kernel/macc.h))
$(eval $(call add_include_file,kernel/utils.h))
$(eval $(call add_include_file,kernel/satgen.h))
+$(eval $(call add_include_file,kernel/ff.h))
+$(eval $(call add_include_file,kernel/ffinit.h))
$(eval $(call add_include_file,libs/ezsat/ezsat.h))
$(eval $(call add_include_file,libs/ezsat/ezminisat.h))
$(eval $(call add_include_file,libs/sha1/sha1.h))
@@ -600,7 +602,7 @@ $(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.cc))
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.h))
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
-OBJS += kernel/cellaigs.o kernel/celledges.o
+OBJS += kernel/cellaigs.o kernel/celledges.o kernel/satgen.o
kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"'
kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' -DYOSYS_PROGRAM_PREFIX='"$(PROGRAM_PREFIX)"'
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index f0d7b9fc7..e3c96d422 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -452,10 +452,11 @@ struct value : public expr_base<value<Bits>> {
bool carry = CarryIn;
for (size_t n = 0; n < result.chunks; n++) {
result.data[n] = data[n] + (Invert ? ~other.data[n] : other.data[n]) + carry;
+ if (result.chunks - 1 == n)
+ result.data[result.chunks - 1] &= result.msb_mask;
carry = (result.data[n] < data[n]) ||
(result.data[n] == data[n] && carry);
}
- result.data[result.chunks - 1] &= result.msb_mask;
return {result, carry};
}
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index 526b36352..a79c0bd99 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -1387,6 +1387,10 @@ struct Smt2Backend : public Backend {
log(" use the given template file. the line containing only the token '%%%%'\n");
log(" is replaced with the regular output of this command.\n");
log("\n");
+ log(" -solver-option <option> <value>\n");
+ log(" emit a `; yosys-smt2-solver-option` directive for yosys-smtbmc to write\n");
+ log(" the given option as a `(set-option ...)` command in the SMT-LIBv2.\n");
+ log("\n");
log("[1] For more information on SMT-LIBv2 visit http://smt-lib.org/ or read David\n");
log("R. Cok's tutorial: http://www.grammatech.com/resources/smt/SMTLIBTutorial.pdf\n");
log("\n");
@@ -1441,6 +1445,7 @@ struct Smt2Backend : public Backend {
std::ifstream template_f;
bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false;
bool forallmode = false;
+ dict<std::string, std::string> solver_options;
log_header(design, "Executing SMT2 backend.\n");
@@ -1484,6 +1489,11 @@ struct Smt2Backend : public Backend {
verbose = true;
continue;
}
+ if (args[argidx] == "-solver-option" && argidx+2 < args.size()) {
+ solver_options.emplace(args[argidx+1], args[argidx+2]);
+ argidx += 2;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
@@ -1514,6 +1524,9 @@ struct Smt2Backend : public Backend {
if (statedt)
*f << stringf("; yosys-smt2-stdt\n");
+ for (auto &it : solver_options)
+ *f << stringf("; yosys-smt2-solver-option %s %s\n", it.first.c_str(), it.second.c_str());
+
std::vector<RTLIL::Module*> sorted_modules;
// extract module dependencies
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index 03f001bfd..69dab5590 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -1275,10 +1275,10 @@ def smt_pop():
asserts_consequent_cache.pop()
smt.write("(pop 1)")
-def smt_check_sat():
+def smt_check_sat(expected=["sat", "unsat"]):
if asserts_cache_dirty:
smt_forall_assert()
- return smt.check_sat()
+ return smt.check_sat(expected=expected)
if tempind:
retstatus = "FAILED"
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 72ab39d39..516091011 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -124,6 +124,7 @@ class SmtIo:
self.timeout = 0
self.produce_models = True
self.smt2cache = [list()]
+ self.smt2_options = dict()
self.p = None
self.p_index = solvers_index
solvers_index += 1
@@ -258,14 +259,24 @@ class SmtIo:
for stmt in self.info_stmts:
self.write(stmt)
- if self.forall and self.solver == "yices":
- self.write("(set-option :yices-ef-max-iters 1000000000)")
-
if self.produce_models:
self.write("(set-option :produce-models true)")
+ #See the SMT-LIB Standard, Section 4.1.7
+ modestart_options = [":global-declarations", ":interactive-mode", ":produce-assertions", ":produce-assignments", ":produce-models", ":produce-proofs", ":produce-unsat-assumptions", ":produce-unsat-cores", ":random-seed"]
+ for key, val in self.smt2_options.items():
+ if key in modestart_options:
+ self.write("(set-option {} {})".format(key, val))
+
self.write("(set-logic %s)" % self.logic)
+ if self.forall and self.solver == "yices":
+ self.write("(set-option :yices-ef-max-iters 1000000000)")
+
+ for key, val in self.smt2_options.items():
+ if key not in modestart_options:
+ self.write("(set-option {} {})".format(key, val))
+
def timestamp(self):
secs = int(time() - self.start_time)
return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60)
@@ -468,6 +479,9 @@ class SmtIo:
fields = stmt.split()
+ if fields[1] == "yosys-smt2-solver-option":
+ self.smt2_options[fields[2]] = fields[3]
+
if fields[1] == "yosys-smt2-nomem":
if self.logic is None:
self.logic_ax = False
@@ -653,7 +667,7 @@ class SmtIo:
return stmt
- def check_sat(self):
+ def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted"]):
if self.debug_print:
print("> (check-sat)")
if self.debug_file and not self.nocomments:
@@ -740,7 +754,7 @@ class SmtIo:
print("(check-sat)", file=self.debug_file)
self.debug_file.flush()
- if result not in ["sat", "unsat", "unknown", "timeout", "interrupted"]:
+ if result not in expected:
if result == "":
print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
else:
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 71f71554b..a0e677d13 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -25,6 +25,7 @@
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/sigtools.h"
+#include "kernel/ff.h"
#include <string>
#include <sstream>
#include <set>
@@ -36,7 +37,7 @@ PRIVATE_NAMESPACE_BEGIN
bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog;
int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
std::map<RTLIL::IdString, int> auto_name_map;
-std::set<RTLIL::IdString> reg_wires, reg_ct;
+std::set<RTLIL::IdString> reg_wires;
std::string auto_prefix, extmem_prefix;
RTLIL::Module *active_module;
@@ -451,7 +452,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b
std::string cellname(RTLIL::Cell *cell)
{
- if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort(ID::Q))
+ if (!norename && cell->name[0] == '$' && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_)))
{
RTLIL::SigSpec sig = cell->getPort(ID::Q);
if (GetSize(sig) != 1 || sig.is_fully_const())
@@ -605,93 +606,6 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type.begins_with("$_DFF_"))
- {
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
- f << ";\n";
- }
-
- dump_attributes(f, indent, cell->attributes);
- f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", cell->type[6] == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::C));
- if (cell->type[7] != '_') {
- f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::R));
- }
- f << stringf(")\n");
-
- if (cell->type[7] != '_') {
- f << stringf("%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
- dump_sigspec(f, cell->getPort(ID::R));
- f << stringf(")\n");
- f << stringf("%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
- f << stringf("%s" " else\n", indent.c_str());
- }
-
- f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
- dump_cell_expr_port(f, cell, "D", false);
- f << stringf(";\n");
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
- if (cell->type.begins_with("$_DFFSR_"))
- {
- char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
-
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
- f << ";\n";
- }
-
- dump_attributes(f, indent, cell->attributes);
- f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", pol_c == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::C));
- f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::S));
- f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::R));
- f << stringf(")\n");
-
- f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!");
- dump_sigspec(f, cell->getPort(ID::R));
- f << stringf(")\n");
- f << stringf("%s" " %s <= 0;\n", indent.c_str(), reg_name.c_str());
-
- f << stringf("%s" " else if (%s", indent.c_str(), pol_s == 'P' ? "" : "!");
- dump_sigspec(f, cell->getPort(ID::S));
- f << stringf(")\n");
- f << stringf("%s" " %s <= 1;\n", indent.c_str(), reg_name.c_str());
-
- f << stringf("%s" " else\n", indent.c_str());
- f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
- dump_cell_expr_port(f, cell, "D", false);
- f << stringf(";\n");
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
#define HANDLE_UNIOP(_type, _operator) \
if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
#define HANDLE_BINOP(_type, _operator) \
@@ -986,154 +900,151 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type == ID($dffsr))
+ if (RTLIL::builtin_ff_cell_types().count(cell->type))
{
- SigSpec sig_clk = cell->getPort(ID::CLK);
- SigSpec sig_set = cell->getPort(ID::SET);
- SigSpec sig_clr = cell->getPort(ID::CLR);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
+ FfData ff(nullptr, cell);
- int width = cell->parameters[ID::WIDTH].as_int();
- bool pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool();
- bool pol_set = cell->parameters[ID::SET_POLARITY].as_bool();
- bool pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool();
+ // $ff / $_FF_ cell: not supported.
+ if (ff.has_d && !ff.has_clk && !ff.has_en)
+ return false;
std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str());
- dump_reg_init(f, sig_q);
- f << ";\n";
- }
-
- for (int i = 0; i < width; i++) {
- f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", pol_clk ? "pos" : "neg");
- dump_sigspec(f, sig_clk);
- f << stringf(", %sedge ", pol_set ? "pos" : "neg");
- dump_sigspec(f, sig_set);
- f << stringf(", %sedge ", pol_clr ? "pos" : "neg");
- dump_sigspec(f, sig_clr);
- f << stringf(")\n");
-
- f << stringf("%s" " if (%s", indent.c_str(), pol_clr ? "" : "!");
- dump_sigspec(f, sig_clr);
- f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i);
-
- f << stringf("%s" " else if (%s", indent.c_str(), pol_set ? "" : "!");
- dump_sigspec(f, sig_set);
- f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i);
-
- f << stringf("%s" " else %s[%d] <= ", indent.c_str(), reg_name.c_str(), i);
- dump_sigspec(f, sig_d[i]);
- f << stringf(";\n");
- }
+ bool out_is_reg_wire = is_reg_wire(ff.sig_q, reg_name);
if (!out_is_reg_wire) {
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, sig_q);
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
- if (cell->type.in(ID($dff), ID($adff), ID($dffe)))
- {
- RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
- bool pol_clk, pol_arst = false, pol_en = false;
-
- sig_clk = cell->getPort(ID::CLK);
- pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool();
-
- if (cell->type == ID($adff)) {
- sig_arst = cell->getPort(ID::ARST);
- pol_arst = cell->parameters[ID::ARST_POLARITY].as_bool();
- val_arst = RTLIL::SigSpec(cell->parameters[ID::ARST_VALUE]);
- }
-
- if (cell->type == ID($dffe)) {
- sig_en = cell->getPort(ID::EN);
- pol_en = cell->parameters[ID::EN_POLARITY].as_bool();
- }
-
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
+ if (ff.width == 1)
+ f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
+ else
+ f << stringf("%s" "reg [%d:0] %s", indent.c_str(), ff.width-1, reg_name.c_str());
+ dump_reg_init(f, ff.sig_q);
f << ";\n";
}
- f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", pol_clk ? "pos" : "neg");
- dump_sigspec(f, sig_clk);
- if (cell->type == ID($adff)) {
- f << stringf(" or %sedge ", pol_arst ? "pos" : "neg");
- dump_sigspec(f, sig_arst);
- }
- f << stringf(")\n");
-
- if (cell->type == ID($adff)) {
- f << stringf("%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
- dump_sigspec(f, sig_arst);
- f << stringf(")\n");
- f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
- dump_sigspec(f, val_arst);
- f << stringf(";\n");
- f << stringf("%s" " else\n", indent.c_str());
- }
-
- if (cell->type == ID($dffe)) {
- f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
- dump_sigspec(f, sig_en);
- f << stringf(")\n");
- }
-
- f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
- dump_cell_expr_port(f, cell, "D", false);
- f << stringf(";\n");
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
+ // If the FF has CLR/SET inputs, emit every bit slice separately.
+ int chunks = ff.has_sr ? ff.width : 1;
+ bool chunky = ff.has_sr && ff.width != 1;
- if (cell->type == ID($dlatch))
- {
- RTLIL::SigSpec sig_en;
- bool pol_en = false;
+ for (int i = 0; i < chunks; i++)
+ {
+ SigSpec sig_d;
+ Const val_arst, val_srst;
+ std::string reg_bit_name;
+ if (chunky) {
+ reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i);
+ if (ff.has_d)
+ sig_d = ff.sig_d[i];
+ } else {
+ reg_bit_name = reg_name;
+ if (ff.has_d)
+ sig_d = ff.sig_d;
+ }
+ if (ff.has_arst)
+ val_arst = chunky ? ff.val_arst[i] : ff.val_arst;
+ if (ff.has_srst)
+ val_srst = chunky ? ff.val_srst[i] : ff.val_srst;
- sig_en = cell->getPort(ID::EN);
- pol_en = cell->parameters[ID::EN_POLARITY].as_bool();
+ dump_attributes(f, indent, cell->attributes);
+ if (ff.has_clk)
+ {
+ // FFs.
+ f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", ff.pol_clk ? "pos" : "neg");
+ dump_sigspec(f, ff.sig_clk);
+ if (ff.has_sr) {
+ f << stringf(", %sedge ", ff.pol_set ? "pos" : "neg");
+ dump_sigspec(f, ff.sig_set[i]);
+ f << stringf(", %sedge ", ff.pol_clr ? "pos" : "neg");
+ dump_sigspec(f, ff.sig_clr[i]);
+ } else if (ff.has_arst) {
+ f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg");
+ dump_sigspec(f, ff.sig_arst);
+ }
+ f << stringf(")\n");
+
+ f << stringf("%s" " ", indent.c_str());
+ if (ff.has_sr) {
+ f << stringf("if (%s", ff.pol_clr ? "" : "!");
+ dump_sigspec(f, ff.sig_clr[i]);
+ f << stringf(") %s <= 1'b0;\n", reg_bit_name.c_str());
+ f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
+ dump_sigspec(f, ff.sig_set[i]);
+ f << stringf(") %s <= 1'b1;\n", reg_bit_name.c_str());
+ f << stringf("%s" " else ", indent.c_str());
+ } else if (ff.has_arst) {
+ f << stringf("if (%s", ff.pol_arst ? "" : "!");
+ dump_sigspec(f, ff.sig_arst);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, val_arst);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
+ }
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
+ if (ff.has_srst && ff.has_en && ff.ce_over_srst) {
+ f << stringf("if (%s", ff.pol_en ? "" : "!");
+ dump_sigspec(f, ff.sig_en);
+ f << stringf(")\n");
+ f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!");
+ dump_sigspec(f, ff.sig_srst);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, val_srst);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
+ } else {
+ if (ff.has_srst) {
+ f << stringf("if (%s", ff.pol_srst ? "" : "!");
+ dump_sigspec(f, ff.sig_srst);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, val_srst);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
+ }
+ if (ff.has_en) {
+ f << stringf("if (%s", ff.pol_en ? "" : "!");
+ dump_sigspec(f, ff.sig_en);
+ f << stringf(") ");
+ }
+ }
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
- f << ";\n";
+ f << stringf("%s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, sig_d);
+ f << stringf(";\n");
+ }
+ else
+ {
+ // Latches.
+ f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*");
+
+ f << stringf("%s" " ", indent.c_str());
+ if (ff.has_sr) {
+ f << stringf("if (%s", ff.pol_clr ? "" : "!");
+ dump_sigspec(f, ff.sig_clr[i]);
+ f << stringf(") %s = 1'b0;\n", reg_bit_name.c_str());
+ f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
+ dump_sigspec(f, ff.sig_set[i]);
+ f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str());
+ if (ff.has_d)
+ f << stringf("%s" " else ", indent.c_str());
+ } else if (ff.has_arst) {
+ f << stringf("if (%s", ff.pol_arst ? "" : "!");
+ dump_sigspec(f, ff.sig_arst);
+ f << stringf(") %s = ", reg_bit_name.c_str());
+ dump_sigspec(f, val_arst);
+ f << stringf(";\n");
+ if (ff.has_d)
+ f << stringf("%s" " else ", indent.c_str());
+ }
+ if (ff.has_d) {
+ f << stringf("if (%s", ff.pol_en ? "" : "!");
+ dump_sigspec(f, ff.sig_en);
+ f << stringf(") %s = ", reg_bit_name.c_str());
+ dump_sigspec(f, sig_d);
+ f << stringf(";\n");
+ }
+ }
}
- f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*");
-
- f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
- dump_sigspec(f, sig_en);
- f << stringf(")\n");
-
- f << stringf("%s" " %s = ", indent.c_str(), reg_name.c_str());
- dump_cell_expr_port(f, cell, "D", false);
- f << stringf(";\n");
-
if (!out_is_reg_wire) {
f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, cell->getPort(ID::Q));
+ dump_sigspec(f, ff.sig_q);
f << stringf(" = %s;\n", reg_name.c_str());
}
@@ -1528,8 +1439,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
- // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
+ // FIXME: $memrd, $memwr, $fsm
return false;
}
@@ -1602,7 +1512,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
}
}
- if (siminit && reg_ct.count(cell->type) && cell->hasPort(ID::Q)) {
+ if (siminit && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) {
std::stringstream ss;
dump_reg_init(ss, cell->getPort(ID::Q));
if (!ss.str().empty()) {
@@ -1812,7 +1722,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
for (auto cell : module->cells())
{
- if (!reg_ct.count(cell->type) || !cell->hasPort(ID::Q))
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type) || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_)))
continue;
RTLIL::SigSpec sig = cell->getPort(ID::Q);
@@ -1984,33 +1894,6 @@ struct VerilogBackend : public Backend {
auto_name_map.clear();
reg_wires.clear();
- reg_ct.clear();
-
- reg_ct.insert(ID($dff));
- reg_ct.insert(ID($adff));
- reg_ct.insert(ID($dffe));
- reg_ct.insert(ID($dlatch));
-
- reg_ct.insert(ID($_DFF_N_));
- reg_ct.insert(ID($_DFF_P_));
-
- reg_ct.insert(ID($_DFF_NN0_));
- reg_ct.insert(ID($_DFF_NN1_));
- reg_ct.insert(ID($_DFF_NP0_));
- reg_ct.insert(ID($_DFF_NP1_));
- reg_ct.insert(ID($_DFF_PN0_));
- reg_ct.insert(ID($_DFF_PN1_));
- reg_ct.insert(ID($_DFF_PP0_));
- reg_ct.insert(ID($_DFF_PP1_));
-
- reg_ct.insert(ID($_DFFSR_NNN_));
- reg_ct.insert(ID($_DFFSR_NNP_));
- reg_ct.insert(ID($_DFFSR_NPN_));
- reg_ct.insert(ID($_DFFSR_NPP_));
- reg_ct.insert(ID($_DFFSR_PNN_));
- reg_ct.insert(ID($_DFFSR_PNP_));
- reg_ct.insert(ID($_DFFSR_PPN_));
- reg_ct.insert(ID($_DFFSR_PPP_));
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -2107,7 +1990,6 @@ struct VerilogBackend : public Backend {
auto_name_map.clear();
reg_wires.clear();
- reg_ct.clear();
}
} VerilogBackend;
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index fda064237..5a9707976 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -483,6 +483,27 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name)
return wnode;
}
+// check if a node or its children contains an assignment to the given variable
+static bool node_contains_assignment_to(const AstNode* node, const AstNode* var)
+{
+ if (node->type == AST_ASSIGN_EQ || node->type == AST_ASSIGN_LE) {
+ // current node is iteslf an assignment
+ log_assert(node->children.size() >= 2);
+ const AstNode* lhs = node->children[0];
+ if (lhs->type == AST_IDENTIFIER && lhs->str == var->str)
+ return false;
+ }
+ for (const AstNode* child : node->children) {
+ // if this child shadows the given variable
+ if (child != var && child->str == var->str && child->type == AST_WIRE)
+ break; // skip the remainder of this block/scope
+ // depth-first short circuit
+ if (!node_contains_assignment_to(child, var))
+ return false;
+ }
+ return true;
+}
+
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@@ -1687,25 +1708,27 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK)
body_ast = body_ast->children.at(0);
+ const char* loop_type_str = "procedural";
+ const char* var_type_str = "register";
+ AstNodeType var_type = AST_WIRE;
+ if (type == AST_GENFOR) {
+ loop_type_str = "generate";
+ var_type_str = "genvar";
+ var_type = AST_GENVAR;
+ }
+
if (init_ast->type != AST_ASSIGN_EQ)
- log_file_error(filename, location.first_line, "Unsupported 1st expression of generate for-loop!\n");
+ log_file_error(filename, location.first_line, "Unsupported 1st expression of %s for-loop!\n", loop_type_str);
if (next_ast->type != AST_ASSIGN_EQ)
- log_file_error(filename, location.first_line, "Unsupported 3rd expression of generate for-loop!\n");
+ log_file_error(filename, location.first_line, "Unsupported 3rd expression of %s for-loop!\n", loop_type_str);
- if (type == AST_GENFOR) {
- if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR)
- log_file_error(filename, location.first_line, "Left hand side of 1st expression of generate for-loop is not a gen var!\n");
- if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR)
- log_file_error(filename, location.first_line, "Left hand side of 3rd expression of generate for-loop is not a gen var!\n");
- } else {
- if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE)
- log_file_error(filename, location.first_line, "Left hand side of 1st expression of generate for-loop is not a register!\n");
- if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE)
- log_file_error(filename, location.first_line, "Left hand side of 3rd expression of generate for-loop is not a register!\n");
- }
+ if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != var_type)
+ log_file_error(filename, location.first_line, "Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str);
+ if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != var_type)
+ log_file_error(filename, location.first_line, "Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str);
if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast)
- log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of generate for-loop!\n");
+ log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str);
// eval 1st expression
AstNode *varbuf = init_ast->children[1]->clone();
@@ -1717,7 +1740,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
if (varbuf->type != AST_CONSTANT)
- log_file_error(filename, location.first_line, "Right hand side of 1st expression of generate for-loop is not constant!\n");
+ log_file_error(filename, location.first_line, "Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str);
auto resolved = current_scope.at(init_ast->children[0]->str);
if (resolved->range_valid) {
@@ -1758,7 +1781,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
if (buf->type != AST_CONSTANT)
- log_file_error(filename, location.first_line, "2nd expression of generate for-loop is not constant!\n");
+ log_file_error(filename, location.first_line, "2nd expression of %s for-loop is not constant!\n", loop_type_str);
if (buf->integer == 0) {
delete buf;
@@ -1804,7 +1827,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
if (buf->type != AST_CONSTANT)
- log_file_error(filename, location.first_line, "Right hand side of 3rd expression of generate for-loop is not constant (%s)!\n", type2str(buf->type).c_str());
+ log_file_error(filename, location.first_line, "Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str());
delete varbuf->children[0];
varbuf->children[0] = buf;
@@ -3196,6 +3219,13 @@ skip_dynamic_range_lvalue_expansion:;
if ((child->is_input || child->is_output) && arg_count < children.size())
{
AstNode *arg = children[arg_count++]->clone();
+ // convert purely constant arguments into localparams
+ if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) {
+ wire->type = AST_LOCALPARAM;
+ wire->attributes.erase(ID::nosync);
+ wire->children.insert(wire->children.begin(), arg->clone());
+ continue;
+ }
AstNode *wire_id = new AstNode(AST_IDENTIFIER);
wire_id->str = wire->str;
AstNode *assign = child->is_input ?
diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc
index 0276618b4..632dc51fd 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -2354,8 +2354,10 @@ struct VerificPass : public Pass {
while (argidx < GetSize(args))
file_names.Insert(args[argidx++].c_str());
- if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU))
+ if (!veri_file::AnalyzeMultipleFiles(&file_names, verilog_mode, work.c_str(), veri_file::MFCU)) {
+ verific_error_msg.clear();
log_cmd_error("Reading Verilog/SystemVerilog sources failed.\n");
+ }
verific_import_pending = true;
goto check_error;
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index 12dea93b8..944cb301a 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -139,12 +139,12 @@ struct CellTypes
setup_type(ID($dff), {ID::CLK, ID::D}, {ID::Q});
setup_type(ID($dffe), {ID::CLK, ID::EN, ID::D}, {ID::Q});
setup_type(ID($dffsr), {ID::CLK, ID::SET, ID::CLR, ID::D}, {ID::Q});
- setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::E}, {ID::Q});
+ setup_type(ID($dffsre), {ID::CLK, ID::SET, ID::CLR, ID::D, ID::EN}, {ID::Q});
setup_type(ID($adff), {ID::CLK, ID::ARST, ID::D}, {ID::Q});
- setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::E}, {ID::Q});
+ setup_type(ID($adffe), {ID::CLK, ID::ARST, ID::D, ID::EN}, {ID::Q});
setup_type(ID($sdff), {ID::CLK, ID::SRST, ID::D}, {ID::Q});
- setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::E}, {ID::Q});
- setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::E}, {ID::Q});
+ setup_type(ID($sdffe), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q});
+ setup_type(ID($sdffce), {ID::CLK, ID::SRST, ID::D, ID::EN}, {ID::Q});
setup_type(ID($dlatch), {ID::EN, ID::D}, {ID::Q});
setup_type(ID($adlatch), {ID::EN, ID::D, ID::ARST}, {ID::Q});
setup_type(ID($dlatchsr), {ID::EN, ID::SET, ID::CLR, ID::D}, {ID::Q});
diff --git a/kernel/constids.inc b/kernel/constids.inc
index 69bc06d2c..3c2ff9beb 100644
--- a/kernel/constids.inc
+++ b/kernel/constids.inc
@@ -172,6 +172,7 @@ X(T)
X(TABLE)
X(techmap_autopurge)
X(_TECHMAP_BITS_CONNMAP_)
+X(_TECHMAP_CELLNAME_)
X(_TECHMAP_CELLTYPE_)
X(techmap_celltype)
X(_TECHMAP_FAIL_)
diff --git a/kernel/ff.h b/kernel/ff.h
new file mode 100644
index 000000000..0aecbaa2a
--- /dev/null
+++ b/kernel/ff.h
@@ -0,0 +1,486 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 Marcelina Kościelnicka <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.
+ *
+ */
+
+#ifndef FF_H
+#define FF_H
+
+#include "kernel/yosys.h"
+#include "kernel/ffinit.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct FfData {
+ FfInitVals *initvals;
+ SigSpec sig_q;
+ SigSpec sig_d;
+ SigSpec sig_clk;
+ SigSpec sig_en;
+ SigSpec sig_arst;
+ SigSpec sig_srst;
+ SigSpec sig_clr;
+ SigSpec sig_set;
+ bool has_d;
+ bool has_clk;
+ bool has_en;
+ bool has_srst;
+ bool has_arst;
+ bool has_sr;
+ bool ce_over_srst;
+ bool is_fine;
+ bool pol_clk;
+ bool pol_en;
+ bool pol_arst;
+ bool pol_srst;
+ bool pol_clr;
+ bool pol_set;
+ Const val_arst;
+ Const val_srst;
+ Const val_init;
+ Const val_d;
+ bool d_is_const;
+ int width;
+ dict<IdString, Const> attributes;
+
+ FfData(FfInitVals *initvals, Cell *cell = nullptr) : initvals(initvals) {
+ width = 0;
+ has_d = true;
+ has_clk = false;
+ has_en = false;
+ has_srst = false;
+ has_arst = false;
+ has_sr = false;
+ ce_over_srst = false;
+ is_fine = false;
+ pol_clk = false;
+ pol_en = false;
+ pol_arst = false;
+ pol_srst = false;
+ pol_clr = false;
+ pol_set = false;
+ d_is_const = false;
+
+ if (!cell)
+ return;
+
+ sig_q = cell->getPort(ID::Q);
+ width = GetSize(sig_q);
+ attributes = cell->attributes;
+
+ if (initvals)
+ val_init = (*initvals)(sig_q);
+
+ std::string type_str = cell->type.str();
+
+ if (cell->type.in(ID($ff), ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) {
+ if (cell->type == ID($sr)) {
+ has_d = false;
+ } else {
+ sig_d = cell->getPort(ID::D);
+ }
+ if (!cell->type.in(ID($ff), ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr))) {
+ has_clk = true;
+ sig_clk = cell->getPort(ID::CLK);
+ pol_clk = cell->getParam(ID::CLK_POLARITY).as_bool();
+ }
+ if (cell->type.in(ID($dffe), ID($dffsre), ID($adffe), ID($sdffe), ID($sdffce), ID($dlatch), ID($adlatch), ID($dlatchsr))) {
+ has_en = true;
+ sig_en = cell->getPort(ID::EN);
+ pol_en = cell->getParam(ID::EN_POLARITY).as_bool();
+ }
+ if (cell->type.in(ID($dffsr), ID($dffsre), ID($dlatchsr), ID($sr))) {
+ has_sr = true;
+ sig_clr = cell->getPort(ID::CLR);
+ sig_set = cell->getPort(ID::SET);
+ pol_clr = cell->getParam(ID::CLR_POLARITY).as_bool();
+ pol_set = cell->getParam(ID::SET_POLARITY).as_bool();
+ }
+ if (cell->type.in(ID($adff), ID($adffe), ID($adlatch))) {
+ has_arst = true;
+ sig_arst = cell->getPort(ID::ARST);
+ pol_arst = cell->getParam(ID::ARST_POLARITY).as_bool();
+ val_arst = cell->getParam(ID::ARST_VALUE);
+ }
+ if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) {
+ has_srst = true;
+ sig_srst = cell->getPort(ID::SRST);
+ pol_srst = cell->getParam(ID::SRST_POLARITY).as_bool();
+ val_srst = cell->getParam(ID::SRST_VALUE);
+ ce_over_srst = cell->type == ID($sdffce);
+ }
+ } else if (cell->type == ID($_FF_)) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ } else if (type_str.substr(0, 5) == "$_SR_") {
+ is_fine = true;
+ has_d = false;
+ has_sr = true;
+ pol_set = type_str[5] == 'P';
+ pol_clr = type_str[6] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[6] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[7] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_en = true;
+ pol_en = type_str[8] == 'P';
+ sig_en = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[6] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_arst = true;
+ pol_arst = type_str[7] == 'P';
+ sig_arst = cell->getPort(ID::R);
+ val_arst = type_str[8] == '1' ? State::S1 : State::S0;
+ } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[7] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_arst = true;
+ pol_arst = type_str[8] == 'P';
+ sig_arst = cell->getPort(ID::R);
+ val_arst = type_str[9] == '1' ? State::S1 : State::S0;
+ has_en = true;
+ pol_en = type_str[10] == 'P';
+ sig_en = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[8] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_sr = true;
+ pol_set = type_str[9] == 'P';
+ pol_clr = type_str[10] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[9] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_sr = true;
+ pol_set = type_str[10] == 'P';
+ pol_clr = type_str[11] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ has_en = true;
+ pol_en = type_str[12] == 'P';
+ sig_en = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[7] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_srst = true;
+ pol_srst = type_str[8] == 'P';
+ sig_srst = cell->getPort(ID::R);
+ val_srst = type_str[9] == '1' ? State::S1 : State::S0;
+ } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[8] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_srst = true;
+ pol_srst = type_str[9] == 'P';
+ sig_srst = cell->getPort(ID::R);
+ val_srst = type_str[10] == '1' ? State::S1 : State::S0;
+ has_en = true;
+ pol_en = type_str[11] == 'P';
+ sig_en = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_clk = true;
+ pol_clk = type_str[9] == 'P';
+ sig_clk = cell->getPort(ID::C);
+ has_srst = true;
+ pol_srst = type_str[10] == 'P';
+ sig_srst = cell->getPort(ID::R);
+ val_srst = type_str[11] == '1' ? State::S1 : State::S0;
+ has_en = true;
+ pol_en = type_str[12] == 'P';
+ sig_en = cell->getPort(ID::E);
+ ce_over_srst = true;
+ } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_en = true;
+ pol_en = type_str[9] == 'P';
+ sig_en = cell->getPort(ID::E);
+ } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_en = true;
+ pol_en = type_str[9] == 'P';
+ sig_en = cell->getPort(ID::E);
+ has_arst = true;
+ pol_arst = type_str[10] == 'P';
+ sig_arst = cell->getPort(ID::R);
+ val_arst = type_str[11] == '1' ? State::S1 : State::S0;
+ } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) {
+ is_fine = true;
+ sig_d = cell->getPort(ID::D);
+ has_en = true;
+ pol_en = type_str[11] == 'P';
+ sig_en = cell->getPort(ID::E);
+ has_sr = true;
+ pol_set = type_str[12] == 'P';
+ pol_clr = type_str[13] == 'P';
+ sig_set = cell->getPort(ID::S);
+ sig_clr = cell->getPort(ID::R);
+ } else {
+ log_assert(0);
+ }
+ if (has_d && sig_d.is_fully_const()) {
+ d_is_const = true;
+ val_d = sig_d.as_const();
+ if (has_en && !has_clk && !has_sr && !has_arst) {
+ // Plain D latches with const D treated specially.
+ has_en = has_d = false;
+ has_arst = true;
+ sig_arst = sig_en;
+ pol_arst = pol_en;
+ val_arst = val_d;
+ }
+ }
+ }
+
+ // Returns a FF identical to this one, but only keeping bit indices from the argument.
+ FfData slice(const std::vector<int> &bits) {
+ FfData res(initvals);
+ res.sig_clk = sig_clk;
+ res.sig_en = sig_en;
+ res.sig_arst = sig_arst;
+ res.sig_srst = sig_srst;
+ res.has_d = has_d;
+ res.has_clk = has_clk;
+ res.has_en = has_en;
+ res.has_arst = has_arst;
+ res.has_srst = has_srst;
+ res.has_sr = has_sr;
+ res.ce_over_srst = ce_over_srst;
+ res.is_fine = is_fine;
+ res.pol_clk = pol_clk;
+ res.pol_en = pol_en;
+ res.pol_arst = pol_arst;
+ res.pol_srst = pol_srst;
+ res.pol_clr = pol_clr;
+ res.pol_set = pol_set;
+ res.attributes = attributes;
+ for (int i : bits) {
+ res.sig_q.append(sig_q[i]);
+ if (has_d)
+ res.sig_d.append(sig_d[i]);
+ if (has_sr) {
+ res.sig_clr.append(sig_clr[i]);
+ res.sig_set.append(sig_set[i]);
+ }
+ if (has_arst)
+ res.val_arst.bits.push_back(val_arst[i]);
+ if (has_srst)
+ res.val_srst.bits.push_back(val_srst[i]);
+ res.val_init.bits.push_back(val_init[i]);
+ }
+ res.width = GetSize(res.sig_q);
+ // Slicing bits out may cause D to become const.
+ if (has_d && res.sig_d.is_fully_const()) {
+ res.d_is_const = true;
+ res.val_d = res.sig_d.as_const();
+ }
+ return res;
+ }
+
+ void unmap_ce(Module *module) {
+ if (!has_en)
+ return;
+ log_assert(has_clk);
+ if (has_srst && ce_over_srst)
+ unmap_srst(module);
+
+ if (!is_fine) {
+ if (pol_en)
+ sig_d = module->Mux(NEW_ID, sig_q, sig_d, sig_en);
+ else
+ sig_d = module->Mux(NEW_ID, sig_d, sig_q, sig_en);
+ } else {
+ if (pol_en)
+ sig_d = module->MuxGate(NEW_ID, sig_q, sig_d, sig_en);
+ else
+ sig_d = module->MuxGate(NEW_ID, sig_d, sig_q, sig_en);
+ }
+ has_en = false;
+ }
+
+ void unmap_srst(Module *module) {
+ if (!has_srst)
+ return;
+ if (has_en && !ce_over_srst)
+ unmap_ce(module);
+
+ if (!is_fine) {
+ if (pol_srst)
+ sig_d = module->Mux(NEW_ID, sig_d, val_srst, sig_srst);
+ else
+ sig_d = module->Mux(NEW_ID, val_srst, sig_d, sig_srst);
+ } else {
+ if (pol_srst)
+ sig_d = module->MuxGate(NEW_ID, sig_d, val_srst[0], sig_srst);
+ else
+ sig_d = module->MuxGate(NEW_ID, val_srst[0], sig_d, sig_srst);
+ }
+ has_srst = false;
+ }
+
+ void unmap_ce_srst(Module *module) {
+ unmap_ce(module);
+ unmap_srst(module);
+ }
+
+ Cell *emit(Module *module, IdString name) {
+ if (!width)
+ return nullptr;
+ if (!has_d && !has_sr) {
+ if (has_arst) {
+ // Convert this case to a D latch.
+ has_d = has_en = true;
+ has_arst = false;
+ sig_d = val_arst;
+ sig_en = sig_arst;
+ pol_en = pol_arst;
+ } else {
+ // No control inputs left. Turn into a const driver.
+ initvals->remove_init(sig_q);
+ module->connect(sig_q, val_init);
+ return nullptr;
+ }
+ }
+ initvals->set_init(sig_q, val_init);
+ Cell *cell;
+ if (!is_fine) {
+ if (!has_d) {
+ log_assert(has_sr);
+ cell = module->addSr(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
+ } else if (!has_clk && !has_en) {
+ log_assert(!has_arst);
+ log_assert(!has_srst);
+ log_assert(!has_sr);
+ cell = module->addFf(name, sig_d, sig_q);
+ } else if (!has_clk) {
+ log_assert(!has_srst);
+ if (has_sr)
+ cell = module->addDlatchsr(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr);
+ else if (has_arst)
+ cell = module->addAdlatch(name, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_en, pol_arst);
+ else
+ cell = module->addDlatch(name, sig_en, sig_d, sig_q, pol_en);
+ } else {
+ if (has_sr) {
+ if (has_en)
+ cell = module->addDffsre(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr);
+ else
+ cell = module->addDffsr(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
+ } else if (has_arst) {
+ if (has_en)
+ cell = module->addAdffe(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_en, pol_arst);
+ else
+ cell = module->addAdff(name, sig_clk, sig_arst, sig_d, sig_q, val_arst, pol_clk, pol_arst);
+ } else if (has_srst) {
+ if (has_en)
+ if (ce_over_srst)
+ cell = module->addSdffce(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst);
+ else
+ cell = module->addSdffe(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_en, pol_srst);
+ else
+ cell = module->addSdff(name, sig_clk, sig_srst, sig_d, sig_q, val_srst, pol_clk, pol_srst);
+ } else {
+ if (has_en)
+ cell = module->addDffe(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en);
+ else
+ cell = module->addDff(name, sig_clk, sig_d, sig_q, pol_clk);
+ }
+ }
+ } else {
+ if (!has_d) {
+ log_assert(has_sr);
+ cell = module->addSrGate(name, sig_set, sig_clr, sig_q, pol_set, pol_clr);
+ } else if (!has_clk && !has_en) {
+ log_assert(!has_arst);
+ log_assert(!has_srst);
+ log_assert(!has_sr);
+ cell = module->addFfGate(name, sig_d, sig_q);
+ } else if (!has_clk) {
+ log_assert(!has_srst);
+ if (has_sr)
+ cell = module->addDlatchsrGate(name, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_en, pol_set, pol_clr);
+ else if (has_arst)
+ cell = module->addAdlatchGate(name, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_en, pol_arst);
+ else
+ cell = module->addDlatchGate(name, sig_en, sig_d, sig_q, pol_en);
+ } else {
+ if (has_sr) {
+ if (has_en)
+ cell = module->addDffsreGate(name, sig_clk, sig_en, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_en, pol_set, pol_clr);
+ else
+ cell = module->addDffsrGate(name, sig_clk, sig_set, sig_clr, sig_d, sig_q, pol_clk, pol_set, pol_clr);
+ } else if (has_arst) {
+ if (has_en)
+ cell = module->addAdffeGate(name, sig_clk, sig_en, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_en, pol_arst);
+ else
+ cell = module->addAdffGate(name, sig_clk, sig_arst, sig_d, sig_q, val_arst.as_bool(), pol_clk, pol_arst);
+ } else if (has_srst) {
+ if (has_en)
+ if (ce_over_srst)
+ cell = module->addSdffceGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst);
+ else
+ cell = module->addSdffeGate(name, sig_clk, sig_en, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_en, pol_srst);
+ else
+ cell = module->addSdffGate(name, sig_clk, sig_srst, sig_d, sig_q, val_srst.as_bool(), pol_clk, pol_srst);
+ } else {
+ if (has_en)
+ cell = module->addDffeGate(name, sig_clk, sig_en, sig_d, sig_q, pol_clk, pol_en);
+ else
+ cell = module->addDffGate(name, sig_clk, sig_d, sig_q, pol_clk);
+ }
+ }
+ }
+ cell->attributes = attributes;
+ return cell;
+ }
+};
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/ffinit.h b/kernel/ffinit.h
new file mode 100644
index 000000000..025b0c862
--- /dev/null
+++ b/kernel/ffinit.h
@@ -0,0 +1,141 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 Marcelina Kościelnicka <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.
+ *
+ */
+
+#ifndef FFINIT_H
+#define FFINIT_H
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+YOSYS_NAMESPACE_BEGIN
+
+struct FfInitVals
+{
+ const SigMap *sigmap;
+ RTLIL::Module *module;
+ dict<SigBit, std::pair<State,SigBit>> initbits;
+
+ void set(const SigMap *sigmap_, RTLIL::Module *module)
+ {
+ sigmap = sigmap_;
+ initbits.clear();
+ for (auto wire : module->wires())
+ {
+ if (wire->attributes.count(ID::init) == 0)
+ continue;
+
+ SigSpec wirebits = (*sigmap)(wire);
+ Const initval = wire->attributes.at(ID::init);
+
+ for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
+ {
+ SigBit bit = wirebits[i];
+ State val = initval[i];
+
+ if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
+ continue;
+
+ if (initbits.count(bit)) {
+ if (initbits.at(bit).first != val)
+ log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
+ log_signal(bit), log_signal(SigBit(wire, i)),
+ log_signal(val), log_signal(initbits.at(bit).first));
+ continue;
+ }
+
+ initbits[bit] = std::make_pair(val,SigBit(wire,i));
+ }
+ }
+ }
+
+ RTLIL::State operator()(RTLIL::SigBit bit) const
+ {
+ auto it = initbits.find((*sigmap)(bit));
+ if (it != initbits.end())
+ return it->second.first;
+ else
+ return State::Sx;
+ }
+
+ RTLIL::Const operator()(const RTLIL::SigSpec &sig) const
+ {
+ RTLIL::Const res;
+ for (auto bit : sig)
+ res.bits.push_back((*this)(bit));
+ return res;
+ }
+
+ void set_init(RTLIL::SigBit bit, RTLIL::State val)
+ {
+ SigBit mbit = (*sigmap)(bit);
+ SigBit abit = bit;
+ auto it = initbits.find(mbit);
+ if (it != initbits.end())
+ abit = it->second.second;
+ else if (val == State::Sx)
+ return;
+ log_assert(abit.wire);
+ initbits[mbit] = std::make_pair(val,abit);
+ auto it2 = abit.wire->attributes.find(ID::init);
+ if (it2 != abit.wire->attributes.end()) {
+ it2->second[abit.offset] = val;
+ if (it2->second.is_fully_undef())
+ abit.wire->attributes.erase(it2);
+ } else if (val != State::Sx) {
+ Const cval(State::Sx, GetSize(abit.wire));
+ cval[abit.offset] = val;
+ abit.wire->attributes[ID::init] = cval;
+ }
+ }
+
+ void set_init(const RTLIL::SigSpec &sig, RTLIL::Const val)
+ {
+ log_assert(GetSize(sig) == GetSize(val));
+ for (int i = 0; i < GetSize(sig); i++)
+ set_init(sig[i], val[i]);
+ }
+
+ void remove_init(RTLIL::SigBit bit)
+ {
+ set_init(bit, State::Sx);
+ }
+
+ void remove_init(const RTLIL::SigSpec &sig)
+ {
+ for (auto bit : sig)
+ remove_init(bit);
+ }
+
+ void clear()
+ {
+ initbits.clear();
+ }
+
+ FfInitVals (const SigMap *sigmap, RTLIL::Module *module)
+ {
+ set(sigmap, module);
+ }
+
+ FfInitVals () {}
+};
+
+
+YOSYS_NAMESPACE_END
+
+#endif
diff --git a/kernel/satgen.cc b/kernel/satgen.cc
new file mode 100644
index 000000000..73839d37a
--- /dev/null
+++ b/kernel/satgen.cc
@@ -0,0 +1,1239 @@
+/*
+ * 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/satgen.h"
+#include "kernel/ff.h"
+
+USING_YOSYS_NAMESPACE
+
+bool SatGen::importCell(RTLIL::Cell *cell, int timestep)
+{
+ bool arith_undef_handled = false;
+ bool is_arith_compare = cell->type.in(ID($lt), ID($le), ID($ge), ID($gt));
+
+ if (model_undef && (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor)) || is_arith_compare))
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ if (is_arith_compare)
+ extendSignalWidth(undef_a, undef_b, cell, true);
+ else
+ extendSignalWidth(undef_a, undef_b, undef_y, cell, true);
+
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+ int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
+
+ if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
+ std::vector<int> b = importSigSpec(cell->getPort(ID::B), timestep);
+ undef_y_bit = ez->OR(undef_y_bit, ez->NOT(ez->expression(ezSAT::OpOr, b)));
+ }
+
+ if (is_arith_compare) {
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+ ez->SET(undef_y_bit, undef_y.at(0));
+ } else {
+ std::vector<int> undef_y_bits(undef_y.size(), undef_y_bit);
+ ez->assume(ez->vec_eq(undef_y_bits, undef_y));
+ }
+
+ arith_undef_handled = true;
+ }
+
+ if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_),
+ ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($sub)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(a, b, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type.in(ID($and), ID($_AND_)))
+ ez->assume(ez->vec_eq(ez->vec_and(a, b), yy));
+ if (cell->type == ID($_NAND_))
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_and(a, b)), yy));
+ if (cell->type.in(ID($or), ID($_OR_)))
+ ez->assume(ez->vec_eq(ez->vec_or(a, b), yy));
+ if (cell->type == ID($_NOR_))
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_or(a, b)), yy));
+ if (cell->type.in(ID($xor), ID($_XOR_)))
+ ez->assume(ez->vec_eq(ez->vec_xor(a, b), yy));
+ if (cell->type.in(ID($xnor), ID($_XNOR_)))
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(a, b)), yy));
+ if (cell->type == ID($_ANDNOT_))
+ ez->assume(ez->vec_eq(ez->vec_and(a, ez->vec_not(b)), yy));
+ if (cell->type == ID($_ORNOT_))
+ ez->assume(ez->vec_eq(ez->vec_or(a, ez->vec_not(b)), yy));
+ if (cell->type == ID($add))
+ ez->assume(ez->vec_eq(ez->vec_add(a, b), yy));
+ if (cell->type == ID($sub))
+ ez->assume(ez->vec_eq(ez->vec_sub(a, b), yy));
+
+ if (model_undef && !arith_undef_handled)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(undef_a, undef_b, undef_y, cell, false);
+
+ if (cell->type.in(ID($and), ID($_AND_), ID($_NAND_))) {
+ std::vector<int> a0 = ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a));
+ std::vector<int> b0 = ez->vec_and(ez->vec_not(b), ez->vec_not(undef_b));
+ std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a0, b0)));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else if (cell->type.in(ID($or), ID($_OR_), ID($_NOR_))) {
+ std::vector<int> a1 = ez->vec_and(a, ez->vec_not(undef_a));
+ std::vector<int> b1 = ez->vec_and(b, ez->vec_not(undef_b));
+ std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a1, b1)));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else if (cell->type.in(ID($xor), ID($xnor), ID($_XOR_), ID($_XNOR_))) {
+ std::vector<int> yX = ez->vec_or(undef_a, undef_b);
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else if (cell->type == ID($_ANDNOT_)) {
+ std::vector<int> a0 = ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a));
+ std::vector<int> b1 = ez->vec_and(b, ez->vec_not(undef_b));
+ std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a0, b1)));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+
+ else if (cell->type == ID($_ORNOT_)) {
+ std::vector<int> a1 = ez->vec_and(a, ez->vec_not(undef_a));
+ std::vector<int> b0 = ez->vec_and(ez->vec_not(b), ez->vec_not(undef_b));
+ std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a1, b0)));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ }
+ else
+ log_abort();
+
+ undefGating(y, yy, undef_y);
+ }
+ else if (model_undef)
+ {
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_)))
+ {
+ bool aoi_mode = cell->type.in(ID($_AOI3_), ID($_AOI4_));
+ bool three_mode = cell->type.in(ID($_AOI3_), ID($_OAI3_));
+
+ int a = importDefSigSpec(cell->getPort(ID::A), timestep).at(0);
+ int b = importDefSigSpec(cell->getPort(ID::B), timestep).at(0);
+ int c = importDefSigSpec(cell->getPort(ID::C), timestep).at(0);
+ int d = three_mode ? (aoi_mode ? ez->CONST_TRUE : ez->CONST_FALSE) : importDefSigSpec(cell->getPort(ID::D), timestep).at(0);
+ int y = importDefSigSpec(cell->getPort(ID::Y), timestep).at(0);
+ int yy = model_undef ? ez->literal() : y;
+
+ if (cell->type.in(ID($_AOI3_), ID($_AOI4_)))
+ ez->assume(ez->IFF(ez->NOT(ez->OR(ez->AND(a, b), ez->AND(c, d))), yy));
+ else
+ ez->assume(ez->IFF(ez->NOT(ez->AND(ez->OR(a, b), ez->OR(c, d))), yy));
+
+ if (model_undef)
+ {
+ int undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep).at(0);
+ int undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep).at(0);
+ int undef_c = importUndefSigSpec(cell->getPort(ID::C), timestep).at(0);
+ int undef_d = three_mode ? ez->CONST_FALSE : importUndefSigSpec(cell->getPort(ID::D), timestep).at(0);
+ int undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep).at(0);
+
+ if (aoi_mode)
+ {
+ int a0 = ez->AND(ez->NOT(a), ez->NOT(undef_a));
+ int b0 = ez->AND(ez->NOT(b), ez->NOT(undef_b));
+ int c0 = ez->AND(ez->NOT(c), ez->NOT(undef_c));
+ int d0 = ez->AND(ez->NOT(d), ez->NOT(undef_d));
+
+ int ab = ez->AND(a, b), cd = ez->AND(c, d);
+ int undef_ab = ez->AND(ez->OR(undef_a, undef_b), ez->NOT(ez->OR(a0, b0)));
+ int undef_cd = ez->AND(ez->OR(undef_c, undef_d), ez->NOT(ez->OR(c0, d0)));
+
+ int ab1 = ez->AND(ab, ez->NOT(undef_ab));
+ int cd1 = ez->AND(cd, ez->NOT(undef_cd));
+ int yX = ez->AND(ez->OR(undef_ab, undef_cd), ez->NOT(ez->OR(ab1, cd1)));
+
+ ez->assume(ez->IFF(yX, undef_y));
+ }
+ else
+ {
+ int a1 = ez->AND(a, ez->NOT(undef_a));
+ int b1 = ez->AND(b, ez->NOT(undef_b));
+ int c1 = ez->AND(c, ez->NOT(undef_c));
+ int d1 = ez->AND(d, ez->NOT(undef_d));
+
+ int ab = ez->OR(a, b), cd = ez->OR(c, d);
+ int undef_ab = ez->AND(ez->OR(undef_a, undef_b), ez->NOT(ez->OR(a1, b1)));
+ int undef_cd = ez->AND(ez->OR(undef_c, undef_d), ez->NOT(ez->OR(c1, d1)));
+
+ int ab0 = ez->AND(ez->NOT(ab), ez->NOT(undef_ab));
+ int cd0 = ez->AND(ez->NOT(cd), ez->NOT(undef_cd));
+ int yX = ez->AND(ez->OR(undef_ab, undef_cd), ez->NOT(ez->OR(ab0, cd0)));
+
+ ez->assume(ez->IFF(yX, undef_y));
+ }
+
+ undefGating(y, yy, undef_y);
+ }
+
+ return true;
+ }
+
+ if (cell->type.in(ID($_NOT_), ID($not)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidthUnary(a, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ ez->assume(ez->vec_eq(ez->vec_not(a), yy));
+
+ if (model_undef) {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidthUnary(undef_a, undef_y, cell, false);
+ ez->assume(ez->vec_eq(undef_a, undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($_MUX_), ID($mux), ID($_NMUX_)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ if (cell->type == ID($_NMUX_))
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_ite(s.at(0), b, a)), yy));
+ else
+ ez->assume(ez->vec_eq(ez->vec_ite(s.at(0), b, a), yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a, b));
+ std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a, undef_b));
+ std::vector<int> yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a));
+ ez->assume(ez->vec_eq(yX, undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($pmux))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ std::vector<int> tmp = a;
+ for (size_t i = 0; i < s.size(); i++) {
+ std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size());
+ tmp = ez->vec_ite(s.at(i), part_of_b, tmp);
+ }
+ ez->assume(ez->vec_eq(tmp, yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+
+ int maybe_a = ez->CONST_TRUE;
+
+ std::vector<int> bits_set = std::vector<int>(undef_y.size(), ez->CONST_FALSE);
+ std::vector<int> bits_clr = std::vector<int>(undef_y.size(), ez->CONST_FALSE);
+
+ for (size_t i = 0; i < s.size(); i++)
+ {
+ std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size());
+ std::vector<int> part_of_undef_b(undef_b.begin()+i*a.size(), undef_b.begin()+(i+1)*a.size());
+
+ int maybe_s = ez->OR(s.at(i), undef_s.at(i));
+ int sure_s = ez->AND(s.at(i), ez->NOT(undef_s.at(i)));
+
+ maybe_a = ez->AND(maybe_a, ez->NOT(sure_s));
+
+ bits_set = ez->vec_ite(maybe_s, ez->vec_or(bits_set, ez->vec_or(part_of_b, part_of_undef_b)), bits_set);
+ bits_clr = ez->vec_ite(maybe_s, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(part_of_b), part_of_undef_b)), bits_clr);
+ }
+
+ bits_set = ez->vec_ite(maybe_a, ez->vec_or(bits_set, ez->vec_or(bits_set, ez->vec_or(a, undef_a))), bits_set);
+ bits_clr = ez->vec_ite(maybe_a, ez->vec_or(bits_clr, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(a), undef_a))), bits_clr);
+
+ ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(bits_set, bits_clr)), undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($pos), ID($neg)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidthUnary(a, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == ID($pos)) {
+ ez->assume(ez->vec_eq(a, yy));
+ } else {
+ std::vector<int> zero(a.size(), ez->CONST_FALSE);
+ ez->assume(ez->vec_eq(ez->vec_sub(zero, a), yy));
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidthUnary(undef_a, undef_y, cell);
+
+ if (cell->type == ID($pos)) {
+ ez->assume(ez->vec_eq(undef_a, undef_y));
+ } else {
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ std::vector<int> undef_y_bits(undef_y.size(), undef_any_a);
+ ez->assume(ez->vec_eq(undef_y_bits, undef_y));
+ }
+
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == ID($reduce_and))
+ ez->SET(ez->expression(ez->OpAnd, a), yy.at(0));
+ if (cell->type.in(ID($reduce_or), ID($reduce_bool)))
+ ez->SET(ez->expression(ez->OpOr, a), yy.at(0));
+ if (cell->type == ID($reduce_xor))
+ ez->SET(ez->expression(ez->OpXor, a), yy.at(0));
+ if (cell->type == ID($reduce_xnor))
+ ez->SET(ez->NOT(ez->expression(ez->OpXor, a)), yy.at(0));
+ if (cell->type == ID($logic_not))
+ ez->SET(ez->NOT(ez->expression(ez->OpOr, a)), yy.at(0));
+ for (size_t i = 1; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, yy.at(i));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ int aX = ez->expression(ezSAT::OpOr, undef_a);
+
+ if (cell->type == ID($reduce_and)) {
+ int a0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a)));
+ ez->assume(ez->IFF(ez->AND(ez->NOT(a0), aX), undef_y.at(0)));
+ }
+ else if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not))) {
+ int a1 = ez->expression(ezSAT::OpOr, ez->vec_and(a, ez->vec_not(undef_a)));
+ ez->assume(ez->IFF(ez->AND(ez->NOT(a1), aX), undef_y.at(0)));
+ }
+ else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) {
+ ez->assume(ez->IFF(aX, undef_y.at(0)));
+ } else
+ log_abort();
+
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($logic_and), ID($logic_or)))
+ {
+ std::vector<int> vec_a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> vec_b = importDefSigSpec(cell->getPort(ID::B), timestep);
+
+ int a = ez->expression(ez->OpOr, vec_a);
+ int b = ez->expression(ez->OpOr, vec_b);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (cell->type == ID($logic_and))
+ ez->SET(ez->expression(ez->OpAnd, a, b), yy.at(0));
+ else
+ ez->SET(ez->expression(ez->OpOr, a, b), yy.at(0));
+ for (size_t i = 1; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, yy.at(i));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+
+ int a0 = ez->NOT(ez->OR(ez->expression(ezSAT::OpOr, vec_a), ez->expression(ezSAT::OpOr, undef_a)));
+ int b0 = ez->NOT(ez->OR(ez->expression(ezSAT::OpOr, vec_b), ez->expression(ezSAT::OpOr, undef_b)));
+ int a1 = ez->expression(ezSAT::OpOr, ez->vec_and(vec_a, ez->vec_not(undef_a)));
+ int b1 = ez->expression(ezSAT::OpOr, ez->vec_and(vec_b, ez->vec_not(undef_b)));
+ int aX = ez->expression(ezSAT::OpOr, undef_a);
+ int bX = ez->expression(ezSAT::OpOr, undef_b);
+
+ if (cell->type == ID($logic_and))
+ ez->SET(ez->AND(ez->OR(aX, bX), ez->NOT(ez->AND(a1, b1)), ez->NOT(a0), ez->NOT(b0)), undef_y.at(0));
+ else if (cell->type == ID($logic_or))
+ ez->SET(ez->AND(ez->OR(aX, bX), ez->NOT(ez->AND(a0, b0)), ez->NOT(a1), ez->NOT(b1)), undef_y.at(0));
+ else
+ log_abort();
+
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt)))
+ {
+ bool is_signed = cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool();
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(a, b, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ if (model_undef && cell->type.in(ID($eqx), ID($nex))) {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ extendSignalWidth(undef_a, undef_b, cell, true);
+ a = ez->vec_or(a, undef_a);
+ b = ez->vec_or(b, undef_b);
+ }
+
+ if (cell->type == ID($lt))
+ ez->SET(is_signed ? ez->vec_lt_signed(a, b) : ez->vec_lt_unsigned(a, b), yy.at(0));
+ if (cell->type == ID($le))
+ ez->SET(is_signed ? ez->vec_le_signed(a, b) : ez->vec_le_unsigned(a, b), yy.at(0));
+ if (cell->type.in(ID($eq), ID($eqx)))
+ ez->SET(ez->vec_eq(a, b), yy.at(0));
+ if (cell->type.in(ID($ne), ID($nex)))
+ ez->SET(ez->vec_ne(a, b), yy.at(0));
+ if (cell->type == ID($ge))
+ ez->SET(is_signed ? ez->vec_ge_signed(a, b) : ez->vec_ge_unsigned(a, b), yy.at(0));
+ if (cell->type == ID($gt))
+ ez->SET(is_signed ? ez->vec_gt_signed(a, b) : ez->vec_gt_unsigned(a, b), yy.at(0));
+ for (size_t i = 1; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, yy.at(i));
+
+ if (model_undef && cell->type.in(ID($eqx), ID($nex)))
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(undef_a, undef_b, cell, true);
+
+ if (cell->type == ID($eqx))
+ yy.at(0) = ez->AND(yy.at(0), ez->vec_eq(undef_a, undef_b));
+ else
+ yy.at(0) = ez->OR(yy.at(0), ez->vec_ne(undef_a, undef_b));
+
+ for (size_t i = 0; i < y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+
+ ez->assume(ez->vec_eq(y, yy));
+ }
+ else if (model_undef && cell->type.in(ID($eq), ID($ne)))
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(undef_a, undef_b, cell, true);
+
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+ int undef_any = ez->OR(undef_any_a, undef_any_b);
+
+ std::vector<int> masked_a_bits = ez->vec_or(a, ez->vec_or(undef_a, undef_b));
+ std::vector<int> masked_b_bits = ez->vec_or(b, ez->vec_or(undef_a, undef_b));
+
+ int masked_ne = ez->vec_ne(masked_a_bits, masked_b_bits);
+ int undef_y_bit = ez->AND(undef_any, ez->NOT(masked_ne));
+
+ for (size_t i = 1; i < undef_y.size(); i++)
+ ez->SET(ez->CONST_FALSE, undef_y.at(i));
+ ez->SET(undef_y_bit, undef_y.at(0));
+
+ undefGating(y, yy, undef_y);
+ }
+ else
+ {
+ if (model_undef) {
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ log_assert(!model_undef || arith_undef_handled);
+ }
+ return true;
+ }
+
+ if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ int extend_bit = ez->CONST_FALSE;
+
+ if (!cell->type.in(ID($shift), ID($shiftx)) && cell->parameters[ID::A_SIGNED].as_bool())
+ extend_bit = a.back();
+
+ while (y.size() < a.size())
+ y.push_back(ez->literal());
+ while (y.size() > a.size())
+ a.push_back(extend_bit);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> shifted_a;
+
+ if (cell->type.in( ID($shl), ID($sshl)))
+ shifted_a = ez->vec_shift_left(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == ID($shr))
+ shifted_a = ez->vec_shift_right(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == ID($sshr))
+ shifted_a = ez->vec_shift_right(a, b, false, cell->parameters[ID::A_SIGNED].as_bool() ? a.back() : ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type.in(ID($shift), ID($shiftx)))
+ shifted_a = ez->vec_shift_right(a, b, cell->parameters[ID::B_SIGNED].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE);
+
+ ez->assume(ez->vec_eq(shifted_a, yy));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> undef_a_shifted;
+
+ extend_bit = cell->type == ID($shiftx) ? ez->CONST_TRUE : ez->CONST_FALSE;
+ if (!cell->type.in(ID($shift), ID($shiftx)) && cell->parameters[ID::A_SIGNED].as_bool())
+ extend_bit = undef_a.back();
+
+ while (undef_y.size() < undef_a.size())
+ undef_y.push_back(ez->literal());
+ while (undef_y.size() > undef_a.size())
+ undef_a.push_back(extend_bit);
+
+ if (cell->type.in(ID($shl), ID($sshl)))
+ undef_a_shifted = ez->vec_shift_left(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == ID($shr))
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == ID($sshr))
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, false, cell->parameters[ID::A_SIGNED].as_bool() ? undef_a.back() : ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == ID($shift))
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters[ID::B_SIGNED].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE);
+
+ if (cell->type == ID($shiftx))
+ undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters[ID::B_SIGNED].as_bool(), ez->CONST_TRUE, ez->CONST_TRUE);
+
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+ std::vector<int> undef_all_y_bits(undef_y.size(), undef_any_b);
+ ez->assume(ez->vec_eq(ez->vec_or(undef_a_shifted, undef_all_y_bits), undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($mul))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(a, b, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ std::vector<int> tmp(a.size(), ez->CONST_FALSE);
+ for (int i = 0; i < int(a.size()); i++)
+ {
+ std::vector<int> shifted_a(a.size(), ez->CONST_FALSE);
+ for (int j = i; j < int(a.size()); j++)
+ shifted_a.at(j) = a.at(j-i);
+ tmp = ez->vec_ite(b.at(i), ez->vec_add(tmp, shifted_a), tmp);
+ }
+ ez->assume(ez->vec_eq(tmp, yy));
+
+ if (model_undef) {
+ log_assert(arith_undef_handled);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($macc))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ Macc macc;
+ macc.from_cell(cell);
+
+ std::vector<int> tmp(GetSize(y), ez->CONST_FALSE);
+
+ for (auto &port : macc.ports)
+ {
+ std::vector<int> in_a = importDefSigSpec(port.in_a, timestep);
+ std::vector<int> in_b = importDefSigSpec(port.in_b, timestep);
+
+ while (GetSize(in_a) < GetSize(y))
+ in_a.push_back(port.is_signed && !in_a.empty() ? in_a.back() : ez->CONST_FALSE);
+ in_a.resize(GetSize(y));
+
+ if (GetSize(in_b))
+ {
+ while (GetSize(in_b) < GetSize(y))
+ in_b.push_back(port.is_signed && !in_b.empty() ? in_b.back() : ez->CONST_FALSE);
+ in_b.resize(GetSize(y));
+
+ for (int i = 0; i < GetSize(in_b); i++) {
+ std::vector<int> shifted_a(in_a.size(), ez->CONST_FALSE);
+ for (int j = i; j < int(in_a.size()); j++)
+ shifted_a.at(j) = in_a.at(j-i);
+ if (port.do_subtract)
+ tmp = ez->vec_ite(in_b.at(i), ez->vec_sub(tmp, shifted_a), tmp);
+ else
+ tmp = ez->vec_ite(in_b.at(i), ez->vec_add(tmp, shifted_a), tmp);
+ }
+ }
+ else
+ {
+ if (port.do_subtract)
+ tmp = ez->vec_sub(tmp, in_a);
+ else
+ tmp = ez->vec_add(tmp, in_a);
+ }
+ }
+
+ for (int i = 0; i < GetSize(b); i++) {
+ std::vector<int> val(GetSize(y), ez->CONST_FALSE);
+ val.at(0) = b.at(i);
+ tmp = ez->vec_add(tmp, val);
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+
+ int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
+ int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
+
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ ez->assume(ez->vec_eq(undef_y, std::vector<int>(GetSize(y), ez->OR(undef_any_a, undef_any_b))));
+
+ undefGating(y, tmp, undef_y);
+ }
+ else
+ ez->assume(ez->vec_eq(y, tmp));
+
+ return true;
+ }
+
+ if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidth(a, b, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+
+ std::vector<int> a_u, b_u;
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
+ a_u = ez->vec_ite(a.back(), ez->vec_neg(a), a);
+ b_u = ez->vec_ite(b.back(), ez->vec_neg(b), b);
+ } else {
+ a_u = a;
+ b_u = b;
+ }
+
+ std::vector<int> chain_buf = a_u;
+ std::vector<int> y_u(a_u.size(), ez->CONST_FALSE);
+ for (int i = int(a.size())-1; i >= 0; i--)
+ {
+ chain_buf.insert(chain_buf.end(), chain_buf.size(), ez->CONST_FALSE);
+
+ std::vector<int> b_shl(i, ez->CONST_FALSE);
+ b_shl.insert(b_shl.end(), b_u.begin(), b_u.end());
+ b_shl.insert(b_shl.end(), chain_buf.size()-b_shl.size(), ez->CONST_FALSE);
+
+ y_u.at(i) = ez->vec_ge_unsigned(chain_buf, b_shl);
+ chain_buf = ez->vec_ite(y_u.at(i), ez->vec_sub(chain_buf, b_shl), chain_buf);
+
+ chain_buf.erase(chain_buf.begin() + a_u.size(), chain_buf.end());
+ }
+
+ std::vector<int> y_tmp = ignore_div_by_zero ? yy : ez->vec_var(y.size());
+
+ // modulo calculation
+ std::vector<int> modulo_trunc;
+ int floored_eq_trunc;
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
+ modulo_trunc = ez->vec_ite(a.back(), ez->vec_neg(chain_buf), chain_buf);
+ // floor == trunc when sgn(a) == sgn(b) or trunc == 0
+ floored_eq_trunc = ez->OR(ez->IFF(a.back(), b.back()), ez->NOT(ez->expression(ezSAT::OpOr, modulo_trunc)));
+ } else {
+ modulo_trunc = chain_buf;
+ floored_eq_trunc = ez->CONST_TRUE;
+ }
+
+ if (cell->type == ID($div)) {
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(ez->XOR(a.back(), b.back()), ez->vec_neg(y_u), y_u)));
+ else
+ ez->assume(ez->vec_eq(y_tmp, y_u));
+ } else if (cell->type == ID($mod)) {
+ ez->assume(ez->vec_eq(y_tmp, modulo_trunc));
+ } else if (cell->type == ID($divfloor)) {
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(
+ ez->XOR(a.back(), b.back()),
+ ez->vec_neg(ez->vec_ite(
+ ez->vec_reduce_or(modulo_trunc),
+ ez->vec_add(y_u, ez->vec_const_unsigned(1, y_u.size())),
+ y_u
+ )),
+ y_u
+ )));
+ else
+ ez->assume(ez->vec_eq(y_tmp, y_u));
+ } else if (cell->type == ID($modfloor)) {
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(floored_eq_trunc, modulo_trunc, ez->vec_add(modulo_trunc, b))));
+ }
+
+ if (ignore_div_by_zero) {
+ ez->assume(ez->expression(ezSAT::OpOr, b));
+ } else {
+ std::vector<int> div_zero_result;
+ if (cell->type.in(ID($div), ID($divfloor))) {
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
+ std::vector<int> all_ones(y.size(), ez->CONST_TRUE);
+ std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
+ only_first_one.at(0) = ez->CONST_TRUE;
+ div_zero_result = ez->vec_ite(a.back(), only_first_one, all_ones);
+ } else {
+ div_zero_result.insert(div_zero_result.end(), cell->getPort(ID::A).size(), ez->CONST_TRUE);
+ div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
+ }
+ } else if (cell->type.in(ID($mod), ID($modfloor))) {
+ // a mod 0 = a
+ int copy_a_bits = min(cell->getPort(ID::A).size(), cell->getPort(ID::B).size());
+ div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits);
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
+ div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), div_zero_result.back());
+ else
+ div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
+ }
+ ez->assume(ez->vec_eq(yy, ez->vec_ite(ez->expression(ezSAT::OpOr, b), y_tmp, div_zero_result)));
+ }
+
+ if (model_undef) {
+ log_assert(arith_undef_handled);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($lut))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> lut;
+ for (auto bit : cell->getParam(ID::LUT).bits)
+ lut.push_back(bit == State::S1 ? ez->CONST_TRUE : ez->CONST_FALSE);
+ while (GetSize(lut) < (1 << GetSize(a)))
+ lut.push_back(ez->CONST_FALSE);
+ lut.resize(1 << GetSize(a));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> t(lut), u(GetSize(t), ez->CONST_FALSE);
+
+ for (int i = GetSize(a)-1; i >= 0; i--)
+ {
+ std::vector<int> t0(t.begin(), t.begin() + GetSize(t)/2);
+ std::vector<int> t1(t.begin() + GetSize(t)/2, t.end());
+
+ std::vector<int> u0(u.begin(), u.begin() + GetSize(u)/2);
+ std::vector<int> u1(u.begin() + GetSize(u)/2, u.end());
+
+ t = ez->vec_ite(a[i], t1, t0);
+ u = ez->vec_ite(undef_a[i], ez->vec_or(ez->vec_xor(t0, t1), ez->vec_or(u0, u1)), ez->vec_ite(a[i], u1, u0));
+ }
+
+ log_assert(GetSize(t) == 1);
+ log_assert(GetSize(u) == 1);
+ undefGating(y, t, u);
+ ez->assume(ez->vec_eq(importUndefSigSpec(cell->getPort(ID::Y), timestep), u));
+ }
+ else
+ {
+ std::vector<int> t = lut;
+ for (int i = GetSize(a)-1; i >= 0; i--)
+ {
+ std::vector<int> t0(t.begin(), t.begin() + GetSize(t)/2);
+ std::vector<int> t1(t.begin() + GetSize(t)/2, t.end());
+ t = ez->vec_ite(a[i], t1, t0);
+ }
+
+ log_assert(GetSize(t) == 1);
+ ez->assume(ez->vec_eq(y, t));
+ }
+ return true;
+ }
+
+ if (cell->type == ID($sop))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ int y = importDefSigSpec(cell->getPort(ID::Y), timestep).at(0);
+
+ int width = cell->getParam(ID::WIDTH).as_int();
+ int depth = cell->getParam(ID::DEPTH).as_int();
+
+ vector<State> table_raw = cell->getParam(ID::TABLE).bits;
+ while (GetSize(table_raw) < 2*width*depth)
+ table_raw.push_back(State::S0);
+
+ vector<vector<int>> table(depth);
+
+ for (int i = 0; i < depth; i++)
+ for (int j = 0; j < width; j++)
+ {
+ bool pat0 = (table_raw[2*width*i + 2*j + 0] == State::S1);
+ bool pat1 = (table_raw[2*width*i + 2*j + 1] == State::S1);
+
+ if (pat0 && !pat1)
+ table.at(i).push_back(0);
+ else if (!pat0 && pat1)
+ table.at(i).push_back(1);
+ else
+ table.at(i).push_back(-1);
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> products, undef_products;
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ int undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep).at(0);
+
+ for (int i = 0; i < depth; i++)
+ {
+ std::vector<int> cmp_a, cmp_ua, cmp_b;
+
+ for (int j = 0; j < width; j++)
+ if (table.at(i).at(j) >= 0) {
+ cmp_a.push_back(a.at(j));
+ cmp_ua.push_back(undef_a.at(j));
+ cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE);
+ }
+
+ std::vector<int> masked_a = ez->vec_or(cmp_a, cmp_ua);
+ std::vector<int> masked_b = ez->vec_or(cmp_b, cmp_ua);
+
+ int masked_eq = ez->vec_eq(masked_a, masked_b);
+ int any_undef = ez->expression(ezSAT::OpOr, cmp_ua);
+
+ undef_products.push_back(ez->AND(any_undef, masked_eq));
+ products.push_back(ez->AND(ez->NOT(any_undef), masked_eq));
+ }
+
+ int yy = ez->expression(ezSAT::OpOr, products);
+ ez->SET(undef_y, ez->AND(ez->NOT(yy), ez->expression(ezSAT::OpOr, undef_products)));
+ undefGating(y, yy, undef_y);
+ }
+ else
+ {
+ std::vector<int> products;
+
+ for (int i = 0; i < depth; i++)
+ {
+ std::vector<int> cmp_a, cmp_b;
+
+ for (int j = 0; j < width; j++)
+ if (table.at(i).at(j) >= 0) {
+ cmp_a.push_back(a.at(j));
+ cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE);
+ }
+
+ products.push_back(ez->vec_eq(cmp_a, cmp_b));
+ }
+
+ ez->SET(y, ez->expression(ezSAT::OpOr, products));
+ }
+
+ return true;
+ }
+
+ if (cell->type == ID($fa))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> c = importDefSigSpec(cell->getPort(ID::C), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> x = importDefSigSpec(cell->getPort(ID::X), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> xx = model_undef ? ez->vec_var(x.size()) : x;
+
+ std::vector<int> t1 = ez->vec_xor(a, b);
+ ez->assume(ez->vec_eq(yy, ez->vec_xor(t1, c)));
+
+ std::vector<int> t2 = ez->vec_and(a, b);
+ std::vector<int> t3 = ez->vec_and(c, t1);
+ ez->assume(ez->vec_eq(xx, ez->vec_or(t2, t3)));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_c = importUndefSigSpec(cell->getPort(ID::C), timestep);
+
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> undef_x = importUndefSigSpec(cell->getPort(ID::X), timestep);
+
+ ez->assume(ez->vec_eq(undef_y, ez->vec_or(ez->vec_or(undef_a, undef_b), undef_c)));
+ ez->assume(ez->vec_eq(undef_x, undef_y));
+
+ undefGating(y, yy, undef_y);
+ undefGating(x, xx, undef_x);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($lcu))
+ {
+ std::vector<int> p = importDefSigSpec(cell->getPort(ID::P), timestep);
+ std::vector<int> g = importDefSigSpec(cell->getPort(ID::G), timestep);
+ std::vector<int> ci = importDefSigSpec(cell->getPort(ID::CI), timestep);
+ std::vector<int> co = importDefSigSpec(cell->getPort(ID::CO), timestep);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(co.size()) : co;
+
+ for (int i = 0; i < GetSize(co); i++)
+ ez->SET(yy[i], ez->OR(g[i], ez->AND(p[i], i ? yy[i-1] : ci[0])));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_p = importUndefSigSpec(cell->getPort(ID::P), timestep);
+ std::vector<int> undef_g = importUndefSigSpec(cell->getPort(ID::G), timestep);
+ std::vector<int> undef_ci = importUndefSigSpec(cell->getPort(ID::CI), timestep);
+ std::vector<int> undef_co = importUndefSigSpec(cell->getPort(ID::CO), timestep);
+
+ int undef_any_p = ez->expression(ezSAT::OpOr, undef_p);
+ int undef_any_g = ez->expression(ezSAT::OpOr, undef_g);
+ int undef_any_ci = ez->expression(ezSAT::OpOr, undef_ci);
+ int undef_co_bit = ez->OR(undef_any_p, undef_any_g, undef_any_ci);
+
+ std::vector<int> undef_co_bits(undef_co.size(), undef_co_bit);
+ ez->assume(ez->vec_eq(undef_co_bits, undef_co));
+
+ undefGating(co, yy, undef_co);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($alu))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> x = importDefSigSpec(cell->getPort(ID::X), timestep);
+ std::vector<int> ci = importDefSigSpec(cell->getPort(ID::CI), timestep);
+ std::vector<int> bi = importDefSigSpec(cell->getPort(ID::BI), timestep);
+ std::vector<int> co = importDefSigSpec(cell->getPort(ID::CO), timestep);
+
+ extendSignalWidth(a, b, y, cell);
+ extendSignalWidth(a, b, x, cell);
+ extendSignalWidth(a, b, co, cell);
+
+ std::vector<int> def_y = model_undef ? ez->vec_var(y.size()) : y;
+ std::vector<int> def_x = model_undef ? ez->vec_var(x.size()) : x;
+ std::vector<int> def_co = model_undef ? ez->vec_var(co.size()) : co;
+
+ log_assert(GetSize(y) == GetSize(x));
+ log_assert(GetSize(y) == GetSize(co));
+ log_assert(GetSize(ci) == 1);
+ log_assert(GetSize(bi) == 1);
+
+ for (int i = 0; i < GetSize(y); i++)
+ {
+ int s1 = a.at(i), s2 = ez->XOR(b.at(i), bi.at(0)), s3 = i ? co.at(i-1) : ci.at(0);
+ ez->SET(def_x.at(i), ez->XOR(s1, s2));
+ ez->SET(def_y.at(i), ez->XOR(def_x.at(i), s3));
+ ez->SET(def_co.at(i), ez->OR(ez->AND(s1, s2), ez->AND(s1, s3), ez->AND(s2, s3)));
+ }
+
+ if (model_undef)
+ {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
+ std::vector<int> undef_ci = importUndefSigSpec(cell->getPort(ID::CI), timestep);
+ std::vector<int> undef_bi = importUndefSigSpec(cell->getPort(ID::BI), timestep);
+
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ std::vector<int> undef_x = importUndefSigSpec(cell->getPort(ID::X), timestep);
+ std::vector<int> undef_co = importUndefSigSpec(cell->getPort(ID::CO), timestep);
+
+ extendSignalWidth(undef_a, undef_b, undef_y, cell);
+ extendSignalWidth(undef_a, undef_b, undef_x, cell);
+ extendSignalWidth(undef_a, undef_b, undef_co, cell);
+
+ std::vector<int> all_inputs_undef;
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_a.begin(), undef_a.end());
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_b.begin(), undef_b.end());
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_ci.begin(), undef_ci.end());
+ all_inputs_undef.insert(all_inputs_undef.end(), undef_bi.begin(), undef_bi.end());
+ int undef_any = ez->expression(ezSAT::OpOr, all_inputs_undef);
+
+ for (int i = 0; i < GetSize(undef_y); i++) {
+ ez->SET(undef_y.at(i), undef_any);
+ ez->SET(undef_x.at(i), ez->OR(undef_a.at(i), undef_b.at(i), undef_bi.at(0)));
+ ez->SET(undef_co.at(i), undef_any);
+ }
+
+ undefGating(y, def_y, undef_y);
+ undefGating(x, def_x, undef_x);
+ undefGating(co, def_co, undef_co);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($slice))
+ {
+ RTLIL::SigSpec a = cell->getPort(ID::A);
+ RTLIL::SigSpec y = cell->getPort(ID::Y);
+ ez->assume(signals_eq(a.extract(cell->parameters.at(ID::OFFSET).as_int(), y.size()), y, timestep));
+ return true;
+ }
+
+ if (cell->type == ID($concat))
+ {
+ RTLIL::SigSpec a = cell->getPort(ID::A);
+ RTLIL::SigSpec b = cell->getPort(ID::B);
+ RTLIL::SigSpec y = cell->getPort(ID::Y);
+
+ RTLIL::SigSpec ab = a;
+ ab.append(b);
+
+ ez->assume(signals_eq(ab, y, timestep));
+ return true;
+ }
+
+ if (timestep > 0 && RTLIL::builtin_ff_cell_types().count(cell->type))
+ {
+ FfData ff(nullptr, cell);
+
+ // Latches and FFs with async inputs are not supported — use clk2fflogic or async2sync first.
+ if (!ff.has_d || ff.has_arst || ff.has_sr || (ff.has_en && !ff.has_clk))
+ return false;
+
+ if (timestep == 1)
+ {
+ initial_state.add((*sigmap)(cell->getPort(ID::Q)));
+ }
+ else
+ {
+ std::vector<int> d = importDefSigSpec(cell->getPort(ID::D), timestep-1);
+ std::vector<int> undef_d;
+ if (model_undef)
+ undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1);
+ if (ff.has_srst && ff.has_en && ff.ce_over_srst) {
+ int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0);
+ std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1);
+ int undef_srst;
+ std::vector<int> undef_rval;
+ if (model_undef) {
+ undef_srst = importUndefSigSpec(ff.sig_srst, timestep-1).at(0);
+ undef_rval = importUndefSigSpec(ff.val_srst, timestep-1);
+ }
+ if (ff.pol_srst)
+ std::tie(d, undef_d) = mux(srst, undef_srst, d, undef_d, rval, undef_rval);
+ else
+ std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d);
+ }
+ if (ff.has_en) {
+ int en = importDefSigSpec(ff.sig_en, timestep-1).at(0);
+ std::vector<int> old_q = importDefSigSpec(ff.sig_q, timestep-1);
+ int undef_en;
+ std::vector<int> undef_old_q;
+ if (model_undef) {
+ undef_en = importUndefSigSpec(ff.sig_en, timestep-1).at(0);
+ undef_old_q = importUndefSigSpec(ff.sig_q, timestep-1);
+ }
+ if (ff.pol_en)
+ std::tie(d, undef_d) = mux(en, undef_en, old_q, undef_old_q, d, undef_d);
+ else
+ std::tie(d, undef_d) = mux(en, undef_en, d, undef_d, old_q, undef_old_q);
+ }
+ if (ff.has_srst && !(ff.has_en && ff.ce_over_srst)) {
+ int srst = importDefSigSpec(ff.sig_srst, timestep-1).at(0);
+ std::vector<int> rval = importDefSigSpec(ff.val_srst, timestep-1);
+ int undef_srst;
+ std::vector<int> undef_rval;
+ if (model_undef) {
+ undef_srst = importUndefSigSpec(ff.sig_srst, timestep-1).at(0);
+ undef_rval = importUndefSigSpec(ff.val_srst, timestep-1);
+ }
+ if (ff.pol_srst)
+ std::tie(d, undef_d) = mux(srst, undef_srst, d, undef_d, rval, undef_rval);
+ else
+ std::tie(d, undef_d) = mux(srst, undef_srst, rval, undef_rval, d, undef_d);
+ }
+ std::vector<int> q = importDefSigSpec(cell->getPort(ID::Q), timestep);
+
+ std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q;
+ ez->assume(ez->vec_eq(d, qq));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Q), timestep);
+
+ ez->assume(ez->vec_eq(undef_d, undef_q));
+ undefGating(q, qq, undef_q);
+ }
+ }
+ return true;
+ }
+
+ if (cell->type == ID($anyconst))
+ {
+ if (timestep < 2)
+ return true;
+
+ std::vector<int> d = importDefSigSpec(cell->getPort(ID::Y), timestep-1);
+ std::vector<int> q = importDefSigSpec(cell->getPort(ID::Y), timestep);
+
+ std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q;
+ ez->assume(ez->vec_eq(d, qq));
+
+ if (model_undef)
+ {
+ std::vector<int> undef_d = importUndefSigSpec(cell->getPort(ID::Y), timestep-1);
+ std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+
+ ez->assume(ez->vec_eq(undef_d, undef_q));
+ undefGating(q, qq, undef_q);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($anyseq))
+ {
+ return true;
+ }
+
+ if (cell->type.in(ID($_BUF_), ID($equiv)))
+ {
+ std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidthUnary(a, y, cell);
+
+ std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
+ ez->assume(ez->vec_eq(a, yy));
+
+ if (model_undef) {
+ std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ extendSignalWidthUnary(undef_a, undef_y, cell, false);
+ ez->assume(ez->vec_eq(undef_a, undef_y));
+ undefGating(y, yy, undef_y);
+ }
+ return true;
+ }
+
+ if (cell->type == ID($initstate))
+ {
+ auto key = make_pair(prefix, timestep);
+ if (initstates.count(key) == 0)
+ initstates[key] = false;
+
+ std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
+ log_assert(GetSize(y) == 1);
+ ez->SET(y[0], initstates[key] ? ez->CONST_TRUE : ez->CONST_FALSE);
+
+ if (model_undef) {
+ std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
+ log_assert(GetSize(undef_y) == 1);
+ ez->SET(undef_y[0], ez->CONST_FALSE);
+ }
+
+ return true;
+ }
+
+ if (cell->type == ID($assert))
+ {
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ asserts_a[pf].append((*sigmap)(cell->getPort(ID::A)));
+ asserts_en[pf].append((*sigmap)(cell->getPort(ID::EN)));
+ return true;
+ }
+
+ if (cell->type == ID($assume))
+ {
+ std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
+ assumes_a[pf].append((*sigmap)(cell->getPort(ID::A)));
+ assumes_en[pf].append((*sigmap)(cell->getPort(ID::EN)));
+ return true;
+ }
+
+ // Unsupported internal cell types: $pow $fsm $mem*
+ // .. and all sequential cells with asynchronous inputs
+ return false;
+}
diff --git a/kernel/satgen.h b/kernel/satgen.h
index 3929a8708..cf2db733f 100644
--- a/kernel/satgen.h
+++ b/kernel/satgen.h
@@ -262,6 +262,18 @@ struct SatGen
}
}
+ std::pair<std::vector<int>, std::vector<int>> mux(int s, int undef_s, const std::vector<int> &a, const std::vector<int> &undef_a, const std::vector<int> &b, const std::vector<int> &undef_b) {
+ std::vector<int> res;
+ std::vector<int> undef_res;
+ res = ez->vec_ite(s, b, a);
+ if (model_undef) {
+ std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a, b));
+ std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a, undef_b));
+ undef_res = ez->vec_ite(undef_s, undef_ab, ez->vec_ite(s, undef_b, undef_a));
+ }
+ return std::make_pair(res, undef_res);
+ }
+
void undefGating(int y, int yy, int undef)
{
ez->assume(ez->OR(undef, ez->IFF(y, yy)));
@@ -274,1171 +286,7 @@ struct SatGen
initstates[key] = true;
}
- bool importCell(RTLIL::Cell *cell, int timestep = -1)
- {
- bool arith_undef_handled = false;
- bool is_arith_compare = cell->type.in(ID($lt), ID($le), ID($ge), ID($gt));
-
- if (model_undef && (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor)) || is_arith_compare))
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- if (is_arith_compare)
- extendSignalWidth(undef_a, undef_b, cell, true);
- else
- extendSignalWidth(undef_a, undef_b, undef_y, cell, true);
-
- int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
- int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
- int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
-
- if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
- std::vector<int> b = importSigSpec(cell->getPort(ID::B), timestep);
- undef_y_bit = ez->OR(undef_y_bit, ez->NOT(ez->expression(ezSAT::OpOr, b)));
- }
-
- if (is_arith_compare) {
- for (size_t i = 1; i < undef_y.size(); i++)
- ez->SET(ez->CONST_FALSE, undef_y.at(i));
- ez->SET(undef_y_bit, undef_y.at(0));
- } else {
- std::vector<int> undef_y_bits(undef_y.size(), undef_y_bit);
- ez->assume(ez->vec_eq(undef_y_bits, undef_y));
- }
-
- arith_undef_handled = true;
- }
-
- if (cell->type.in(ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_),
- ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($sub)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(a, b, y, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- if (cell->type.in(ID($and), ID($_AND_)))
- ez->assume(ez->vec_eq(ez->vec_and(a, b), yy));
- if (cell->type == ID($_NAND_))
- ez->assume(ez->vec_eq(ez->vec_not(ez->vec_and(a, b)), yy));
- if (cell->type.in(ID($or), ID($_OR_)))
- ez->assume(ez->vec_eq(ez->vec_or(a, b), yy));
- if (cell->type == ID($_NOR_))
- ez->assume(ez->vec_eq(ez->vec_not(ez->vec_or(a, b)), yy));
- if (cell->type.in(ID($xor), ID($_XOR_)))
- ez->assume(ez->vec_eq(ez->vec_xor(a, b), yy));
- if (cell->type.in(ID($xnor), ID($_XNOR_)))
- ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(a, b)), yy));
- if (cell->type == ID($_ANDNOT_))
- ez->assume(ez->vec_eq(ez->vec_and(a, ez->vec_not(b)), yy));
- if (cell->type == ID($_ORNOT_))
- ez->assume(ez->vec_eq(ez->vec_or(a, ez->vec_not(b)), yy));
- if (cell->type == ID($add))
- ez->assume(ez->vec_eq(ez->vec_add(a, b), yy));
- if (cell->type == ID($sub))
- ez->assume(ez->vec_eq(ez->vec_sub(a, b), yy));
-
- if (model_undef && !arith_undef_handled)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(undef_a, undef_b, undef_y, cell, false);
-
- if (cell->type.in(ID($and), ID($_AND_), ID($_NAND_))) {
- std::vector<int> a0 = ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a));
- std::vector<int> b0 = ez->vec_and(ez->vec_not(b), ez->vec_not(undef_b));
- std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a0, b0)));
- ez->assume(ez->vec_eq(yX, undef_y));
- }
- else if (cell->type.in(ID($or), ID($_OR_), ID($_NOR_))) {
- std::vector<int> a1 = ez->vec_and(a, ez->vec_not(undef_a));
- std::vector<int> b1 = ez->vec_and(b, ez->vec_not(undef_b));
- std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a1, b1)));
- ez->assume(ez->vec_eq(yX, undef_y));
- }
- else if (cell->type.in(ID($xor), ID($xnor), ID($_XOR_), ID($_XNOR_))) {
- std::vector<int> yX = ez->vec_or(undef_a, undef_b);
- ez->assume(ez->vec_eq(yX, undef_y));
- }
- else if (cell->type == ID($_ANDNOT_)) {
- std::vector<int> a0 = ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a));
- std::vector<int> b1 = ez->vec_and(b, ez->vec_not(undef_b));
- std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a0, b1)));
- ez->assume(ez->vec_eq(yX, undef_y));
- }
-
- else if (cell->type == ID($_ORNOT_)) {
- std::vector<int> a1 = ez->vec_and(a, ez->vec_not(undef_a));
- std::vector<int> b0 = ez->vec_and(ez->vec_not(b), ez->vec_not(undef_b));
- std::vector<int> yX = ez->vec_and(ez->vec_or(undef_a, undef_b), ez->vec_not(ez->vec_or(a1, b0)));
- ez->assume(ez->vec_eq(yX, undef_y));
- }
- else
- log_abort();
-
- undefGating(y, yy, undef_y);
- }
- else if (model_undef)
- {
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_)))
- {
- bool aoi_mode = cell->type.in(ID($_AOI3_), ID($_AOI4_));
- bool three_mode = cell->type.in(ID($_AOI3_), ID($_OAI3_));
-
- int a = importDefSigSpec(cell->getPort(ID::A), timestep).at(0);
- int b = importDefSigSpec(cell->getPort(ID::B), timestep).at(0);
- int c = importDefSigSpec(cell->getPort(ID::C), timestep).at(0);
- int d = three_mode ? (aoi_mode ? ez->CONST_TRUE : ez->CONST_FALSE) : importDefSigSpec(cell->getPort(ID::D), timestep).at(0);
- int y = importDefSigSpec(cell->getPort(ID::Y), timestep).at(0);
- int yy = model_undef ? ez->literal() : y;
-
- if (cell->type.in(ID($_AOI3_), ID($_AOI4_)))
- ez->assume(ez->IFF(ez->NOT(ez->OR(ez->AND(a, b), ez->AND(c, d))), yy));
- else
- ez->assume(ez->IFF(ez->NOT(ez->AND(ez->OR(a, b), ez->OR(c, d))), yy));
-
- if (model_undef)
- {
- int undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep).at(0);
- int undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep).at(0);
- int undef_c = importUndefSigSpec(cell->getPort(ID::C), timestep).at(0);
- int undef_d = three_mode ? ez->CONST_FALSE : importUndefSigSpec(cell->getPort(ID::D), timestep).at(0);
- int undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep).at(0);
-
- if (aoi_mode)
- {
- int a0 = ez->AND(ez->NOT(a), ez->NOT(undef_a));
- int b0 = ez->AND(ez->NOT(b), ez->NOT(undef_b));
- int c0 = ez->AND(ez->NOT(c), ez->NOT(undef_c));
- int d0 = ez->AND(ez->NOT(d), ez->NOT(undef_d));
-
- int ab = ez->AND(a, b), cd = ez->AND(c, d);
- int undef_ab = ez->AND(ez->OR(undef_a, undef_b), ez->NOT(ez->OR(a0, b0)));
- int undef_cd = ez->AND(ez->OR(undef_c, undef_d), ez->NOT(ez->OR(c0, d0)));
-
- int ab1 = ez->AND(ab, ez->NOT(undef_ab));
- int cd1 = ez->AND(cd, ez->NOT(undef_cd));
- int yX = ez->AND(ez->OR(undef_ab, undef_cd), ez->NOT(ez->OR(ab1, cd1)));
-
- ez->assume(ez->IFF(yX, undef_y));
- }
- else
- {
- int a1 = ez->AND(a, ez->NOT(undef_a));
- int b1 = ez->AND(b, ez->NOT(undef_b));
- int c1 = ez->AND(c, ez->NOT(undef_c));
- int d1 = ez->AND(d, ez->NOT(undef_d));
-
- int ab = ez->OR(a, b), cd = ez->OR(c, d);
- int undef_ab = ez->AND(ez->OR(undef_a, undef_b), ez->NOT(ez->OR(a1, b1)));
- int undef_cd = ez->AND(ez->OR(undef_c, undef_d), ez->NOT(ez->OR(c1, d1)));
-
- int ab0 = ez->AND(ez->NOT(ab), ez->NOT(undef_ab));
- int cd0 = ez->AND(ez->NOT(cd), ez->NOT(undef_cd));
- int yX = ez->AND(ez->OR(undef_ab, undef_cd), ez->NOT(ez->OR(ab0, cd0)));
-
- ez->assume(ez->IFF(yX, undef_y));
- }
-
- undefGating(y, yy, undef_y);
- }
-
- return true;
- }
-
- if (cell->type.in(ID($_NOT_), ID($not)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidthUnary(a, y, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
- ez->assume(ez->vec_eq(ez->vec_not(a), yy));
-
- if (model_undef) {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidthUnary(undef_a, undef_y, cell, false);
- ez->assume(ez->vec_eq(undef_a, undef_y));
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type.in(ID($_MUX_), ID($mux), ID($_NMUX_)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
- if (cell->type == ID($_NMUX_))
- ez->assume(ez->vec_eq(ez->vec_not(ez->vec_ite(s.at(0), b, a)), yy));
- else
- ez->assume(ez->vec_eq(ez->vec_ite(s.at(0), b, a), yy));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> unequal_ab = ez->vec_not(ez->vec_iff(a, b));
- std::vector<int> undef_ab = ez->vec_or(unequal_ab, ez->vec_or(undef_a, undef_b));
- std::vector<int> yX = ez->vec_ite(undef_s.at(0), undef_ab, ez->vec_ite(s.at(0), undef_b, undef_a));
- ez->assume(ez->vec_eq(yX, undef_y));
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type == ID($pmux))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> s = importDefSigSpec(cell->getPort(ID::S), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- std::vector<int> tmp = a;
- for (size_t i = 0; i < s.size(); i++) {
- std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size());
- tmp = ez->vec_ite(s.at(i), part_of_b, tmp);
- }
- ez->assume(ez->vec_eq(tmp, yy));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_s = importUndefSigSpec(cell->getPort(ID::S), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
-
- int maybe_a = ez->CONST_TRUE;
-
- std::vector<int> bits_set = std::vector<int>(undef_y.size(), ez->CONST_FALSE);
- std::vector<int> bits_clr = std::vector<int>(undef_y.size(), ez->CONST_FALSE);
-
- for (size_t i = 0; i < s.size(); i++)
- {
- std::vector<int> part_of_b(b.begin()+i*a.size(), b.begin()+(i+1)*a.size());
- std::vector<int> part_of_undef_b(undef_b.begin()+i*a.size(), undef_b.begin()+(i+1)*a.size());
-
- int maybe_s = ez->OR(s.at(i), undef_s.at(i));
- int sure_s = ez->AND(s.at(i), ez->NOT(undef_s.at(i)));
-
- maybe_a = ez->AND(maybe_a, ez->NOT(sure_s));
-
- bits_set = ez->vec_ite(maybe_s, ez->vec_or(bits_set, ez->vec_or(part_of_b, part_of_undef_b)), bits_set);
- bits_clr = ez->vec_ite(maybe_s, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(part_of_b), part_of_undef_b)), bits_clr);
- }
-
- bits_set = ez->vec_ite(maybe_a, ez->vec_or(bits_set, ez->vec_or(bits_set, ez->vec_or(a, undef_a))), bits_set);
- bits_clr = ez->vec_ite(maybe_a, ez->vec_or(bits_clr, ez->vec_or(bits_clr, ez->vec_or(ez->vec_not(a), undef_a))), bits_clr);
-
- ez->assume(ez->vec_eq(ez->vec_not(ez->vec_xor(bits_set, bits_clr)), undef_y));
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type.in(ID($pos), ID($neg)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidthUnary(a, y, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- if (cell->type == ID($pos)) {
- ez->assume(ez->vec_eq(a, yy));
- } else {
- std::vector<int> zero(a.size(), ez->CONST_FALSE);
- ez->assume(ez->vec_eq(ez->vec_sub(zero, a), yy));
- }
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidthUnary(undef_a, undef_y, cell);
-
- if (cell->type == ID($pos)) {
- ez->assume(ez->vec_eq(undef_a, undef_y));
- } else {
- int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
- std::vector<int> undef_y_bits(undef_y.size(), undef_any_a);
- ez->assume(ez->vec_eq(undef_y_bits, undef_y));
- }
-
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- if (cell->type == ID($reduce_and))
- ez->SET(ez->expression(ez->OpAnd, a), yy.at(0));
- if (cell->type.in(ID($reduce_or), ID($reduce_bool)))
- ez->SET(ez->expression(ez->OpOr, a), yy.at(0));
- if (cell->type == ID($reduce_xor))
- ez->SET(ez->expression(ez->OpXor, a), yy.at(0));
- if (cell->type == ID($reduce_xnor))
- ez->SET(ez->NOT(ez->expression(ez->OpXor, a)), yy.at(0));
- if (cell->type == ID($logic_not))
- ez->SET(ez->NOT(ez->expression(ez->OpOr, a)), yy.at(0));
- for (size_t i = 1; i < y.size(); i++)
- ez->SET(ez->CONST_FALSE, yy.at(i));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- int aX = ez->expression(ezSAT::OpOr, undef_a);
-
- if (cell->type == ID($reduce_and)) {
- int a0 = ez->expression(ezSAT::OpOr, ez->vec_and(ez->vec_not(a), ez->vec_not(undef_a)));
- ez->assume(ez->IFF(ez->AND(ez->NOT(a0), aX), undef_y.at(0)));
- }
- else if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not))) {
- int a1 = ez->expression(ezSAT::OpOr, ez->vec_and(a, ez->vec_not(undef_a)));
- ez->assume(ez->IFF(ez->AND(ez->NOT(a1), aX), undef_y.at(0)));
- }
- else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) {
- ez->assume(ez->IFF(aX, undef_y.at(0)));
- } else
- log_abort();
-
- for (size_t i = 1; i < undef_y.size(); i++)
- ez->SET(ez->CONST_FALSE, undef_y.at(i));
-
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type.in(ID($logic_and), ID($logic_or)))
- {
- std::vector<int> vec_a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> vec_b = importDefSigSpec(cell->getPort(ID::B), timestep);
-
- int a = ez->expression(ez->OpOr, vec_a);
- int b = ez->expression(ez->OpOr, vec_b);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- if (cell->type == ID($logic_and))
- ez->SET(ez->expression(ez->OpAnd, a, b), yy.at(0));
- else
- ez->SET(ez->expression(ez->OpOr, a, b), yy.at(0));
- for (size_t i = 1; i < y.size(); i++)
- ez->SET(ez->CONST_FALSE, yy.at(i));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
-
- int a0 = ez->NOT(ez->OR(ez->expression(ezSAT::OpOr, vec_a), ez->expression(ezSAT::OpOr, undef_a)));
- int b0 = ez->NOT(ez->OR(ez->expression(ezSAT::OpOr, vec_b), ez->expression(ezSAT::OpOr, undef_b)));
- int a1 = ez->expression(ezSAT::OpOr, ez->vec_and(vec_a, ez->vec_not(undef_a)));
- int b1 = ez->expression(ezSAT::OpOr, ez->vec_and(vec_b, ez->vec_not(undef_b)));
- int aX = ez->expression(ezSAT::OpOr, undef_a);
- int bX = ez->expression(ezSAT::OpOr, undef_b);
-
- if (cell->type == ID($logic_and))
- ez->SET(ez->AND(ez->OR(aX, bX), ez->NOT(ez->AND(a1, b1)), ez->NOT(a0), ez->NOT(b0)), undef_y.at(0));
- else if (cell->type == ID($logic_or))
- ez->SET(ez->AND(ez->OR(aX, bX), ez->NOT(ez->AND(a0, b0)), ez->NOT(a1), ez->NOT(b1)), undef_y.at(0));
- else
- log_abort();
-
- for (size_t i = 1; i < undef_y.size(); i++)
- ez->SET(ez->CONST_FALSE, undef_y.at(i));
-
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type.in(ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt)))
- {
- bool is_signed = cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool();
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(a, b, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- if (model_undef && cell->type.in(ID($eqx), ID($nex))) {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- extendSignalWidth(undef_a, undef_b, cell, true);
- a = ez->vec_or(a, undef_a);
- b = ez->vec_or(b, undef_b);
- }
-
- if (cell->type == ID($lt))
- ez->SET(is_signed ? ez->vec_lt_signed(a, b) : ez->vec_lt_unsigned(a, b), yy.at(0));
- if (cell->type == ID($le))
- ez->SET(is_signed ? ez->vec_le_signed(a, b) : ez->vec_le_unsigned(a, b), yy.at(0));
- if (cell->type.in(ID($eq), ID($eqx)))
- ez->SET(ez->vec_eq(a, b), yy.at(0));
- if (cell->type.in(ID($ne), ID($nex)))
- ez->SET(ez->vec_ne(a, b), yy.at(0));
- if (cell->type == ID($ge))
- ez->SET(is_signed ? ez->vec_ge_signed(a, b) : ez->vec_ge_unsigned(a, b), yy.at(0));
- if (cell->type == ID($gt))
- ez->SET(is_signed ? ez->vec_gt_signed(a, b) : ez->vec_gt_unsigned(a, b), yy.at(0));
- for (size_t i = 1; i < y.size(); i++)
- ez->SET(ez->CONST_FALSE, yy.at(i));
-
- if (model_undef && cell->type.in(ID($eqx), ID($nex)))
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(undef_a, undef_b, cell, true);
-
- if (cell->type == ID($eqx))
- yy.at(0) = ez->AND(yy.at(0), ez->vec_eq(undef_a, undef_b));
- else
- yy.at(0) = ez->OR(yy.at(0), ez->vec_ne(undef_a, undef_b));
-
- for (size_t i = 0; i < y.size(); i++)
- ez->SET(ez->CONST_FALSE, undef_y.at(i));
-
- ez->assume(ez->vec_eq(y, yy));
- }
- else if (model_undef && cell->type.in(ID($eq), ID($ne)))
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(undef_a, undef_b, cell, true);
-
- int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
- int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
- int undef_any = ez->OR(undef_any_a, undef_any_b);
-
- std::vector<int> masked_a_bits = ez->vec_or(a, ez->vec_or(undef_a, undef_b));
- std::vector<int> masked_b_bits = ez->vec_or(b, ez->vec_or(undef_a, undef_b));
-
- int masked_ne = ez->vec_ne(masked_a_bits, masked_b_bits);
- int undef_y_bit = ez->AND(undef_any, ez->NOT(masked_ne));
-
- for (size_t i = 1; i < undef_y.size(); i++)
- ez->SET(ez->CONST_FALSE, undef_y.at(i));
- ez->SET(undef_y_bit, undef_y.at(0));
-
- undefGating(y, yy, undef_y);
- }
- else
- {
- if (model_undef) {
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- undefGating(y, yy, undef_y);
- }
- log_assert(!model_undef || arith_undef_handled);
- }
- return true;
- }
-
- if (cell->type.in(ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- int extend_bit = ez->CONST_FALSE;
-
- if (!cell->type.in(ID($shift), ID($shiftx)) && cell->parameters[ID::A_SIGNED].as_bool())
- extend_bit = a.back();
-
- while (y.size() < a.size())
- y.push_back(ez->literal());
- while (y.size() > a.size())
- a.push_back(extend_bit);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
- std::vector<int> shifted_a;
-
- if (cell->type.in( ID($shl), ID($sshl)))
- shifted_a = ez->vec_shift_left(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type == ID($shr))
- shifted_a = ez->vec_shift_right(a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type == ID($sshr))
- shifted_a = ez->vec_shift_right(a, b, false, cell->parameters[ID::A_SIGNED].as_bool() ? a.back() : ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type.in(ID($shift), ID($shiftx)))
- shifted_a = ez->vec_shift_right(a, b, cell->parameters[ID::B_SIGNED].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE);
-
- ez->assume(ez->vec_eq(shifted_a, yy));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- std::vector<int> undef_a_shifted;
-
- extend_bit = cell->type == ID($shiftx) ? ez->CONST_TRUE : ez->CONST_FALSE;
- if (!cell->type.in(ID($shift), ID($shiftx)) && cell->parameters[ID::A_SIGNED].as_bool())
- extend_bit = undef_a.back();
-
- while (undef_y.size() < undef_a.size())
- undef_y.push_back(ez->literal());
- while (undef_y.size() > undef_a.size())
- undef_a.push_back(extend_bit);
-
- if (cell->type.in(ID($shl), ID($sshl)))
- undef_a_shifted = ez->vec_shift_left(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type == ID($shr))
- undef_a_shifted = ez->vec_shift_right(undef_a, b, false, ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type == ID($sshr))
- undef_a_shifted = ez->vec_shift_right(undef_a, b, false, cell->parameters[ID::A_SIGNED].as_bool() ? undef_a.back() : ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type == ID($shift))
- undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters[ID::B_SIGNED].as_bool(), ez->CONST_FALSE, ez->CONST_FALSE);
-
- if (cell->type == ID($shiftx))
- undef_a_shifted = ez->vec_shift_right(undef_a, b, cell->parameters[ID::B_SIGNED].as_bool(), ez->CONST_TRUE, ez->CONST_TRUE);
-
- int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
- std::vector<int> undef_all_y_bits(undef_y.size(), undef_any_b);
- ez->assume(ez->vec_eq(ez->vec_or(undef_a_shifted, undef_all_y_bits), undef_y));
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type == ID($mul))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(a, b, y, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- std::vector<int> tmp(a.size(), ez->CONST_FALSE);
- for (int i = 0; i < int(a.size()); i++)
- {
- std::vector<int> shifted_a(a.size(), ez->CONST_FALSE);
- for (int j = i; j < int(a.size()); j++)
- shifted_a.at(j) = a.at(j-i);
- tmp = ez->vec_ite(b.at(i), ez->vec_add(tmp, shifted_a), tmp);
- }
- ez->assume(ez->vec_eq(tmp, yy));
-
- if (model_undef) {
- log_assert(arith_undef_handled);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type == ID($macc))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- Macc macc;
- macc.from_cell(cell);
-
- std::vector<int> tmp(GetSize(y), ez->CONST_FALSE);
-
- for (auto &port : macc.ports)
- {
- std::vector<int> in_a = importDefSigSpec(port.in_a, timestep);
- std::vector<int> in_b = importDefSigSpec(port.in_b, timestep);
-
- while (GetSize(in_a) < GetSize(y))
- in_a.push_back(port.is_signed && !in_a.empty() ? in_a.back() : ez->CONST_FALSE);
- in_a.resize(GetSize(y));
-
- if (GetSize(in_b))
- {
- while (GetSize(in_b) < GetSize(y))
- in_b.push_back(port.is_signed && !in_b.empty() ? in_b.back() : ez->CONST_FALSE);
- in_b.resize(GetSize(y));
-
- for (int i = 0; i < GetSize(in_b); i++) {
- std::vector<int> shifted_a(in_a.size(), ez->CONST_FALSE);
- for (int j = i; j < int(in_a.size()); j++)
- shifted_a.at(j) = in_a.at(j-i);
- if (port.do_subtract)
- tmp = ez->vec_ite(in_b.at(i), ez->vec_sub(tmp, shifted_a), tmp);
- else
- tmp = ez->vec_ite(in_b.at(i), ez->vec_add(tmp, shifted_a), tmp);
- }
- }
- else
- {
- if (port.do_subtract)
- tmp = ez->vec_sub(tmp, in_a);
- else
- tmp = ez->vec_add(tmp, in_a);
- }
- }
-
- for (int i = 0; i < GetSize(b); i++) {
- std::vector<int> val(GetSize(y), ez->CONST_FALSE);
- val.at(0) = b.at(i);
- tmp = ez->vec_add(tmp, val);
- }
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
-
- int undef_any_a = ez->expression(ezSAT::OpOr, undef_a);
- int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
-
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- ez->assume(ez->vec_eq(undef_y, std::vector<int>(GetSize(y), ez->OR(undef_any_a, undef_any_b))));
-
- undefGating(y, tmp, undef_y);
- }
- else
- ez->assume(ez->vec_eq(y, tmp));
-
- return true;
- }
-
- if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidth(a, b, y, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
-
- std::vector<int> a_u, b_u;
- if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
- a_u = ez->vec_ite(a.back(), ez->vec_neg(a), a);
- b_u = ez->vec_ite(b.back(), ez->vec_neg(b), b);
- } else {
- a_u = a;
- b_u = b;
- }
-
- std::vector<int> chain_buf = a_u;
- std::vector<int> y_u(a_u.size(), ez->CONST_FALSE);
- for (int i = int(a.size())-1; i >= 0; i--)
- {
- chain_buf.insert(chain_buf.end(), chain_buf.size(), ez->CONST_FALSE);
-
- std::vector<int> b_shl(i, ez->CONST_FALSE);
- b_shl.insert(b_shl.end(), b_u.begin(), b_u.end());
- b_shl.insert(b_shl.end(), chain_buf.size()-b_shl.size(), ez->CONST_FALSE);
-
- y_u.at(i) = ez->vec_ge_unsigned(chain_buf, b_shl);
- chain_buf = ez->vec_ite(y_u.at(i), ez->vec_sub(chain_buf, b_shl), chain_buf);
-
- chain_buf.erase(chain_buf.begin() + a_u.size(), chain_buf.end());
- }
-
- std::vector<int> y_tmp = ignore_div_by_zero ? yy : ez->vec_var(y.size());
-
- // modulo calculation
- std::vector<int> modulo_trunc;
- int floored_eq_trunc;
- if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
- modulo_trunc = ez->vec_ite(a.back(), ez->vec_neg(chain_buf), chain_buf);
- // floor == trunc when sgn(a) == sgn(b) or trunc == 0
- floored_eq_trunc = ez->OR(ez->IFF(a.back(), b.back()), ez->NOT(ez->expression(ezSAT::OpOr, modulo_trunc)));
- } else {
- modulo_trunc = chain_buf;
- floored_eq_trunc = ez->CONST_TRUE;
- }
-
- if (cell->type == ID($div)) {
- if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
- ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(ez->XOR(a.back(), b.back()), ez->vec_neg(y_u), y_u)));
- else
- ez->assume(ez->vec_eq(y_tmp, y_u));
- } else if (cell->type == ID($mod)) {
- ez->assume(ez->vec_eq(y_tmp, modulo_trunc));
- } else if (cell->type == ID($divfloor)) {
- if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
- ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(
- ez->XOR(a.back(), b.back()),
- ez->vec_neg(ez->vec_ite(
- ez->vec_reduce_or(modulo_trunc),
- ez->vec_add(y_u, ez->vec_const_unsigned(1, y_u.size())),
- y_u
- )),
- y_u
- )));
- else
- ez->assume(ez->vec_eq(y_tmp, y_u));
- } else if (cell->type == ID($modfloor)) {
- ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(floored_eq_trunc, modulo_trunc, ez->vec_add(modulo_trunc, b))));
- }
-
- if (ignore_div_by_zero) {
- ez->assume(ez->expression(ezSAT::OpOr, b));
- } else {
- std::vector<int> div_zero_result;
- if (cell->type.in(ID($div), ID($divfloor))) {
- if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
- std::vector<int> all_ones(y.size(), ez->CONST_TRUE);
- std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
- only_first_one.at(0) = ez->CONST_TRUE;
- div_zero_result = ez->vec_ite(a.back(), only_first_one, all_ones);
- } else {
- div_zero_result.insert(div_zero_result.end(), cell->getPort(ID::A).size(), ez->CONST_TRUE);
- div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
- }
- } else if (cell->type.in(ID($mod), ID($modfloor))) {
- // a mod 0 = a
- int copy_a_bits = min(cell->getPort(ID::A).size(), cell->getPort(ID::B).size());
- div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits);
- if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
- div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), div_zero_result.back());
- else
- div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
- }
- ez->assume(ez->vec_eq(yy, ez->vec_ite(ez->expression(ezSAT::OpOr, b), y_tmp, div_zero_result)));
- }
-
- if (model_undef) {
- log_assert(arith_undef_handled);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type == ID($lut))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> lut;
- for (auto bit : cell->getParam(ID::LUT).bits)
- lut.push_back(bit == State::S1 ? ez->CONST_TRUE : ez->CONST_FALSE);
- while (GetSize(lut) < (1 << GetSize(a)))
- lut.push_back(ez->CONST_FALSE);
- lut.resize(1 << GetSize(a));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> t(lut), u(GetSize(t), ez->CONST_FALSE);
-
- for (int i = GetSize(a)-1; i >= 0; i--)
- {
- std::vector<int> t0(t.begin(), t.begin() + GetSize(t)/2);
- std::vector<int> t1(t.begin() + GetSize(t)/2, t.end());
-
- std::vector<int> u0(u.begin(), u.begin() + GetSize(u)/2);
- std::vector<int> u1(u.begin() + GetSize(u)/2, u.end());
-
- t = ez->vec_ite(a[i], t1, t0);
- u = ez->vec_ite(undef_a[i], ez->vec_or(ez->vec_xor(t0, t1), ez->vec_or(u0, u1)), ez->vec_ite(a[i], u1, u0));
- }
-
- log_assert(GetSize(t) == 1);
- log_assert(GetSize(u) == 1);
- undefGating(y, t, u);
- ez->assume(ez->vec_eq(importUndefSigSpec(cell->getPort(ID::Y), timestep), u));
- }
- else
- {
- std::vector<int> t = lut;
- for (int i = GetSize(a)-1; i >= 0; i--)
- {
- std::vector<int> t0(t.begin(), t.begin() + GetSize(t)/2);
- std::vector<int> t1(t.begin() + GetSize(t)/2, t.end());
- t = ez->vec_ite(a[i], t1, t0);
- }
-
- log_assert(GetSize(t) == 1);
- ez->assume(ez->vec_eq(y, t));
- }
- return true;
- }
-
- if (cell->type == ID($sop))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- int y = importDefSigSpec(cell->getPort(ID::Y), timestep).at(0);
-
- int width = cell->getParam(ID::WIDTH).as_int();
- int depth = cell->getParam(ID::DEPTH).as_int();
-
- vector<State> table_raw = cell->getParam(ID::TABLE).bits;
- while (GetSize(table_raw) < 2*width*depth)
- table_raw.push_back(State::S0);
-
- vector<vector<int>> table(depth);
-
- for (int i = 0; i < depth; i++)
- for (int j = 0; j < width; j++)
- {
- bool pat0 = (table_raw[2*width*i + 2*j + 0] == State::S1);
- bool pat1 = (table_raw[2*width*i + 2*j + 1] == State::S1);
-
- if (pat0 && !pat1)
- table.at(i).push_back(0);
- else if (!pat0 && pat1)
- table.at(i).push_back(1);
- else
- table.at(i).push_back(-1);
- }
-
- if (model_undef)
- {
- std::vector<int> products, undef_products;
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- int undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep).at(0);
-
- for (int i = 0; i < depth; i++)
- {
- std::vector<int> cmp_a, cmp_ua, cmp_b;
-
- for (int j = 0; j < width; j++)
- if (table.at(i).at(j) >= 0) {
- cmp_a.push_back(a.at(j));
- cmp_ua.push_back(undef_a.at(j));
- cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE);
- }
-
- std::vector<int> masked_a = ez->vec_or(cmp_a, cmp_ua);
- std::vector<int> masked_b = ez->vec_or(cmp_b, cmp_ua);
-
- int masked_eq = ez->vec_eq(masked_a, masked_b);
- int any_undef = ez->expression(ezSAT::OpOr, cmp_ua);
-
- undef_products.push_back(ez->AND(any_undef, masked_eq));
- products.push_back(ez->AND(ez->NOT(any_undef), masked_eq));
- }
-
- int yy = ez->expression(ezSAT::OpOr, products);
- ez->SET(undef_y, ez->AND(ez->NOT(yy), ez->expression(ezSAT::OpOr, undef_products)));
- undefGating(y, yy, undef_y);
- }
- else
- {
- std::vector<int> products;
-
- for (int i = 0; i < depth; i++)
- {
- std::vector<int> cmp_a, cmp_b;
-
- for (int j = 0; j < width; j++)
- if (table.at(i).at(j) >= 0) {
- cmp_a.push_back(a.at(j));
- cmp_b.push_back(table.at(i).at(j) ? ez->CONST_TRUE : ez->CONST_FALSE);
- }
-
- products.push_back(ez->vec_eq(cmp_a, cmp_b));
- }
-
- ez->SET(y, ez->expression(ezSAT::OpOr, products));
- }
-
- return true;
- }
-
- if (cell->type == ID($fa))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> c = importDefSigSpec(cell->getPort(ID::C), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- std::vector<int> x = importDefSigSpec(cell->getPort(ID::X), timestep);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
- std::vector<int> xx = model_undef ? ez->vec_var(x.size()) : x;
-
- std::vector<int> t1 = ez->vec_xor(a, b);
- ez->assume(ez->vec_eq(yy, ez->vec_xor(t1, c)));
-
- std::vector<int> t2 = ez->vec_and(a, b);
- std::vector<int> t3 = ez->vec_and(c, t1);
- ez->assume(ez->vec_eq(xx, ez->vec_or(t2, t3)));
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_c = importUndefSigSpec(cell->getPort(ID::C), timestep);
-
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- std::vector<int> undef_x = importUndefSigSpec(cell->getPort(ID::X), timestep);
-
- ez->assume(ez->vec_eq(undef_y, ez->vec_or(ez->vec_or(undef_a, undef_b), undef_c)));
- ez->assume(ez->vec_eq(undef_x, undef_y));
-
- undefGating(y, yy, undef_y);
- undefGating(x, xx, undef_x);
- }
- return true;
- }
-
- if (cell->type == ID($lcu))
- {
- std::vector<int> p = importDefSigSpec(cell->getPort(ID::P), timestep);
- std::vector<int> g = importDefSigSpec(cell->getPort(ID::G), timestep);
- std::vector<int> ci = importDefSigSpec(cell->getPort(ID::CI), timestep);
- std::vector<int> co = importDefSigSpec(cell->getPort(ID::CO), timestep);
-
- std::vector<int> yy = model_undef ? ez->vec_var(co.size()) : co;
-
- for (int i = 0; i < GetSize(co); i++)
- ez->SET(yy[i], ez->OR(g[i], ez->AND(p[i], i ? yy[i-1] : ci[0])));
-
- if (model_undef)
- {
- std::vector<int> undef_p = importUndefSigSpec(cell->getPort(ID::P), timestep);
- std::vector<int> undef_g = importUndefSigSpec(cell->getPort(ID::G), timestep);
- std::vector<int> undef_ci = importUndefSigSpec(cell->getPort(ID::CI), timestep);
- std::vector<int> undef_co = importUndefSigSpec(cell->getPort(ID::CO), timestep);
-
- int undef_any_p = ez->expression(ezSAT::OpOr, undef_p);
- int undef_any_g = ez->expression(ezSAT::OpOr, undef_g);
- int undef_any_ci = ez->expression(ezSAT::OpOr, undef_ci);
- int undef_co_bit = ez->OR(undef_any_p, undef_any_g, undef_any_ci);
-
- std::vector<int> undef_co_bits(undef_co.size(), undef_co_bit);
- ez->assume(ez->vec_eq(undef_co_bits, undef_co));
-
- undefGating(co, yy, undef_co);
- }
- return true;
- }
-
- if (cell->type == ID($alu))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- std::vector<int> x = importDefSigSpec(cell->getPort(ID::X), timestep);
- std::vector<int> ci = importDefSigSpec(cell->getPort(ID::CI), timestep);
- std::vector<int> bi = importDefSigSpec(cell->getPort(ID::BI), timestep);
- std::vector<int> co = importDefSigSpec(cell->getPort(ID::CO), timestep);
-
- extendSignalWidth(a, b, y, cell);
- extendSignalWidth(a, b, x, cell);
- extendSignalWidth(a, b, co, cell);
-
- std::vector<int> def_y = model_undef ? ez->vec_var(y.size()) : y;
- std::vector<int> def_x = model_undef ? ez->vec_var(x.size()) : x;
- std::vector<int> def_co = model_undef ? ez->vec_var(co.size()) : co;
-
- log_assert(GetSize(y) == GetSize(x));
- log_assert(GetSize(y) == GetSize(co));
- log_assert(GetSize(ci) == 1);
- log_assert(GetSize(bi) == 1);
-
- for (int i = 0; i < GetSize(y); i++)
- {
- int s1 = a.at(i), s2 = ez->XOR(b.at(i), bi.at(0)), s3 = i ? co.at(i-1) : ci.at(0);
- ez->SET(def_x.at(i), ez->XOR(s1, s2));
- ez->SET(def_y.at(i), ez->XOR(def_x.at(i), s3));
- ez->SET(def_co.at(i), ez->OR(ez->AND(s1, s2), ez->AND(s1, s3), ez->AND(s2, s3)));
- }
-
- if (model_undef)
- {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
- std::vector<int> undef_ci = importUndefSigSpec(cell->getPort(ID::CI), timestep);
- std::vector<int> undef_bi = importUndefSigSpec(cell->getPort(ID::BI), timestep);
-
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- std::vector<int> undef_x = importUndefSigSpec(cell->getPort(ID::X), timestep);
- std::vector<int> undef_co = importUndefSigSpec(cell->getPort(ID::CO), timestep);
-
- extendSignalWidth(undef_a, undef_b, undef_y, cell);
- extendSignalWidth(undef_a, undef_b, undef_x, cell);
- extendSignalWidth(undef_a, undef_b, undef_co, cell);
-
- std::vector<int> all_inputs_undef;
- all_inputs_undef.insert(all_inputs_undef.end(), undef_a.begin(), undef_a.end());
- all_inputs_undef.insert(all_inputs_undef.end(), undef_b.begin(), undef_b.end());
- all_inputs_undef.insert(all_inputs_undef.end(), undef_ci.begin(), undef_ci.end());
- all_inputs_undef.insert(all_inputs_undef.end(), undef_bi.begin(), undef_bi.end());
- int undef_any = ez->expression(ezSAT::OpOr, all_inputs_undef);
-
- for (int i = 0; i < GetSize(undef_y); i++) {
- ez->SET(undef_y.at(i), undef_any);
- ez->SET(undef_x.at(i), ez->OR(undef_a.at(i), undef_b.at(i), undef_bi.at(0)));
- ez->SET(undef_co.at(i), undef_any);
- }
-
- undefGating(y, def_y, undef_y);
- undefGating(x, def_x, undef_x);
- undefGating(co, def_co, undef_co);
- }
- return true;
- }
-
- if (cell->type == ID($slice))
- {
- RTLIL::SigSpec a = cell->getPort(ID::A);
- RTLIL::SigSpec y = cell->getPort(ID::Y);
- ez->assume(signals_eq(a.extract(cell->parameters.at(ID::OFFSET).as_int(), y.size()), y, timestep));
- return true;
- }
-
- if (cell->type == ID($concat))
- {
- RTLIL::SigSpec a = cell->getPort(ID::A);
- RTLIL::SigSpec b = cell->getPort(ID::B);
- RTLIL::SigSpec y = cell->getPort(ID::Y);
-
- RTLIL::SigSpec ab = a;
- ab.append(b);
-
- ez->assume(signals_eq(ab, y, timestep));
- return true;
- }
-
- if (timestep > 0 && cell->type.in(ID($ff), ID($dff), ID($_FF_), ID($_DFF_N_), ID($_DFF_P_)))
- {
- if (timestep == 1)
- {
- initial_state.add((*sigmap)(cell->getPort(ID::Q)));
- }
- else
- {
- std::vector<int> d = importDefSigSpec(cell->getPort(ID::D), timestep-1);
- std::vector<int> q = importDefSigSpec(cell->getPort(ID::Q), timestep);
-
- std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q;
- ez->assume(ez->vec_eq(d, qq));
-
- if (model_undef)
- {
- std::vector<int> undef_d = importUndefSigSpec(cell->getPort(ID::D), timestep-1);
- std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Q), timestep);
-
- ez->assume(ez->vec_eq(undef_d, undef_q));
- undefGating(q, qq, undef_q);
- }
- }
- return true;
- }
-
- if (cell->type == ID($anyconst))
- {
- if (timestep < 2)
- return true;
-
- std::vector<int> d = importDefSigSpec(cell->getPort(ID::Y), timestep-1);
- std::vector<int> q = importDefSigSpec(cell->getPort(ID::Y), timestep);
-
- std::vector<int> qq = model_undef ? ez->vec_var(q.size()) : q;
- ez->assume(ez->vec_eq(d, qq));
-
- if (model_undef)
- {
- std::vector<int> undef_d = importUndefSigSpec(cell->getPort(ID::Y), timestep-1);
- std::vector<int> undef_q = importUndefSigSpec(cell->getPort(ID::Y), timestep);
-
- ez->assume(ez->vec_eq(undef_d, undef_q));
- undefGating(q, qq, undef_q);
- }
- return true;
- }
-
- if (cell->type == ID($anyseq))
- {
- return true;
- }
-
- if (cell->type.in(ID($_BUF_), ID($equiv)))
- {
- std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidthUnary(a, y, cell);
-
- std::vector<int> yy = model_undef ? ez->vec_var(y.size()) : y;
- ez->assume(ez->vec_eq(a, yy));
-
- if (model_undef) {
- std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- extendSignalWidthUnary(undef_a, undef_y, cell, false);
- ez->assume(ez->vec_eq(undef_a, undef_y));
- undefGating(y, yy, undef_y);
- }
- return true;
- }
-
- if (cell->type == ID($initstate))
- {
- auto key = make_pair(prefix, timestep);
- if (initstates.count(key) == 0)
- initstates[key] = false;
-
- std::vector<int> y = importDefSigSpec(cell->getPort(ID::Y), timestep);
- log_assert(GetSize(y) == 1);
- ez->SET(y[0], initstates[key] ? ez->CONST_TRUE : ez->CONST_FALSE);
-
- if (model_undef) {
- std::vector<int> undef_y = importUndefSigSpec(cell->getPort(ID::Y), timestep);
- log_assert(GetSize(undef_y) == 1);
- ez->SET(undef_y[0], ez->CONST_FALSE);
- }
-
- return true;
- }
-
- if (cell->type == ID($assert))
- {
- std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
- asserts_a[pf].append((*sigmap)(cell->getPort(ID::A)));
- asserts_en[pf].append((*sigmap)(cell->getPort(ID::EN)));
- return true;
- }
-
- if (cell->type == ID($assume))
- {
- std::string pf = prefix + (timestep == -1 ? "" : stringf("@%d:", timestep));
- assumes_a[pf].append((*sigmap)(cell->getPort(ID::A)));
- assumes_en[pf].append((*sigmap)(cell->getPort(ID::EN)));
- return true;
- }
-
- // Unsupported internal cell types: $pow $lut
- // .. and all sequential cells except $dff and $_DFF_[NP]_
- return false;
- }
+ bool importCell(RTLIL::Cell *cell, int timestep = -1);
};
YOSYS_NAMESPACE_END
diff --git a/passes/equiv/equiv_induct.cc b/passes/equiv/equiv_induct.cc
index 596c938fc..37aec50cd 100644
--- a/passes/equiv/equiv_induct.cc
+++ b/passes/equiv/equiv_induct.cc
@@ -65,8 +65,10 @@ struct EquivInductWorker
int ez_a = satgen.importSigBit(bit_a, step);
int ez_b = satgen.importSigBit(bit_b, step);
int cond = ez->IFF(ez_a, ez_b);
- if (satgen.model_undef)
+ if (satgen.model_undef) {
+ cond = ez->AND(cond, ez->NOT(satgen.importUndefSigBit(bit_b, step)));
cond = ez->OR(cond, satgen.importUndefSigBit(bit_a, step));
+ }
ez_equal_terms.push_back(cond);
}
}
diff --git a/passes/memory/memory_dff.cc b/passes/memory/memory_dff.cc
index 947cf7b35..68023fd11 100644
--- a/passes/memory/memory_dff.cc
+++ b/passes/memory/memory_dff.cc
@@ -20,6 +20,7 @@
#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -34,22 +35,14 @@ struct MemoryDffWorker
dict<SigBit, int> sigbit_users_count;
dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
pool<Cell*> forward_merged_dffs, candidate_dffs;
- pool<SigBit> init_bits;
+ FfInitVals initvals;
MemoryDffWorker(Module *module) : module(module), sigmap(module)
{
- for (auto wire : module->wires()) {
- if (wire->attributes.count(ID::init) == 0)
- continue;
- SigSpec sig = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- init_bits.insert(sig[i]);
- }
+ initvals.set(&sigmap, module);
}
- bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
+ bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity)
{
sigmap.apply(sig);
@@ -58,42 +51,134 @@ struct MemoryDffWorker
if (bit.wire == NULL)
continue;
- if (!after && init_bits.count(sigmap(bit)))
+ if (initvals(bit) != State::Sx)
return false;
for (auto cell : dff_cells)
{
- if (after && forward_merged_dffs.count(cell))
+ SigSpec this_clk = cell->getPort(ID::CLK);
+ bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
+
+ if (invbits.count(this_clk)) {
+ this_clk = invbits.at(this_clk);
+ this_clk_polarity = !this_clk_polarity;
+ }
+
+ if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
+ if (this_clk != clk)
+ continue;
+ if (this_clk_polarity != clk_polarity)
+ continue;
+ }
+
+ RTLIL::SigSpec q_norm = cell->getPort(ID::Q);
+ sigmap.apply(q_norm);
+
+ RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D));
+ if (d.size() != 1)
+ continue;
+
+ if (cell->type == ID($sdffce)) {
+ SigSpec rval = cell->parameters[ID::SRST_VALUE];
+ SigSpec rbit = q_norm.extract(bit, &rval);
+ if (cell->parameters[ID::SRST_POLARITY].as_bool())
+ d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
+ else
+ d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
+ }
+
+ if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) {
+ if (cell->parameters[ID::EN_POLARITY].as_bool())
+ d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN));
+ else
+ d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN));
+ }
+
+ if (cell->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec rval = cell->parameters[ID::SRST_VALUE];
+ SigSpec rbit = q_norm.extract(bit, &rval);
+ if (cell->parameters[ID::SRST_POLARITY].as_bool())
+ d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
+ else
+ d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
+ }
+
+ bit = d;
+ clk = this_clk;
+ clk_polarity = this_clk_polarity;
+ candidate_dffs.insert(cell);
+ goto replaced_this_bit;
+ }
+
+ return false;
+ replaced_this_bit:;
+ }
+
+ return true;
+ }
+
+ bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity)
+ {
+ sigmap.apply(sig);
+
+ for (auto &bit : sig)
+ {
+ if (bit.wire == NULL)
+ continue;
+
+ for (auto cell : dff_cells)
+ {
+ if (forward_merged_dffs.count(cell))
+ continue;
+ if (!cell->type.in(ID($dff), ID($dffe)))
continue;
SigSpec this_clk = cell->getPort(ID::CLK);
bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
+ SigSpec this_en = State::S1;
+ bool this_en_polarity = true;
+
+ if (cell->type == ID($dffe)) {
+ this_en = cell->getPort(ID::EN);
+ this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool();
+ }
if (invbits.count(this_clk)) {
this_clk = invbits.at(this_clk);
this_clk_polarity = !this_clk_polarity;
}
+ if (invbits.count(this_en)) {
+ this_en = invbits.at(this_en);
+ this_en_polarity = !this_en_polarity;
+ }
+
if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
if (this_clk != clk)
continue;
if (this_clk_polarity != clk_polarity)
continue;
+ if (this_en != en)
+ continue;
+ if (this_en_polarity != en_polarity)
+ continue;
}
- RTLIL::SigSpec q_norm = cell->getPort(after ? ID::D : ID::Q);
+ RTLIL::SigSpec q_norm = cell->getPort(ID::D);
sigmap.apply(q_norm);
- RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? ID::Q : ID::D));
+ RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q));
if (d.size() != 1)
continue;
- if (after && init_bits.count(d))
+ if (initvals(d) != State::Sx)
return false;
bit = d;
clk = this_clk;
clk_polarity = this_clk_polarity;
+ en = this_en;
+ en_polarity = this_en_polarity;
candidate_dffs.insert(cell);
goto replaced_this_bit;
}
@@ -161,7 +246,7 @@ struct MemoryDffWorker
RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());
for (auto cell : module->cells())
- if (cell->type == ID($dff)) {
+ if (cell->type.in(ID($dff), ID($dffe))) {
RTLIL::SigSpec new_q = cell->getPort(ID::Q);
new_q.replace(sig, new_sig);
cell->setPort(ID::Q, new_q);
@@ -173,8 +258,10 @@ struct MemoryDffWorker
log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());
bool clk_polarity = 0;
+ bool en_polarity = 0;
RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
+ RTLIL::SigSpec en_data;
RTLIL::SigSpec sig_data = cell->getPort(ID::DATA);
for (auto bit : sigmap(sig_data))
@@ -198,9 +285,14 @@ struct MemoryDffWorker
if (sigbit_users_count[bit] > 1)
goto skip_ff_after_read_merging;
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
+ if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) &&
std::all_of(check_q.begin(), check_q.end(), [&](const SigSpec &cq) {return cq == sig_data; }))
{
+ if (en_data != State::S1 || !en_polarity) {
+ if (!en_polarity)
+ en_data = module->LogicNot(NEW_ID, en_data);
+ en.append(en_data);
+ }
disconnect_dff(sig_data);
cell->setPort(ID::CLK, clk_data);
cell->setPort(ID::EN, en.size() > 1 ? module->ReduceAnd(NEW_ID, en) : en);
@@ -214,11 +306,13 @@ struct MemoryDffWorker
}
else
{
- if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
+ if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
{
+ if (!en_polarity)
+ en_data = module->LogicNot(NEW_ID, en_data);
disconnect_dff(sig_data);
cell->setPort(ID::CLK, clk_data);
- cell->setPort(ID::EN, State::S1);
+ cell->setPort(ID::EN, en_data);
cell->setPort(ID::DATA, sig_data);
cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
@@ -256,7 +350,7 @@ struct MemoryDffWorker
}
for (auto cell : module->cells()) {
- if (cell->type == ID($dff))
+ if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)))
dff_cells.push_back(cell);
if (cell->type == ID($mux)) {
mux_cells_a[sigmap(cell->getPort(ID::A))] = cell;
diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc
index 3133927bb..64fee76b3 100644
--- a/passes/opt/Makefile.inc
+++ b/passes/opt/Makefile.inc
@@ -5,6 +5,7 @@ OBJS += passes/opt/opt_mem.o
OBJS += passes/opt/opt_muxtree.o
OBJS += passes/opt/opt_reduce.o
OBJS += passes/opt/opt_rmdff.o
+OBJS += passes/opt/opt_dff.o
OBJS += passes/opt/opt_share.o
OBJS += passes/opt/opt_clean.o
OBJS += passes/opt/opt_expr.o
diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc
index 8be94e345..4b052d9a2 100644
--- a/passes/opt/opt.cc
+++ b/passes/opt/opt.cc
@@ -37,7 +37,7 @@ struct OptPass : public Pass {
log("a series of trivial optimizations and cleanups. This pass executes the other\n");
log("passes in the following order:\n");
log("\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all] -nomux\n");
log("\n");
log(" do\n");
@@ -45,19 +45,19 @@ struct OptPass : public Pass {
log(" opt_reduce [-fine] [-full]\n");
log(" opt_merge [-share_all]\n");
log(" opt_share (-full only)\n");
- log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n");
log(" opt_clean [-purge]\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" while <changed design>\n");
log("\n");
log("When called with -fast the following script is used instead:\n");
log("\n");
log(" do\n");
- log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-clkinv] [-fine] [-full] [-keepdc]\n");
+ log(" opt_expr [-mux_undef] [-mux_bool] [-undriven] [-noclkinv] [-fine] [-full] [-keepdc]\n");
log(" opt_merge [-share_all]\n");
- log(" opt_rmdff [-keepdc] [-sat] (except when called with -noff)\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] (except when called with -noff)\n");
log(" opt_clean [-purge]\n");
- log(" while <changed design in opt_rmdff>\n");
+ log(" while <changed design in opt_dff>\n");
log("\n");
log("Note: Options in square brackets (such as [-keepdc]) are passed through to\n");
log("the opt_* commands when given to 'opt'.\n");
@@ -70,7 +70,7 @@ struct OptPass : public Pass {
std::string opt_expr_args;
std::string opt_reduce_args;
std::string opt_merge_args;
- std::string opt_rmdff_args;
+ std::string opt_dff_args;
bool opt_share = false;
bool fast_mode = false;
bool noff_mode = false;
@@ -96,8 +96,8 @@ struct OptPass : public Pass {
opt_expr_args += " -undriven";
continue;
}
- if (args[argidx] == "-clkinv") {
- opt_expr_args += " -clkinv";
+ if (args[argidx] == "-noclkinv") {
+ opt_expr_args += " -noclkinv";
continue;
}
if (args[argidx] == "-fine") {
@@ -113,11 +113,19 @@ struct OptPass : public Pass {
}
if (args[argidx] == "-keepdc") {
opt_expr_args += " -keepdc";
- opt_rmdff_args += " -keepdc";
+ opt_dff_args += " -keepdc";
+ continue;
+ }
+ if (args[argidx] == "-nodffe") {
+ opt_dff_args += " -nodffe";
+ continue;
+ }
+ if (args[argidx] == "-nosdff") {
+ opt_dff_args += " -nosdff";
continue;
}
if (args[argidx] == "-sat") {
- opt_rmdff_args += " -sat";
+ opt_dff_args += " -sat";
continue;
}
if (args[argidx] == "-share_all") {
@@ -143,7 +151,7 @@ struct OptPass : public Pass {
Pass::call(design, "opt_merge" + opt_merge_args);
design->scratchpad_unset("opt.did_something");
if (!noff_mode)
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_dff" + opt_dff_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
break;
Pass::call(design, "opt_clean" + opt_clean_args);
@@ -163,7 +171,7 @@ struct OptPass : public Pass {
if (opt_share)
Pass::call(design, "opt_share");
if (!noff_mode)
- Pass::call(design, "opt_rmdff" + opt_rmdff_args);
+ Pass::call(design, "opt_dff" + opt_dff_args);
Pass::call(design, "opt_clean" + opt_clean_args);
Pass::call(design, "opt_expr" + opt_expr_args);
if (design->scratchpad_get_bool("opt.did_something") == false)
diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc
new file mode 100644
index 000000000..a47071a30
--- /dev/null
+++ b/passes/opt/opt_dff.cc
@@ -0,0 +1,875 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2020 Marcelina Kościelnicka <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/log.h"
+#include "kernel/register.h"
+#include "kernel/rtlil.h"
+#include "kernel/satgen.h"
+#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
+#include "passes/techmap/simplemap.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct OptDffOptions
+{
+ bool nosdff;
+ bool nodffe;
+ bool simple_dffe;
+ bool sat;
+ bool keepdc;
+};
+
+struct OptDffWorker
+{
+ const OptDffOptions &opt;
+
+ Module *module;
+ typedef std::pair<RTLIL::Cell*, int> cell_int_t;
+ SigMap sigmap;
+ FfInitVals initvals;
+ dict<SigBit, int> bitusers;
+ dict<SigBit, cell_int_t> bit2mux;
+ dict<SigBit, RTLIL::Cell*> bit2driver;
+
+ typedef std::map<RTLIL::SigBit, bool> pattern_t;
+ typedef std::set<pattern_t> patterns_t;
+ typedef std::pair<RTLIL::SigBit, bool> ctrl_t;
+ typedef std::set<ctrl_t> ctrls_t;
+
+ ezSatPtr ez;
+ SatGen satgen;
+ pool<Cell*> sat_cells;
+
+ // Used as a queue.
+ std::vector<Cell *> dff_cells;
+
+ OptDffWorker(const OptDffOptions &opt, Module *mod) : opt(opt), module(mod), sigmap(mod), initvals(&sigmap, mod), ez(), satgen(ez.get(), &sigmap) {
+ // Gathering three kinds of information here for every sigmapped SigBit:
+ //
+ // - bitusers: how many users it has (muxes will only be merged into FFs if this is 1, making the FF the only user)
+ // - bit2mux: the mux cell and bit index that drives it, if any
+ // - bit2driver: the cell driving it, if any
+
+ for (auto wire : module->wires())
+ {
+ if (wire->port_output)
+ for (auto bit : sigmap(wire))
+ bitusers[bit]++;
+ }
+
+ for (auto cell : module->cells()) {
+ if (cell->type.in(ID($mux), ID($pmux), ID($_MUX_))) {
+ RTLIL::SigSpec sig_y = sigmap(cell->getPort(ID::Y));
+ for (int i = 0; i < GetSize(sig_y); i++)
+ bit2mux[sig_y[i]] = cell_int_t(cell, i);
+ }
+
+ for (auto conn : cell->connections()) {
+ bool is_output = cell->output(conn.first);
+ if (is_output) {
+ for (auto bit : sigmap(conn.second))
+ bit2driver[bit] = cell;
+ }
+ if (!is_output || !cell->known()) {
+ for (auto bit : sigmap(conn.second))
+ bitusers[bit]++;
+ }
+ }
+
+ if (module->design->selected(module, cell) && RTLIL::builtin_ff_cell_types().count(cell->type))
+ dff_cells.push_back(cell);
+ }
+
+ }
+
+ std::function<void(Cell*)> sat_import_cell = [&](Cell *c) {
+ if (!sat_cells.insert(c).second)
+ return;
+ if (!satgen.importCell(c))
+ return;
+ for (auto &conn : c->connections()) {
+ if (!c->input(conn.first))
+ continue;
+ for (auto bit : sigmap(conn.second))
+ if (bit2driver.count(bit))
+ sat_import_cell(bit2driver.at(bit));
+ }
+ };
+
+ State combine_const(State a, State b) {
+ if (a == State::Sx && !opt.keepdc)
+ return b;
+ if (b == State::Sx && !opt.keepdc)
+ return a;
+ if (a == b)
+ return a;
+ return State::Sm;
+ }
+
+ patterns_t find_muxtree_feedback_patterns(RTLIL::SigBit d, RTLIL::SigBit q, pattern_t path)
+ {
+ patterns_t ret;
+
+ if (d == q) {
+ ret.insert(path);
+ return ret;
+ }
+
+ if (bit2mux.count(d) == 0 || bitusers[d] > 1)
+ return ret;
+
+ cell_int_t mbit = bit2mux.at(d);
+ RTLIL::SigSpec sig_a = sigmap(mbit.first->getPort(ID::A));
+ RTLIL::SigSpec sig_b = sigmap(mbit.first->getPort(ID::B));
+ RTLIL::SigSpec sig_s = sigmap(mbit.first->getPort(ID::S));
+ int width = GetSize(sig_a), index = mbit.second;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ if (path.count(sig_s[i]) && path.at(sig_s[i]))
+ {
+ ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::B);
+ s[i*width + index] = RTLIL::Sx;
+ mbit.first->setPort(ID::B, s);
+ }
+
+ return ret;
+ }
+
+ pattern_t path_else = path;
+
+ for (int i = 0; i < GetSize(sig_s); i++)
+ {
+ if (path.count(sig_s[i]))
+ continue;
+
+ pattern_t path_this = path;
+ path_else[sig_s[i]] = false;
+ path_this[sig_s[i]] = true;
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_b[i*width + index], q, path_this))
+ ret.insert(pat);
+
+ if (sig_b[i*width + index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::B);
+ s[i*width + index] = RTLIL::Sx;
+ mbit.first->setPort(ID::B, s);
+ }
+ }
+
+ for (auto &pat : find_muxtree_feedback_patterns(sig_a[index], q, path_else))
+ ret.insert(pat);
+
+ if (sig_a[index] == q) {
+ RTLIL::SigSpec s = mbit.first->getPort(ID::A);
+ s[index] = RTLIL::Sx;
+ mbit.first->setPort(ID::A, s);
+ }
+
+ return ret;
+ }
+
+ void simplify_patterns(patterns_t&)
+ {
+ // TBD
+ }
+
+ ctrl_t make_patterns_logic(const patterns_t &patterns, const ctrls_t &ctrls, bool make_gates)
+ {
+ if (patterns.empty() && GetSize(ctrls) == 1) {
+ return *ctrls.begin();
+ }
+
+ RTLIL::SigSpec or_input;
+
+ for (auto pat : patterns)
+ {
+ RTLIL::SigSpec s1, s2;
+ for (auto it : pat) {
+ s1.append(it.first);
+ s2.append(it.second);
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addNe(NEW_ID, s1, s2, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ or_input.append(y);
+ }
+ for (auto item : ctrls) {
+ if (item.second)
+ or_input.append(item.first);
+ else if (make_gates)
+ or_input.append(module->NotGate(NEW_ID, item.first));
+ else
+ or_input.append(module->Not(NEW_ID, item.first));
+ }
+
+ if (GetSize(or_input) == 0)
+ return ctrl_t(State::S1, true);
+
+ if (GetSize(or_input) == 1)
+ return ctrl_t(or_input, true);
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return ctrl_t(y, true);
+ }
+
+ ctrl_t combine_resets(const ctrls_t &ctrls, bool make_gates)
+ {
+ if (GetSize(ctrls) == 1) {
+ return *ctrls.begin();
+ }
+
+ RTLIL::SigSpec or_input;
+
+ bool final_pol = false;
+ for (auto item : ctrls) {
+ if (item.second)
+ final_pol = true;
+ }
+
+ for (auto item : ctrls) {
+ if (item.second == final_pol)
+ or_input.append(item.first);
+ else if (make_gates)
+ or_input.append(module->NotGate(NEW_ID, item.first));
+ else
+ or_input.append(module->Not(NEW_ID, item.first));
+ }
+
+ RTLIL::SigSpec y = module->addWire(NEW_ID);
+ RTLIL::Cell *c = final_pol ? module->addReduceOr(NEW_ID, or_input, y) : module->addReduceAnd(NEW_ID, or_input, y);
+
+ if (make_gates) {
+ simplemap(module, c);
+ module->remove(c);
+ }
+
+ return ctrl_t(y, final_pol);
+ }
+
+ bool run() {
+ // We have all the information we need, and the list of FFs to process as well. Do it.
+ bool did_something = false;
+ while (!dff_cells.empty()) {
+ Cell *cell = dff_cells.back();
+ dff_cells.pop_back();
+ // Break down the FF into pieces.
+ FfData ff(&initvals, cell);
+ bool changed = false;
+
+ if (!ff.width) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+
+ if (ff.has_sr) {
+ bool sr_removed = false;
+ std::vector<int> keep_bits;
+ // Check for always-active S/R bits.
+ for (int i = 0; i < ff.width; i++) {
+ if (ff.sig_clr[i] == (ff.pol_clr ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_clr[i] == State::Sx)) {
+ // Always-active clear — connect Q bit to 0.
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], State::S0);
+ log("Handling always-active CLR at position %d on %s (%s) from module %s (changing to const driver).\n",
+ i, log_id(cell), log_id(cell->type), log_id(module));
+ sr_removed = true;
+ } else if (ff.sig_set[i] == (ff.pol_set ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_set[i] == State::Sx)) {
+ // Always-active set — connect Q bit to 1 if clear inactive, 0 if reset active.
+ initvals.remove_init(ff.sig_q[i]);
+ if (!ff.pol_clr) {
+ module->connect(ff.sig_q[i], ff.sig_clr[i]);
+ } else if (ff.is_fine) {
+ module->addNotGate(NEW_ID, ff.sig_q[i], ff.sig_clr[i]);
+ } else {
+ module->addNot(NEW_ID, ff.sig_q[i], ff.sig_clr[i]);
+ }
+ log("Handling always-active SET at position %d on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ i, log_id(cell), log_id(cell->type), log_id(module));
+ sr_removed = true;
+ } else {
+ keep_bits.push_back(i);
+ }
+ }
+ if (sr_removed) {
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ changed = true;
+ }
+
+ if (ff.pol_clr ? ff.sig_clr.is_fully_zero() : ff.sig_clr.is_fully_ones()) {
+ // CLR is useless, try to kill it.
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++)
+ if (ff.sig_set[i] != ff.sig_set[0])
+ failed = true;
+ if (!failed) {
+ log("Removing never-active CLR on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.pol_arst = ff.pol_set;
+ ff.sig_arst = ff.sig_set[0];
+ ff.val_arst = Const(State::S1, ff.width);
+ changed = true;
+ }
+ } else if (ff.pol_set ? ff.sig_set.is_fully_zero() : ff.sig_set.is_fully_ones()) {
+ // SET is useless, try to kill it.
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++)
+ if (ff.sig_clr[i] != ff.sig_clr[0])
+ failed = true;
+ if (!failed) {
+ log("Removing never-active SET on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.pol_arst = ff.pol_clr;
+ ff.sig_arst = ff.sig_clr[0];
+ ff.val_arst = Const(State::S0, ff.width);
+ changed = true;
+ }
+ } else if (ff.pol_clr == ff.pol_set) {
+ // Try a more complex conversion to plain async reset.
+ State val_neutral = ff.pol_set ? State::S0 : State::S1;
+ Const val_arst;
+ SigSpec sig_arst;
+ if (ff.sig_clr[0] == val_neutral)
+ sig_arst = ff.sig_set[0];
+ else
+ sig_arst = ff.sig_clr[0];
+ bool failed = false;
+ for (int i = 0; i < ff.width; i++) {
+ if (ff.sig_clr[i] == sig_arst && ff.sig_set[i] == val_neutral)
+ val_arst.bits.push_back(State::S0);
+ else if (ff.sig_set[i] == sig_arst && ff.sig_clr[i] == val_neutral)
+ val_arst.bits.push_back(State::S1);
+ else
+ failed = true;
+ }
+ if (!failed) {
+ log("Converting CLR/SET to ARST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_sr = false;
+ ff.has_arst = true;
+ ff.val_arst = val_arst;
+ ff.sig_arst = sig_arst;
+ ff.pol_arst = ff.pol_clr;
+ changed = true;
+ }
+ }
+ }
+
+ if (ff.has_arst) {
+ if (ff.sig_arst == (ff.pol_arst ? State::S0 : State::S1)) {
+ // Always-inactive reset — remove.
+ log("Removing never-active ARST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_arst = false;
+ changed = true;
+ } else if (ff.sig_arst == (ff.pol_arst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_arst == State::Sx)) {
+ // Always-active async reset — change to const driver.
+ log("Handling always-active ARST on %s (%s) from module %s (changing to const driver).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ initvals.remove_init(ff.sig_q);
+ module->remove(cell);
+ module->connect(ff.sig_q, ff.val_arst);
+ did_something = true;
+ continue;
+ }
+ }
+
+ if (ff.has_srst) {
+ if (ff.sig_srst == (ff.pol_srst ? State::S0 : State::S1)) {
+ // Always-inactive reset — remove.
+ log("Removing never-active SRST on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_srst = false;
+ changed = true;
+ } else if (ff.sig_srst == (ff.pol_srst ? State::S1 : State::S0) || (!opt.keepdc && ff.sig_srst == State::Sx)) {
+ // Always-active sync reset — connect to D instead.
+ log("Handling always-active SRST on %s (%s) from module %s (changing to const D).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_srst = false;
+ if (!ff.ce_over_srst)
+ ff.has_en = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ }
+ }
+
+ if (ff.has_en) {
+ if (ff.sig_en == (ff.pol_en ? State::S0 : State::S1) || (!opt.keepdc && ff.sig_en == State::Sx)) {
+ // Always-inactive enable — remove.
+ if (ff.has_clk && ff.has_srst && !ff.ce_over_srst) {
+ log("Handling never-active EN on %s (%s) from module %s (connecting SRST instead).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ // FF with sync reset — connect the sync reset to D instead.
+ ff.pol_en = ff.pol_srst;
+ ff.sig_en = ff.sig_srst;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ } else {
+ log("Handling never-active EN on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ ff.has_d = ff.has_en = ff.has_clk = false;
+ changed = true;
+ }
+ } else if (ff.sig_en == (ff.pol_en ? State::S1 : State::S0)) {
+ // Always-active enable.
+ if (ff.has_clk) {
+ // For FF, just remove the useless enable.
+ log("Removing always-active EN on %s (%s) from module %s.\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_en = false;
+ changed = true;
+ } else {
+ // For latches, make a comb circuit, nuke the latch.
+ log("Handling always-active EN on %s (%s) from module %s (changing to combinatorial circuit).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ initvals.remove_init(ff.sig_q);
+ module->remove(cell);
+ if (ff.has_sr) {
+ SigSpec tmp;
+ if (ff.is_fine) {
+ if (ff.pol_set)
+ tmp = module->MuxGate(NEW_ID, ff.sig_d, State::S1, ff.sig_set);
+ else
+ tmp = module->MuxGate(NEW_ID, State::S1, ff.sig_d, ff.sig_set);
+ if (ff.pol_clr)
+ module->addMuxGate(NEW_ID, tmp, State::S0, ff.sig_clr, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, State::S0, tmp, ff.sig_clr, ff.sig_q);
+ } else {
+ if (ff.pol_set)
+ tmp = module->Or(NEW_ID, ff.sig_d, ff.sig_set);
+ else
+ tmp = module->Or(NEW_ID, ff.sig_d, module->Not(NEW_ID, ff.sig_set));
+ if (ff.pol_clr)
+ module->addAnd(NEW_ID, tmp, module->Not(NEW_ID, ff.sig_clr), ff.sig_q);
+ else
+ module->addAnd(NEW_ID, tmp, ff.sig_clr, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ if (ff.is_fine) {
+ if (ff.pol_arst)
+ module->addMuxGate(NEW_ID, ff.sig_d, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], ff.sig_d, ff.sig_arst, ff.sig_q);
+ } else {
+ if (ff.pol_arst)
+ module->addMux(NEW_ID, ff.sig_d, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMux(NEW_ID, ff.val_arst, ff.sig_d, ff.sig_arst, ff.sig_q);
+ }
+ } else {
+ module->connect(ff.sig_q, ff.sig_d);
+ }
+ did_something = true;
+ continue;
+ }
+ }
+ }
+
+ if (ff.has_clk) {
+ if (ff.sig_clk.is_fully_const()) {
+ // Const clock — the D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ log("Handling const CLK on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_d = ff.has_en = ff.has_clk = ff.has_srst = false;
+ changed = true;
+ }
+ }
+
+ if (ff.has_d && ff.sig_d == ff.sig_q) {
+ // Q wrapped back to D, can be removed.
+ if (ff.has_clk && ff.has_srst) {
+ // FF with sync reset — connect the sync reset to D instead.
+ log("Handling D = Q on %s (%s) from module %s (conecting SRST instead).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ if (ff.has_en && ff.ce_over_srst) {
+ if (!ff.pol_en) {
+ if (ff.is_fine)
+ ff.sig_en = module->NotGate(NEW_ID, ff.sig_en);
+ else
+ ff.sig_en = module->Not(NEW_ID, ff.sig_en);
+ }
+ if (!ff.pol_srst) {
+ if (ff.is_fine)
+ ff.sig_srst = module->NotGate(NEW_ID, ff.sig_srst);
+ else
+ ff.sig_srst = module->Not(NEW_ID, ff.sig_srst);
+ }
+ if (ff.is_fine)
+ ff.sig_en = module->AndGate(NEW_ID, ff.sig_en, ff.sig_srst);
+ else
+ ff.sig_en = module->And(NEW_ID, ff.sig_en, ff.sig_srst);
+ ff.pol_en = true;
+ } else {
+ ff.pol_en = ff.pol_srst;
+ ff.sig_en = ff.sig_srst;
+ }
+ ff.has_en = true;
+ ff.has_srst = false;
+ ff.sig_d = ff.val_d = ff.val_srst;
+ ff.d_is_const = true;
+ changed = true;
+ } else {
+ // The D input path is effectively useless, so remove it (this will be a const-input D latch, SR latch, or a const driver).
+ log("Handling D = Q on %s (%s) from module %s (removing D path).\n",
+ log_id(cell), log_id(cell->type), log_id(module));
+ ff.has_d = ff.has_en = ff.has_clk = false;
+ changed = true;
+ }
+ }
+
+ // Now check if any bit can be replaced by a constant.
+ pool<int> removed_sigbits;
+ for (int i = 0; i < ff.width; i++) {
+ State val = ff.val_init[i];
+ if (ff.has_arst)
+ val = combine_const(val, ff.val_arst[i]);
+ if (ff.has_srst)
+ val = combine_const(val, ff.val_srst[i]);
+ if (ff.has_sr) {
+ if (ff.sig_clr[i] != (ff.pol_clr ? State::S0 : State::S1))
+ val = combine_const(val, State::S0);
+ if (ff.sig_set[i] != (ff.pol_set ? State::S0 : State::S1))
+ val = combine_const(val, State::S1);
+ }
+ if (val == State::Sm)
+ continue;
+ if (ff.has_d) {
+ if (!ff.sig_d[i].wire) {
+ val = combine_const(val, ff.sig_d[i].data);
+ if (val == State::Sm)
+ continue;
+ } else {
+ if (!opt.sat)
+ continue;
+ // For each register bit, try to prove that it cannot change from the initial value. If so, remove it
+ if (!bit2driver.count(ff.sig_d[i]))
+ continue;
+ if (val != State::S0 && val != State::S1)
+ continue;
+
+ sat_import_cell(bit2driver.at(ff.sig_d[i]));
+
+ int init_sat_pi = satgen.importSigSpec(val).front();
+ int q_sat_pi = satgen.importSigBit(ff.sig_q[i]);
+ int d_sat_pi = satgen.importSigBit(ff.sig_d[i]);
+
+ // Try to find out whether the register bit can change under some circumstances
+ bool counter_example_found = ez->solve(ez->IFF(q_sat_pi, init_sat_pi), ez->NOT(ez->IFF(d_sat_pi, init_sat_pi)));
+
+ // If the register bit cannot change, we can replace it with a constant
+ if (counter_example_found)
+ continue;
+ }
+ }
+ log("Setting constant %d-bit at position %d on %s (%s) from module %s.\n", val ? 1 : 0,
+ i, log_id(cell), log_id(cell->type), log_id(module));
+
+ initvals.remove_init(ff.sig_q[i]);
+ module->connect(ff.sig_q[i], val);
+ removed_sigbits.insert(i);
+ }
+ if (!removed_sigbits.empty()) {
+ std::vector<int> keep_bits;
+ for (int i = 0; i < ff.width; i++)
+ if (!removed_sigbits.count(i))
+ keep_bits.push_back(i);
+ if (keep_bits.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ }
+ ff = ff.slice(keep_bits);
+ changed = true;
+ }
+
+ // The cell has been simplified as much as possible already. Now try to spice it up with enables / sync resets.
+ if (ff.has_clk) {
+ if (!ff.has_arst && !ff.has_sr && (!ff.has_srst || !ff.has_en || ff.ce_over_srst) && !opt.nosdff) {
+ // Try to merge sync resets.
+ std::map<ctrls_t, std::vector<int>> groups;
+ std::vector<int> remaining_indices;
+ Const val_srst;
+
+ for (int i = 0 ; i < ff.width; i++) {
+ ctrls_t resets;
+ State reset_val = State::Sx;
+ if (ff.has_srst)
+ reset_val = ff.val_srst[i];
+ while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) {
+ cell_int_t mbit = bit2mux.at(ff.sig_d[i]);
+ if (GetSize(mbit.first->getPort(ID::S)) != 1)
+ break;
+ SigBit s = mbit.first->getPort(ID::S);
+ SigBit a = mbit.first->getPort(ID::A)[mbit.second];
+ SigBit b = mbit.first->getPort(ID::B)[mbit.second];
+ // Workaround for funny memory WE pattern.
+ if ((a == State::S0 || a == State::S1) && (b == State::S0 || b == State::S1))
+ break;
+ if ((b == State::S0 || b == State::S1) && (b == reset_val || reset_val == State::Sx)) {
+ // This is better handled by CE pattern.
+ if (a == ff.sig_q[i])
+ break;
+ reset_val = b.data;
+ resets.insert(ctrl_t(s, true));
+ ff.sig_d[i] = a;
+ } else if ((a == State::S0 || a == State::S1) && (a == reset_val || reset_val == State::Sx)) {
+ // This is better handled by CE pattern.
+ if (b == ff.sig_q[i])
+ break;
+ reset_val = a.data;
+ resets.insert(ctrl_t(s, false));
+ ff.sig_d[i] = b;
+ } else {
+ break;
+ }
+ }
+
+ if (!resets.empty()) {
+ if (ff.has_srst)
+ resets.insert(ctrl_t(ff.sig_srst, ff.pol_srst));
+ groups[resets].push_back(i);
+ } else
+ remaining_indices.push_back(i);
+ val_srst.bits.push_back(reset_val);
+ }
+
+ for (auto &it : groups) {
+ FfData new_ff = ff.slice(it.second);
+ new_ff.val_srst = Const();
+ for (int i = 0; i < new_ff.width; i++) {
+ int j = it.second[i];
+ new_ff.val_srst.bits.push_back(val_srst[j]);
+ }
+ ctrl_t srst = combine_resets(it.first, ff.is_fine);
+
+ new_ff.has_srst = true;
+ new_ff.sig_srst = srst.first;
+ new_ff.pol_srst = srst.second;
+ if (new_ff.has_en)
+ new_ff.ce_over_srst = true;
+ Cell *new_cell = new_ff.emit(module, NEW_ID);
+ if (new_cell)
+ dff_cells.push_back(new_cell);
+ log("Adding SRST signal on %s (%s) from module %s (D = %s, Q = %s, rval = %s).\n",
+ log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q), log_signal(new_ff.val_srst));
+ }
+
+ if (remaining_indices.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ } else if (GetSize(remaining_indices) != ff.width) {
+ ff = ff.slice(remaining_indices);
+ changed = true;
+ }
+ }
+ if ((!ff.has_srst || !ff.has_en || !ff.ce_over_srst) && !opt.nodffe) {
+ // Try to merge enables.
+ std::map<std::pair<patterns_t, ctrls_t>, std::vector<int>> groups;
+ std::vector<int> remaining_indices;
+
+ for (int i = 0 ; i < ff.width; i++) {
+ // First, eat up as many simple muxes as possible.
+ ctrls_t enables;
+ while (bit2mux.count(ff.sig_d[i]) && bitusers[ff.sig_d[i]] == 1) {
+ cell_int_t mbit = bit2mux.at(ff.sig_d[i]);
+ if (GetSize(mbit.first->getPort(ID::S)) != 1)
+ break;
+ SigBit s = mbit.first->getPort(ID::S);
+ SigBit a = mbit.first->getPort(ID::A)[mbit.second];
+ SigBit b = mbit.first->getPort(ID::B)[mbit.second];
+ if (a == ff.sig_q[i]) {
+ enables.insert(ctrl_t(s, true));
+ ff.sig_d[i] = b;
+ } else if (b == ff.sig_q[i]) {
+ enables.insert(ctrl_t(s, false));
+ ff.sig_d[i] = a;
+ } else {
+ break;
+ }
+ }
+
+ patterns_t patterns;
+ if (!opt.simple_dffe)
+ patterns = find_muxtree_feedback_patterns(ff.sig_d[i], ff.sig_q[i], pattern_t());
+ if (!patterns.empty() || !enables.empty()) {
+ if (ff.has_en)
+ enables.insert(ctrl_t(ff.sig_en, ff.pol_en));
+ simplify_patterns(patterns);
+ groups[std::make_pair(patterns, enables)].push_back(i);
+ } else
+ remaining_indices.push_back(i);
+ }
+
+ for (auto &it : groups) {
+ FfData new_ff = ff.slice(it.second);
+ ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine);
+
+ new_ff.has_en = true;
+ new_ff.sig_en = en.first;
+ new_ff.pol_en = en.second;
+ new_ff.ce_over_srst = false;
+ Cell *new_cell = new_ff.emit(module, NEW_ID);
+ if (new_cell)
+ dff_cells.push_back(new_cell);
+ log("Adding EN signal on %s (%s) from module %s (D = %s, Q = %s).\n",
+ log_id(cell), log_id(cell->type), log_id(module), log_signal(new_ff.sig_d), log_signal(new_ff.sig_q));
+ }
+
+ if (remaining_indices.empty()) {
+ module->remove(cell);
+ did_something = true;
+ continue;
+ } else if (GetSize(remaining_indices) != ff.width) {
+ ff = ff.slice(remaining_indices);
+ changed = true;
+ }
+ }
+ }
+
+ if (changed) {
+ // Rebuild the FF.
+ IdString name = cell->name;
+ module->remove(cell);
+ ff.emit(module, name);
+ did_something = true;
+ }
+ }
+ return did_something;
+ }
+};
+
+struct OptDffPass : public Pass {
+ OptDffPass() : Pass("opt_dff", "perform DFF optimizations") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" opt_dff [-nodffe] [-nosdff] [-keepdc] [-sat] [selection]\n");
+ log("\n");
+ log("This pass converts flip-flops to a more suitable type by merging clock enables\n");
+ log("and synchronous reset multiplexers, removing unused control inputs, or potentially\n");
+ log("removes the flip-flop altogether, converting it to a constant driver.\n");
+ log("\n");
+ log(" -nodffe\n");
+ log(" disables dff -> dffe conversion, and other transforms recognizing clock enable\n");
+ log("\n");
+ log(" -nosdff\n");
+ log(" disables dff -> sdff conversion, and other transforms recognizing sync resets\n");
+ log("\n");
+ log(" -simple-dffe\n");
+ log(" only enables clock enable recognition transform for obvious cases\n");
+ log("\n");
+ log(" -sat\n");
+ log(" additionally invoke SAT solver to detect and remove flip-flops (with\n");
+ log(" non-constant inputs) that can also be replaced with a constant driver\n");
+ log("\n");
+ log(" -keepdc\n");
+ log(" some optimizations change the behavior of the circuit with respect to\n");
+ log(" don't-care bits. for example in 'a+0' a single x-bit in 'a' will cause\n");
+ log(" all result bits to be set to x. this behavior changes when 'a+0' is\n");
+ log(" replaced by 'a'. the -keepdc option disables all such optimizations.\n");
+ log("\n");
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing OPT_DFF pass (perform DFF optimizations).\n");
+ OptDffOptions opt;
+ opt.nodffe = false;
+ opt.nosdff = false;
+ opt.simple_dffe = false;
+ opt.keepdc = false;
+ opt.sat = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-nodffe") {
+ opt.nodffe = true;
+ continue;
+ }
+ if (args[argidx] == "-nosdff") {
+ opt.nosdff = true;
+ continue;
+ }
+ if (args[argidx] == "-simple-dffe") {
+ opt.simple_dffe = true;
+ continue;
+ }
+ if (args[argidx] == "-keepdc") {
+ opt.keepdc = true;
+ continue;
+ }
+ if (args[argidx] == "-sat") {
+ opt.sat = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ bool did_something = false;
+ for (auto mod : design->selected_modules()) {
+ OptDffWorker worker(opt, mod);
+ if (worker.run())
+ did_something = true;
+ }
+
+ if (did_something)
+ design->scratchpad_set_bool("opt.did_something", true);
+ }
+} OptDffPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 649ad83a6..e36e4419d 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -416,7 +416,7 @@ int get_onehot_bit_index(RTLIL::SigSpec signal)
return bit_index;
}
-void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool clkinv)
+void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv)
{
if (!design->selected(module))
return;
@@ -465,7 +465,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
#define ACTION_DO(_p_, _s_) do { cover("opt.opt_expr.action_" S__LINE__); replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0)
#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
- if (clkinv)
+ if (!noclkinv)
{
if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memwr)))
handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map);
@@ -604,7 +604,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type.in(ID($xnor), ID($_XNOR_))) {
cover("opt.opt_expr.const_xnor");
// For consistency since simplemap does $xnor -> $_XOR_ + $_NOT_
- int width = cell->getParam(ID::Y_WIDTH).as_int();
+ int width = GetSize(cell->getPort(ID::Y));
replace_cell(assign_map, module, cell, "const_xnor", ID::Y, SigSpec(RTLIL::State::S1, width));
goto next_cell;
}
@@ -2064,8 +2064,8 @@ struct OptExprPass : public Pass {
log(" -undriven\n");
log(" replace undriven nets with undef (x) constants\n");
log("\n");
- log(" -clkinv\n");
- log(" optimize clock inverters by changing FF types\n");
+ log(" -noclkinv\n");
+ log(" do not optimize clock inverters by changing FF types\n");
log("\n");
log(" -fine\n");
log(" perform fine-grain optimizations\n");
@@ -2085,7 +2085,7 @@ struct OptExprPass : public Pass {
bool mux_undef = false;
bool mux_bool = false;
bool undriven = false;
- bool clkinv = false;
+ bool noclkinv = false;
bool do_fine = false;
bool keepdc = false;
@@ -2106,8 +2106,8 @@ struct OptExprPass : public Pass {
undriven = true;
continue;
}
- if (args[argidx] == "-clkinv") {
- clkinv = true;
+ if (args[argidx] == "-noclkinv") {
+ noclkinv = true;
continue;
}
if (args[argidx] == "-fine") {
@@ -2144,12 +2144,12 @@ struct OptExprPass : public Pass {
do {
do {
did_something = false;
- replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
if (!keepdc)
- replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, clkinv);
+ replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv);
if (did_something)
design->scratchpad_set_bool("opt.did_something", true);
} while (did_something);
diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc
index 9f226e12d..f3b1fd377 100644
--- a/passes/opt/pmux2shiftx.cc
+++ b/passes/opt/pmux2shiftx.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -30,7 +31,7 @@ struct OnehotDatabase
bool verbose = false;
bool initialized = false;
- pool<SigBit> init_ones;
+ FfInitVals initvals;
dict<SigSpec, pool<SigSpec>> sig_sources_db;
dict<SigSpec, bool> sig_onehot_cache;
pool<SigSpec> recursion_guard;
@@ -44,19 +45,7 @@ struct OnehotDatabase
log_assert(!initialized);
initialized = true;
- for (auto wire : module->wires())
- {
- auto it = wire->attributes.find(ID::init);
- if (it == wire->attributes.end())
- continue;
-
- auto &val = it->second;
- int width = std::max(GetSize(wire), GetSize(val));
-
- for (int i = 0; i < width; i++)
- if (val[i] == State::S1)
- init_ones.insert(sigmap(SigBit(wire, i)));
- }
+ initvals.set(&sigmap, module);
for (auto cell : module->cells())
{
@@ -119,7 +108,7 @@ struct OnehotDatabase
bool found_init_ones = false;
for (auto bit : sig) {
- if (init_ones.count(bit)) {
+ if (initvals(bit) == State::S1) {
if (found_init_ones) {
if (verbose)
log("%*s - non-onehot init value\n", indent, "");
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 78e2bcbea..a216f36d4 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/modtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
@@ -54,8 +55,7 @@ struct WreduceWorker
std::set<Cell*, IdString::compare_ptr_by_name<Cell>> work_queue_cells;
std::set<SigBit> work_queue_bits;
pool<SigBit> keep_bits;
- dict<SigBit, State> init_bits;
- pool<SigBit> remove_init_bits;
+ FfInitVals initvals;
WreduceWorker(WreduceConfig *config, Module *module) :
config(config), module(module), mi(module) { }
@@ -145,7 +145,7 @@ struct WreduceWorker
SigSpec sig_d = mi.sigmap(cell->getPort(ID::D));
SigSpec sig_q = mi.sigmap(cell->getPort(ID::Q));
bool has_reset = false;
- Const initval, rst_value;
+ Const initval = initvals(sig_q), rst_value;
int width_before = GetSize(sig_q);
@@ -163,20 +163,12 @@ struct WreduceWorker
bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0;
bool sign_ext = !zero_ext;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sig_q[i];
- if (init_bits.count(bit))
- initval.bits.push_back(init_bits.at(bit));
- else
- initval.bits.push_back(State::Sx);
- }
-
for (int i = GetSize(sig_q)-1; i >= 0; i--)
{
if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || initval[i] == State::Sx) &&
(!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || rst_value[i] == State::Sx)) {
module->connect(sig_q[i], State::S0);
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
@@ -185,7 +177,7 @@ struct WreduceWorker
if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1] && initval[i] == initval[i-1] &&
(!has_reset || i >= GetSize(rst_value) || rst_value[i] == rst_value[i-1])) {
module->connect(sig_q[i], sig_q[i-1]);
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
continue;
@@ -195,7 +187,7 @@ struct WreduceWorker
if (info == nullptr)
return;
if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) {
- remove_init_bits.insert(sig_q[i]);
+ initvals.remove_init(sig_q[i]);
sig_d.remove(i);
sig_q.remove(i);
zero_ext = false;
@@ -409,18 +401,12 @@ struct WreduceWorker
{
// create a copy as mi.sigmap will be updated as we process the module
SigMap init_attr_sigmap = mi.sigmap;
+ initvals.set(&init_attr_sigmap, module);
for (auto w : module->wires()) {
if (w->get_bool_attribute(ID::keep))
for (auto bit : mi.sigmap(w))
keep_bits.insert(bit);
- if (w->attributes.count(ID::init)) {
- Const initval = w->attributes.at(ID::init);
- SigSpec initsig = init_attr_sigmap(w);
- int width = std::min(GetSize(initval), GetSize(initsig));
- for (int i = 0; i < width; i++)
- init_bits[initsig[i]] = initval[i];
- }
}
for (auto c : module->selected_cells())
@@ -469,22 +455,6 @@ struct WreduceWorker
module->connect(nw, SigSpec(w).extract(0, GetSize(nw)));
module->swap_names(w, nw);
}
-
- if (!remove_init_bits.empty()) {
- for (auto w : module->wires()) {
- if (w->attributes.count(ID::init)) {
- Const initval = w->attributes.at(ID::init);
- Const new_initval(State::Sx, GetSize(w));
- SigSpec initsig = init_attr_sigmap(w);
- int width = std::min(GetSize(initval), GetSize(initsig));
- for (int i = 0; i < width; i++) {
- if (!remove_init_bits.count(initsig[i]))
- new_initval[i] = initval[i];
- }
- w->attributes.at(ID::init) = new_initval;
- }
- }
- }
}
};
diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc
index fff04074b..c46f5d58f 100644
--- a/passes/pmgen/ice40_dsp.cc
+++ b/passes/pmgen/ice40_dsp.cc
@@ -31,15 +31,15 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(st.mul));
- log_debug("ffA: %s %s %s\n", log_id(st.ffA, "--"), log_id(st.ffAholdmux, "--"), log_id(st.ffArstmux, "--"));
- log_debug("ffB: %s %s %s\n", log_id(st.ffB, "--"), log_id(st.ffBholdmux, "--"), log_id(st.ffBrstmux, "--"));
- log_debug("ffCD: %s %s\n", log_id(st.ffCD, "--"), log_id(st.ffCDholdmux, "--"));
+ log_debug("ffA: %s\n", log_id(st.ffA, "--"));
+ log_debug("ffB: %s\n", log_id(st.ffB, "--"));
+ log_debug("ffCD: %s\n", log_id(st.ffCD, "--"));
log_debug("mul: %s\n", log_id(st.mul, "--"));
log_debug("ffFJKG: %s\n", log_id(st.ffFJKG, "--"));
log_debug("ffH: %s\n", log_id(st.ffH, "--"));
log_debug("add: %s\n", log_id(st.add, "--"));
log_debug("mux: %s\n", log_id(st.mux, "--"));
- log_debug("ffO: %s %s %s\n", log_id(st.ffO, "--"), log_id(st.ffOholdmux, "--"), log_id(st.ffOrstmux, "--"));
+ log_debug("ffO: %s\n", log_id(st.ffO, "--"));
log_debug("\n");
if (GetSize(st.sigA) > 16) {
@@ -97,16 +97,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setParam(ID(D_REG), st.ffCD ? State::S1 : State::S0);
SigSpec AHOLD, BHOLD, CDHOLD;
- if (st.ffAholdmux)
- AHOLD = st.ffAholdpol ? st.ffAholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffAholdmux->getPort(ID::S));
+ if (st.ffA && st.ffA->hasPort(ID::EN))
+ AHOLD = st.ffA->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffA->getPort(ID::EN)) : st.ffA->getPort(ID::EN);
else
AHOLD = State::S0;
- if (st.ffBholdmux)
- BHOLD = st.ffBholdpol ? st.ffBholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffBholdmux->getPort(ID::S));
+ if (st.ffB && st.ffB->hasPort(ID::EN))
+ BHOLD = st.ffB->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffB->getPort(ID::EN)) : st.ffB->getPort(ID::EN);
else
BHOLD = State::S0;
- if (st.ffCDholdmux)
- CDHOLD = st.ffCDholdpol ? st.ffCDholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffCDholdmux->getPort(ID::S));
+ if (st.ffCD && st.ffCD->hasPort(ID::EN))
+ CDHOLD = st.ffCD->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffCD->getPort(ID::EN)) : st.ffCD->getPort(ID::EN);
else
CDHOLD = State::S0;
cell->setPort(ID(AHOLD), AHOLD);
@@ -115,12 +115,12 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
cell->setPort(ID(DHOLD), CDHOLD);
SigSpec IRSTTOP, IRSTBOT;
- if (st.ffArstmux)
- IRSTTOP = st.ffArstpol ? st.ffArstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffArstmux->getPort(ID::S));
+ if (st.ffA && st.ffA->hasPort(ID::ARST))
+ IRSTTOP = st.ffA->getParam(ID::ARST_POLARITY).as_bool() ? st.ffA->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffA->getPort(ID::ARST));
else
IRSTTOP = State::S0;
- if (st.ffBrstmux)
- IRSTBOT = st.ffBrstpol ? st.ffBrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffBrstmux->getPort(ID::S));
+ if (st.ffB && st.ffB->hasPort(ID::ARST))
+ IRSTBOT = st.ffB->getParam(ID::ARST_POLARITY).as_bool() ? st.ffB->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffB->getPort(ID::ARST));
else
IRSTBOT = State::S0;
cell->setPort(ID(IRSTTOP), IRSTTOP);
@@ -207,16 +207,16 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
}
SigSpec OHOLD;
- if (st.ffOholdmux)
- OHOLD = st.ffOholdpol ? st.ffOholdmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOholdmux->getPort(ID::S));
+ if (st.ffO && st.ffO->hasPort(ID::EN))
+ OHOLD = st.ffO->getParam(ID::EN_POLARITY).as_bool() ? pm.module->Not(NEW_ID, st.ffO->getPort(ID::EN)) : st.ffO->getPort(ID::EN);
else
OHOLD = State::S0;
cell->setPort(ID(OHOLDTOP), OHOLD);
cell->setPort(ID(OHOLDBOT), OHOLD);
SigSpec ORST;
- if (st.ffOrstmux)
- ORST = st.ffOrstpol ? st.ffOrstmux->getPort(ID::S) : pm.module->Not(NEW_ID, st.ffOrstmux->getPort(ID::S));
+ if (st.ffO && st.ffO->hasPort(ID::ARST))
+ ORST = st.ffO->getParam(ID::ARST_POLARITY).as_bool() ? st.ffO->getPort(ID::ARST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::ARST));
else
ORST = State::S0;
cell->setPort(ID(ORSTTOP), ORST);
@@ -228,6 +228,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm)
acc_reset = st.mux->getPort(ID::S);
else
acc_reset = pm.module->Not(NEW_ID, st.mux->getPort(ID::S));
+ } else if (st.ffO && st.ffO->hasPort(ID::SRST)) {
+ acc_reset = st.ffO->getParam(ID::SRST_POLARITY).as_bool() ? st.ffO->getPort(ID::SRST) : pm.module->Not(NEW_ID, st.ffO->getPort(ID::SRST));
}
cell->setPort(ID(OLOADTOP), acc_reset);
cell->setPort(ID(OLOADBOT), acc_reset);
diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg
index 2456a49dc..7a01cbd51 100644
--- a/passes/pmgen/ice40_dsp.pmg
+++ b/passes/pmgen/ice40_dsp.pmg
@@ -6,20 +6,16 @@ state <SigSpec> sigA sigB sigCD sigH sigO
state <Cell*> add mux
state <IdString> addAB muxAB
-state <bool> ffAholdpol ffBholdpol ffCDholdpol ffOholdpol
-state <bool> ffArstpol ffBrstpol ffCDrstpol ffOrstpol
-
-state <Cell*> ffA ffAholdmux ffArstmux ffB ffBholdmux ffBrstmux ffCD ffCDholdmux
-state <Cell*> ffFJKG ffH ffO ffOholdmux ffOrstmux
+state <Cell*> ffA ffB ffCD
+state <Cell*> ffFJKG ffH ffO
// subpattern
+state <bool> argSdff
state <SigSpec> argQ argD
-state <bool> ffholdpol ffrstpol
-state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffholdmux dffrstmux
-udata <bool> dffholdpol dffrstpol dffclock_pol
+udata <Cell*> dff
+udata <bool> dffclock_pol
match mul
select mul->type.in($mul, \SB_MAC16)
@@ -64,7 +60,7 @@ code sigA sigB sigH
log_assert(nusers(O.extract_end(i)) <= 1);
endcode
-code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
+code argQ ffA sigA clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) {
argQ = sigA;
subpattern(in_dffe);
@@ -72,20 +68,12 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol
ffA = dff;
clock = dffclock;
clock_pol = dffclock_pol;
- if (dffrstmux) {
- ffArstmux = dffrstmux;
- ffArstpol = dffrstpol;
- }
- if (dffholdmux) {
- ffAholdmux = dffholdmux;
- ffAholdpol = dffholdpol;
- }
sigA = dffD;
}
}
endcode
-code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
+code argQ ffB sigB clock clock_pol
if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) {
argQ = sigB;
subpattern(in_dffe);
@@ -93,47 +81,44 @@ code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol
ffB = dff;
clock = dffclock;
clock_pol = dffclock_pol;
- if (dffrstmux) {
- ffBrstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffholdmux) {
- ffBholdmux = dffholdmux;
- ffBholdpol = dffholdpol;
- }
sigB = dffD;
}
}
endcode
-code argD ffFJKG sigH clock clock_pol
+code argD argSdff ffFJKG sigH clock clock_pol
if (nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 ||
(!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) {
argD = sigH;
+ argSdff = false;
subpattern(out_dffe);
if (dff) {
// F/J/K/G do not have a CE-like (hold) input
- if (dffholdmux)
+ if (dff->hasPort(\EN))
goto reject_ffFJKG;
// Reset signal of F/J (IRSTTOP) and K/G (IRSTBOT)
// shared with A and B
- if ((ffArstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffFJKG;
- if ((ffBrstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffFJKG;
- if (ffArstmux) {
- if (port(ffArstmux, \S) != port(dffrstmux, \S))
- goto reject_ffFJKG;
- if (ffArstpol != dffrstpol)
+ if (ffA) {
+ if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffFJKG;
+ if (ffA->hasPort(\ARST)) {
+ if (port(ffA, \ARST) != port(dff, \ARST))
+ goto reject_ffFJKG;
+ if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffFJKG;
+ }
}
- if (ffBrstmux) {
- if (port(ffBrstmux, \S) != port(dffrstmux, \S))
- goto reject_ffFJKG;
- if (ffBrstpol != dffrstpol)
+ if (ffB) {
+ if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffFJKG;
+ if (ffB->hasPort(\ARST)) {
+ if (port(ffB, \ARST) != port(dff, \ARST))
+ goto reject_ffFJKG;
+ if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffFJKG;
+ }
}
ffFJKG = dff;
@@ -146,23 +131,24 @@ reject_ffFJKG: ;
}
endcode
-code argD ffH sigH sigO clock clock_pol
+code argD argSdff ffH sigH sigO clock clock_pol
if (ffFJKG && nusers(sigH) == 2 &&
(mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) {
argD = sigH;
+ argSdff = false;
subpattern(out_dffe);
if (dff) {
// H does not have a CE-like (hold) input
- if (dffholdmux)
+ if (dff->hasPort(\EN))
goto reject_ffH;
// Reset signal of H (IRSTBOT) shared with B
- if ((ffBrstmux != NULL) != (dffrstmux != NULL))
+ if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffH;
- if (ffBrstmux) {
- if (port(ffBrstmux, \S) != port(dffrstmux, \S))
+ if (ffB->hasPort(\ARST)) {
+ if (port(ffB, \ARST) != port(dff, \ARST))
goto reject_ffH;
- if (ffBrstpol != dffrstpol)
+ if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
goto reject_ffH;
}
@@ -226,7 +212,7 @@ code sigO
sigO = port(mux, \Y);
endcode
-code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_pol cd_signed o_lo
+code argD argSdff ffO sigO sigCD clock clock_pol cd_signed o_lo
if (mul->type != \SB_MAC16 ||
// Ensure that register is not already used
((param(mul, \TOPOUTPUT_SELECT).as_int() != 1 && param(mul, \BOTOUTPUT_SELECT).as_int() != 1) &&
@@ -238,6 +224,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
// First try entire sigO
if (nusers(sigO) == 2) {
argD = sigO;
+ argSdff = !mux;
subpattern(out_dffe);
}
@@ -245,6 +232,7 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
if (!dff && GetSize(sigO) > 16) {
argD = sigO.extract(0, 16);
if (nusers(argD) == 2) {
+ argSdff = !mux;
subpattern(out_dffe);
o_lo = dff;
}
@@ -254,14 +242,6 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
ffO = dff;
clock = dffclock;
clock_pol = dffclock_pol;
- if (dffrstmux) {
- ffOrstmux = dffrstmux;
- ffOrstpol = dffrstpol;
- }
- if (dffholdmux) {
- ffOholdmux = dffholdmux;
- ffOholdpol = dffholdpol;
- }
sigO.replace(sigO.extract(0, GetSize(dffQ)), dffQ);
}
@@ -274,38 +254,43 @@ code argD ffO ffOholdmux ffOrstmux ffOholdpol ffOrstpol sigO sigCD clock clock_p
sigCD = port(mux, muxAB == \B ? \A : \B);
cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
+ } else if (dff && dff->hasPort(\SRST)) {
+ if (sigCD != sigO)
+ reject;
+ sigCD = param(dff, \SRST_VALUE);
+
+ cd_signed = add && param(add, \A_SIGNED).as_bool() && param(add, \B_SIGNED).as_bool();
}
}
endcode
-code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol
+code argQ ffCD sigCD clock clock_pol
if (!sigCD.empty() && sigCD != sigO &&
(mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) {
argQ = sigCD;
subpattern(in_dffe);
if (dff) {
- if (dffholdmux) {
- ffCDholdmux = dffholdmux;
- ffCDholdpol = dffholdpol;
- }
-
// Reset signal of C (IRSTTOP) and D (IRSTBOT)
// shared with A and B
- if ((ffArstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffCD;
- if ((ffBrstmux != NULL) != (dffrstmux != NULL))
- goto reject_ffCD;
- if (ffArstmux) {
- if (port(ffArstmux, \S) != port(dffrstmux, \S))
- goto reject_ffCD;
- if (ffArstpol != dffrstpol)
+ if (ffA) {
+ if (ffA->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffCD;
+ if (ffA->hasPort(\ARST)) {
+ if (port(ffA, \ARST) != port(dff, \ARST))
+ goto reject_ffCD;
+ if (param(ffA, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffCD;
+ }
}
- if (ffBrstmux) {
- if (port(ffBrstmux, \S) != port(dffrstmux, \S))
- goto reject_ffCD;
- if (ffBrstpol != dffrstpol)
+ if (ffB) {
+ if (ffB->hasPort(\ARST) != dff->hasPort(\ARST))
goto reject_ffCD;
+ if (ffB->hasPort(\ARST)) {
+ if (port(ffB, \ARST) != port(dff, \ARST))
+ goto reject_ffCD;
+ if (param(ffB, \ARST_POLARITY) != param(dff, \ARST_POLARITY))
+ goto reject_ffCD;
+ }
}
ffCD = dff;
@@ -347,7 +332,7 @@ code
endcode
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
@@ -357,8 +342,6 @@ match ff
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
-
- set ffoffset offset
endmatch
code argQ argD
@@ -378,72 +361,13 @@ code argQ argD
argD = port(ff, \D);
argQ = Q;
dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
}
endcode
-match ffrstmux
- if false /* TODO: ice40 resets are actually async */
-
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffholdmux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-match ffholdmux
- if !argD.empty()
- select ffholdmux->type.in($mux)
- index <SigSpec> port(ffholdmux, \Y) === argD
- choice <IdString> BA {\B, \A}
- index <SigSpec> port(ffholdmux, BA) === argQ
- define <bool> pol (BA == \B)
- set ffholdpol pol
- semioptional
-endmatch
-
-code argD
- if (ffholdmux) {
- dffholdmux = ffholdmux;
- dffholdpol = ffholdpol;
- argD = port(ffholdmux, ffholdpol ? \A : \B);
- dffD.replace(port(ffholdmux, \Y), argD);
- }
- else
- dffholdmux = nullptr;
-endcode
-
// #######################
subpattern out_dffe
-arg argD argQ clock clock_pol
+arg argD argSdff argQ clock clock_pol
code
dff = nullptr;
@@ -452,101 +376,19 @@ code
reject;
endcode
-match ffholdmux
- select ffholdmux->type.in($mux)
- // ffholdmux output must have two users: ffholdmux and ff.D
- select nusers(port(ffholdmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // keep-last-value net must have at least three users: ffholdmux, ff, downstream sink(s)
- select nusers(port(ffholdmux, BA)) >= 3
-
- slice offset GetSize(port(ffholdmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffholdmux, AB)[offset] === argD[0]
-
- // Check that the rest of argD is present
- filter GetSize(port(ffholdmux, AB)) >= offset + GetSize(argD)
- filter port(ffholdmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (BA == \B)
- set ffholdpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffholdmux = ffholdmux;
- if (ffholdmux) {
- SigSpec AB = port(ffholdmux, ffholdpol ? \A : \B);
- SigSpec Y = port(ffholdmux, \Y);
- argQ = argD;
- argD.replace(AB, Y);
- argQ.replace(AB, port(ffholdmux, ffholdpol ? \B : \A));
-
- dffholdmux = ffholdmux;
- dffholdpol = ffholdpol;
- }
-endcode
-
-match ffrstmux
- if false /* TODO: ice40 resets are actually async */
-
- select ffrstmux->type.in($mux)
- // ffrstmux output must have two users: ffrstmux and ff.D
- select nusers(port(ffrstmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- slice offset GetSize(port(ffrstmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
-
- // Check that offset is consistent
- filter !ffholdmux || ffoffset == offset
- // Check that the rest of argD is present
- filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
- filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffrstpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffrstmux = ffrstmux;
- if (ffrstmux) {
- SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
- SigSpec Y = port(ffrstmux, \Y);
- argD.replace(AB, Y);
-
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- }
-endcode
-
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffce)
// SB_MAC16 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
- // Check that offset is consistent
- filter (!ffholdmux && !ffrstmux) || ffoffset == offset
+ // Only allow sync reset if requested.
+ filter argSdff || ff->type.in($dff, $dffe)
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
- // Check that FF.Q is connected to CE-mux
- filter !ffholdmux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
-
- set ffoffset offset
endmatch
code argQ
@@ -559,10 +401,8 @@ code argQ
}
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
- if (!ffholdmux) {
- argQ = argD;
- argQ.replace(D, Q);
- }
+ argQ = argD;
+ argQ.replace(D, Q);
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, State::Sx);
@@ -575,7 +415,4 @@ code argQ
dffclock = port(ff, \CLK);
dffclock_pol = param(ff, \CLK_POLARITY).as_bool();
}
- // No enable/reset mux possible without flop
- else if (dffholdmux || dffrstmux)
- reject;
endcode
diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc
index d05157270..cf7703d36 100644
--- a/passes/pmgen/xilinx_dsp.cc
+++ b/passes/pmgen/xilinx_dsp.cc
@@ -263,17 +263,17 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
log("Analysing %s.%s for Xilinx DSP packing.\n", log_id(pm.module), log_id(st.dsp));
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
- log_debug("ffAD: %s %s %s\n", log_id(st.ffAD, "--"), log_id(st.ffADcemux, "--"), log_id(st.ffADrstmux, "--"));
- log_debug("ffA2: %s %s %s\n", log_id(st.ffA2, "--"), log_id(st.ffA2cemux, "--"), log_id(st.ffA2rstmux, "--"));
- log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
- log_debug("ffB2: %s %s %s\n", log_id(st.ffB2, "--"), log_id(st.ffB2cemux, "--"), log_id(st.ffB2rstmux, "--"));
- log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
- log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
+ log_debug("ffAD: %s\n", log_id(st.ffAD, "--"));
+ log_debug("ffA2: %s\n", log_id(st.ffA2, "--"));
+ log_debug("ffA1: %s\n", log_id(st.ffA1, "--"));
+ log_debug("ffB2: %s\n", log_id(st.ffB2, "--"));
+ log_debug("ffB1: %s\n", log_id(st.ffB1, "--"));
+ log_debug("ffD: %s\n", log_id(st.ffD, "--"));
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
- log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
+ log_debug("ffM: %s\n", log_id(st.ffM, "--"));
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
- log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
+ log_debug("ffP: %s\n", log_id(st.ffP, "--"));
log_debug("overflow: %s\n", log_id(st.overflow, "--"));
Cell *cell = st.dsp;
@@ -291,9 +291,10 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
cell->setPort(ID(INMODE), Const::from_string("00100"));
if (st.ffAD) {
- if (st.ffADcemux) {
- SigSpec S = st.ffADcemux->getPort(ID::S);
- cell->setPort(ID(CEAD), st.ffADcepol ? S : pm.module->Not(NEW_ID, S));
+ if (st.ffAD->type.in(ID($dffe), ID($sdffe))) {
+ bool pol = st.ffAD->getParam(ID::EN_POLARITY).as_bool();
+ SigSpec S = st.ffAD->getPort(ID::EN);
+ cell->setPort(ID(CEAD), pol ? S : pm.module->Not(NEW_ID, S));
}
else
cell->setPort(ID(CEAD), State::S1);
@@ -363,30 +364,24 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
{
cell->setPort(ID::CLK, st.clock);
- auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) {
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
- if (rstmux) {
- SigSpec Y = rstmux->getPort(ID::Y);
- SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B);
- if (!A.empty())
- A.replace(Y, AB);
- if (rstport != IdString()) {
- SigSpec S = rstmux->getPort(ID::S);
- cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ if (rstport != IdString()) {
+ if (ff->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec srst = ff->getPort(ID::SRST);
+ bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool();
+ cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst));
+ } else {
+ cell->setPort(rstport, State::S0);
}
}
- else if (rstport != IdString())
- cell->setPort(rstport, State::S0);
- if (cemux) {
- SigSpec Y = cemux->getPort(ID::Y);
- SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A);
- SigSpec S = cemux->getPort(ID::S);
- if (!A.empty())
- A.replace(Y, BA);
- cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ if (ff->type.in(ID($dffe), ID($sdffe))) {
+ SigSpec ce = ff->getPort(ID::EN);
+ bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
+ cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
}
else
cell->setPort(ceport, State::S1);
@@ -404,9 +399,9 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
if (st.ffA2) {
SigSpec A = cell->getPort(ID::A);
- f(A, st.ffA2, st.ffA2cemux, st.ffA2cepol, ID(CEA2), st.ffA2rstmux, st.ffArstpol, ID(RSTA));
+ f(A, st.ffA2, ID(CEA2), ID(RSTA));
if (st.ffA1) {
- f(A, st.ffA1, st.ffA1cemux, st.ffA1cepol, ID(CEA1), st.ffA1rstmux, st.ffArstpol, IdString());
+ f(A, st.ffA1, ID(CEA1), IdString());
cell->setParam(ID(AREG), 2);
cell->setParam(ID(ACASCREG), 2);
}
@@ -419,9 +414,9 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
}
if (st.ffB2) {
SigSpec B = cell->getPort(ID::B);
- f(B, st.ffB2, st.ffB2cemux, st.ffB2cepol, ID(CEB2), st.ffB2rstmux, st.ffBrstpol, ID(RSTB));
+ f(B, st.ffB2, ID(CEB2), ID(RSTB));
if (st.ffB1) {
- f(B, st.ffB1, st.ffB1cemux, st.ffB1cepol, ID(CEB1), st.ffB1rstmux, st.ffBrstpol, IdString());
+ f(B, st.ffB1, ID(CEB1), IdString());
cell->setParam(ID(BREG), 2);
cell->setParam(ID(BCASCREG), 2);
}
@@ -434,20 +429,20 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm)
}
if (st.ffD) {
SigSpec D = cell->getPort(ID::D);
- f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
+ f(D, st.ffD, ID(CED), ID(RSTD));
pm.add_siguser(D, cell);
cell->setPort(ID::D, D);
cell->setParam(ID(DREG), 1);
}
if (st.ffM) {
SigSpec M; // unused
- f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
+ f(M, st.ffM, ID(CEM), ID(RSTM));
st.ffM->connections_.at(ID::Q).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
cell->setParam(ID(MREG), State::S1);
}
if (st.ffP) {
SigSpec P; // unused
- f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
+ f(P, st.ffP, ID(CEP), ID(RSTP));
st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
cell->setParam(ID(PREG), State::S1);
}
@@ -495,16 +490,16 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
log("Analysing %s.%s for Xilinx DSP48A/DSP48A1 packing.\n", log_id(pm.module), log_id(st.dsp));
log_debug("preAdd: %s\n", log_id(st.preAdd, "--"));
- log_debug("ffA1: %s %s %s\n", log_id(st.ffA1, "--"), log_id(st.ffA1cemux, "--"), log_id(st.ffA1rstmux, "--"));
- log_debug("ffA0: %s %s %s\n", log_id(st.ffA0, "--"), log_id(st.ffA0cemux, "--"), log_id(st.ffA0rstmux, "--"));
- log_debug("ffB1: %s %s %s\n", log_id(st.ffB1, "--"), log_id(st.ffB1cemux, "--"), log_id(st.ffB1rstmux, "--"));
- log_debug("ffB0: %s %s %s\n", log_id(st.ffB0, "--"), log_id(st.ffB0cemux, "--"), log_id(st.ffB0rstmux, "--"));
- log_debug("ffD: %s %s %s\n", log_id(st.ffD, "--"), log_id(st.ffDcemux, "--"), log_id(st.ffDrstmux, "--"));
+ log_debug("ffA1: %s\n", log_id(st.ffA1, "--"));
+ log_debug("ffA0: %s\n", log_id(st.ffA0, "--"));
+ log_debug("ffB1: %s\n", log_id(st.ffB1, "--"));
+ log_debug("ffB0: %s\n", log_id(st.ffB0, "--"));
+ log_debug("ffD: %s\n", log_id(st.ffD, "--"));
log_debug("dsp: %s\n", log_id(st.dsp, "--"));
- log_debug("ffM: %s %s %s\n", log_id(st.ffM, "--"), log_id(st.ffMcemux, "--"), log_id(st.ffMrstmux, "--"));
+ log_debug("ffM: %s\n", log_id(st.ffM, "--"));
log_debug("postAdd: %s\n", log_id(st.postAdd, "--"));
log_debug("postAddMux: %s\n", log_id(st.postAddMux, "--"));
- log_debug("ffP: %s %s %s\n", log_id(st.ffP, "--"), log_id(st.ffPcemux, "--"), log_id(st.ffPrstmux, "--"));
+ log_debug("ffP: %s\n", log_id(st.ffP, "--"));
Cell *cell = st.dsp;
SigSpec &opmode = cell->connections_.at(ID(OPMODE));
@@ -556,30 +551,24 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
{
cell->setPort(ID::CLK, st.clock);
- auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) {
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
- if (rstmux) {
- SigSpec Y = rstmux->getPort(ID::Y);
- SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B);
- if (!A.empty())
- A.replace(Y, AB);
- if (rstport != IdString()) {
- SigSpec S = rstmux->getPort(ID::S);
- cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ if (rstport != IdString()) {
+ if (ff->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec srst = ff->getPort(ID::SRST);
+ bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool();
+ cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst));
+ } else {
+ cell->setPort(rstport, State::S0);
}
}
- else if (rstport != IdString())
- cell->setPort(rstport, State::S0);
- if (cemux) {
- SigSpec Y = cemux->getPort(ID::Y);
- SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A);
- SigSpec S = cemux->getPort(ID::S);
- if (!A.empty())
- A.replace(Y, BA);
- cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ if (ff->type.in(ID($dffe), ID($sdffe))) {
+ SigSpec ce = ff->getPort(ID::EN);
+ bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
+ cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
}
else
cell->setPort(ceport, State::S1);
@@ -598,11 +587,11 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
if (st.ffA0 || st.ffA1) {
SigSpec A = cell->getPort(ID::A);
if (st.ffA1) {
- f(A, st.ffA1, st.ffA1cemux, st.ffAcepol, ID(CEA), st.ffA1rstmux, st.ffArstpol, ID(RSTA));
+ f(A, st.ffA1, ID(CEA), ID(RSTA));
cell->setParam(ID(A1REG), 1);
}
if (st.ffA0) {
- f(A, st.ffA0, st.ffA0cemux, st.ffAcepol, ID(CEA), st.ffA0rstmux, st.ffArstpol, ID(RSTA));
+ f(A, st.ffA0, ID(CEA), ID(RSTA));
cell->setParam(ID(A0REG), 1);
}
pm.add_siguser(A, cell);
@@ -611,11 +600,11 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
if (st.ffB0 || st.ffB1) {
SigSpec B = cell->getPort(ID::B);
if (st.ffB1) {
- f(B, st.ffB1, st.ffB1cemux, st.ffBcepol, ID(CEB), st.ffB1rstmux, st.ffBrstpol, ID(RSTB));
+ f(B, st.ffB1, ID(CEB), ID(RSTB));
cell->setParam(ID(B1REG), 1);
}
if (st.ffB0) {
- f(B, st.ffB0, st.ffB0cemux, st.ffBcepol, ID(CEB), st.ffB0rstmux, st.ffBrstpol, ID(RSTB));
+ f(B, st.ffB0, ID(CEB), ID(RSTB));
cell->setParam(ID(B0REG), 1);
}
pm.add_siguser(B, cell);
@@ -623,20 +612,20 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm)
}
if (st.ffD) {
SigSpec D = cell->getPort(ID::D);
- f(D, st.ffD, st.ffDcemux, st.ffDcepol, ID(CED), st.ffDrstmux, st.ffDrstpol, ID(RSTD));
+ f(D, st.ffD, ID(CED), ID(RSTD));
pm.add_siguser(D, cell);
cell->setPort(ID::D, D);
cell->setParam(ID(DREG), 1);
}
if (st.ffM) {
SigSpec M; // unused
- f(M, st.ffM, st.ffMcemux, st.ffMcepol, ID(CEM), st.ffMrstmux, st.ffMrstpol, ID(RSTM));
+ f(M, st.ffM, ID(CEM), ID(RSTM));
st.ffM->connections_.at(ID::Q).replace(st.sigM, pm.module->addWire(NEW_ID, GetSize(st.sigM)));
cell->setParam(ID(MREG), State::S1);
}
if (st.ffP) {
SigSpec P; // unused
- f(P, st.ffP, st.ffPcemux, st.ffPcepol, ID(CEP), st.ffPrstmux, st.ffPrstpol, ID(RSTP));
+ f(P, st.ffP, ID(CEP), ID(RSTP));
st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
cell->setParam(ID(PREG), State::S1);
}
@@ -677,7 +666,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
auto &st = pm.st_xilinx_dsp_packC;
log_debug("Analysing %s.%s for Xilinx DSP packing (CREG).\n", log_id(pm.module), log_id(st.dsp));
- log_debug("ffC: %s %s %s\n", log_id(st.ffC, "--"), log_id(st.ffCcemux, "--"), log_id(st.ffCrstmux, "--"));
+ log_debug("ffC: %s\n", log_id(st.ffC, "--"));
Cell *cell = st.dsp;
@@ -685,30 +674,24 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
{
cell->setPort(ID::CLK, st.clock);
- auto f = [&pm,cell](SigSpec &A, Cell* ff, Cell* cemux, bool cepol, IdString ceport, Cell* rstmux, bool rstpol, IdString rstport) {
+ auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) {
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
- if (rstmux) {
- SigSpec Y = rstmux->getPort(ID::Y);
- SigSpec AB = rstmux->getPort(rstpol ? ID::A : ID::B);
- if (!A.empty())
- A.replace(Y, AB);
- if (rstport != IdString()) {
- SigSpec S = rstmux->getPort(ID::S);
- cell->setPort(rstport, rstpol ? S : pm.module->Not(NEW_ID, S));
+ if (rstport != IdString()) {
+ if (ff->type.in(ID($sdff), ID($sdffe))) {
+ SigSpec srst = ff->getPort(ID::SRST);
+ bool rstpol = ff->getParam(ID::SRST_POLARITY).as_bool();
+ cell->setPort(rstport, rstpol ? srst : pm.module->Not(NEW_ID, srst));
+ } else {
+ cell->setPort(rstport, State::S0);
}
}
- else if (rstport != IdString())
- cell->setPort(rstport, State::S0);
- if (cemux) {
- SigSpec Y = cemux->getPort(ID::Y);
- SigSpec BA = cemux->getPort(cepol ? ID::B : ID::A);
- SigSpec S = cemux->getPort(ID::S);
- if (!A.empty())
- A.replace(Y, BA);
- cell->setPort(ceport, cepol ? S : pm.module->Not(NEW_ID, S));
+ if (ff->type.in(ID($dffe), ID($sdffe))) {
+ SigSpec ce = ff->getPort(ID::EN);
+ bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
+ cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
}
else
cell->setPort(ceport, State::S1);
@@ -726,7 +709,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm)
if (st.ffC) {
SigSpec C = cell->getPort(ID::C);
- f(C, st.ffC, st.ffCcemux, st.ffCcepol, ID(CEC), st.ffCrstmux, st.ffCrstpol, ID(RSTC));
+ f(C, st.ffC, ID(CEC), ID(RSTC));
pm.add_siguser(C, cell);
cell->setPort(ID::C, C);
cell->setParam(ID(CREG), 1);
diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg
index d40f073c9..0cd23c09d 100644
--- a/passes/pmgen/xilinx_dsp.pmg
+++ b/passes/pmgen/xilinx_dsp.pmg
@@ -2,9 +2,7 @@
// forms the `xilinx_dsp` pass described in xilinx_dsp.cc
// At a high level, it works as follows:
// ( 1) Starting from a DSP48E1 cell
-// ( 2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
-// (attached to at most two $mux cells that implement clock-enable or
-// reset functionality, using a subpattern discussed below)
+// ( 2) Match the driver of the 'A' input to a possible $sdffe cell (ADREG)
// If ADREG matched, treat 'A' input as input of ADREG
// ( 3) Match the driver of the 'A' and 'D' inputs for a possible $add cell
// (pre-adder)
@@ -44,7 +42,7 @@
// DSP48E1 cells inferred from multiply operations by Yosys, as well as for
// user instantiations that may already contain the cells being packed...
// (though the latter is currently untested)
-// - Since the $dff-with-optional-clock-enable-or-reset-mux pattern is used
+// - Since the $sdffe pattern is used
// for each *REG match, it has been factored out into two subpatterns:
// in_dffe and out_dffe located at the bottom of this file.
// - Matching for pattern detector features is currently incomplete. For
@@ -57,20 +55,15 @@ pattern xilinx_dsp_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigM sigP
state <IdString> postAddAB postAddMuxAB
-state <bool> ffA1cepol ffA2cepol ffADcepol ffB1cepol ffB2cepol ffDcepol ffMcepol ffPcepol
-state <bool> ffArstpol ffADrstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
-state <Cell*> ffAD ffADcemux ffADrstmux ffA1 ffA1cemux ffA1rstmux ffA2 ffA2cemux ffA2rstmux
-state <Cell*> ffB1 ffB1cemux ffB1rstmux ffB2 ffB2cemux ffB2rstmux
-state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
+state <Cell*> ffAD ffA1 ffA2
+state <Cell*> ffB1 ffB2
+state <Cell*> ffD ffM ffP
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
-state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
// (1) Starting from a DSP48E1 cell
match dsp
@@ -115,25 +108,15 @@ code sigA sigB sigC sigD sigM clock
clock = port(dsp, \CLK, SigBit());
endcode
-// (2) Match the driver of the 'A' input to a possible $dff cell (ADREG)
-// (attached to at most two $mux cells that implement clock-enable or
-// reset functionality, using a subpattern discussed above)
+// (2) Match the driver of the 'A' input to a possible $sdffe cell (ADREG)
// If matched, treat 'A' input as input of ADREG
-code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock
+code argQ ffAD sigA clock
if (param(dsp, \ADREG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffAD = dff;
clock = dffclock;
- if (dffrstmux) {
- ffADrstmux = dffrstmux;
- ffADrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffADcemux = dffcemux;
- ffADcepol = dffcepol;
- }
sigA = dffD;
}
}
@@ -172,7 +155,7 @@ endcode
// (4) If pre-adder was present, find match 'A' input for A2REG
// If pre-adder was not present, move ADREG to A2REG
// Then match 'A' input for A1REG
-code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cemux ffA2rstmux ffA2cepol ffArstpol ffA1 ffA1cemux ffA1rstmux ffA1cepol
+code argQ ffAD sigA clock ffA2 ffA1
// Only search for ffA2 if there was a pre-adder
// (otherwise ffA2 would have been matched as ffAD)
if (preAdd) {
@@ -182,14 +165,6 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
if (dff) {
ffA2 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffA2rstmux = dffrstmux;
- ffArstpol = dffrstpol;
- }
- if (dffcemux) {
- ffA2cepol = dffcepol;
- ffA2cemux = dffcemux;
- }
sigA = dffD;
}
}
@@ -197,12 +172,8 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
// And if there wasn't a pre-adder,
// move AD register to A
else if (ffAD) {
- log_assert(!ffA2 && !ffA2cemux && !ffA2rstmux);
+ log_assert(!ffA2);
std::swap(ffA2, ffAD);
- std::swap(ffA2cemux, ffADcemux);
- std::swap(ffA2rstmux, ffADrstmux);
- ffA2cepol = ffADcepol;
- ffArstpol = ffADrstpol;
}
// Now attempt to match A1
@@ -210,23 +181,23 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem
argQ = sigA;
subpattern(in_dffe);
if (dff) {
- if ((ffA2rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffA2->type)
goto ffA1_end;
- if (dffrstmux) {
- if (ffArstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffA2, \SRST_POLARITY))
goto ffA1_end;
- if (port(ffA2rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \SRST) != port(ffA2, \SRST))
+ goto ffA1_end;
+ }
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffA2, \EN_POLARITY))
+ goto ffA1_end;
+ if (port(dff, \EN) != port(ffA2, \EN))
goto ffA1_end;
- ffA1rstmux = dffrstmux;
}
ffA1 = dff;
clock = dffclock;
-
- if (dffcemux) {
- ffA1cemux = dffcemux;
- ffA1cepol = dffcepol;
- }
sigA = dffD;
ffA1_end: ;
@@ -236,21 +207,13 @@ endcode
// (5) Match 'B' input for B2REG
// If B2REG, then match 'B' input for B1REG
-code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol
+code argQ ffB2 sigB clock ffB1
if (param(dsp, \BREG).as_int() == 0) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB2 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffB2rstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffB2cemux = dffcemux;
- ffB2cepol = dffcepol;
- }
sigB = dffD;
// Now attempt to match B1
@@ -258,23 +221,23 @@ code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemu
argQ = sigB;
subpattern(in_dffe);
if (dff) {
- if ((ffB2rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffB2->type)
goto ffB1_end;
- if (dffrstmux) {
- if (ffBrstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffB2, \SRST_POLARITY))
+ goto ffB1_end;
+ if (port(dff, \SRST) != port(ffB2, \SRST))
+ goto ffB1_end;
+ }
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffB2, \EN_POLARITY))
goto ffB1_end;
- if (port(ffB2rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \EN) != port(ffB2, \EN))
goto ffB1_end;
- ffB1rstmux = dffrstmux;
}
ffB1 = dff;
clock = dffclock;
-
- if (dffcemux) {
- ffB1cemux = dffcemux;
- ffB1cepol = dffcepol;
- }
sigB = dffD;
ffB1_end: ;
@@ -286,42 +249,26 @@ ffB1_end: ;
endcode
// (6) Match 'D' input for DREG
-code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
+code argQ ffD sigD clock
if (param(dsp, \DREG).as_int() == 0) {
argQ = sigD;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
- if (dffrstmux) {
- ffDrstmux = dffrstmux;
- ffDrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffDcemux = dffcemux;
- ffDcepol = dffcepol;
- }
sigD = dffD;
}
}
endcode
// (7) Match 'P' output that exclusively drives an MREG
-code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
+code argD ffM sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM;
subpattern(out_dffe);
if (dff) {
ffM = dff;
clock = dffclock;
- if (dffrstmux) {
- ffMrstmux = dffrstmux;
- ffMrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffMcemux = dffcemux;
- ffMcepol = dffcepol;
- }
sigM = dffQ;
}
}
@@ -340,9 +287,7 @@ match postAdd
select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48
choice <IdString> AB {\A, \B}
- select nusers(port(postAdd, AB)) <= 3
- filter ffMcemux || nusers(port(postAdd, AB)) == 2
- filter !ffMcemux || nusers(port(postAdd, AB)) == 3
+ select nusers(port(postAdd, AB)) == 2
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
@@ -362,25 +307,14 @@ code sigC sigP
endcode
// (9) Match 'P' output that exclusively drives a PREG
-code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+code argD ffP sigP clock
if (param(dsp, \PREG).as_int() == 0) {
- int users = 2;
- // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
- if (ffMcemux && !postAdd) users++;
- if (nusers(sigP) == users) {
+ if (nusers(sigP) == 2) {
argD = sigP;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
- if (dffrstmux) {
- ffPrstmux = dffrstmux;
- ffPrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffPcemux = dffcemux;
- ffPcepol = dffcepol;
- }
sigP = dffQ;
}
}
@@ -441,22 +375,9 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
@@ -479,13 +400,14 @@ code
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -494,82 +416,16 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
// #######################
@@ -597,119 +453,26 @@ code
reject;
endcode
-// (1) Starting from an optional $mux cell that implements clock enable
-// semantics --- one where the given 'D' argument (partially or fully)
-// drives one of its two inputs
-match ffcemux
- select ffcemux->type.in($mux)
- // ffcemux output must have two users: ffcemux and ff.D
- select nusers(port(ffcemux, \Y)) == 2
-
- choice <IdString> AB {\A, \B}
- // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
- select nusers(port(ffcemux, AB)) >= 3
-
- slice offset GetSize(port(ffcemux, \Y))
- define <IdString> BA (AB == \A ? \B : \A)
- index <SigBit> port(ffcemux, BA)[offset] === argD[0]
-
- // Check that the rest of argD is present
- filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
- filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffcepol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffcemux = ffcemux;
- if (ffcemux) {
- SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
- SigSpec Y = port(ffcemux, \Y);
- argQ = argD;
- argD.replace(BA, Y);
- argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
-
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- }
-endcode
-
-// (2) Starting from, or continuing onto, another optional $mux cell that
-// implements synchronous reset semantics --- one where the given 'D'
-// argument (or the clock enable $mux output) drives one of its two inputs
-// and where the other input is fully zero
-match ffrstmux
- select ffrstmux->type.in($mux)
- // ffrstmux output must have two users: ffrstmux and ff.D
- select nusers(port(ffrstmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- slice offset GetSize(port(ffrstmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
-
- // Check that offset is consistent
- filter !ffcemux || ffoffset == offset
- // Check that the rest of argD is present
- filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
- filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffrstpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffrstmux = ffrstmux;
- if (ffrstmux) {
- SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
- SigSpec Y = port(ffrstmux, \Y);
- argD.replace(AB, Y);
-
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- }
-endcode
-
-// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
-// output of the previous clock enable or reset $mux cells)
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
- // Check that offset is consistent
- filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
- // Check that FF.Q is connected to CE-mux
- filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
code argQ
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
- if (!ffcemux) {
- argQ = argD;
- argQ.replace(D, Q);
- }
+ argQ = argD;
+ argQ.replace(D, Q);
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
diff --git a/passes/pmgen/xilinx_dsp48a.pmg b/passes/pmgen/xilinx_dsp48a.pmg
index 16f5e598d..dce1b61b0 100644
--- a/passes/pmgen/xilinx_dsp48a.pmg
+++ b/passes/pmgen/xilinx_dsp48a.pmg
@@ -4,8 +4,6 @@
// At a high level, it works as follows:
// ( 1) Starting from a DSP48A/DSP48A1 cell
// ( 2) Match the driver of the 'B' input to a possible $dff cell (B1REG)
-// (attached to at most two $mux cells that implement clock-enable or
-// reset functionality, using a subpattern discussed below)
// If B1REG matched, treat 'B' input as input of B1REG
// ( 3) Match the driver of the 'B' and 'D' inputs for a possible $add cell
// (pre-adder)
@@ -40,20 +38,15 @@ pattern xilinx_dsp48a_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigM sigP
state <IdString> postAddAB postAddMuxAB
-state <bool> ffAcepol ffBcepol ffDcepol ffMcepol ffPcepol
-state <bool> ffArstpol ffBrstpol ffDrstpol ffMrstpol ffPrstpol
-state <Cell*> ffA0 ffA0cemux ffA0rstmux ffA1 ffA1cemux ffA1rstmux
-state <Cell*> ffB0 ffB0cemux ffB0rstmux ffB1 ffB1cemux ffB1rstmux
-state <Cell*> ffD ffDcemux ffDrstmux ffM ffMcemux ffMrstmux ffP ffPcemux ffPrstmux
+state <Cell*> ffA0 ffA1
+state <Cell*> ffB0 ffB1
+state <Cell*> ffD ffM ffP
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
-state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
// (1) Starting from a DSP48A/DSP48A1 cell
match dsp
@@ -98,21 +91,13 @@ endcode
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using a subpattern discussed above)
// If matched, treat 'B' input as input of B1REG
-code argQ ffB1 ffB1cemux ffB1rstmux ffBcepol ffBrstpol sigB clock
+code argQ ffB1 sigB clock
if (param(dsp, \B1REG).as_int() == 0 && param(dsp, \B0REG).as_int() == 0 && port(dsp, \OPMODE, SigSpec()).extract(4, 1).is_fully_zero()) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
ffB1 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffB1rstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffB1cemux = dffcemux;
- ffBcepol = dffcepol;
- }
sigB = dffD;
}
}
@@ -147,41 +132,29 @@ code sigB sigD
endcode
// (4) Match 'B' input for B0REG
-code argQ ffB0 ffB0cemux ffB0rstmux ffBcepol ffBrstpol sigB clock
+code argQ ffB0 sigB clock
if (param(dsp, \B0REG).as_int() == 0) {
argQ = sigB;
subpattern(in_dffe);
if (dff) {
if (ffB1) {
- if ((ffB1rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffB1->type)
goto ffB0_end;
- if ((ffB1cemux != nullptr) ^ (dffcemux != nullptr))
- goto ffB0_end;
- if (dffrstmux) {
- if (ffBrstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffB1, \SRST_POLARITY))
goto ffB0_end;
- if (port(ffB1rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \SRST) != port(ffB1, \SRST))
goto ffB0_end;
- ffB0rstmux = dffrstmux;
}
- if (dffcemux) {
- if (ffBcepol != dffcepol)
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffB1, \EN_POLARITY))
goto ffB0_end;
- if (port(ffB1cemux, \S) != port(dffcemux, \S))
+ if (port(dff, \EN) != port(ffB1, \EN))
goto ffB0_end;
- ffB0cemux = dffcemux;
}
}
ffB0 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffB0rstmux = dffrstmux;
- ffBrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffB0cemux = dffcemux;
- ffBcepol = dffcepol;
- }
sigB = dffD;
}
}
@@ -190,21 +163,13 @@ endcode
// (5) Match 'A' input for A1REG
// If A1REG, then match 'A' input for A0REG
-code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux ffA0rstmux
+code argQ ffA1 sigA clock ffA0
if (param(dsp, \A0REG).as_int() == 0 && param(dsp, \A1REG).as_int() == 0) {
argQ = sigA;
subpattern(in_dffe);
if (dff) {
ffA1 = dff;
clock = dffclock;
- if (dffrstmux) {
- ffA1rstmux = dffrstmux;
- ffArstpol = dffrstpol;
- }
- if (dffcemux) {
- ffA1cemux = dffcemux;
- ffAcepol = dffcepol;
- }
sigA = dffD;
// Now attempt to match A0
@@ -212,32 +177,23 @@ code argQ ffA1 ffA1cemux ffA1rstmux ffAcepol ffArstpol sigA clock ffA0 ffA0cemux
argQ = sigA;
subpattern(in_dffe);
if (dff) {
- if ((ffA1rstmux != nullptr) ^ (dffrstmux != nullptr))
+ if (dff->type != ffA1->type)
goto ffA0_end;
- if ((ffA1cemux != nullptr) ^ (dffcemux != nullptr))
- goto ffA0_end;
- if (dffrstmux) {
- if (ffArstpol != dffrstpol)
+ if (dff->type.in($sdff, $sdffe, $sdffce)) {
+ if (param(dff, \SRST_POLARITY) != param(ffA1, \SRST_POLARITY))
goto ffA0_end;
- if (port(ffA1rstmux, \S) != port(dffrstmux, \S))
+ if (port(dff, \SRST) != port(ffA1, \SRST))
goto ffA0_end;
- ffA0rstmux = dffrstmux;
}
- if (dffcemux) {
- if (ffAcepol != dffcepol)
+ if (dff->type.in($dffe, $sdffe, $sdffce)) {
+ if (param(dff, \EN_POLARITY) != param(ffA1, \EN_POLARITY))
goto ffA0_end;
- if (port(ffA1cemux, \S) != port(dffcemux, \S))
+ if (port(dff, \EN) != port(ffA1, \EN))
goto ffA0_end;
- ffA0cemux = dffcemux;
}
ffA0 = dff;
clock = dffclock;
-
- if (dffcemux) {
- ffA0cemux = dffcemux;
- ffAcepol = dffcepol;
- }
sigA = dffD;
ffA0_end: ;
@@ -249,42 +205,26 @@ ffA0_end: ;
endcode
// (6) Match 'D' input for DREG
-code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock
+code argQ ffD sigD clock
if (param(dsp, \DREG).as_int() == 0) {
argQ = sigD;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
- if (dffrstmux) {
- ffDrstmux = dffrstmux;
- ffDrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffDcemux = dffcemux;
- ffDcepol = dffcepol;
- }
sigD = dffD;
}
}
endcode
// (7) Match 'P' output that exclusively drives an MREG
-code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock
+code argD ffM sigM sigP clock
if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) {
argD = sigM;
subpattern(out_dffe);
if (dff) {
ffM = dff;
clock = dffclock;
- if (dffrstmux) {
- ffMrstmux = dffrstmux;
- ffMrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffMcemux = dffcemux;
- ffMcepol = dffcepol;
- }
sigM = dffQ;
}
}
@@ -303,9 +243,7 @@ match postAdd
select postAdd->type.in($add)
select GetSize(port(postAdd, \Y)) <= 48
choice <IdString> AB {\A, \B}
- select nusers(port(postAdd, AB)) <= 3
- filter ffMcemux || nusers(port(postAdd, AB)) == 2
- filter !ffMcemux || nusers(port(postAdd, AB)) == 3
+ select nusers(port(postAdd, AB)) == 2
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
@@ -325,25 +263,14 @@ code sigC sigP
endcode
// (9) Match 'P' output that exclusively drives a PREG
-code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock
+code argD ffP sigP clock
if (param(dsp, \PREG).as_int() == 0) {
- int users = 2;
- // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux
- if (ffMcemux && !postAdd) users++;
- if (nusers(sigP) == users) {
+ if (nusers(sigP) == 2) {
argD = sigP;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
- if (dffrstmux) {
- ffPrstmux = dffrstmux;
- ffPrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffPcemux = dffcemux;
- ffPcepol = dffcepol;
- }
sigP = dffQ;
}
}
@@ -387,26 +314,13 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
- if (GetSize(argQ) == 0)
+ if (argQ.empty())
reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
@@ -425,13 +339,14 @@ code
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -440,82 +355,16 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
// #######################
@@ -543,119 +392,26 @@ code
reject;
endcode
-// (1) Starting from an optional $mux cell that implements clock enable
-// semantics --- one where the given 'D' argument (partially or fully)
-// drives one of its two inputs
-match ffcemux
- select ffcemux->type.in($mux)
- // ffcemux output must have two users: ffcemux and ff.D
- select nusers(port(ffcemux, \Y)) == 2
-
- choice <IdString> AB {\A, \B}
- // keep-last-value net must have at least three users: ffcemux, ff, downstream sink(s)
- select nusers(port(ffcemux, AB)) >= 3
-
- slice offset GetSize(port(ffcemux, \Y))
- define <IdString> BA (AB == \A ? \B : \A)
- index <SigBit> port(ffcemux, BA)[offset] === argD[0]
-
- // Check that the rest of argD is present
- filter GetSize(port(ffcemux, BA)) >= offset + GetSize(argD)
- filter port(ffcemux, BA).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffcepol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffcemux = ffcemux;
- if (ffcemux) {
- SigSpec BA = port(ffcemux, ffcepol ? \B : \A);
- SigSpec Y = port(ffcemux, \Y);
- argQ = argD;
- argD.replace(BA, Y);
- argQ.replace(BA, port(ffcemux, ffcepol ? \A : \B));
-
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- }
-endcode
-
-// (2) Starting from, or continuing onto, another optional $mux cell that
-// implements synchronous reset semantics --- one where the given 'D'
-// argument (or the clock enable $mux output) drives one of its two inputs
-// and where the other input is fully zero
-match ffrstmux
- select ffrstmux->type.in($mux)
- // ffrstmux output must have two users: ffrstmux and ff.D
- select nusers(port(ffrstmux, \Y)) == 2
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- slice offset GetSize(port(ffrstmux, \Y))
- define <IdString> AB (BA == \B ? \A : \B)
- index <SigBit> port(ffrstmux, AB)[offset] === argD[0]
-
- // Check that offset is consistent
- filter !ffcemux || ffoffset == offset
- // Check that the rest of argD is present
- filter GetSize(port(ffrstmux, AB)) >= offset + GetSize(argD)
- filter port(ffrstmux, AB).extract(offset, GetSize(argD)) == argD
-
- set ffoffset offset
- define <bool> pol (AB == \A)
- set ffrstpol pol
-
- semioptional
-endmatch
-
-code argD argQ
- dffrstmux = ffrstmux;
- if (ffrstmux) {
- SigSpec AB = port(ffrstmux, ffrstpol ? \A : \B);
- SigSpec Y = port(ffrstmux, \Y);
- argD.replace(AB, Y);
-
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- }
-endcode
-
-// (3) Match for a $dff cell (whose 'D' input is the 'D' argument, or the
-// output of the previous clock enable or reset $mux cells)
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
- // Check that offset is consistent
- filter (!ffcemux && !ffrstmux) || ffoffset == offset
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
- // Check that FF.Q is connected to CE-mux
- filter !ffcemux || port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
code argQ
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
- if (!ffcemux) {
- argQ = argD;
- argQ.replace(D, Q);
- }
+ argQ = argD;
+ argQ.replace(D, Q);
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
diff --git a/passes/pmgen/xilinx_dsp_CREG.pmg b/passes/pmgen/xilinx_dsp_CREG.pmg
index 42d4d1b9b..95379771a 100644
--- a/passes/pmgen/xilinx_dsp_CREG.pmg
+++ b/passes/pmgen/xilinx_dsp_CREG.pmg
@@ -26,17 +26,14 @@ pattern xilinx_dsp_packC
udata <std::function<SigSpec(const SigSpec&)>> unextend
state <SigBit> clock
state <SigSpec> sigC sigP
-state <bool> ffCcepol ffCrstpol
-state <Cell*> ffC ffCcemux ffCrstmux
+state <Cell*> ffC
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
// (1) Starting from a DSP48* cell that (a) doesn't have a CREG already,
// and (b) uses the 'C' port
@@ -80,20 +77,12 @@ endcode
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using the in_dffe subpattern)
-code argQ ffC ffCcemux ffCrstmux ffCcepol ffCrstpol sigC clock
+code argQ ffC sigC clock
argQ = sigC;
subpattern(in_dffe);
if (dff) {
ffC = dff;
clock = dffclock;
- if (dffrstmux) {
- ffCrstmux = dffrstmux;
- ffCrstpol = dffrstpol;
- }
- if (dffcemux) {
- ffCcemux = dffcemux;
- ffCcepol = dffcepol;
- }
sigC = dffD;
}
endcode
@@ -106,25 +95,14 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
+ if (argQ.empty())
+ reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
@@ -135,19 +113,21 @@ code
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
Const init = c.wire->attributes.at(\init, Const());
- for (auto b : init.extract(c.offset, c.width))
- if (b != State::Sx && b != State::S0)
- reject;
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -156,80 +136,14 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
diff --git a/passes/pmgen/xilinx_dsp_cascade.pmg b/passes/pmgen/xilinx_dsp_cascade.pmg
index 8babb88e6..06601554c 100644
--- a/passes/pmgen/xilinx_dsp_cascade.pmg
+++ b/passes/pmgen/xilinx_dsp_cascade.pmg
@@ -51,12 +51,10 @@ state <int> AREG BREG
// Variables used for subpatterns
state <SigSpec> argQ argD
-state <bool> ffcepol ffrstpol
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
-udata <Cell*> dff dffcemux dffrstmux
-udata <bool> dffcepol dffrstpol
+udata <Cell*> dff
code
#define MAX_DSP_CASCADE 20
@@ -254,9 +252,9 @@ code argQ clock AREG
clock = port(prev, \CLK);
subpattern(in_dffe);
if (dff) {
- if (!dffrstmux && port(prev, \RSTA, State::S0) != State::S0)
+ if (!dff->type.in($sdff, $sdffe) && port(prev, \RSTA, State::S0) != State::S0)
goto reject_AREG;
- if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTA, State::S0))
+ if (dff->type.in($sdff, $sdffe) && (port(dff, \SRST) != port(prev, \RSTA, State::S0) || !param(dff, \SRST_POLARITY).as_bool()))
goto reject_AREG;
IdString CEA;
if (param(prev, \AREG) == 1)
@@ -264,9 +262,9 @@ code argQ clock AREG
else if (param(prev, \AREG) == 2)
CEA = \CEA1;
else log_abort();
- if (!dffcemux && port(prev, CEA, State::S0) != State::S1)
+ if (!dff->type.in($dffe, $sdffe) && port(prev, CEA, State::S0) != State::S1)
goto reject_AREG;
- if (dffcemux && port(dffcemux, \S) != port(prev, CEA, State::S0))
+ if (dff->type.in($dffe, $sdffe) && (port(dff, \EN) != port(prev, CEA, State::S0) || !param(dff, \EN_POLARITY).as_bool()))
goto reject_AREG;
if (dffD == unextend(port(prev, \A)))
AREG = 1;
@@ -295,9 +293,9 @@ code argQ clock BREG
clock = port(prev, \CLK);
subpattern(in_dffe);
if (dff) {
- if (!dffrstmux && port(prev, \RSTB, State::S0) != State::S0)
+ if (!dff->type.in($sdff, $sdffe) && port(prev, \RSTB, State::S0) != State::S0)
goto reject_BREG;
- if (dffrstmux && port(dffrstmux, \S) != port(prev, \RSTB, State::S0))
+ if (dff->type.in($sdff, $sdffe) && (port(dff, \SRST) != port(prev, \RSTB, State::S0) || !param(dff, \SRST_POLARITY).as_bool()))
goto reject_BREG;
IdString CEB;
if (next->type.in(\DSP48A, \DSP48A1))
@@ -310,9 +308,9 @@ code argQ clock BREG
else log_abort();
}
else log_abort();
- if (!dffcemux && port(prev, CEB, State::S0) != State::S1)
+ if (!dff->type.in($dffe, $sdffe) && port(prev, CEB, State::S0) != State::S1)
goto reject_BREG;
- if (dffcemux && port(dffcemux, \S) != port(prev, CEB, State::S0))
+ if (dff->type.in($dffe, $sdffe) && (port(dff, \EN) != port(prev, CEB, State::S0) || !param(dff, \EN_POLARITY).as_bool()))
goto reject_BREG;
if (dffD == unextend(port(prev, \B))) {
if (next->type.in(\DSP48A, \DSP48A1) && param(prev, \B0REG) != 0)
@@ -357,25 +355,14 @@ endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
-// 'Q' input. Typically, identifying registers with clock-enable and reset
-// capability would be a task would be handled by other Yosys passes such as
-// dff2dffe, but since DSP inference happens much before this, these patterns
-// have to be manually identified.
-// At a high level:
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// one that exclusively drives the 'D' input of the $dff, with one of its
-// $mux inputs being fully zero
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
+// 'Q' input.
subpattern in_dffe
-arg argD argQ clock
+arg argQ clock
code
dff = nullptr;
+ if (argQ.empty())
+ reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
@@ -386,19 +373,21 @@ code
// Abandon matches when 'Q' has a non-zero init attribute set
// (not supported by DSP48E1)
Const init = c.wire->attributes.at(\init, Const());
- for (auto b : init.extract(c.offset, c.width))
- if (b != State::Sx && b != State::S0)
- reject;
+ if (!init.empty())
+ for (auto b : init.extract(c.offset, c.width))
+ if (b != State::Sx && b != State::S0)
+ reject;
}
endcode
-// (1) Starting from a $dff cell that (partially or fully) drives the given
-// 'Q' argument
match ff
- select ff->type.in($dff)
+ select ff->type.in($dff, $dffe, $sdff, $sdffe)
// DSP48E1 does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
+ // Check that reset value, if present, is fully 0.
+ filter ff->type.in($dff, $dffe) || param(ff, \SRST_VALUE).is_fully_zero()
+
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
@@ -407,80 +396,14 @@ match ff
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK) == clock
-
- set ffoffset offset
endmatch
-code argQ argD
+code argQ
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
- argD = port(ff, \D);
+ SigSpec D = port(ff, \D);
argQ = Q;
- dffD.replace(argQ, argD);
- // Only search for ffrstmux if dffD only
- // has two (ff, ffrstmux) users
- if (nusers(dffD) > 2)
- argD = SigSpec();
-endcode
-
-// (2) Match for a $mux cell implementing synchronous reset semantics ---
-// exclusively drives the 'D' input of the $dff, with one of the $mux
-// inputs being fully zero
-match ffrstmux
- if !argD.empty()
- select ffrstmux->type.in($mux)
- index <SigSpec> port(ffrstmux, \Y) === argD
-
- choice <IdString> BA {\B, \A}
- // DSP48E1 only supports reset to zero
- select port(ffrstmux, BA).is_fully_zero()
-
- define <bool> pol (BA == \B)
- set ffrstpol pol
- semioptional
-endmatch
-
-code argD
- if (ffrstmux) {
- dffrstmux = ffrstmux;
- dffrstpol = ffrstpol;
- argD = port(ffrstmux, ffrstpol ? \A : \B);
- dffD.replace(port(ffrstmux, \Y), argD);
-
- // Only search for ffcemux if argQ has at
- // least 3 users (ff, <upstream>, ffrstmux) and
- // dffD only has two (ff, ffrstmux)
- if (!(nusers(argQ) >= 3 && nusers(dffD) == 2))
- argD = SigSpec();
- }
- else
- dffrstmux = nullptr;
-endcode
-
-// (3) Match for a $mux cell implement clock enable semantics --- one that
-// exclusively drives the 'D' input of the $dff (or the other input of
-// the reset $mux) and where one of this $mux's inputs is connected to
-// the 'Q' output of the $dff
-match ffcemux
- if !argD.empty()
- select ffcemux->type.in($mux)
- index <SigSpec> port(ffcemux, \Y) === argD
- choice <IdString> AB {\A, \B}
- index <SigSpec> port(ffcemux, AB) === argQ
- define <bool> pol (AB == \A)
- set ffcepol pol
- semioptional
-endmatch
-
-code argD
- if (ffcemux) {
- dffcemux = ffcemux;
- dffcepol = ffcepol;
- argD = port(ffcemux, ffcepol ? \B : \A);
- dffD.replace(port(ffcemux, \Y), argD);
- }
- else
- dffcemux = nullptr;
+ dffD.replace(argQ, D);
endcode
diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc
index 3ed158f37..7b8c05b21 100644
--- a/passes/proc/proc_dlatch.cc
+++ b/passes/proc/proc_dlatch.cc
@@ -19,6 +19,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
#include "kernel/consteval.h"
#include "kernel/log.h"
#include <sstream>
@@ -32,15 +33,17 @@ struct proc_dlatch_db_t
{
Module *module;
SigMap sigmap;
+ FfInitVals initvals;
pool<Cell*> generated_dlatches;
dict<Cell*, vector<SigBit>> mux_srcbits;
dict<SigBit, pair<Cell*, int>> mux_drivers;
dict<SigBit, int> sigusers;
- dict<SigBit, std::pair<State,SigBit>> initbits;
proc_dlatch_db_t(Module *module) : module(module), sigmap(module)
{
+ initvals.set(&sigmap, module);
+
for (auto cell : module->cells())
{
if (cell->type.in(ID($mux), ID($pmux)))
@@ -74,29 +77,6 @@ struct proc_dlatch_db_t
if (wire->port_input)
for (auto bit : sigmap(wire))
sigusers[bit]++;
- if (wire->attributes.count(ID::init)) {
- SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
- {
- SigBit bit = wirebits[i];
- State val = initval[i];
-
- if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
- continue;
-
- if (initbits.count(bit)) {
- if (initbits.at(bit).first != val)
- log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
- log_signal(bit), log_signal(SigBit(wire, i)),
- log_signal(val), log_signal(initbits.at(bit).first));
- continue;
- }
-
- initbits[bit] = std::make_pair(val,SigBit(wire,i));
- }
- }
}
}
@@ -420,11 +400,11 @@ void proc_dlatch(proc_dlatch_db_t &db, RTLIL::Process *proc)
log("No latch inferred for signal `%s.%s' from process `%s.%s'.\n",
db.module->name.c_str(), log_signal(lhs), db.module->name.c_str(), proc->name.c_str());
for (auto &bit : lhs) {
- auto it = db.initbits.find(bit);
- if (it != db.initbits.end()) {
- log("Removing init bit %s for non-memory siginal `%s.%s` in process `%s.%s`.\n", log_signal(it->second.first), db.module->name.c_str(), log_signal(bit), db.module->name.c_str(), proc->name.c_str());
- it->second.second.wire->attributes.at(ID::init)[it->second.second.offset] = State::Sx;
+ State val = db.initvals(bit);
+ if (db.initvals(bit) != State::Sx) {
+ log("Removing init bit %s for non-memory siginal `%s.%s` in process `%s.%s`.\n", log_signal(val), db.module->name.c_str(), log_signal(bit), db.module->name.c_str(), proc->name.c_str());
}
+ db.initvals.remove_init(bit);
}
db.module->connect(lhs, rhs);
offset += chunk.width;
diff --git a/passes/sat/async2sync.cc b/passes/sat/async2sync.cc
index 6fc480925..3fa5a614c 100644
--- a/passes/sat/async2sync.cc
+++ b/passes/sat/async2sync.cc
@@ -19,6 +19,8 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -62,169 +64,183 @@ struct Async2syncPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, State> initbits;
- pool<SigBit> del_initbits;
-
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- initbits[initsig[i]] = initval[i];
- }
+ FfInitVals initvals(&sigmap, module);
for (auto cell : vector<Cell*>(module->selected_cells()))
{
- if (cell->type.in(ID($adff)))
- {
- // bool clk_pol = cell->parameters[ID::CLK_POLARITY].as_bool();
- bool arst_pol = cell->parameters[ID::ARST_POLARITY].as_bool();
- Const arst_val = cell->parameters[ID::ARST_VALUE];
-
- // SigSpec sig_clk = cell->getPort(ID::CLK);
- SigSpec sig_arst = cell->getPort(ID::ARST);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
- log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(sig_arst), log_signal(sig_d), log_signal(sig_q));
-
- Const init_val;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sigmap(sig_q[i]);
- init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
- del_initbits.insert(bit);
- }
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+ continue;
- Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
- Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
- new_q->attributes[ID::init] = init_val;
+ FfData ff(&initvals, cell);
- if (arst_pol) {
- module->addMux(NEW_ID, sig_d, arst_val, sig_arst, new_d);
- module->addMux(NEW_ID, new_q, arst_val, sig_arst, sig_q);
- } else {
- module->addMux(NEW_ID, arst_val, sig_d, sig_arst, new_d);
- module->addMux(NEW_ID, arst_val, new_q, sig_arst, sig_q);
- }
-
- cell->setPort(ID::D, new_d);
- cell->setPort(ID::Q, new_q);
- cell->unsetPort(ID::ARST);
- cell->unsetParam(ID::ARST_POLARITY);
- cell->unsetParam(ID::ARST_VALUE);
- cell->type = ID($dff);
+ // Skip for $_FF_ and $ff cells.
+ if (ff.has_d && !ff.has_clk && !ff.has_en)
continue;
- }
- if (cell->type.in(ID($dffsr)))
+ if (ff.has_clk)
{
- // bool clk_pol = cell->parameters[ID::CLK_POLARITY].as_bool();
- bool set_pol = cell->parameters[ID::SET_POLARITY].as_bool();
- bool clr_pol = cell->parameters[ID::CLR_POLARITY].as_bool();
-
- // SigSpec sig_clk = cell->getPort(ID::CLK);
- SigSpec sig_set = cell->getPort(ID::SET);
- SigSpec sig_clr = cell->getPort(ID::CLR);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
- log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(sig_set), log_signal(sig_clr), log_signal(sig_d), log_signal(sig_q));
-
- Const init_val;
- for (int i = 0; i < GetSize(sig_q); i++) {
- SigBit bit = sigmap(sig_q[i]);
- init_val.bits.push_back(initbits.count(bit) ? initbits.at(bit) : State::Sx);
- del_initbits.insert(bit);
+ if (!ff.has_sr && !ff.has_arst)
+ continue;
+
+ if (ff.has_sr) {
+ ff.unmap_ce_srst(module);
+
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_d), log_signal(ff.sig_q));
+
+ initvals.remove_init(ff.sig_q);
+
+ Wire *new_d = module->addWire(NEW_ID, ff.width);
+ Wire *new_q = module->addWire(NEW_ID, ff.width);
+
+ SigSpec sig_set = ff.sig_set;
+ SigSpec sig_clr = ff.sig_clr;
+
+ if (!ff.pol_set) {
+ if (!ff.is_fine)
+ sig_set = module->Not(NEW_ID, sig_set);
+ else
+ sig_set = module->NotGate(NEW_ID, sig_set);
+ }
+
+ if (ff.pol_clr) {
+ if (!ff.is_fine)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+ else
+ sig_clr = module->NotGate(NEW_ID, sig_clr);
+ }
+
+ if (!ff.is_fine) {
+ SigSpec tmp = module->Or(NEW_ID, ff.sig_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->Or(NEW_ID, new_q, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, ff.sig_q);
+ } else {
+ SigSpec tmp = module->OrGate(NEW_ID, ff.sig_d, sig_set);
+ module->addAndGate(NEW_ID, tmp, sig_clr, new_d);
+
+ tmp = module->OrGate(NEW_ID, new_q, sig_set);
+ module->addAndGate(NEW_ID, tmp, sig_clr, ff.sig_q);
+ }
+
+ ff.sig_d = new_d;
+ ff.sig_q = new_q;
+ ff.has_sr = false;
+ } else if (ff.has_arst) {
+ ff.unmap_srst(module);
+
+ log("Replacing %s.%s (%s): ARST=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_arst), log_signal(ff.sig_d), log_signal(ff.sig_q));
+
+ initvals.remove_init(ff.sig_q);
+
+ Wire *new_q = module->addWire(NEW_ID, ff.width);
+
+ if (ff.pol_arst) {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, new_q, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, new_q, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ } else {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, ff.val_arst, new_q, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], new_q, ff.sig_arst, ff.sig_q);
+ }
+
+ ff.sig_q = new_q;
+ ff.has_arst = false;
+ ff.has_srst = true;
+ ff.val_srst = ff.val_arst;
+ ff.sig_srst = ff.sig_arst;
+ ff.pol_srst = ff.pol_arst;
}
-
- Wire *new_d = module->addWire(NEW_ID, GetSize(sig_d));
- Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
- new_q->attributes[ID::init] = init_val;
-
- if (!set_pol)
- sig_set = module->Not(NEW_ID, sig_set);
-
- if (clr_pol)
- sig_clr = module->Not(NEW_ID, sig_clr);
-
- SigSpec tmp = module->Or(NEW_ID, sig_d, sig_set);
- module->addAnd(NEW_ID, tmp, sig_clr, new_d);
-
- tmp = module->Or(NEW_ID, new_q, sig_set);
- module->addAnd(NEW_ID, tmp, sig_clr, sig_q);
-
- cell->setPort(ID::D, new_d);
- cell->setPort(ID::Q, new_q);
- cell->unsetPort(ID::SET);
- cell->unsetPort(ID::CLR);
- cell->unsetParam(ID::SET_POLARITY);
- cell->unsetParam(ID::CLR_POLARITY);
- cell->type = ID($dff);
- continue;
}
-
- if (cell->type.in(ID($dlatch)))
+ else
{
- bool en_pol = cell->parameters[ID::EN_POLARITY].as_bool();
-
- SigSpec sig_en = cell->getPort(ID::EN);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
-
+ // Latch.
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);
+ log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q));
+
+ initvals.remove_init(ff.sig_q);
+
+ Wire *new_q = module->addWire(NEW_ID, ff.width);
+ Wire *new_d;
+
+ if (ff.has_d) {
+ new_d = module->addWire(NEW_ID, ff.width);
+ if (ff.pol_en) {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d);
+ else
+ module->addMuxGate(NEW_ID, new_q, ff.sig_d, ff.sig_en, new_d);
+ } else {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d);
+ else
+ module->addMuxGate(NEW_ID, ff.sig_d, new_q, ff.sig_en, new_d);
+ }
+ } else {
+ new_d = new_q;
}
- Wire *new_q = module->addWire(NEW_ID, GetSize(sig_q));
- new_q->attributes[ID::init] = init_val;
-
- if (en_pol) {
- module->addMux(NEW_ID, new_q, sig_d, sig_en, sig_q);
+ if (ff.has_sr) {
+ SigSpec sig_set = ff.sig_set;
+ SigSpec sig_clr = ff.sig_clr;
+
+ if (!ff.pol_set) {
+ if (!ff.is_fine)
+ sig_set = module->Not(NEW_ID, sig_set);
+ else
+ sig_set = module->NotGate(NEW_ID, sig_set);
+ }
+
+ if (ff.pol_clr) {
+ if (!ff.is_fine)
+ sig_clr = module->Not(NEW_ID, sig_clr);
+ else
+ sig_clr = module->NotGate(NEW_ID, sig_clr);
+ }
+
+ if (!ff.is_fine) {
+ SigSpec tmp = module->Or(NEW_ID, new_d, sig_set);
+ module->addAnd(NEW_ID, tmp, sig_clr, ff.sig_q);
+ } else {
+ SigSpec tmp = module->OrGate(NEW_ID, new_d, sig_set);
+ module->addAndGate(NEW_ID, tmp, sig_clr, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ if (ff.pol_arst) {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, new_d, ff.val_arst, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, new_d, ff.val_arst[0], ff.sig_arst, ff.sig_q);
+ } else {
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, ff.val_arst, new_d, ff.sig_arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, ff.val_arst[0], new_d, ff.sig_arst, ff.sig_q);
+ }
} else {
- module->addMux(NEW_ID, sig_d, new_q, sig_en, sig_q);
+ module->connect(ff.sig_q, new_d);
}
- cell->setPort(ID::D, sig_q);
- cell->setPort(ID::Q, new_q);
- cell->unsetPort(ID::EN);
- cell->unsetParam(ID::EN_POLARITY);
- cell->type = ID($ff);
- continue;
+ ff.sig_d = new_d;
+ ff.sig_q = new_q;
+ ff.has_en = false;
+ ff.has_arst = false;
+ ff.has_sr = false;
+ ff.has_d = true;
}
- }
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- bool delete_initattr = true;
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (del_initbits.count(initsig[i]) > 0)
- initval[i] = State::Sx;
- else if (initval[i] != State::Sx)
- delete_initattr = false;
-
- if (delete_initattr)
- wire->attributes.erase(ID::init);
- else
- wire->attributes.at(ID::init) = initval;
- }
+ IdString name = cell->name;
+ module->remove(cell);
+ ff.emit(module, name);
+ }
}
}
} Async2syncPass;
diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc
index cc24db6d4..2cb91c009 100644
--- a/passes/sat/clk2fflogic.cc
+++ b/passes/sat/clk2fflogic.cc
@@ -19,6 +19,8 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
+#include "kernel/ff.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -80,19 +82,7 @@ struct Clk2fflogicPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, State> initbits;
- pool<SigBit> del_initbits;
-
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (initval[i] == State::S0 || initval[i] == State::S1)
- initbits[initsig[i]] = initval[i];
- }
+ FfInitVals initvals(&sigmap, module);
for (auto cell : vector<Cell*>(module->selected_cells()))
{
@@ -177,221 +167,112 @@ struct Clk2fflogicPass : public Pass {
cell->setPort(ID::WR_DATA, wr_data_port);
}
- if (cell->type.in(ID($dlatch), ID($adlatch), ID($dlatchsr)))
- {
- bool enpol = cell->parameters[ID::EN_POLARITY].as_bool();
-
- SigSpec sig_en = cell->getPort(ID::EN);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::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));
-
- sig_en = wrap_async_control(module, sig_en, enpol);
+ SigSpec qval;
+ if (RTLIL::builtin_ff_cell_types().count(cell->type)) {
+ FfData ff(&initvals, cell);
- Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
- module->addFf(NEW_ID, sig_q, past_q);
-
- if (cell->type == ID($dlatch))
- {
- module->addMux(NEW_ID, past_q, sig_d, sig_en, sig_q);
+ if (ff.has_d && !ff.has_clk && !ff.has_en) {
+ // Already a $ff or $_FF_ cell.
+ continue;
}
- else if (cell->type == ID($adlatch))
- {
- SigSpec t = module->Mux(NEW_ID, past_q, sig_d, sig_en);
- SigSpec arst = wrap_async_control(module, cell->getPort(ID::ARST), cell->parameters[ID::ARST_POLARITY].as_bool());
- Const rstval = cell->parameters[ID::ARST_VALUE];
-
- module->addMux(NEW_ID, t, rstval, arst, sig_q);
- }
- else
- {
- SigSpec t = module->Mux(NEW_ID, past_q, sig_d, sig_en);
-
- SigSpec s = wrap_async_control(module, cell->getPort(ID::SET), cell->parameters[ID::SET_POLARITY].as_bool());
- t = module->Or(NEW_ID, t, s);
- SigSpec c = wrap_async_control(module, cell->getPort(ID::CLR), cell->parameters[ID::CLR_POLARITY].as_bool());
- c = module->Not(NEW_ID, c);
- module->addAnd(NEW_ID, t, c, sig_q);
- }
-
- Const initval;
- bool assign_initval = false;
- for (int i = 0; i < GetSize(sig_d); i++) {
- SigBit qbit = sigmap(sig_q[i]);
- if (initbits.count(qbit)) {
- initval.bits.push_back(initbits.at(qbit));
- del_initbits.insert(qbit);
- } else
- initval.bits.push_back(State::Sx);
- if (initval.bits.back() != State::Sx)
- assign_initval = true;
+ Wire *past_q = module->addWire(NEW_ID, ff.width);
+ if (!ff.is_fine) {
+ module->addFf(NEW_ID, ff.sig_q, past_q);
+ } else {
+ module->addFfGate(NEW_ID, ff.sig_q, past_q);
}
+ if (!ff.val_init.is_fully_undef())
+ initvals.set_init(past_q, ff.val_init);
- if (assign_initval)
- past_q->attributes[ID::init] = initval;
-
- module->remove(cell);
- continue;
- }
-
- bool word_dff = cell->type.in(ID($dff), ID($adff), ID($dffsr));
- if (word_dff || cell->type.in(ID($_DFF_N_), ID($_DFF_P_),
- ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_),
- ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
- {
- bool clkpol;
- SigSpec clk;
- if (word_dff) {
- clkpol = cell->parameters[ID::CLK_POLARITY].as_bool();
- clk = cell->getPort(ID::CLK);
- }
- else {
- if (cell->type.in(ID($_DFF_P_), ID($_DFF_N_),
- ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_)))
- clkpol = cell->type[6] == 'P';
- else if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
- clkpol = cell->type[8] == 'P';
- else log_abort();
- clk = cell->getPort(ID::C);
- }
+ if (ff.has_clk) {
+ ff.unmap_ce_srst(module);
- Wire *past_clk = module->addWire(NEW_ID);
- past_clk->attributes[ID::init] = clkpol ? State::S1 : State::S0;
+ Wire *past_clk = module->addWire(NEW_ID);
+ initvals.set_init(past_clk, ff.pol_clk ? State::S1 : State::S0);
- if (word_dff)
- module->addFf(NEW_ID, clk, past_clk);
- else
- module->addFfGate(NEW_ID, clk, past_clk);
+ if (!ff.is_fine)
+ module->addFf(NEW_ID, ff.sig_clk, past_clk);
+ else
+ module->addFfGate(NEW_ID, ff.sig_clk, past_clk);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
+ log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_clk), log_signal(ff.sig_d), log_signal(ff.sig_q));
- log("Replacing %s.%s (%s): CLK=%s, D=%s, Q=%s\n",
- log_id(module), log_id(cell), log_id(cell->type),
- log_signal(clk), log_signal(sig_d), log_signal(sig_q));
+ SigSpec clock_edge_pattern;
- SigSpec clock_edge_pattern;
+ if (ff.pol_clk) {
+ clock_edge_pattern.append(State::S0);
+ clock_edge_pattern.append(State::S1);
+ } else {
+ clock_edge_pattern.append(State::S1);
+ clock_edge_pattern.append(State::S0);
+ }
- if (clkpol) {
- clock_edge_pattern.append(State::S0);
- clock_edge_pattern.append(State::S1);
- } else {
- clock_edge_pattern.append(State::S1);
- clock_edge_pattern.append(State::S0);
- }
+ SigSpec clock_edge = module->Eqx(NEW_ID, {ff.sig_clk, SigSpec(past_clk)}, clock_edge_pattern);
- SigSpec clock_edge = module->Eqx(NEW_ID, {clk, SigSpec(past_clk)}, clock_edge_pattern);
+ Wire *past_d = module->addWire(NEW_ID, ff.width);
+ if (!ff.is_fine)
+ module->addFf(NEW_ID, ff.sig_d, past_d);
+ else
+ module->addFfGate(NEW_ID, ff.sig_d, past_d);
- Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d));
- Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q));
- if (word_dff) {
- module->addFf(NEW_ID, sig_d, past_d);
- module->addFf(NEW_ID, sig_q, past_q);
- }
- else {
- module->addFfGate(NEW_ID, sig_d, past_d);
- module->addFfGate(NEW_ID, sig_q, past_q);
- }
+ if (!ff.val_init.is_fully_undef())
+ initvals.set_init(past_d, ff.val_init);
- if (cell->type == ID($adff))
- {
- SigSpec arst = wrap_async_control(module, cell->getPort(ID::ARST), cell->parameters[ID::ARST_POLARITY].as_bool());
- SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
- Const rstval = cell->parameters[ID::ARST_VALUE];
+ if (!ff.is_fine)
+ qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
+ else
+ qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
+ } else if (ff.has_d) {
- module->addMux(NEW_ID, qval, rstval, arst, sig_q);
- }
- else
- if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_),
- ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_)))
- {
- SigSpec arst = wrap_async_control_gate(module, cell->getPort(ID::R), cell->type[7] == 'P');
- SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
- SigBit rstval = (cell->type[8] == '1');
+ log("Replacing %s.%s (%s): EN=%s, D=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_en), log_signal(ff.sig_d), log_signal(ff.sig_q));
- module->addMuxGate(NEW_ID, qval, rstval, arst, sig_q);
- }
- else
- if (cell->type == ID($dffsr))
- {
- SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge);
- SigSpec setval = wrap_async_control(module, cell->getPort(ID::SET), cell->parameters[ID::SET_POLARITY].as_bool());
- SigSpec clrval = wrap_async_control(module, cell->getPort(ID::CLR), cell->parameters[ID::CLR_POLARITY].as_bool());
+ SigSpec sig_en = wrap_async_control(module, ff.sig_en, ff.pol_en);
- clrval = module->Not(NEW_ID, clrval);
- qval = module->Or(NEW_ID, qval, setval);
- module->addAnd(NEW_ID, qval, clrval, sig_q);
- }
- else
- if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_),
- ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_)))
- {
- SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge);
- SigSpec setval = wrap_async_control_gate(module, cell->getPort(ID::S), cell->type[9] == 'P');
- SigSpec clrval = wrap_async_control_gate(module, cell->getPort(ID::R), cell->type[10] == 'P');
+ if (!ff.is_fine)
+ qval = module->Mux(NEW_ID, past_q, ff.sig_d, sig_en);
+ else
+ qval = module->MuxGate(NEW_ID, past_q, ff.sig_d, sig_en);
+ } else {
- clrval = module->NotGate(NEW_ID, clrval);
- qval = module->OrGate(NEW_ID, qval, setval);
- module->addAndGate(NEW_ID, qval, clrval, sig_q);
- }
- else if (cell->type == ID($dff))
- {
- module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q);
- }
- else
- {
- module->addMuxGate(NEW_ID, past_q, past_d, clock_edge, sig_q);
- }
+ log("Replacing %s.%s (%s): SET=%s, CLR=%s, Q=%s\n",
+ log_id(module), log_id(cell), log_id(cell->type),
+ log_signal(ff.sig_set), log_signal(ff.sig_clr), log_signal(ff.sig_q));
- Const initval;
- bool assign_initval = false;
- for (int i = 0; i < GetSize(sig_d); i++) {
- SigBit qbit = sigmap(sig_q[i]);
- if (initbits.count(qbit)) {
- initval.bits.push_back(initbits.at(qbit));
- del_initbits.insert(qbit);
- } else
- initval.bits.push_back(State::Sx);
- if (initval.bits.back() != State::Sx)
- assign_initval = true;
+ qval = past_q;
}
- if (assign_initval) {
- past_d->attributes[ID::init] = initval;
- past_q->attributes[ID::init] = initval;
+ if (ff.has_sr) {
+ SigSpec setval = wrap_async_control(module, ff.sig_set, ff.pol_set);
+ SigSpec clrval = wrap_async_control(module, ff.sig_clr, ff.pol_clr);
+ if (!ff.is_fine) {
+ clrval = module->Not(NEW_ID, clrval);
+ qval = module->Or(NEW_ID, qval, setval);
+ module->addAnd(NEW_ID, qval, clrval, ff.sig_q);
+ } else {
+ clrval = module->NotGate(NEW_ID, clrval);
+ qval = module->OrGate(NEW_ID, qval, setval);
+ module->addAndGate(NEW_ID, qval, clrval, ff.sig_q);
+ }
+ } else if (ff.has_arst) {
+ SigSpec arst = wrap_async_control(module, ff.sig_arst, ff.pol_arst);
+ if (!ff.is_fine)
+ module->addMux(NEW_ID, qval, ff.val_arst, arst, ff.sig_q);
+ else
+ module->addMuxGate(NEW_ID, qval, ff.val_arst[0], arst, ff.sig_q);
+ } else {
+ module->connect(ff.sig_q, qval);
}
+ initvals.remove_init(ff.sig_q);
module->remove(cell);
continue;
}
}
-
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init) > 0)
- {
- bool delete_initattr = true;
- Const initval = wire->attributes.at(ID::init);
- SigSpec initsig = sigmap(wire);
-
- for (int i = 0; i < GetSize(initval) && i < GetSize(initsig); i++)
- if (del_initbits.count(initsig[i]) > 0)
- initval[i] = State::Sx;
- else if (initval[i] != State::Sx)
- delete_initattr = false;
-
- if (delete_initattr)
- wire->attributes.erase(ID::init);
- else
- wire->attributes.at(ID::init) = initval;
- }
}
}
diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc
index 46f7f5070..6db7d4b64 100644
--- a/passes/sat/qbfsat.cc
+++ b/passes/sat/qbfsat.cc
@@ -215,7 +215,6 @@ QbfSolutionType call_qbf_solver(RTLIL::Module *mod, const QbfSolveOptions &opt,
//Execute and capture stdout from `yosys-smtbmc -s z3 -t 1 -g --binary [--dump-smt2 <file>]`
QbfSolutionType ret;
const std::string yosys_smtbmc_exe = proc_self_dirname() + "yosys-smtbmc";
- const std::string smt2_command = stringf("write_smt2 -stbv -wires %s/problem%d.smt2", tempdir_name.c_str(), iter_num);
const std::string smtbmc_warning = "z3: WARNING:";
const std::string smtbmc_cmd = stringf("%s -s %s %s -t 1 -g --binary %s %s/problem%d.smt2 2>&1",
yosys_smtbmc_exe.c_str(), opt.get_solver_name().c_str(),
@@ -223,6 +222,10 @@ QbfSolutionType call_qbf_solver(RTLIL::Module *mod, const QbfSolveOptions &opt,
(opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file : "").c_str(),
tempdir_name.c_str(), iter_num);
+ std::string smt2_command = "write_smt2 -stbv -wires ";
+ for (auto &solver_opt : opt.solver_options)
+ smt2_command += stringf("-solver-option %s %s ", solver_opt.first.c_str(), solver_opt.second.c_str());
+ smt2_command += stringf("%s/problem%d.smt2", tempdir_name.c_str(), iter_num);
Pass::call(mod->design, smt2_command);
auto process_line = [&ret, &smtbmc_warning, &opt, &quiet](const std::string &line) {
@@ -419,6 +422,13 @@ QbfSolveOptions parse_args(const std::vector<std::string> &args) {
}
continue;
}
+ else if (args[opt.argidx] == "-solver-option") {
+ if (args.size() <= opt.argidx + 2)
+ log_cmd_error("solver option name and value not fully specified.\n");
+ opt.solver_options.emplace(args[opt.argidx+1], args[opt.argidx+2]);
+ opt.argidx += 2;
+ continue;
+ }
else if (args[opt.argidx] == "-timeout") {
if (args.size() <= opt.argidx + 1)
log_cmd_error("timeout not specified.\n");
@@ -533,6 +543,9 @@ struct QbfSatPass : public Pass {
log(" Use a particular solver. Choose one of: \"z3\", \"yices\", and \"cvc4\".\n");
log(" (default: yices)\n");
log("\n");
+ log(" -solver-option <name> <value>\n");
+ log(" Set the specified solver option in the SMT-LIBv2 problem file.\n");
+ log("\n");
log(" -timeout <value>\n");
log(" Set the per-iteration timeout in seconds.\n");
log(" (default: no timeout)\n");
diff --git a/passes/sat/qbfsat.h b/passes/sat/qbfsat.h
index 401f9c7a6..c96c6f818 100644
--- a/passes/sat/qbfsat.h
+++ b/passes/sat/qbfsat.h
@@ -31,6 +31,7 @@ struct QbfSolveOptions {
bool nobisection = false, sat = false, unsat = false, show_smtbmc = false;
enum Solver{Z3, Yices, CVC4} solver = Yices;
enum OptimizationLevel{O0, O1, O2} oflag = O0;
+ dict<std::string, std::string> solver_options;
int timeout = 0;
std::string specialize_soln_file = "";
std::string write_soln_soln_file = "";
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index e3b3d8dea..5a4d84f94 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -43,6 +43,7 @@ OBJS += passes/techmap/attrmap.o
OBJS += passes/techmap/zinit.o
OBJS += passes/techmap/dfflegalize.o
OBJS += passes/techmap/dff2dffs.o
+OBJS += passes/techmap/dffunmap.o
OBJS += passes/techmap/flowmap.o
OBJS += passes/techmap/extractinv.o
endif
diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc
index 0a58fdcc0..ce50e9a5b 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -44,6 +44,7 @@
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
+#include "kernel/ffinit.h"
#include "kernel/cost.h"
#include "kernel/log.h"
#include <stdlib.h>
@@ -111,7 +112,7 @@ SigMap assign_map;
RTLIL::Module *module;
std::vector<gate_t> signal_list;
std::map<RTLIL::SigBit, int> signal_map;
-std::map<RTLIL::SigBit, RTLIL::State> signal_init;
+FfInitVals initvals;
pool<std::string> enabled_gates;
bool recover_init, cmos_cost;
@@ -133,10 +134,7 @@ int map_signal(RTLIL::SigBit bit, gate_type_t gate_type = G(NONE), int in1 = -1,
gate.in4 = -1;
gate.is_port = false;
gate.bit = bit;
- if (signal_init.count(bit))
- gate.init = signal_init.at(bit);
- else
- gate.init = State::Sx;
+ gate.init = initvals(bit);
signal_list.push_back(gate);
signal_map[bit] = gate.id;
}
@@ -1468,7 +1466,7 @@ struct AbcPass : public Pass {
assign_map.clear();
signal_list.clear();
signal_map.clear();
- signal_init.clear();
+ initvals.clear();
pi_map.clear();
po_map.clear();
@@ -1854,24 +1852,7 @@ struct AbcPass : public Pass {
}
assign_map.set(mod);
- signal_init.clear();
-
- for (Wire *wire : mod->wires())
- if (wire->attributes.count(ID::init)) {
- SigSpec initsig = assign_map(wire);
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
- switch (initval[i]) {
- case State::S0:
- signal_init[initsig[i]] = State::S0;
- break;
- case State::S1:
- signal_init[initsig[i]] = State::S1;
- break;
- default:
- break;
- }
- }
+ initvals.set(&assign_map, mod);
if (!dff_mode || !clk_str.empty()) {
abc_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
@@ -2028,7 +2009,7 @@ struct AbcPass : public Pass {
assign_map.clear();
signal_list.clear();
signal_map.clear();
- signal_init.clear();
+ initvals.clear();
pi_map.clear();
po_map.clear();
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
index e99c56d8d..7d017ac40 100644
--- a/passes/techmap/abc9.cc
+++ b/passes/techmap/abc9.cc
@@ -295,7 +295,7 @@ struct Abc9Pass : public ScriptPass
run("proc");
run("wbflip");
run("techmap -wb -map %$abc9 -map +/techmap.v A:abc9_flop");
- run("opt");
+ run("opt -nodffe -nosdff");
if (dff_mode || help_mode) {
if (!help_mode)
active_design->scratchpad_unset("abc9_ops.prep_dff_submod.did_something");
diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc
index c60a901c1..44af043db 100644
--- a/passes/techmap/dffinit.cc
+++ b/passes/techmap/dffinit.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -94,29 +95,10 @@ struct DffinitPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, State> init_bits;
- pool<SigBit> cleanup_bits;
- pool<SigBit> used_bits;
-
- for (auto wire : module->selected_wires()) {
- if (wire->attributes.count(ID::init)) {
- Const value = wire->attributes.at(ID::init);
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- if (value[i] != State::Sx)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
- }
- if (wire->port_output)
- for (auto bit : sigmap(wire))
- used_bits.insert(bit);
- }
+ FfInitVals initvals(&sigmap, module);
for (auto cell : module->selected_cells())
{
- for (auto it : cell->connections())
- if (!cell->known() || cell->input(it.first))
- for (auto bit : sigmap(it.second))
- used_bits.insert(bit);
-
if (ff_types.count(cell->type) == 0)
continue;
@@ -131,17 +113,18 @@ struct DffinitPass : public Pass {
if (cell->hasParam(it.second))
value = cell->getParam(it.second);
+ Const initval = initvals(sig);
+ initvals.remove_init(sig);
for (int i = 0; i < GetSize(sig); i++) {
- if (init_bits.count(sig[i]) == 0)
+ if (initval[i] == State::Sx)
continue;
while (GetSize(value.bits) <= i)
value.bits.push_back(State::S0);
- if (noreinit && value.bits[i] != State::Sx && value.bits[i] != init_bits.at(sig[i]))
+ if (noreinit && value.bits[i] != State::Sx && value.bits[i] != initval[i])
log_error("Trying to assign a different init value for %s.%s.%s which technically "
"have a conflicted init value.\n",
log_id(module), log_id(cell), log_id(it.second));
- value.bits[i] = init_bits.at(sig[i]);
- cleanup_bits.insert(sig[i]);
+ value.bits[i] = initval[i];
}
if (highlow_mode && GetSize(value) != 0) {
@@ -161,23 +144,6 @@ struct DffinitPass : public Pass {
}
}
}
-
- for (auto wire : module->selected_wires())
- if (wire->attributes.count(ID::init)) {
- Const &value = wire->attributes.at(ID::init);
- bool do_cleanup = true;
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
- SigBit bit = sigmap(SigBit(wire, i));
- if (cleanup_bits.count(bit) || !used_bits.count(bit))
- value[i] = State::Sx;
- else if (value[i] != State::Sx)
- do_cleanup = false;
- }
- if (do_cleanup) {
- log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
- wire->attributes.erase(ID::init);
- }
- }
}
}
} DffinitPass;
diff --git a/passes/techmap/dfflegalize.cc b/passes/techmap/dfflegalize.cc
index 13ce4f49a..8ad65493f 100644
--- a/passes/techmap/dfflegalize.cc
+++ b/passes/techmap/dfflegalize.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -170,7 +171,7 @@ struct DffLegalizePass : public Pass {
dict<SigBit, int> srst_used;
SigMap sigmap;
- dict<SigBit, std::pair<State,SigBit>> initbits;
+ FfInitVals initvals;
int flip_initmask(int mask) {
int res = mask & INIT_X;
@@ -303,13 +304,7 @@ struct DffLegalizePass : public Pass {
return;
}
- State initval = State::Sx;
- SigBit initbit;
- if (GetSize(sig_q) > 0 && initbits.count(sigmap(sig_q[0]))) {
- const auto &d = initbits.at(sigmap(sig_q[0]));
- initval = d.first;
- initbit = d.second;
- }
+ State initval = initvals(sig_q[0]);
FfInit initmask = INIT_X;
if (initval == State::S0)
@@ -345,12 +340,8 @@ flip_dqi:
sig_d = cell->module->NotGate(NEW_ID, sig_d[0]);
SigBit new_q = SigSpec(cell->module->addWire(NEW_ID))[0];
cell->module->addNotGate(NEW_ID, new_q, sig_q[0]);
- if (initbit.wire) {
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
- initbit = new_q;
- new_q.wire->attributes[ID::init] = initval;
- initbits[new_q] = std::make_pair(initval, new_q);
- }
+ initvals.remove_init(sig_q[0]);
+ initvals.set_init(new_q, initval);
sig_q = new_q;
continue;
}
@@ -484,15 +475,12 @@ unmap_enable:
}
log_warning("Emulating mismatched async reset and init with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adff_q = cell->module->addWire(NEW_ID);
Wire *dff_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- dff_q->attributes[ID::init] = initval;
- initbits[SigBit(dff_q, 0)] = std::make_pair(initval, SigBit(dff_q, 0));
- sel_q->attributes[ID::init] = State::S0;
- initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0));
+ initvals.set_init(SigBit(dff_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), State::S0);
Cell *cell_dff;
Cell *cell_adff;
Cell *cell_sel;
@@ -588,21 +576,15 @@ flip_dqisr:;
}
log_warning("Emulating async set + reset with several FFs and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adff0_q = cell->module->addWire(NEW_ID);
Wire *adff1_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- if (init0) {
- adff0_q->attributes[ID::init] = initval;
- initbits[SigBit(adff0_q, 0)] = std::make_pair(initval, SigBit(adff0_q, 0));
- }
- if (init1) {
- adff1_q->attributes[ID::init] = initval;
- initbits[SigBit(adff1_q, 0)] = std::make_pair(initval, SigBit(adff1_q, 0));
- }
- sel_q->attributes[ID::init] = initsel;
- initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0));
+ if (init0)
+ initvals.set_init(SigBit(adff0_q, 0), initval);
+ if (init1)
+ initvals.set_init(SigBit(adff1_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), initsel);
Cell *cell_adff0;
Cell *cell_adff1;
Cell *cell_sel;
@@ -741,15 +723,12 @@ flip_dqisr:;
// The only hope left is breaking down to adff + dff + dlatch + mux.
log_warning("Emulating mismatched async reset and init with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adlatch_q = cell->module->addWire(NEW_ID);
Wire *dlatch_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- dlatch_q->attributes[ID::init] = initval;
- initbits[SigBit(dlatch_q, 0)] = std::make_pair(initval, SigBit(dlatch_q, 0));
- sel_q->attributes[ID::init] = State::S0;
- initbits[SigBit(sel_q, 0)] = std::make_pair(State::S0, SigBit(sel_q, 0));
+ initvals.set_init(SigBit(dlatch_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), State::S0);
Cell *cell_dlatch;
Cell *cell_adlatch;
Cell *cell_sel;
@@ -797,21 +776,15 @@ flip_dqisr:;
}
log_warning("Emulating async set + reset with several latches and a mux for %s.%s\n", log_id(cell->module->name), log_id(cell->name));
- if (initbit.wire)
- initbit.wire->attributes.at(ID::init)[initbit.offset] = State::Sx;
+ initvals.remove_init(sig_q[0]);
Wire *adlatch0_q = cell->module->addWire(NEW_ID);
Wire *adlatch1_q = cell->module->addWire(NEW_ID);
Wire *sel_q = cell->module->addWire(NEW_ID);
- if (init0) {
- adlatch0_q->attributes[ID::init] = initval;
- initbits[SigBit(adlatch0_q, 0)] = std::make_pair(initval, SigBit(adlatch0_q, 0));
- }
- if (init1) {
- adlatch1_q->attributes[ID::init] = initval;
- initbits[SigBit(adlatch1_q, 0)] = std::make_pair(initval, SigBit(adlatch1_q, 0));
- }
- sel_q->attributes[ID::init] = initsel;
- initbits[SigBit(sel_q, 0)] = std::make_pair(initsel, SigBit(sel_q, 0));
+ if (init0)
+ initvals.set_init(SigBit(adlatch0_q, 0), initval);
+ if (init1)
+ initvals.set_init(SigBit(adlatch1_q, 0), initval);
+ initvals.set_init(SigBit(sel_q, 0), initsel);
Cell *cell_adlatch0;
Cell *cell_adlatch1;
Cell *cell_sel;
@@ -1294,35 +1267,7 @@ unrecognized:
for (auto module : design->selected_modules())
{
sigmap.set(module);
- initbits.clear();
-
- for (auto wire : module->wires())
- {
- if (wire->attributes.count(ID::init) == 0)
- continue;
-
- SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
- {
- SigBit bit = wirebits[i];
- State val = initval[i];
-
- if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
- continue;
-
- if (initbits.count(bit)) {
- if (initbits.at(bit).first != val)
- log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
- log_signal(bit), log_signal(SigBit(wire, i)),
- log_signal(val), log_signal(initbits.at(bit).first));
- continue;
- }
-
- initbits[bit] = std::make_pair(val,SigBit(wire,i));
- }
- }
+ initvals.set(&sigmap, module);
if (mince || minsrst) {
ce_used.clear();
@@ -1365,7 +1310,7 @@ unrecognized:
}
sigmap.clear();
- initbits.clear();
+ initvals.clear();
ce_used.clear();
srst_used.clear();
}
diff --git a/passes/techmap/dffunmap.cc b/passes/techmap/dffunmap.cc
new file mode 100644
index 000000000..fb107ff75
--- /dev/null
+++ b/passes/techmap/dffunmap.cc
@@ -0,0 +1,107 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 Marcelina Kościelnicka <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"
+#include "kernel/ff.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct DffunmapPass : public Pass {
+ DffunmapPass() : Pass("dffunmap", "unmap clock enable and synchronous reset from FFs") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" dffunmap [options] [selection]\n");
+ log("\n");
+ log("This pass transforms FF types with clock enable and/or synchronous reset into\n");
+ log("their base type (with neither clock enable nor sync reset) by emulating the clock\n");
+ log("enable and synchronous reset with multiplexers on the cell input.\n");
+ log("\n");
+ log(" -ce-only\n");
+ log(" unmap only clock enables, leave synchronous resets alone.\n");
+ log("\n");
+ log(" -srst-only\n");
+ log(" unmap only synchronous resets, leave clock enables alone.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ log_header(design, "Executing DFFUNMAP pass (unmap clock enable and synchronous reset from FFs).\n");
+
+ bool ce_only = false;
+ bool srst_only = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-ce-only") {
+ ce_only = true;
+ continue;
+ }
+ if (args[argidx] == "-srst-only") {
+ srst_only = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ if (ce_only && srst_only)
+ log_cmd_error("Options -ce-only and -srst-only are mutually exclusive!\n");
+
+ for (auto mod : design->selected_modules())
+ {
+ SigMap sigmap(mod);
+ FfInitVals initvals(&sigmap, mod);
+
+ for (auto cell : mod->selected_cells())
+ {
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type))
+ continue;
+
+ FfData ff(&initvals, cell);
+ IdString name = cell->name;
+
+ if (!ff.has_clk)
+ continue;
+
+ if (ce_only) {
+ if (!ff.has_en)
+ continue;
+ ff.unmap_ce(mod);
+ } else if (srst_only) {
+ if (!ff.has_srst)
+ continue;
+ ff.unmap_srst(mod);
+ } else {
+ if (!ff.has_en && !ff.has_srst)
+ continue;
+ ff.unmap_ce_srst(mod);
+ }
+
+ mod->remove(cell);
+ ff.emit(mod, name);
+ }
+ }
+ }
+} DffunmapPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc
index 237c261ae..b971068f7 100644
--- a/passes/techmap/shregmap.cc
+++ b/passes/techmap/shregmap.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -100,9 +101,8 @@ struct ShregmapWorker
int dff_count, shreg_count;
pool<Cell*> remove_cells;
- pool<SigBit> remove_init;
- dict<SigBit, bool> sigbit_init;
+ FfInitVals initvals;
dict<SigBit, Cell*> sigbit_chain_next;
dict<SigBit, Cell*> sigbit_chain_prev;
pool<SigBit> sigbit_with_non_chain_users;
@@ -116,16 +116,6 @@ struct ShregmapWorker
for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
}
-
- if (wire->attributes.count(ID::init)) {
- SigSpec initsig = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
- for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
- if (initval[i] == State::S0 && !opts.zinit)
- sigbit_init[initsig[i]] = false;
- else if (initval[i] == State::S1)
- sigbit_init[initsig[i]] = true;
- }
}
for (auto cell : module->cells())
@@ -137,8 +127,9 @@ struct ShregmapWorker
SigBit d_bit = sigmap(cell->getPort(d_port).as_bit());
SigBit q_bit = sigmap(cell->getPort(q_port).as_bit());
+ State initval = initvals(q_bit);
- if (opts.init || sigbit_init.count(q_bit) == 0)
+ if (opts.init || initval == State::Sx || (opts.zinit && initval == State::S0))
{
auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell));
if (!r.second) {
@@ -310,22 +301,17 @@ struct ShregmapWorker
if (opts.init) {
vector<State> initval;
for (int i = depth-1; i >= 0; i--) {
- SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
- if (sigbit_init.count(bit) == 0)
- initval.push_back(State::Sx);
- else if (sigbit_init.at(bit))
- initval.push_back(State::S1);
- else
- initval.push_back(State::S0);
- remove_init.insert(bit);
+ SigBit bit = chain[cursor+i]->getPort(q_port).as_bit();
+ initval.push_back(initvals(bit));
+ initvals.remove_init(bit);
}
first_cell->setParam(ID::INIT, initval);
}
if (opts.zinit)
for (int i = depth-1; i >= 0; i--) {
- SigBit bit = sigmap(chain[cursor+i]->getPort(q_port).as_bit());
- remove_init.insert(bit);
+ SigBit bit = chain[cursor+i]->getPort(q_port).as_bit();
+ initvals.remove_init(bit);
}
if (opts.params)
@@ -364,22 +350,6 @@ struct ShregmapWorker
for (auto cell : remove_cells)
module->remove(cell);
- for (auto wire : module->wires())
- {
- if (wire->attributes.count(ID::init) == 0)
- continue;
-
- SigSpec initsig = sigmap(wire);
- Const &initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
- if (remove_init.count(initsig[i]))
- initval[i] = State::Sx;
-
- if (SigSpec(initval).is_fully_undef())
- wire->attributes.erase(ID::init);
- }
-
remove_cells.clear();
sigbit_chain_next.clear();
sigbit_chain_prev.clear();
@@ -389,6 +359,7 @@ struct ShregmapWorker
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
{
+ initvals.set(&sigmap, module);
make_sigbit_chain_next_prev();
find_chain_start_cells();
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index f98d1564a..c22ae8ef0 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -20,6 +20,7 @@
#include "kernel/yosys.h"
#include "kernel/utils.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
#include "libs/sha1/sha1.h"
#include <stdlib.h>
@@ -426,18 +427,7 @@ struct TechmapWorker
LogMakeDebugHdl mkdebug;
SigMap sigmap(module);
-
- dict<SigBit, State> init_bits;
- pool<SigBit> remove_init_bits;
-
- for (auto wire : module->wires()) {
- if (wire->attributes.count(ID::init)) {
- Const value = wire->attributes.at(ID::init);
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
- if (value[i] != State::Sx)
- init_bits[sigmap(SigBit(wire, i))] = value[i];
- }
- }
+ FfInitVals initvals(&sigmap, module);
TopoSort<RTLIL::Cell*, IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_inbit;
@@ -643,6 +633,8 @@ struct TechmapWorker
if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0)
parameters.emplace(ID::_TECHMAP_CELLTYPE_, RTLIL::unescape_id(cell->type));
+ if (tpl->avail_parameters.count(ID::_TECHMAP_CELLNAME_) != 0)
+ parameters.emplace(ID::_TECHMAP_CELLNAME_, RTLIL::unescape_id(cell->name));
for (auto &conn : cell->connections()) {
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first))) != 0) {
@@ -659,15 +651,7 @@ struct TechmapWorker
parameters.emplace(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const());
}
if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first))) != 0) {
- auto sig = sigmap(conn.second);
- RTLIL::Const value(State::Sx, sig.size());
- for (int i = 0; i < sig.size(); i++) {
- auto it = init_bits.find(sig[i]);
- if (it != init_bits.end()) {
- value[i] = it->second;
- }
- }
- parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), value);
+ parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), initvals(conn.second));
}
}
@@ -912,7 +896,7 @@ struct TechmapWorker
auto sig = sigmap(it->second);
for (int i = 0; i < sig.size(); i++)
if (val[i] == State::S1)
- remove_init_bits.insert(sig[i]);
+ initvals.remove_init(sig[i]);
}
}
}
@@ -961,25 +945,6 @@ struct TechmapWorker
handled_cells.insert(cell);
}
- if (!remove_init_bits.empty()) {
- for (auto wire : module->wires())
- if (wire->attributes.count(ID::init)) {
- Const &value = wire->attributes.at(ID::init);
- bool do_cleanup = true;
- for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
- SigBit bit = sigmap(SigBit(wire, i));
- if (remove_init_bits.count(bit))
- value[i] = State::Sx;
- else if (value[i] != State::Sx)
- do_cleanup = false;
- }
- if (do_cleanup) {
- log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
- wire->attributes.erase(ID::init);
- }
- }
- }
-
if (log_continue) {
log_header(design, "Continuing TECHMAP pass.\n");
log_continue = false;
@@ -1111,6 +1076,10 @@ struct TechmapPass : public Pass {
log(" When a parameter with this name exists, it will be set to the type name\n");
log(" of the cell that matches the module.\n");
log("\n");
+ log(" _TECHMAP_CELLNAME_\n");
+ log(" When a parameter with this name exists, it will be set to the name\n");
+ log(" of the cell that matches the module.\n");
+ log("\n");
log(" _TECHMAP_CONSTMSK_<port-name>_\n");
log(" _TECHMAP_CONSTVAL_<port-name>_\n");
log(" When this pair of parameters is available in a module for a port, then\n");
diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc
index cc0b26bcc..e3b4ae573 100644
--- a/passes/techmap/zinit.cc
+++ b/passes/techmap/zinit.cc
@@ -19,6 +19,7 @@
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/ffinit.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -57,35 +58,7 @@ struct ZinitPass : public Pass {
for (auto module : design->selected_modules())
{
SigMap sigmap(module);
- dict<SigBit, std::pair<State,SigBit>> initbits;
-
- for (auto wire : module->selected_wires())
- {
- if (wire->attributes.count(ID::init) == 0)
- continue;
-
- SigSpec wirebits = sigmap(wire);
- Const initval = wire->attributes.at(ID::init);
-
- for (int i = 0; i < GetSize(wirebits) && i < GetSize(initval); i++)
- {
- SigBit bit = wirebits[i];
- State val = initval[i];
-
- if (val != State::S0 && val != State::S1 && bit.wire != nullptr)
- continue;
-
- if (initbits.count(bit)) {
- if (initbits.at(bit).first != val)
- log_error("Conflicting init values for signal %s (%s = %s != %s).\n",
- log_signal(bit), log_signal(SigBit(wire, i)),
- log_signal(val), log_signal(initbits.at(bit).first));
- continue;
- }
-
- initbits[bit] = std::make_pair(val,SigBit(wire,i));
- }
- }
+ FfInitVals initvals(&sigmap, module);
pool<IdString> dff_types = {
// FIXME: It would appear that supporting
@@ -127,33 +100,28 @@ struct ZinitPass : public Pass {
if (GetSize(sig_d) < 1 || GetSize(sig_q) < 1)
continue;
- Const initval;
+ Const initval = initvals(sig_q);
+ Const newval = initval;
+ initvals.remove_init(sig_q);
- for (int i = 0; i < GetSize(sig_q); i++) {
- if (initbits.count(sig_q[i])) {
- const auto &d = initbits.at(sig_q[i]);
- initval.bits.push_back(d.first);
- const auto &b = d.second;
- b.wire->attributes.at(ID::init)[b.offset] = State::Sx;
- } else
- initval.bits.push_back(all_mode ? State::S0 : State::Sx);
- }
-
- Wire *initwire = module->addWire(NEW_ID, GetSize(initval));
- initwire->attributes[ID::init] = initval;
+ Wire *initwire = module->addWire(NEW_ID, GetSize(sig_q));
for (int i = 0; i < GetSize(initwire); i++)
if (initval[i] == State::S1)
{
sig_d[i] = module->NotGate(NEW_ID, sig_d[i]);
module->addNotGate(NEW_ID, SigSpec(initwire, i), sig_q[i]);
- initwire->attributes[ID::init][i] = State::S0;
+ newval[i] = State::S0;
}
else
{
module->connect(sig_q[i], SigSpec(initwire, i));
+ if (all_mode)
+ newval[i] = State::S0;
}
+ initvals.set_init(initwire, newval);
+
log("FF init value for cell %s (%s): %s = %s\n", log_id(cell), log_id(cell->type),
log_signal(sig_q), log_signal(initval));
diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc
index b4c65e658..89d6e530e 100644
--- a/techlibs/common/synth.cc
+++ b/techlibs/common/synth.cc
@@ -220,6 +220,9 @@ struct SynthPass : public ScriptPass
run("opt_expr");
run("opt_clean");
run("check");
+ run("opt -nodffe -nosdff");
+ if (!nofsm)
+ run("fsm" + fsm_opts, " (unless -nofsm)");
run("opt");
run("wreduce");
run("peepopt");
@@ -233,9 +236,6 @@ struct SynthPass : public ScriptPass
if (!noshare)
run("share", " (unless -noshare)");
run("opt");
- if (!nofsm)
- run("fsm" + fsm_opts, " (unless -nofsm)");
- run("opt -fast");
run("memory -nomap" + memory_opts);
run("opt_clean");
}
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index 46d051e44..3cee9722e 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -257,6 +257,8 @@ struct SynthEcp5Pass : public ScriptPass
run("opt_expr");
run("opt_clean");
run("check");
+ run("opt -nodffe -nosdff");
+ run("fsm");
run("opt");
run("wreduce");
run("peepopt");
@@ -271,8 +273,6 @@ struct SynthEcp5Pass : public ScriptPass
}
run("alumacc");
run("opt");
- run("fsm");
- run("opt -fast");
run("memory -nomap");
run("opt_clean");
}
@@ -311,16 +311,20 @@ struct SynthEcp5Pass : public ScriptPass
if (check_label("map_ffs"))
{
- run("dff2dffs");
run("opt_clean");
- if (!nodffe)
- run("dff2dffe -direct-match $_DFF_* -direct-match $_SDFF_*");
- if (help_mode)
- run("dfflegalize -cell $_DFF_?_ 01 -cell $_DFFE_??_ 01 -cell $_DFF_?P?_ r -cell $_DFFE_?P??_ r -cell $_SDFF_?P?_ r -cell $_SDFFE_?P??_ r -cell $_DLATCH_?_ x [-cell $_DFFSR_?PP_ x]", "($_DFFSR_*_ only if -asyncprld)");
- else if (asyncprld)
- run("dfflegalize -cell $_DFF_?_ 01 -cell $_DFFE_??_ 01 -cell $_DFF_?P?_ r -cell $_DFFE_?P??_ r -cell $_SDFF_?P?_ r -cell $_SDFFE_?P??_ r -cell $_DLATCH_?_ x -cell $_DFFSR_?PP_ x");
- else
- run("dfflegalize -cell $_DFF_?_ 01 -cell $_DFFE_??_ 01 -cell $_DFF_?P?_ r -cell $_DFFE_?P??_ r -cell $_SDFF_?P?_ r -cell $_SDFFE_?P??_ r -cell $_DLATCH_?_ x");
+ std::string dfflegalize_args = " -cell $_DFF_?_ 01 -cell $_DFF_?P?_ r -cell $_SDFF_?P?_ r";
+ if (help_mode) {
+ dfflegalize_args += " [-cell $_DFFE_??_ 01 -cell $_DFFE_?P??_ r -cell $_SDFFE_?P??_ r]";
+ } else if (!nodffe) {
+ dfflegalize_args += " -cell $_DFFE_??_ 01 -cell $_DFFE_?P??_ r -cell $_SDFFE_?P??_ r";
+ }
+ dfflegalize_args += " -cell $_DLATCH_?_ x";
+ if (help_mode) {
+ dfflegalize_args += " [-cell $_DFFSR_?PP_ x]";
+ } else if (asyncprld) {
+ dfflegalize_args += " -cell $_DFFSR_?PP_ x";
+ }
+ run("dfflegalize" + dfflegalize_args, "($_DFFSR_*_ only if -asyncprld, $_*DFFE_* only if not -nodffe)");
if ((abc9 && dff) || help_mode)
run("zinit -all w:* t:$_DFF_?_ t:$_DFFE_??_ t:$_SDFF*", "(only if -abc9 and -dff");
run(stringf("techmap -D NO_LUT %s -map +/ecp5/cells_map.v", help_mode ? "[-D ASYNC_PRLD]" : (asyncprld ? "-D ASYNC_PRLD" : "")));
diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc
index d7b11d431..4d1e968ae 100644
--- a/techlibs/gowin/synth_gowin.cc
+++ b/techlibs/gowin/synth_gowin.cc
@@ -219,11 +219,11 @@ struct SynthGowinPass : public ScriptPass
if (check_label("map_ffs"))
{
- run("dff2dffs -match-init");
run("opt_clean");
- if (!nodffe)
- run("dff2dffe -direct-match $_DFF_* -direct-match $_SDFF_*");
- run("dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_SDFF_?P?_ r -cell $_SDFFE_?P?P_ r -cell $_DFF_?P?_ r -cell $_DFFE_?P?P_ r");
+ if (nodffe)
+ run("dfflegalize -cell $_DFF_?_ 0 -cell $_SDFF_?P?_ r -cell $_DFF_?P?_ r");
+ else
+ run("dfflegalize -cell $_DFF_?_ 0 -cell $_DFFE_?P_ 0 -cell $_SDFF_?P?_ r -cell $_SDFFE_?P?P_ r -cell $_DFF_?P?_ r -cell $_DFFE_?P?P_ r");
run("techmap -map +/gowin/cells_map.v");
run("opt_expr -mux_undef");
run("simplemap");
diff --git a/techlibs/greenpak4/synth_greenpak4.cc b/techlibs/greenpak4/synth_greenpak4.cc
index 17b5d4782..d9af340d9 100644
--- a/techlibs/greenpak4/synth_greenpak4.cc
+++ b/techlibs/greenpak4/synth_greenpak4.cc
@@ -162,7 +162,7 @@ struct SynthGreenPAK4Pass : public ScriptPass
run("opt -undriven -fine");
run("techmap -map +/techmap.v -map +/greenpak4/cells_latch.v");
run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib");
- run("opt -fast");
+ run("opt -fast -noclkinv -noff");
if (retime || help_mode)
run("abc -dff -D 1", "(only if -retime)");
}
diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc
index 4f4faf6d5..8ce3cb024 100644
--- a/techlibs/ice40/Makefile.inc
+++ b/techlibs/ice40/Makefile.inc
@@ -1,7 +1,6 @@
OBJS += techlibs/ice40/synth_ice40.o
OBJS += techlibs/ice40/ice40_braminit.o
-OBJS += techlibs/ice40/ice40_ffssr.o
OBJS += techlibs/ice40/ice40_opt.o
GENFILES += techlibs/ice40/brams_init1.vh
diff --git a/techlibs/ice40/ice40_ffssr.cc b/techlibs/ice40/ice40_ffssr.cc
deleted file mode 100644
index 492029b77..000000000
--- a/techlibs/ice40/ice40_ffssr.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * yosys -- Yosys Open SYnthesis Suite
- *
- * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "kernel/yosys.h"
-#include "kernel/sigtools.h"
-
-USING_YOSYS_NAMESPACE
-PRIVATE_NAMESPACE_BEGIN
-
-struct Ice40FfssrPass : public Pass {
- Ice40FfssrPass() : Pass("ice40_ffssr", "iCE40: merge synchronous set/reset into FF cells") { }
- void help() override
- {
- log("\n");
- log(" ice40_ffssr [options] [selection]\n");
- log("\n");
- log("Merge synchronous set/reset $_MUX_ cells into iCE40 FFs.\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) override
- {
- log_header(design, "Executing ICE40_FFSSR pass (merge synchronous set/reset into FF cells).\n");
-
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++)
- {
- // if (args[argidx] == "-singleton") {
- // singleton_mode = true;
- // continue;
- // }
- break;
- }
- extra_args(args, argidx, design);
-
- pool<IdString> sb_dff_types;
- sb_dff_types.insert(ID(SB_DFF));
- sb_dff_types.insert(ID(SB_DFFE));
- sb_dff_types.insert(ID(SB_DFFN));
- sb_dff_types.insert(ID(SB_DFFNE));
-
- for (auto module : design->selected_modules())
- {
- log("Merging set/reset $_MUX_ cells into SB_FFs in %s.\n", log_id(module));
-
- SigMap sigmap(module);
- dict<SigBit, Cell*> sr_muxes;
- vector<Cell*> ff_cells;
-
- for (auto cell : module->selected_cells())
- {
- if (sb_dff_types.count(cell->type)) {
- ff_cells.push_back(cell);
- continue;
- }
-
- if (cell->type != ID($_MUX_))
- continue;
-
- SigBit bit_a = sigmap(cell->getPort(ID::A));
- SigBit bit_b = sigmap(cell->getPort(ID::B));
-
- if (bit_a.wire == nullptr || bit_b.wire == nullptr)
- sr_muxes[sigmap(cell->getPort(ID::Y))] = cell;
- }
-
- for (auto cell : ff_cells)
- {
- if (cell->get_bool_attribute(ID(dont_touch)))
- continue;
-
- SigSpec sig_d = cell->getPort(ID::D);
-
- if (GetSize(sig_d) < 1)
- continue;
-
- SigBit bit_d = sigmap(sig_d[0]);
-
- if (sr_muxes.count(bit_d) == 0)
- continue;
-
- Cell *mux_cell = sr_muxes.at(bit_d);
- SigBit bit_a = sigmap(mux_cell->getPort(ID::A));
- SigBit bit_b = sigmap(mux_cell->getPort(ID::B));
- SigBit bit_s = sigmap(mux_cell->getPort(ID::S));
-
- log(" Merging %s (A=%s, B=%s, S=%s) into %s (%s).\n", log_id(mux_cell),
- log_signal(bit_a), log_signal(bit_b), log_signal(bit_s), log_id(cell), log_id(cell->type));
-
- SigBit sr_val, sr_sig;
- if (bit_a.wire == nullptr) {
- bit_d = bit_b;
- sr_val = bit_a;
- sr_sig = module->NotGate(NEW_ID, bit_s);
- } else {
- log_assert(bit_b.wire == nullptr);
- bit_d = bit_a;
- sr_val = bit_b;
- sr_sig = bit_s;
- }
-
- if (sr_val == State::S1) {
- cell->type = cell->type.str() + "SS";
- cell->setPort(ID::S, sr_sig);
- cell->setPort(ID::D, bit_d);
- } else {
- cell->type = cell->type.str() + "SR";
- cell->setPort(ID::R, sr_sig);
- cell->setPort(ID::D, bit_d);
- }
- }
- }
- }
-} Ice40FfssrPass;
-
-PRIVATE_NAMESPACE_END
diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc
index 1a70fa8c0..d28607904 100644
--- a/techlibs/ice40/ice40_opt.cc
+++ b/techlibs/ice40/ice40_opt.cc
@@ -215,7 +215,7 @@ struct Ice40OptPass : public Pass {
log(" <ice40 specific optimizations>\n");
log(" opt_expr -mux_undef -undriven [-full]\n");
log(" opt_merge\n");
- log(" opt_rmdff\n");
+ log(" opt_dff\n");
log(" opt_clean\n");
log(" while <changed design>\n");
log("\n");
@@ -247,7 +247,7 @@ struct Ice40OptPass : public Pass {
Pass::call(design, "opt_expr " + opt_expr_args);
Pass::call(design, "opt_merge");
- Pass::call(design, "opt_rmdff");
+ Pass::call(design, "opt_dff");
Pass::call(design, "opt_clean");
if (design->scratchpad_get_bool("opt.did_something") == false)
diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc
index 4ddb6ca70..b945889fe 100644
--- a/techlibs/ice40/synth_ice40.cc
+++ b/techlibs/ice40/synth_ice40.cc
@@ -292,6 +292,8 @@ struct SynthIce40Pass : public ScriptPass
run("opt_expr");
run("opt_clean");
run("check");
+ run("opt -nodffe -nosdff");
+ run("fsm");
run("opt");
run("wreduce");
run("peepopt");
@@ -316,8 +318,6 @@ struct SynthIce40Pass : public ScriptPass
}
run("alumacc");
run("opt");
- run("fsm");
- run("opt -fast");
run("memory -nomap");
run("opt_clean");
}
@@ -354,11 +354,6 @@ struct SynthIce40Pass : public ScriptPass
if (check_label("map_ffs"))
{
- if (!nodffe)
- run("dff2dffe -direct-match $_DFF_*");
- if (min_ce_use >= 0) {
- run("opt_merge");
- }
if (nodffe)
run(stringf("dfflegalize -cell $_DFF_?_ 0 -cell $_DFF_?P?_ 0 -cell $_SDFF_?P?_ 0 -cell $_DLATCH_?_ x"));
else
@@ -366,7 +361,6 @@ struct SynthIce40Pass : public ScriptPass
run("techmap -map +/ice40/ff_map.v");
run("opt_expr -mux_undef");
run("simplemap");
- run("ice40_ffssr");
run("ice40_opt -full");
}
diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc
index 1fa98d098..090237722 100644
--- a/techlibs/intel/synth_intel.cc
+++ b/techlibs/intel/synth_intel.cc
@@ -202,8 +202,6 @@ struct SynthIntelPass : public ScriptPass {
run("opt -fast -mux_undef -undriven -fine -full");
run("memory_map");
run("opt -undriven -fine");
- run("dff2dffe -direct-match $_DFF_*");
- run("opt -fine");
run("techmap -map +/techmap.v");
run("opt -full");
run("clean -purge");
diff --git a/techlibs/intel_alm/Makefile.inc b/techlibs/intel_alm/Makefile.inc
index 552f00c65..e36c81c0e 100644
--- a/techlibs/intel_alm/Makefile.inc
+++ b/techlibs/intel_alm/Makefile.inc
@@ -15,9 +15,9 @@ $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/ds
$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/mem_sim.v))
# RAM
-bramtypes := m10k m20k
-$(foreach bramtype, $(bramtypes), $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_$(bramtype).txt)))
-$(foreach bramtype, $(bramtypes), $(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_$(bramtype)_map.v)))
+$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m10k.txt))
+$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m20k.txt))
+$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/bram_m20k_map.v))
$(eval $(call add_share_file,share/intel_alm/common,techlibs/intel_alm/common/lutram_mlab.txt))
# Miscellaneous
diff --git a/techlibs/intel_alm/common/alm_sim.v b/techlibs/intel_alm/common/alm_sim.v
index 979c51132..906a95b0b 100644
--- a/techlibs/intel_alm/common/alm_sim.v
+++ b/techlibs/intel_alm/common/alm_sim.v
@@ -69,6 +69,14 @@
`default_nettype none
+// Cyclone V LUT output timings (picoseconds):
+//
+// CARRY A B C D E F G
+// COMBOUT - 605 583 510 512 - 97 400 (LUT6)
+// COMBOUT - 602 583 457 510 302 93 483 (LUT7)
+// SUMOUT 368 1342 1323 887 927 - 785 -
+// CARRYOUT 71 1082 1062 866 813 - 1198 -
+
(* abc9_lut=2, lib_whitebox *)
module MISTRAL_ALUT6(input A, B, C, D, E, F, output Q);
@@ -76,12 +84,12 @@ parameter [63:0] LUT = 64'h0000_0000_0000_0000;
`ifdef cyclonev
specify
- (A => Q) = 602;
- (B => Q) = 584;
+ (A => Q) = 605;
+ (B => Q) = 583;
(C => Q) = 510;
- (D => Q) = 510;
- (E => Q) = 339;
- (F => Q) = 94;
+ (D => Q) = 512;
+ (E => Q) = 400;
+ (F => Q) = 97;
endspecify
`endif
`ifdef cyclone10gx
@@ -107,11 +115,11 @@ parameter [31:0] LUT = 32'h0000_0000;
`ifdef cyclonev
specify
- (A => Q) = 584;
+ (A => Q) = 583;
(B => Q) = 510;
- (C => Q) = 510;
- (D => Q) = 339;
- (E => Q) = 94;
+ (C => Q) = 512;
+ (D => Q) = 400;
+ (E => Q) = 97;
endspecify
`endif
`ifdef cyclone10gx
@@ -137,9 +145,9 @@ parameter [15:0] LUT = 16'h0000;
`ifdef cyclonev
specify
(A => Q) = 510;
- (B => Q) = 510;
- (C => Q) = 339;
- (D => Q) = 94;
+ (B => Q) = 512;
+ (C => Q) = 400;
+ (D => Q) = 97;
endspecify
`endif
`ifdef cyclone10gx
@@ -164,8 +172,8 @@ parameter [7:0] LUT = 8'h00;
`ifdef cyclonev
specify
(A => Q) = 510;
- (B => Q) = 339;
- (C => Q) = 94;
+ (B => Q) = 400;
+ (C => Q) = 97;
endspecify
`endif
`ifdef cyclone10gx
@@ -188,8 +196,8 @@ parameter [3:0] LUT = 4'h0;
`ifdef cyclonev
specify
- (A => Q) = 339;
- (B => Q) = 94;
+ (A => Q) = 400;
+ (B => Q) = 97;
endspecify
`endif
`ifdef cyclone10gx
@@ -209,7 +217,7 @@ module MISTRAL_NOT(input A, output Q);
`ifdef cyclonev
specify
- (A => Q) = 94;
+ (A => Q) = 97;
endspecify
`endif
`ifdef cyclone10gx
@@ -230,31 +238,33 @@ parameter LUT1 = 16'h0000;
`ifdef cyclonev
specify
- (A => SO) = 1283;
- (B => SO) = 1167;
- (C => SO) = 866;
- (D0 => SO) = 756;
- (D1 => SO) = 756;
- (CI => SO) = 355;
- (A => CO) = 950;
- (B => CO) = 1039;
- (C => CO) = 820;
- (D0 => CO) = 1006;
- (D1 => CO) = 1006;
- (CI => CO) = 23;
+ (A => SO) = 1342;
+ (B => SO) = 1323;
+ (C => SO) = 927;
+ (D0 => SO) = 887;
+ (D1 => SO) = 785;
+ (CI => SO) = 368;
+
+ (A => CO) = 1082;
+ (B => CO) = 1062;
+ (C => CO) = 813;
+ (D0 => CO) = 866;
+ (D1 => CO) = 1198;
+ (CI => CO) = 36; // Divided by 2 to account for there being two ALUT_ARITHs in an ALM)
endspecify
`endif
`ifdef cyclone10gx
specify
- (A => SO) = 644;
- (B => SO) = 477;
- (C => SO) = 416;
+ (A => SO) = 644;
+ (B => SO) = 477;
+ (C => SO) = 416;
(D0 => SO) = 380;
(D1 => SO) = 431;
(CI => SO) = 276;
- (A => CO) = 525;
- (B => CO) = 433;
- (C => CO) = 712;
+
+ (A => CO) = 525;
+ (B => CO) = 433;
+ (C => CO) = 712;
(D0 => CO) = 653;
(D1 => CO) = 593;
(CI => CO) = 16;
diff --git a/techlibs/intel_alm/common/bram_m10k.txt b/techlibs/intel_alm/common/bram_m10k.txt
index 837e3a330..e9355fe2c 100644
--- a/techlibs/intel_alm/common/bram_m10k.txt
+++ b/techlibs/intel_alm/common/bram_m10k.txt
@@ -1,4 +1,4 @@
-bram __MISTRAL_M10K_SDP
+bram MISTRAL_M10K
init 0 # TODO: Re-enable when I figure out how BRAM init works
abits 13 @D8192x1
dbits 1 @D8192x1
@@ -27,7 +27,7 @@ bram __MISTRAL_M10K_SDP
endbram
-match __MISTRAL_M10K_SDP
+match MISTRAL_M10K
min efficiency 5
make_transp
endmatch
diff --git a/techlibs/intel_alm/common/bram_m10k_map.v b/techlibs/intel_alm/common/bram_m10k_map.v
deleted file mode 100644
index 061463c3e..000000000
--- a/techlibs/intel_alm/common/bram_m10k_map.v
+++ /dev/null
@@ -1,31 +0,0 @@
-module __MISTRAL_M10K_SDP(CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
-
-parameter CFG_ABITS = 10;
-parameter CFG_DBITS = 10;
-parameter CFG_ENABLE_A = 1;
-parameter CFG_ENABLE_B = 1;
-
-input CLK1;
-input [CFG_ABITS-1:0] A1ADDR, B1ADDR;
-input [CFG_DBITS-1:0] A1DATA;
-output [CFG_DBITS-1:0] B1DATA;
-input [CFG_ENABLE_A-1:0] A1EN, B1EN;
-
-altsyncram #(
- .operation_mode("dual_port"),
- .ram_block_type("m10k"),
- .widthad_a(CFG_ABITS),
- .width_a(CFG_DBITS),
- .widthad_b(CFG_ABITS),
- .width_b(CFG_DBITS),
-) _TECHMAP_REPLACE_ (
- .address_a(A1ADDR),
- .data_a(A1DATA),
- .wren_a(A1EN),
- .address_b(B1ADDR),
- .q_b(B1DATA),
- .clock0(CLK1),
- .clock1(CLK1)
-);
-
-endmodule
diff --git a/techlibs/intel_alm/common/dff_sim.v b/techlibs/intel_alm/common/dff_sim.v
index 9ff8f9f67..d2cff0adb 100644
--- a/techlibs/intel_alm/common/dff_sim.v
+++ b/techlibs/intel_alm/common/dff_sim.v
@@ -54,43 +54,44 @@
//
// Note: the DFFEAS primitive is mostly emulated; it does not reflect what the hardware implements.
-`ifdef cyclonev
-`define SYNCPATH 262
-`define SYNCSETUP 522
-`define COMBPATH 0
-`endif
-`ifdef cyclone10gx
-`define SYNCPATH 219
-`define SYNCSETUP 268
-`define COMBPATH 0
-`endif
-
-// fallback for when a family isn't detected (e.g. when techmapping for equivalence)
-`ifndef SYNCPATH
-`define SYNCPATH 0
-`define SYNCSETUP 0
-`define COMBPATH 0
-`endif
-
(* abc9_box, lib_whitebox *)
module MISTRAL_FF(
input DATAIN, CLK, ACLR, ENA, SCLR, SLOAD, SDATA,
output reg Q
);
+`ifdef cyclonev
specify
- if (ENA && ACLR !== 1'b0 && !SCLR && !SLOAD) (posedge CLK => (Q : DATAIN)) = `SYNCPATH;
- if (ENA && SCLR) (posedge CLK => (Q : 1'b0)) = `SYNCPATH;
- if (ENA && !SCLR && SLOAD) (posedge CLK => (Q : SDATA)) = `SYNCPATH;
+ if (ENA && ACLR !== 1'b0 && !SCLR && !SLOAD) (posedge CLK => (Q : DATAIN)) = 731;
+ if (ENA && SCLR) (posedge CLK => (Q : 1'b0)) = 890;
+ if (ENA && !SCLR && SLOAD) (posedge CLK => (Q : SDATA)) = 618;
- $setup(DATAIN, posedge CLK, `SYNCSETUP);
- $setup(ENA, posedge CLK, `SYNCSETUP);
- $setup(SCLR, posedge CLK, `SYNCSETUP);
- $setup(SLOAD, posedge CLK, `SYNCSETUP);
- $setup(SDATA, posedge CLK, `SYNCSETUP);
+ $setup(DATAIN, posedge CLK, /* -196 */ 0);
+ $setup(ENA, posedge CLK, /* -196 */ 0);
+ $setup(SCLR, posedge CLK, /* -196 */ 0);
+ $setup(SLOAD, posedge CLK, /* -196 */ 0);
+ $setup(SDATA, posedge CLK, /* -196 */ 0);
- if (ACLR === 1'b0) (ACLR => Q) = `COMBPATH;
+ if (ACLR === 1'b0) (ACLR => Q) = 282;
endspecify
+`endif
+`ifdef cyclone10gx
+specify
+ // TODO (long-term): investigate these numbers.
+ // It seems relying on the Quartus Timing Analyzer was not the best idea; it's too fiddly.
+ if (ENA && ACLR !== 1'b0 && !SCLR && !SLOAD) (posedge CLK => (Q : DATAIN)) = 219;
+ if (ENA && SCLR) (posedge CLK => (Q : 1'b0)) = 219;
+ if (ENA && !SCLR && SLOAD) (posedge CLK => (Q : SDATA)) = 219;
+
+ $setup(DATAIN, posedge CLK, 268);
+ $setup(ENA, posedge CLK, 268);
+ $setup(SCLR, posedge CLK, 268);
+ $setup(SLOAD, posedge CLK, 268);
+ $setup(SDATA, posedge CLK, 268);
+
+ if (ACLR === 1'b0) (ACLR => Q) = 0;
+endspecify
+`endif
initial begin
// Altera flops initialise to zero.
diff --git a/techlibs/intel_alm/common/dsp_sim.v b/techlibs/intel_alm/common/dsp_sim.v
index 5dc4c02de..45fdebb3f 100644
--- a/techlibs/intel_alm/common/dsp_sim.v
+++ b/techlibs/intel_alm/common/dsp_sim.v
@@ -1,9 +1,10 @@
(* abc9_box *)
-module MISTRAL_MUL27x27(input [26:0] A, input [26:0] B, output [53:0] Y);
+module MISTRAL_MUL27X27(input [26:0] A, input [26:0] B, output [53:0] Y);
+// TODO: Cyclone 10 GX timings; the below are for Cyclone V
specify
- (A *> Y) = 4057;
- (B *> Y) = 4057;
+ (A *> Y) = 3732;
+ (B *> Y) = 3928;
endspecify
assign Y = $signed(A) * $signed(B);
@@ -13,9 +14,10 @@ endmodule
(* abc9_box *)
module MISTRAL_MUL18X18(input [17:0] A, input [17:0] B, output [35:0] Y);
+// TODO: Cyclone 10 GX timings; the below are for Cyclone V
specify
- (A *> Y) = 4057;
- (B *> Y) = 4057;
+ (A *> Y) = 3180;
+ (B *> Y) = 3982;
endspecify
assign Y = $signed(A) * $signed(B);
@@ -25,9 +27,10 @@ endmodule
(* abc9_box *)
module MISTRAL_MUL9X9(input [8:0] A, input [8:0] B, output [17:0] Y);
+// TODO: Cyclone 10 GX timings; the below are for Cyclone V
specify
- (A *> Y) = 4057;
- (B *> Y) = 4057;
+ (A *> Y) = 2818;
+ (B *> Y) = 3051;
endspecify
assign Y = $signed(A) * $signed(B);
diff --git a/techlibs/intel_alm/common/megafunction_bb.v b/techlibs/intel_alm/common/megafunction_bb.v
index b5a3d8892..530e44054 100644
--- a/techlibs/intel_alm/common/megafunction_bb.v
+++ b/techlibs/intel_alm/common/megafunction_bb.v
@@ -2,6 +2,306 @@
`default_nettype none
(* blackbox *)
+module altera_pll
+#(
+ parameter reference_clock_frequency = "0 ps",
+ parameter fractional_vco_multiplier = "false",
+ parameter pll_type = "General",
+ parameter pll_subtype = "General",
+ parameter number_of_clocks = 1,
+ parameter operation_mode = "internal feedback",
+ parameter deserialization_factor = 4,
+ parameter data_rate = 0,
+
+ parameter sim_additional_refclk_cycles_to_lock = 0,
+ parameter output_clock_frequency0 = "0 ps",
+ parameter phase_shift0 = "0 ps",
+ parameter duty_cycle0 = 50,
+
+ parameter output_clock_frequency1 = "0 ps",
+ parameter phase_shift1 = "0 ps",
+ parameter duty_cycle1 = 50,
+
+ parameter output_clock_frequency2 = "0 ps",
+ parameter phase_shift2 = "0 ps",
+ parameter duty_cycle2 = 50,
+
+ parameter output_clock_frequency3 = "0 ps",
+ parameter phase_shift3 = "0 ps",
+ parameter duty_cycle3 = 50,
+
+ parameter output_clock_frequency4 = "0 ps",
+ parameter phase_shift4 = "0 ps",
+ parameter duty_cycle4 = 50,
+
+ parameter output_clock_frequency5 = "0 ps",
+ parameter phase_shift5 = "0 ps",
+ parameter duty_cycle5 = 50,
+
+ parameter output_clock_frequency6 = "0 ps",
+ parameter phase_shift6 = "0 ps",
+ parameter duty_cycle6 = 50,
+
+ parameter output_clock_frequency7 = "0 ps",
+ parameter phase_shift7 = "0 ps",
+ parameter duty_cycle7 = 50,
+
+ parameter output_clock_frequency8 = "0 ps",
+ parameter phase_shift8 = "0 ps",
+ parameter duty_cycle8 = 50,
+
+ parameter output_clock_frequency9 = "0 ps",
+ parameter phase_shift9 = "0 ps",
+ parameter duty_cycle9 = 50,
+
+
+ parameter output_clock_frequency10 = "0 ps",
+ parameter phase_shift10 = "0 ps",
+ parameter duty_cycle10 = 50,
+
+ parameter output_clock_frequency11 = "0 ps",
+ parameter phase_shift11 = "0 ps",
+ parameter duty_cycle11 = 50,
+
+ parameter output_clock_frequency12 = "0 ps",
+ parameter phase_shift12 = "0 ps",
+ parameter duty_cycle12 = 50,
+
+ parameter output_clock_frequency13 = "0 ps",
+ parameter phase_shift13 = "0 ps",
+ parameter duty_cycle13 = 50,
+
+ parameter output_clock_frequency14 = "0 ps",
+ parameter phase_shift14 = "0 ps",
+ parameter duty_cycle14 = 50,
+
+ parameter output_clock_frequency15 = "0 ps",
+ parameter phase_shift15 = "0 ps",
+ parameter duty_cycle15 = 50,
+
+ parameter output_clock_frequency16 = "0 ps",
+ parameter phase_shift16 = "0 ps",
+ parameter duty_cycle16 = 50,
+
+ parameter output_clock_frequency17 = "0 ps",
+ parameter phase_shift17 = "0 ps",
+ parameter duty_cycle17 = 50,
+
+ parameter clock_name_0 = "",
+ parameter clock_name_1 = "",
+ parameter clock_name_2 = "",
+ parameter clock_name_3 = "",
+ parameter clock_name_4 = "",
+ parameter clock_name_5 = "",
+ parameter clock_name_6 = "",
+ parameter clock_name_7 = "",
+ parameter clock_name_8 = "",
+
+ parameter clock_name_global_0 = "false",
+ parameter clock_name_global_1 = "false",
+ parameter clock_name_global_2 = "false",
+ parameter clock_name_global_3 = "false",
+ parameter clock_name_global_4 = "false",
+ parameter clock_name_global_5 = "false",
+ parameter clock_name_global_6 = "false",
+ parameter clock_name_global_7 = "false",
+ parameter clock_name_global_8 = "false",
+
+ parameter m_cnt_hi_div = 1,
+ parameter m_cnt_lo_div = 1,
+ parameter m_cnt_bypass_en = "false",
+ parameter m_cnt_odd_div_duty_en = "false",
+ parameter n_cnt_hi_div = 1,
+ parameter n_cnt_lo_div = 1,
+ parameter n_cnt_bypass_en = "false",
+ parameter n_cnt_odd_div_duty_en = "false",
+ parameter c_cnt_hi_div0 = 1,
+ parameter c_cnt_lo_div0 = 1,
+ parameter c_cnt_bypass_en0 = "false",
+ parameter c_cnt_in_src0 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en0 = "false",
+ parameter c_cnt_prst0 = 1,
+ parameter c_cnt_ph_mux_prst0 = 0,
+ parameter c_cnt_hi_div1 = 1,
+ parameter c_cnt_lo_div1 = 1,
+ parameter c_cnt_bypass_en1 = "false",
+ parameter c_cnt_in_src1 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en1 = "false",
+ parameter c_cnt_prst1 = 1,
+ parameter c_cnt_ph_mux_prst1 = 0,
+ parameter c_cnt_hi_div2 = 1,
+ parameter c_cnt_lo_div2 = 1,
+ parameter c_cnt_bypass_en2 = "false",
+ parameter c_cnt_in_src2 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en2 = "false",
+ parameter c_cnt_prst2 = 1,
+ parameter c_cnt_ph_mux_prst2 = 0,
+ parameter c_cnt_hi_div3 = 1,
+ parameter c_cnt_lo_div3 = 1,
+ parameter c_cnt_bypass_en3 = "false",
+ parameter c_cnt_in_src3 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en3 = "false",
+ parameter c_cnt_prst3 = 1,
+ parameter c_cnt_ph_mux_prst3 = 0,
+ parameter c_cnt_hi_div4 = 1,
+ parameter c_cnt_lo_div4 = 1,
+ parameter c_cnt_bypass_en4 = "false",
+ parameter c_cnt_in_src4 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en4 = "false",
+ parameter c_cnt_prst4 = 1,
+ parameter c_cnt_ph_mux_prst4 = 0,
+ parameter c_cnt_hi_div5 = 1,
+ parameter c_cnt_lo_div5 = 1,
+ parameter c_cnt_bypass_en5 = "false",
+ parameter c_cnt_in_src5 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en5 = "false",
+ parameter c_cnt_prst5 = 1,
+ parameter c_cnt_ph_mux_prst5 = 0,
+ parameter c_cnt_hi_div6 = 1,
+ parameter c_cnt_lo_div6 = 1,
+ parameter c_cnt_bypass_en6 = "false",
+ parameter c_cnt_in_src6 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en6 = "false",
+ parameter c_cnt_prst6 = 1,
+ parameter c_cnt_ph_mux_prst6 = 0,
+ parameter c_cnt_hi_div7 = 1,
+ parameter c_cnt_lo_div7 = 1,
+ parameter c_cnt_bypass_en7 = "false",
+ parameter c_cnt_in_src7 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en7 = "false",
+ parameter c_cnt_prst7 = 1,
+ parameter c_cnt_ph_mux_prst7 = 0,
+ parameter c_cnt_hi_div8 = 1,
+ parameter c_cnt_lo_div8 = 1,
+ parameter c_cnt_bypass_en8 = "false",
+ parameter c_cnt_in_src8 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en8 = "false",
+ parameter c_cnt_prst8 = 1,
+ parameter c_cnt_ph_mux_prst8 = 0,
+ parameter c_cnt_hi_div9 = 1,
+ parameter c_cnt_lo_div9 = 1,
+ parameter c_cnt_bypass_en9 = "false",
+ parameter c_cnt_in_src9 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en9 = "false",
+ parameter c_cnt_prst9 = 1,
+ parameter c_cnt_ph_mux_prst9 = 0,
+ parameter c_cnt_hi_div10 = 1,
+ parameter c_cnt_lo_div10 = 1,
+ parameter c_cnt_bypass_en10 = "false",
+ parameter c_cnt_in_src10 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en10 = "false",
+ parameter c_cnt_prst10 = 1,
+ parameter c_cnt_ph_mux_prst10 = 0,
+ parameter c_cnt_hi_div11 = 1,
+ parameter c_cnt_lo_div11 = 1,
+ parameter c_cnt_bypass_en11 = "false",
+ parameter c_cnt_in_src11 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en11 = "false",
+ parameter c_cnt_prst11 = 1,
+ parameter c_cnt_ph_mux_prst11 = 0,
+ parameter c_cnt_hi_div12 = 1,
+ parameter c_cnt_lo_div12 = 1,
+ parameter c_cnt_bypass_en12 = "false",
+ parameter c_cnt_in_src12 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en12 = "false",
+ parameter c_cnt_prst12 = 1,
+ parameter c_cnt_ph_mux_prst12 = 0,
+ parameter c_cnt_hi_div13 = 1,
+ parameter c_cnt_lo_div13 = 1,
+ parameter c_cnt_bypass_en13 = "false",
+ parameter c_cnt_in_src13 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en13 = "false",
+ parameter c_cnt_prst13 = 1,
+ parameter c_cnt_ph_mux_prst13 = 0,
+ parameter c_cnt_hi_div14 = 1,
+ parameter c_cnt_lo_div14 = 1,
+ parameter c_cnt_bypass_en14 = "false",
+ parameter c_cnt_in_src14 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en14 = "false",
+ parameter c_cnt_prst14 = 1,
+ parameter c_cnt_ph_mux_prst14 = 0,
+ parameter c_cnt_hi_div15 = 1,
+ parameter c_cnt_lo_div15 = 1,
+ parameter c_cnt_bypass_en15 = "false",
+ parameter c_cnt_in_src15 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en15 = "false",
+ parameter c_cnt_prst15 = 1,
+ parameter c_cnt_ph_mux_prst15 = 0,
+ parameter c_cnt_hi_div16 = 1,
+ parameter c_cnt_lo_div16 = 1,
+ parameter c_cnt_bypass_en16 = "false",
+ parameter c_cnt_in_src16 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en16 = "false",
+ parameter c_cnt_prst16 = 1,
+ parameter c_cnt_ph_mux_prst16 = 0,
+ parameter c_cnt_hi_div17 = 1,
+ parameter c_cnt_lo_div17 = 1,
+ parameter c_cnt_bypass_en17 = "false",
+ parameter c_cnt_in_src17 = "ph_mux_clk",
+ parameter c_cnt_odd_div_duty_en17 = "false",
+ parameter c_cnt_prst17 = 1,
+ parameter c_cnt_ph_mux_prst17 = 0,
+ parameter pll_vco_div = 1,
+ parameter pll_slf_rst = "false",
+ parameter pll_bw_sel = "low",
+ parameter pll_output_clk_frequency = "0 MHz",
+ parameter pll_cp_current = 0,
+ parameter pll_bwctrl = 0,
+ parameter pll_fractional_division = 1,
+ parameter pll_fractional_cout = 24,
+ parameter pll_dsm_out_sel = "1st_order",
+ parameter mimic_fbclk_type = "gclk",
+ parameter pll_fbclk_mux_1 = "glb",
+ parameter pll_fbclk_mux_2 = "fb_1",
+ parameter pll_m_cnt_in_src = "ph_mux_clk",
+ parameter pll_vcoph_div = 1,
+ parameter refclk1_frequency = "0 MHz",
+ parameter pll_clkin_0_src = "clk_0",
+ parameter pll_clkin_1_src = "clk_0",
+ parameter pll_clk_loss_sw_en = "false",
+ parameter pll_auto_clk_sw_en = "false",
+ parameter pll_manu_clk_sw_en = "false",
+ parameter pll_clk_sw_dly = 0,
+ parameter pll_extclk_0_cnt_src = "pll_extclk_cnt_src_vss",
+ parameter pll_extclk_1_cnt_src = "pll_extclk_cnt_src_vss"
+) (
+ //input
+ input refclk,
+ input refclk1,
+ input fbclk,
+ input rst,
+ input phase_en,
+ input updn,
+ input [2:0] num_phase_shifts,
+ input scanclk,
+ input [4:0] cntsel,
+ input [63:0] reconfig_to_pll,
+ input extswitch,
+ input adjpllin,
+ input cclk,
+
+ //output
+ output [ number_of_clocks -1 : 0] outclk,
+ output fboutclk,
+ output locked,
+ output phase_done,
+ output [63:0] reconfig_from_pll,
+ output activeclk,
+ output [1:0] clkbad,
+ output [7:0] phout,
+ output [1:0] lvds_clk,
+ output [1:0] loaden,
+ output [1:0] extclk_out,
+ output [ number_of_clocks -1 : 0] cascade_out,
+
+ //inout
+ inout zdbfbclk
+);
+
+endmodule
+
+
+(* blackbox *)
module altera_std_synchronizer(clk, din, dout, reset_n);
parameter depth = 2;
@@ -14,6 +314,137 @@ output dout;
endmodule
(* blackbox *)
+module altddio_in (
+ datain, // required port, DDR input data
+ inclock, // required port, input reference clock to sample data by
+ inclocken, // enable data clock
+ aset, // asynchronous set
+ aclr, // asynchronous clear
+ sset, // synchronous set
+ sclr, // synchronous clear
+ dataout_h, // data sampled at the rising edge of inclock
+ dataout_l // data sampled at the falling edge of inclock
+);
+
+parameter width = 1;
+parameter power_up_high = "OFF";
+parameter invert_input_clocks = "OFF";
+parameter intended_device_family = "Stratix";
+parameter lpm_type = "altddio_in";
+parameter lpm_hint = "UNUSED";
+
+input [width-1:0] datain;
+input inclock;
+input inclocken;
+input aset;
+input aclr;
+input sset;
+input sclr;
+
+output [width-1:0] dataout_h;
+output [width-1:0] dataout_l;
+
+endmodule
+
+
+(* blackbox *)
+module altddio_out (
+ datain_h,
+ datain_l,
+ outclock,
+ outclocken,
+ aset,
+ aclr,
+ sset,
+ sclr,
+ oe,
+ dataout,
+ oe_out
+);
+
+parameter width = 1;
+parameter power_up_high = "OFF";
+parameter oe_reg = "UNUSED";
+parameter extend_oe_disable = "UNUSED";
+parameter intended_device_family = "Stratix";
+parameter invert_output = "OFF";
+parameter lpm_type = "altddio_out";
+parameter lpm_hint = "UNUSED";
+
+input [width-1:0] datain_h;
+input [width-1:0] datain_l;
+input outclock;
+input outclocken;
+input aset;
+input aclr;
+input sset;
+input sclr;
+input oe;
+
+output [width-1:0] dataout;
+output [width-1:0] oe_out;
+
+endmodule
+
+
+(* blackbox *)
+module altddio_bidir (
+ datain_h,
+ datain_l,
+ inclock,
+ inclocken,
+ outclock,
+ outclocken,
+ aset,
+ aclr,
+ sset,
+ sclr,
+ oe,
+ dataout_h,
+ dataout_l,
+ combout,
+ oe_out,
+ dqsundelayedout,
+ padio
+);
+
+// GLOBAL PARAMETER DECLARATION
+parameter width = 1; // required parameter
+parameter power_up_high = "OFF";
+parameter oe_reg = "UNUSED";
+parameter extend_oe_disable = "UNUSED";
+parameter implement_input_in_lcell = "UNUSED";
+parameter invert_output = "OFF";
+parameter intended_device_family = "Stratix";
+parameter lpm_type = "altddio_bidir";
+parameter lpm_hint = "UNUSED";
+
+// INPUT PORT DECLARATION
+input [width-1:0] datain_h;
+input [width-1:0] datain_l;
+input inclock;
+input inclocken;
+input outclock;
+input outclocken;
+input aset;
+input aclr;
+input sset;
+input sclr;
+input oe;
+
+// OUTPUT PORT DECLARATION
+output [width-1:0] dataout_h;
+output [width-1:0] dataout_l;
+output [width-1:0] combout;
+output [width-1:0] oe_out;
+output [width-1:0] dqsundelayedout;
+// BIDIRECTIONAL PORT DECLARATION
+inout [width-1:0] padio;
+
+endmodule
+
+
+(* blackbox *)
module altiobuf_in(datain, dataout);
parameter enable_bus_hold = "FALSE";
@@ -156,4 +587,39 @@ input [ax_width-1:0] ax;
input [ay_scan_in_width-1:0] ay;
output [result_a_width-1:0] resulta;
-endmodule \ No newline at end of file
+endmodule
+
+(* blackbox *)
+module cyclonev_ram_block(portaaddr, portadatain, portawe, portbaddr, portbdataout, portbre, clk0);
+
+parameter operation_mode = "dual_port";
+parameter logical_ram_name = "";
+parameter port_a_address_width = 10;
+parameter port_a_data_width = 10;
+parameter port_a_logical_ram_depth = 1024;
+parameter port_a_logical_ram_width = 10;
+parameter port_a_first_address = 0;
+parameter port_a_last_address = 1023;
+parameter port_a_first_bit_number = 0;
+parameter port_b_address_width = 10;
+parameter port_b_data_width = 10;
+parameter port_b_logical_ram_depth = 1024;
+parameter port_b_logical_ram_width = 10;
+parameter port_b_first_address = 0;
+parameter port_b_last_address = 1023;
+parameter port_b_first_bit_number = 0;
+parameter port_b_address_clock = "clock0";
+parameter port_b_read_enable_clock = "clock0";
+parameter mem_init0 = "";
+parameter mem_init1 = "";
+parameter mem_init2 = "";
+parameter mem_init3 = "";
+parameter mem_init4 = "";
+
+input [port_a_address_width-1:0] portaaddr;
+input [port_b_address_width-1:0] portbaddr;
+input [port_a_data_width-1:0] portadatain;
+output [port_b_data_width-1:0] portbdataout;
+input clk0, portawe, portbre;
+
+endmodule
diff --git a/techlibs/intel_alm/common/mem_sim.v b/techlibs/intel_alm/common/mem_sim.v
index f6f9ecb02..e09aafaa2 100644
--- a/techlibs/intel_alm/common/mem_sim.v
+++ b/techlibs/intel_alm/common/mem_sim.v
@@ -54,12 +54,17 @@ module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN, CLK1, input [4:0] B1
reg [31:0] mem = 32'b0;
-// TODO
+// TODO: Cyclone 10 GX timings; the below timings are for Cyclone V
specify
- $setup(A1ADDR, posedge CLK1, 0);
- $setup(A1DATA, posedge CLK1, 0);
+ $setup(A1ADDR, posedge CLK1, 86);
+ $setup(A1DATA, posedge CLK1, 86);
+ $setup(A1EN, posedge CLK1, 86);
- (B1ADDR *> B1DATA) = 0;
+ (B1ADDR[0] => B1DATA) = 487;
+ (B1ADDR[1] => B1DATA) = 475;
+ (B1ADDR[2] => B1DATA) = 382;
+ (B1ADDR[3] => B1DATA) = 284;
+ (B1ADDR[4] => B1DATA) = 96;
endspecify
always @(posedge CLK1)
@@ -68,3 +73,37 @@ always @(posedge CLK1)
assign B1DATA = mem[B1ADDR];
endmodule
+
+// The M10K
+// --------
+// TODO
+
+module MISTRAL_M10K(CLK1, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN);
+
+parameter CFG_ABITS = 10;
+parameter CFG_DBITS = 10;
+
+input CLK1;
+input [CFG_ABITS-1:0] A1ADDR, B1ADDR;
+input [CFG_DBITS-1:0] A1DATA;
+input A1EN, B1EN;
+output reg [CFG_DBITS-1:0] B1DATA;
+
+reg [2**CFG_ABITS * CFG_DBITS - 1 : 0] mem = 0;
+
+specify
+ $setup(A1ADDR, posedge CLK1, 0);
+ $setup(A1DATA, posedge CLK1, 0);
+
+ if (B1EN) (posedge CLK1 => (B1DATA : A1DATA)) = 0;
+endspecify
+
+always @(posedge CLK1) begin
+ if (A1EN)
+ mem[(A1ADDR + 1) * CFG_DBITS - 1 : A1ADDR * CFG_DBITS] <= A1DATA;
+
+ if (B1EN)
+ B1DATA <= mem[(B1ADDR + 1) * CFG_DBITS - 1 : B1ADDR * CFG_DBITS];
+end
+
+endmodule
diff --git a/techlibs/intel_alm/common/quartus_rename.v b/techlibs/intel_alm/common/quartus_rename.v
index 46ef2aa0d..9bc532ca2 100644
--- a/techlibs/intel_alm/common/quartus_rename.v
+++ b/techlibs/intel_alm/common/quartus_rename.v
@@ -88,6 +88,8 @@ endmodule
module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN, CLK1, input [4:0] B1ADDR, output B1DATA);
+parameter _TECHMAP_CELLNAME_ = "";
+
// Here we get to an unfortunate situation. The cell has a mem_init0 parameter,
// which takes in a hexadecimal string that could be used to initialise RAM.
// In the vendor simulation models, this appears to work fine, but Quartus,
@@ -99,7 +101,7 @@ module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN, CLK1, input [4:0] B1
// or an undocumented way to get Quartus to initialise from mem_init0 is found.
`MLAB #(
- .logical_ram_name("MISTRAL_MLAB"),
+ .logical_ram_name(_TECHMAP_CELLNAME_),
.logical_ram_depth(32),
.logical_ram_width(1),
.mixed_port_feed_through_mode("Dont Care"),
@@ -123,6 +125,53 @@ module MISTRAL_MLAB(input [4:0] A1ADDR, input A1DATA, A1EN, CLK1, input [4:0] B1
endmodule
+module MISTRAL_M10K(A1ADDR, A1DATA, A1EN, CLK1, B1ADDR, B1DATA, B1EN);
+
+parameter CFG_ABITS = 10;
+parameter CFG_DBITS = 10;
+
+parameter _TECHMAP_CELLNAME_ = "";
+
+input [CFG_ABITS-1:0] A1ADDR, B1ADDR;
+input [CFG_DBITS-1:0] A1DATA;
+input CLK1, A1EN, B1EN;
+output [CFG_DBITS-1:0] B1DATA;
+
+// Much like the MLAB, the M10K has mem_init[01234] parameters which would let
+// you initialise the RAM cell via hex literals. If they were implemented.
+
+cyclonev_ram_block #(
+ .operation_mode("dual_port"),
+ .logical_ram_name(_TECHMAP_CELLNAME_),
+ .port_a_address_width(CFG_ABITS),
+ .port_a_data_width(CFG_DBITS),
+ .port_a_logical_ram_depth(2**CFG_ABITS),
+ .port_a_logical_ram_width(CFG_DBITS),
+ .port_a_first_address(0),
+ .port_a_last_address(2**CFG_ABITS - 1),
+ .port_a_first_bit_number(0),
+ .port_b_address_width(CFG_ABITS),
+ .port_b_data_width(CFG_DBITS),
+ .port_b_logical_ram_depth(2**CFG_ABITS),
+ .port_b_logical_ram_width(CFG_DBITS),
+ .port_b_first_address(0),
+ .port_b_last_address(2**CFG_ABITS - 1),
+ .port_b_first_bit_number(0),
+ .port_b_address_clock("clock0"),
+ .port_b_read_enable_clock("clock0")
+) _TECHMAP_REPLACE_ (
+ .portaaddr(A1ADDR),
+ .portadatain(A1DATA),
+ .portawe(A1EN),
+ .portbaddr(B1ADDR),
+ .portbdataout(B1DATA),
+ .portbre(B1EN),
+ .clk0(CLK1)
+);
+
+endmodule
+
+
module MISTRAL_MUL27X27(input [26:0] A, B, output [53:0] Y);
`MAC #(.ax_width(27), .ay_scan_in_width(27), .result_a_width(54), .operation_mode("M27x27")) _TECHMAP_REPLACE_ (.ax(A), .ay(B), .resulta(Y));
diff --git a/techlibs/intel_alm/synth_intel_alm.cc b/techlibs/intel_alm/synth_intel_alm.cc
index b751e8413..83f0768a3 100644
--- a/techlibs/intel_alm/synth_intel_alm.cc
+++ b/techlibs/intel_alm/synth_intel_alm.cc
@@ -200,6 +200,8 @@ struct SynthIntelALMPass : public ScriptPass {
run("opt_expr");
run("opt_clean");
run("check");
+ run("opt -nodffe -nosdff");
+ run("fsm");
run("opt");
run("wreduce");
run("peepopt");
@@ -227,15 +229,14 @@ struct SynthIntelALMPass : public ScriptPass {
run("alumacc");
run("techmap -map +/intel_alm/common/arith_alm_map.v -map +/intel_alm/common/dsp_map.v");
run("opt");
- run("fsm");
- run("opt -fast");
run("memory -nomap");
run("opt_clean");
}
if (!nobram && check_label("map_bram", "(skip if -nobram)")) {
run(stringf("memory_bram -rules +/intel_alm/common/bram_%s.txt", bram_type.c_str()));
- run(stringf("techmap -map +/intel_alm/common/bram_%s_map.v", bram_type.c_str()));
+ if (help_mode || bram_type != "m10k")
+ run(stringf("techmap -map +/intel_alm/common/bram_%s_map.v", bram_type.c_str()));
}
if (!nolutram && check_label("map_lutram", "(skip if -nolutram)")) {
@@ -249,7 +250,6 @@ struct SynthIntelALMPass : public ScriptPass {
if (check_label("map_ffs")) {
run("techmap");
- run("dff2dffe");
run("dfflegalize -cell $_DFFE_PN0P_ 0 -cell $_SDFFCE_PP0P_ 0");
run("techmap -map +/intel_alm/common/dff_map.v");
run("opt -full -undriven -mux_undef");
@@ -258,7 +258,7 @@ struct SynthIntelALMPass : public ScriptPass {
if (check_label("map_luts")) {
run("techmap -map +/intel_alm/common/abc9_map.v");
- run(stringf("abc9 %s -maxlut 6 -W 200", help_mode ? "[-dff]" : dff ? "-dff" : ""));
+ run(stringf("abc9 %s -maxlut 6 -W 600", help_mode ? "[-dff]" : dff ? "-dff" : ""));
run("techmap -map +/intel_alm/common/abc9_unmap.v");
run("techmap -map +/intel_alm/common/alm_map.v");
run("opt -fast");
diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v
index 2fc216908..eb8a04bde 100644
--- a/techlibs/xilinx/arith_map.v
+++ b/techlibs/xilinx/arith_map.v
@@ -35,13 +35,7 @@ module _80_xilinx_lcu (P, G, CI, CO);
genvar i;
-`ifdef _EXPLICIT_CARRY
- localparam EXPLICIT_CARRY = 1'b1;
-`else
- localparam EXPLICIT_CARRY = 1'b0;
-`endif
-
-generate if (EXPLICIT_CARRY || `LUT_SIZE == 4) begin
+generate if (`LUT_SIZE == 4) begin
(* force_downto *)
wire [WIDTH-1:0] C = {CO, CI};
@@ -135,12 +129,6 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
genvar i;
-`ifdef _EXPLICIT_CARRY
- localparam EXPLICIT_CARRY = 1'b1;
-`else
- localparam EXPLICIT_CARRY = 1'b0;
-`endif
-
generate if (`LUT_SIZE == 4) begin
(* force_downto *)
@@ -163,106 +151,6 @@ generate if (`LUT_SIZE == 4) begin
);
end endgenerate
-end else if (EXPLICIT_CARRY) begin
-
- (* force_downto *)
- wire [Y_WIDTH-1:0] S = AA ^ BB;
-
- wire CINIT;
- // Carry chain.
- //
- // VPR requires that the carry chain never hit the fabric. The CO input
- // to this techmap is the carry outputs for synthesis, e.g. might hit the
- // fabric.
- //
- // So we maintain two wire sets, CO_CHAIN is the carry that is for VPR,
- // e.g. off fabric dedicated chain. CO is the carry outputs that are
- // available to the fabric.
- (* force_downto *)
- wire [Y_WIDTH-1:0] CO_CHAIN;
- (* force_downto *)
- wire [Y_WIDTH-1:0] C = {CO_CHAIN, CINIT};
-
- // If carry chain is being initialized to a constant, techmap the constant
- // source. Otherwise techmap the fabric source.
- generate for (i = 0; i < 1; i = i + 1) begin:slice
- CARRY0 #(.CYINIT_FABRIC(1)) carry(
- .CI_INIT(CI),
- .DI(AA[0]),
- .S(S[0]),
- .CO_CHAIN(CO_CHAIN[0]),
- .CO_FABRIC(CO[0]),
- .O(Y[0])
- );
- end endgenerate
-
- generate for (i = 1; i < Y_WIDTH-1; i = i + 1) begin:slice
- if(i % 4 == 0) begin
- CARRY0 carry (
- .CI(C[i]),
- .DI(AA[i]),
- .S(S[i]),
- .CO_CHAIN(CO_CHAIN[i]),
- .CO_FABRIC(CO[i]),
- .O(Y[i])
- );
- end
- else
- begin
- CARRY carry (
- .CI(C[i]),
- .DI(AA[i]),
- .S(S[i]),
- .CO_CHAIN(CO_CHAIN[i]),
- .CO_FABRIC(CO[i]),
- .O(Y[i])
- );
- end
- end endgenerate
-
- generate for (i = Y_WIDTH-1; i < Y_WIDTH; i = i + 1) begin:slice
- if(i % 4 == 0) begin
- CARRY0 top_of_carry (
- .CI(C[i]),
- .DI(AA[i]),
- .S(S[i]),
- .CO_CHAIN(CO_CHAIN[i]),
- .O(Y[i])
- );
- end
- else
- begin
- CARRY top_of_carry (
- .CI(C[i]),
- .DI(AA[i]),
- .S(S[i]),
- .CO_CHAIN(CO_CHAIN[i]),
- .O(Y[i])
- );
- end
- // Turns out CO_FABRIC and O both use [ABCD]MUX, so provide
- // a non-congested path to output the top of the carry chain.
- // Registering the output of the CARRY block would solve this, but not
- // all designs do that.
- if((i+1) % 4 == 0) begin
- CARRY0 carry_output (
- .CI(CO_CHAIN[i]),
- .DI(0),
- .S(0),
- .O(CO[i])
- );
- end
- else
- begin
- CARRY carry_output (
- .CI(CO_CHAIN[i]),
- .DI(0),
- .S(0),
- .O(CO[i])
- );
- end
- end endgenerate
-
end else begin
localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index f5850d8a2..a04587e87 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -455,29 +455,6 @@ module CARRY8(
assign CO[7] = S[7] ? CO[6] : DI[7];
endmodule
-`ifdef _EXPLICIT_CARRY
-
-module CARRY0(output CO_CHAIN, CO_FABRIC, O, input CI, CI_INIT, DI, S);
- parameter CYINIT_FABRIC = 0;
- wire CI_COMBINE;
- if(CYINIT_FABRIC) begin
- assign CI_COMBINE = CI_INIT;
- end else begin
- assign CI_COMBINE = CI;
- end
- assign CO_CHAIN = S ? CI_COMBINE : DI;
- assign CO_FABRIC = S ? CI_COMBINE : DI;
- assign O = S ^ CI_COMBINE;
-endmodule
-
-module CARRY(output CO_CHAIN, CO_FABRIC, O, input CI, DI, S);
- assign CO_CHAIN = S ? CI : DI;
- assign CO_FABRIC = S ? CI : DI;
- assign O = S ^ CI;
-endmodule
-
-`endif
-
module ORCY (output O, input CI, I);
assign O = CI | I;
endmodule
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index 421602e62..0adec57a2 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -77,10 +77,6 @@ struct SynthXilinxPass : public ScriptPass
log(" write the design to the specified BLIF file. writing of an output file\n");
log(" is omitted if this parameter is not specified.\n");
log("\n");
- log(" -vpr\n");
- log(" generate an output netlist (and BLIF file) suitable for VPR\n");
- log(" (this feature is experimental and incomplete)\n");
- log("\n");
log(" -ise\n");
log(" generate an output netlist suitable for ISE\n");
log("\n");
@@ -142,7 +138,7 @@ struct SynthXilinxPass : public ScriptPass
}
std::string top_opt, edif_file, blif_file, family;
- bool flatten, retime, vpr, ise, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram;
+ bool flatten, retime, ise, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram;
bool abc9, dff;
bool flatten_before_abc;
int widemux;
@@ -157,7 +153,6 @@ struct SynthXilinxPass : public ScriptPass
family = "xc7";
flatten = false;
retime = false;
- vpr = false;
ise = false;
noiopad = false;
noclkbuf = false;
@@ -229,10 +224,6 @@ struct SynthXilinxPass : public ScriptPass
nowidelut = true;
continue;
}
- if (args[argidx] == "-vpr") {
- vpr = true;
- continue;
- }
if (args[argidx] == "-ise") {
ise = true;
continue;
@@ -345,8 +336,6 @@ struct SynthXilinxPass : public ScriptPass
if (check_label("begin")) {
std::string read_args;
- if (vpr)
- read_args += " -D_EXPLICIT_CARRY";
read_args += " -lib -specify +/xilinx/cells_sim.v";
run("read_verilog" + read_args);
@@ -368,6 +357,8 @@ struct SynthXilinxPass : public ScriptPass
run("opt_expr");
run("opt_clean");
run("check");
+ run("opt -nodffe -nosdff");
+ run("fsm");
run("opt");
if (help_mode)
run("wreduce [-keepdc]", "(option for '-widemux')");
@@ -455,8 +446,6 @@ struct SynthXilinxPass : public ScriptPass
run("alumacc");
run("share");
run("opt");
- run("fsm");
- run("opt -fast");
run("memory -nomap");
run("opt_clean");
}
@@ -515,28 +504,21 @@ struct SynthXilinxPass : public ScriptPass
}
if (check_label("map_ffram")) {
- // Required for dff2dffs to work.
- run("simplemap t:$dff t:$adff t:$mux");
- // Needs to be done before opt -mux_bool happens.
- if (help_mode)
- run("dff2dffs [-match-init]", "(-match-init for xc6s only)");
- else if (family == "xc6s")
- run("dff2dffs -match-init");
- else
- run("dff2dffs");
- if (widemux > 0)
+ if (widemux > 0) {
run("opt -fast -mux_bool -undriven -fine"); // Necessary to omit -mux_undef otherwise muxcover
// performs less efficiently
- else
+ } else {
run("opt -fast -full");
+ }
run("memory_map");
}
if (check_label("fine")) {
- run("dff2dffe -direct-match $_DFF_* -direct-match $_SDFF_*");
- if (help_mode)
- run("muxcover <internal options> ('-widemux' only)");
- else if (widemux > 0) {
+ if (help_mode) {
+ run("simplemap t:$mux", "('-widemux' only)");
+ run("muxcover <internal options>", "('-widemux' only)");
+ } else if (widemux > 0) {
+ run("simplemap t:$mux");
constexpr int cost_mux2 = 100;
std::string muxcover_args = stringf(" -nodecode -mux2=%d", cost_mux2);
switch (widemux) {
@@ -570,8 +552,6 @@ struct SynthXilinxPass : public ScriptPass
techmap_args += stringf(" -D MIN_MUX_INPUTS=%d -map +/xilinx/mux_map.v", widemux);
if (!nocarry) {
techmap_args += " -map +/xilinx/arith_map.v";
- if (vpr)
- techmap_args += " -D _EXPLICIT_CARRY";
}
run("techmap " + techmap_args);
run("opt -fast");
@@ -603,7 +583,7 @@ struct SynthXilinxPass : public ScriptPass
}
if (check_label("map_luts")) {
- run("opt_expr -mux_undef");
+ run("opt_expr -mux_undef -noclkinv");
if (flatten_before_abc)
run("flatten");
if (help_mode)
diff --git a/tests/arch/anlogic/dffs.ys b/tests/arch/anlogic/dffs.ys
index d3281ab89..deb90e051 100644
--- a/tests/arch/anlogic/dffs.ys
+++ b/tests/arch/anlogic/dffs.ys
@@ -15,6 +15,5 @@ proc
equiv_opt -assert -map +/anlogic/cells_sim.v synth_anlogic # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd dffe # Constrain all select calls below inside the top module
-select -assert-count 1 t:AL_MAP_LUT3
select -assert-count 1 t:AL_MAP_SEQ
-select -assert-none t:AL_MAP_LUT3 t:AL_MAP_SEQ %% t:* %D
+select -assert-none t:AL_MAP_SEQ %% t:* %D
diff --git a/tests/arch/ecp5/fsm.ys b/tests/arch/ecp5/fsm.ys
index ba91e5fc0..a77986bbc 100644
--- a/tests/arch/ecp5/fsm.ys
+++ b/tests/arch/ecp5/fsm.ys
@@ -10,8 +10,8 @@ sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd fsm # Constrain all select calls below inside the top module
-select -assert-count 1 t:L6MUX21
-select -assert-count 15 t:LUT4
-select -assert-count 6 t:PFUMX
+select -assert-max 1 t:L6MUX21
+select -assert-max 16 t:LUT4
+select -assert-max 7 t:PFUMX
select -assert-count 6 t:TRELLIS_FF
select -assert-none t:L6MUX21 t:LUT4 t:PFUMX t:TRELLIS_FF %% t:* %D
diff --git a/tests/arch/efinix/adffs.ys b/tests/arch/efinix/adffs.ys
index 49dc7f256..86d446439 100644
--- a/tests/arch/efinix/adffs.ys
+++ b/tests/arch/efinix/adffs.ys
@@ -32,9 +32,8 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd dffs # Constrain all select calls below inside the top module
select -assert-count 1 t:EFX_FF
select -assert-count 1 t:EFX_GBUFCE
-select -assert-count 1 t:EFX_LUT4
-select -assert-none t:EFX_FF t:EFX_GBUFCE t:EFX_LUT4 %% t:* %D
+select -assert-none t:EFX_FF t:EFX_GBUFCE %% t:* %D
design -load read
@@ -45,6 +44,5 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd ndffnr # Constrain all select calls below inside the top module
select -assert-count 1 t:EFX_FF
select -assert-count 1 t:EFX_GBUFCE
-select -assert-count 1 t:EFX_LUT4
-select -assert-none t:EFX_FF t:EFX_GBUFCE t:EFX_LUT4 %% t:* %D
+select -assert-none t:EFX_FF t:EFX_GBUFCE %% t:* %D
diff --git a/tests/arch/efinix/dffs.ys b/tests/arch/efinix/dffs.ys
index af787ab67..f9111873c 100644
--- a/tests/arch/efinix/dffs.ys
+++ b/tests/arch/efinix/dffs.ys
@@ -19,6 +19,5 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd dffe # Constrain all select calls below inside the top module
select -assert-count 1 t:EFX_FF
select -assert-count 1 t:EFX_GBUFCE
-select -assert-count 1 t:EFX_LUT4
-select -assert-none t:EFX_FF t:EFX_GBUFCE t:EFX_LUT4 %% t:* %D
+select -assert-none t:EFX_FF t:EFX_GBUFCE %% t:* %D
diff --git a/tests/arch/gowin/init.ys b/tests/arch/gowin/init.ys
index 88e88c15a..fba7c2fa5 100644
--- a/tests/arch/gowin/init.ys
+++ b/tests/arch/gowin/init.ys
@@ -45,24 +45,25 @@ flatten
synth_gowin -run coarse:
# check the flops mapped as expected
-select -assert-count 1 t:DFF
+select -assert-count 2 t:DFF
select -assert-count 1 t:DFFC
select -assert-count 1 t:DFFCE
-select -assert-count 1 t:DFFE
-select -assert-count 1 t:DFFN
+select -assert-count 0 t:DFFE
+select -assert-count 2 t:DFFN
select -assert-count 1 t:DFFNC
select -assert-count 1 t:DFFNCE
-select -assert-count 1 t:DFFNE
+select -assert-count 0 t:DFFNE
select -assert-count 1 t:DFFNP
select -assert-count 1 t:DFFNPE
select -assert-count 0 t:DFFNR
select -assert-count 0 t:DFFNRE
-select -assert-count 2 t:DFFNS
-select -assert-count 2 t:DFFNSE
+select -assert-count 3 t:DFFNS
+select -assert-count 1 t:DFFNSE
select -assert-count 1 t:DFFP
select -assert-count 1 t:DFFPE
select -assert-count 0 t:DFFR
select -assert-count 0 t:DFFRE
-select -assert-count 2 t:DFFS
-select -assert-count 2 t:DFFSE
-select -assert-count 12 t:LUT2
+select -assert-count 3 t:DFFS
+select -assert-count 1 t:DFFSE
+select -assert-count 4 t:LUT2
+select -assert-count 4 t:LUT4
diff --git a/tests/arch/ice40/fsm.ys b/tests/arch/ice40/fsm.ys
index 223ba070e..e3b746202 100644
--- a/tests/arch/ice40/fsm.ys
+++ b/tests/arch/ice40/fsm.ys
@@ -12,5 +12,5 @@ cd fsm # Constrain all select calls below inside the top module
select -assert-count 4 t:SB_DFF
select -assert-count 2 t:SB_DFFESR
-select -assert-count 15 t:SB_LUT4
+select -assert-max 15 t:SB_LUT4
select -assert-none t:SB_DFFESR t:SB_DFF t:SB_LUT4 %% t:* %D
diff --git a/tests/arch/intel_alm/adffs.ys b/tests/arch/intel_alm/adffs.ys
index 04fa2ad24..4565dcc64 100644
--- a/tests/arch/intel_alm/adffs.ys
+++ b/tests/arch/intel_alm/adffs.ys
@@ -77,10 +77,9 @@ equiv_opt -async2sync -assert -map +/intel_alm/common/alm_sim.v -map +/intel_alm
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd ndffnr # Constrain all select calls below inside the top module
select -assert-count 1 t:MISTRAL_FF
-select -assert-count 1 t:MISTRAL_NOT
-select -assert-count 1 t:MISTRAL_ALUT2
+select -assert-count 2 t:MISTRAL_NOT
-select -assert-none t:MISTRAL_FF t:MISTRAL_NOT t:MISTRAL_ALUT2 %% t:* %D
+select -assert-none t:MISTRAL_FF t:MISTRAL_NOT %% t:* %D
design -load read
@@ -90,7 +89,6 @@ equiv_opt -async2sync -assert -map +/intel_alm/common/alm_sim.v -map +/intel_alm
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd ndffnr # Constrain all select calls below inside the top module
select -assert-count 1 t:MISTRAL_FF
-select -assert-count 1 t:MISTRAL_NOT
-select -assert-count 1 t:MISTRAL_ALUT2
+select -assert-count 2 t:MISTRAL_NOT
-select -assert-none t:MISTRAL_FF t:MISTRAL_NOT t:MISTRAL_ALUT2 %% t:* %D
+select -assert-none t:MISTRAL_FF t:MISTRAL_NOT %% t:* %D
diff --git a/tests/arch/intel_alm/blockram.ys b/tests/arch/intel_alm/blockram.ys
new file mode 100644
index 000000000..610ae1ffd
--- /dev/null
+++ b/tests/arch/intel_alm/blockram.ys
@@ -0,0 +1,6 @@
+read_verilog ../common/blockram.v
+chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 10 sync_ram_sdp
+synth_intel_alm -family cyclonev
+cd sync_ram_sdp
+select -assert-count 1 t:MISTRAL_M10K
+select -assert-none t:MISTRAL_M10K %% t:* %D
diff --git a/tests/arch/intel_alm/fsm.ys b/tests/arch/intel_alm/fsm.ys
index 6491b2e08..e54b5c21e 100644
--- a/tests/arch/intel_alm/fsm.ys
+++ b/tests/arch/intel_alm/fsm.ys
@@ -12,12 +12,13 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd fsm # Constrain all select calls below inside the top module
select -assert-count 6 t:MISTRAL_FF
+select -assert-max 1 t:MISTRAL_NOT
select -assert-max 2 t:MISTRAL_ALUT2 # Clang returns 2, GCC returns 1
-select -assert-count 1 t:MISTRAL_ALUT3
-select -assert-max 1 t:MISTRAL_ALUT4 # Clang returns 0, GCC returns 1
+select -assert-max 1 t:MISTRAL_ALUT3
+select -assert-max 2 t:MISTRAL_ALUT4 # Clang returns 0, GCC returns 1
select -assert-max 6 t:MISTRAL_ALUT5 # Clang returns 5, GCC returns 4
select -assert-max 2 t:MISTRAL_ALUT6 # Clang returns 1, GCC returns 2
-select -assert-none t:MISTRAL_FF t:MISTRAL_ALUT2 t:MISTRAL_ALUT3 t:MISTRAL_ALUT4 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
+select -assert-none t:MISTRAL_FF t:MISTRAL_NOT t:MISTRAL_ALUT2 t:MISTRAL_ALUT3 t:MISTRAL_ALUT4 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
design -reset
read_verilog ../common/fsm.v
@@ -34,9 +35,10 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd fsm # Constrain all select calls below inside the top module
select -assert-count 6 t:MISTRAL_FF
+select -assert-max 1 t:MISTRAL_NOT
select -assert-max 2 t:MISTRAL_ALUT2 # Clang returns 2, GCC returns 1
select -assert-max 2 t:MISTRAL_ALUT3 # Clang returns 2, GCC returns 1
-select -assert-max 1 t:MISTRAL_ALUT4 # Clang returns 0, GCC returns 1
+select -assert-max 2 t:MISTRAL_ALUT4 # Clang returns 0, GCC returns 1
select -assert-max 6 t:MISTRAL_ALUT5 # Clang returns 5, GCC returns 4
select -assert-max 2 t:MISTRAL_ALUT6 # Clang returns 1, GCC returns 2
-select -assert-none t:MISTRAL_FF t:MISTRAL_ALUT2 t:MISTRAL_ALUT3 t:MISTRAL_ALUT4 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
+select -assert-none t:MISTRAL_FF t:MISTRAL_NOT t:MISTRAL_ALUT2 t:MISTRAL_ALUT3 t:MISTRAL_ALUT4 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
diff --git a/tests/arch/intel_alm/mux.ys b/tests/arch/intel_alm/mux.ys
index d109257bd..01cc78e1b 100644
--- a/tests/arch/intel_alm/mux.ys
+++ b/tests/arch/intel_alm/mux.ys
@@ -48,9 +48,8 @@ equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cycl
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux8 # Constrain all select calls below inside the top module
select -assert-count 1 t:MISTRAL_ALUT3
-select -assert-count 1 t:MISTRAL_ALUT5
select -assert-count 2 t:MISTRAL_ALUT6
-select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
+select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT6 %% t:* %D
design -load read
@@ -71,9 +70,8 @@ equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cycl
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux16 # Constrain all select calls below inside the top module
select -assert-count 1 t:MISTRAL_ALUT3
-select -assert-count 2 t:MISTRAL_ALUT5
-select -assert-count 4 t:MISTRAL_ALUT6
-select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT5 t:MISTRAL_ALUT6 %% t:* %D
+select -assert-count 5 t:MISTRAL_ALUT6
+select -assert-none t:MISTRAL_ALUT3 t:MISTRAL_ALUT6 %% t:* %D
design -load read
diff --git a/tests/arch/xilinx/fsm.ys b/tests/arch/xilinx/fsm.ys
index fec4c6082..ace646af4 100644
--- a/tests/arch/xilinx/fsm.ys
+++ b/tests/arch/xilinx/fsm.ys
@@ -13,12 +13,11 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p
cd fsm # Constrain all select calls below inside the top module
stat
select -assert-count 1 t:BUFG
-select -assert-count 4 t:FDRE
-select -assert-count 1 t:FDSE
-select -assert-count 1 t:LUT2
-select -assert-count 3 t:LUT5
+select -assert-count 6 t:FDRE
+select -assert-count 1 t:LUT4
+select -assert-count 4 t:LUT5
select -assert-count 1 t:LUT6
-select -assert-none t:BUFG t:FDRE t:FDSE t:LUT2 t:LUT5 t:LUT6 %% t:* %D
+select -assert-none t:BUFG t:FDRE t:LUT4 t:LUT5 t:LUT6 %% t:* %D
design -load orig
@@ -32,7 +31,6 @@ stat
select -assert-count 1 t:BUFG
select -assert-count 6 t:FDRE
select -assert-count 1 t:LUT1
-select -assert-count 3 t:LUT3
-select -assert-count 6 t:LUT4
-select -assert-count 6 t:MUXF5
-select -assert-none t:BUFG t:FDRE t:LUT1 t:LUT3 t:LUT4 t:MUXF5 %% t:* %D
+select -assert-count 8 t:LUT4
+select -assert-count 5 t:MUXF5
+select -assert-none t:BUFG t:FDRE t:LUT1 t:LUT4 t:MUXF5 %% t:* %D
diff --git a/tests/arch/xilinx/latches.ys b/tests/arch/xilinx/latches.ys
index e226c2ec8..ee87fee21 100644
--- a/tests/arch/xilinx/latches.ys
+++ b/tests/arch/xilinx/latches.ys
@@ -18,9 +18,8 @@ equiv_opt -async2sync -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad #
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd latchn # Constrain all select calls below inside the top module
select -assert-count 1 t:LDCE
-select -assert-count 1 t:INV
-select -assert-none t:LDCE t:INV %% t:* %D
+select -assert-none t:LDCE %% t:* %D
design -load read
diff --git a/tests/arch/xilinx/pmgen_xilinx_srl.ys b/tests/arch/xilinx/pmgen_xilinx_srl.ys
index e76fb20ab..9a5e70ea9 100644
--- a/tests/arch/xilinx/pmgen_xilinx_srl.ys
+++ b/tests/arch/xilinx/pmgen_xilinx_srl.ys
@@ -35,7 +35,6 @@ 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
@@ -52,6 +51,5 @@ 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/opt/bug2311.ys b/tests/opt/bug2311.ys
new file mode 100644
index 000000000..455147cd3
--- /dev/null
+++ b/tests/opt/bug2311.ys
@@ -0,0 +1,14 @@
+read_verilog -icells << EOT
+
+module top(...);
+
+input A;
+output Y;
+
+$_XNOR_ x (.A(A), .B(A), .Y(Y));
+
+endmodule
+
+EOT
+
+equiv_opt -assert opt_expr
diff --git a/tests/opt/opt_dff_arst.ys b/tests/opt/opt_dff_arst.ys
new file mode 100644
index 000000000..2aa3b7a26
--- /dev/null
+++ b/tests/opt/opt_dff_arst.ys
@@ -0,0 +1,101 @@
+### Always-active ARST removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [1:0] D;
+output [11:0] Q;
+input ARST;
+input EN;
+
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff0 (.CLK(CLK), .ARST(1'b1), .D(D), .Q(Q[1:0]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .ARST_POLARITY(1'b0), .ARST_VALUE(2'h2), .WIDTH(2)) ff1 (.CLK(CLK), .ARST(1'b0), .EN(EN), .D(D), .Q(Q[3:2]));
+$adlatch #(.EN_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff2 (.EN(EN), .ARST(1'b1), .D(D), .Q(Q[5:4]));
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(CLK), .ARST(1'bx), .D(D), .Q(Q[7:6]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .ARST_POLARITY(1'b0), .ARST_VALUE(2'h2), .WIDTH(2)) ff4 (.CLK(CLK), .ARST(1'bx), .EN(EN), .D(D), .Q(Q[9:8]));
+$adlatch #(.EN_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff5 (.EN(EN), .ARST(1'bx), .D(D), .Q(Q[11:10]));
+
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:*
+
+design -load orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$adlatch
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:*
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 2 t:$_DFF_???_
+select -assert-count 2 t:$_DFFE_????_
+select -assert-count 2 t:$_DLATCH_???_
+
+design -reset
+
+
+### Never-active ARST removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [1:0] D;
+output [5:0] Q;
+input ARST;
+input EN;
+
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff0 (.CLK(CLK), .ARST(1'b0), .D(D), .Q(Q[1:0]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .ARST_POLARITY(1'b0), .ARST_VALUE(2'h2), .WIDTH(2)) ff1 (.CLK(CLK), .ARST(1'b1), .EN(EN), .D(D), .Q(Q[3:2]));
+$adlatch #(.EN_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff2 (.EN(EN), .ARST(1'b0), .D(D), .Q(Q[5:4]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:$adff
+select -assert-none t:$adffe
+select -assert-none t:$adlatch
+select -assert-count 1 t:$dff
+select -assert-count 1 t:$dffe
+select -assert-count 1 t:$dlatch
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:$_DFF_???_
+select -assert-none t:$_DFFE_????_
+select -assert-none t:$_DLATCH_???_
+select -assert-count 2 t:$_DFF_P_
+select -assert-count 2 t:$_DFFE_PP_
+select -assert-count 2 t:$_DLATCH_P_
+
+design -reset
diff --git a/tests/opt/opt_dff_clk.ys b/tests/opt/opt_dff_clk.ys
new file mode 100644
index 000000000..f3aefa406
--- /dev/null
+++ b/tests/opt/opt_dff_clk.ys
@@ -0,0 +1,45 @@
+### Never-toggling CLK removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input EN;
+input [1:0] D;
+(* init = 18'h15555 *)
+output [17:0] Q;
+input SRST;
+input ARST;
+input [1:0] CLR;
+input [1:0] SET;
+
+$dff #(.CLK_POLARITY(1'b1), .WIDTH(2)) ff0 (.CLK(1'b0), .D(D), .Q(Q[1:0]));
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(2)) ff1 (.CLK(1'b1), .EN(EN), .D(D), .Q(Q[3:2]));
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(1'bx), .ARST(ARST), .D(D), .Q(Q[5:4]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(1'b0), .EN(EN), .ARST(ARST), .D(D), .Q(Q[7:6]));
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff4 (.CLK(1'b1), .SRST(SRST), .D(D), .Q(Q[9:8]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff5 (.CLK(1'bx), .EN(EN), .SRST(SRST), .D(D), .Q(Q[11:10]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff6 (.CLK(1'bx), .EN(EN), .SRST(SRST), .D(D), .Q(Q[13:12]));
+$dffsr #(.CLK_POLARITY(1'b1), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.CLK(1'b1), .SET(SET), .CLR(CLR), .D(D), .Q(Q[15:14]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff8 (.CLK(1'bx), .EN(EN), .SET(SET), .CLR(CLR), .D(D), .Q(Q[17:16]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 2 t:$dlatch
+select -assert-count 2 t:$sr
+select -assert-none t:$dlatch t:$sr %% %n t:* %i
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 4 t:$_DLATCH_?_
+select -assert-count 4 t:$_SR_??_
+select -assert-none t:$_DLATCH_?_ t:$_SR_??_ %% %n t:* %i
diff --git a/tests/opt/opt_dff_const.ys b/tests/opt/opt_dff_const.ys
new file mode 100644
index 000000000..6a7dec7fa
--- /dev/null
+++ b/tests/opt/opt_dff_const.ys
@@ -0,0 +1,49 @@
+### Replace FFs with a const.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input EN;
+(* init=84'haaaaaaaaaaaaaaaaaaaaa *)
+output [83:0] Q;
+input SRST;
+input ARST;
+input [3:0] CLR;
+input [3:0] SET;
+
+$dff #(.CLK_POLARITY(1'b1), .WIDTH(4)) ff0 (.CLK(CLK), .D(4'hc), .Q(Q[3:0]));
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(4)) ff1 (.CLK(CLK), .EN(EN), .D(4'hc), .Q(Q[7:4]));
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(8'hf0), .WIDTH(8)) ff2 (.CLK(CLK), .ARST(ARST), .D(8'hcc), .Q(Q[15:8]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(8'hf0), .WIDTH(8)) ff3 (.CLK(CLK), .EN(EN), .ARST(ARST), .D(8'hcc), .Q(Q[23:16]));
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(8'hf0), .WIDTH(8)) ff4 (.CLK(CLK), .SRST(SRST), .D(8'hcc), .Q(Q[31:24]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(8'hf0), .WIDTH(8)) ff5 (.CLK(CLK), .EN(EN), .SRST(SRST), .D(8'hcc), .Q(Q[39:32]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(8'hf0), .WIDTH(8)) ff6 (.CLK(CLK), .EN(EN), .SRST(SRST), .D(8'hcc), .Q(Q[47:40]));
+$dffsr #(.CLK_POLARITY(1'b1), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(8)) ff7 (.CLK(CLK), .SET({SET, 4'hf}), .CLR({4'h0, CLR}), .D(8'hcc), .Q(Q[55:48]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b0), .SET_POLARITY(1'b1), .WIDTH(8)) ff8 (.CLK(CLK), .EN(EN), .SET({SET, 4'h0}), .CLR({4'hf, CLR}), .D(8'hcc), .Q(Q[63:56]));
+
+$dlatch #(.EN_POLARITY(1'b1), .WIDTH(4)) ff9 (.EN(EN), .D(4'hc), .Q(Q[67:64]));
+$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(8'hf0), .WIDTH(8)) ff10 (.EN(EN), .ARST(ARST), .D(8'hcc), .Q(Q[75:68]));
+$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b1), .WIDTH(8)) ff11 (.EN(EN), .SET({SET, 4'h0}), .CLR({4'h0, CLR}), .D(8'hcc), .Q(Q[83:76]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-count 1 t:$dff r:WIDTH=2 %i
+select -assert-count 1 t:$dffe r:WIDTH=2 %i
+select -assert-count 1 t:$adff r:WIDTH=6 %i
+select -assert-count 1 t:$adffe r:WIDTH=6 %i
+select -assert-count 1 t:$sdff r:WIDTH=6 %i
+select -assert-count 1 t:$sdffe r:WIDTH=6 %i
+select -assert-count 1 t:$sdffce r:WIDTH=6 %i
+select -assert-count 1 t:$dffsr r:WIDTH=6 %i
+select -assert-count 1 t:$dffsre r:WIDTH=6 %i
+select -assert-count 1 t:$dlatch r:WIDTH=2 %i
+select -assert-count 1 t:$adlatch r:WIDTH=6 %i
+select -assert-count 1 t:$dlatchsr r:WIDTH=6 %i
diff --git a/tests/opt/opt_dff_en.ys b/tests/opt/opt_dff_en.ys
new file mode 100644
index 000000000..06ee6c63d
--- /dev/null
+++ b/tests/opt/opt_dff_en.ys
@@ -0,0 +1,157 @@
+### Always-active EN removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [1:0] D;
+output [15:0] Q;
+input SRST;
+input ARST;
+input [1:0] CLR;
+input [1:0] SET;
+
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(2)) ff0 (.CLK(CLK), .EN(1'b1), .D(D), .Q(Q[1:0]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff1 (.CLK(CLK), .EN(1'b0), .ARST(ARST), .D(D), .Q(Q[3:2]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(CLK), .EN(1'b1), .SRST(SRST), .D(D), .Q(Q[5:4]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(CLK), .EN(1'b1), .SRST(SRST), .D(D), .Q(Q[7:6]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff4 (.CLK(CLK), .EN(1'b0), .SET(SET), .CLR(CLR), .D(D), .Q(Q[9:8]));
+
+$dlatch #(.EN_POLARITY(1'b1), .WIDTH(2)) ff5 (.EN(1'b1), .D(D), .Q(Q[11:10]));
+$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff6 (.EN(1'b0), .ARST(ARST), .D(D), .Q(Q[13:12]));
+$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.EN(1'b0), .SET(SET), .CLR(CLR), .D(D), .Q(Q[15:14]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+# Equivalence check will fail for unmapped adlatch and dlatchsr due to negative hold hack.
+delete top/ff6 top/ff7
+equiv_opt -undef -assert -multiclock opt_dff
+
+design -load orig
+delete top/ff6 top/ff7
+simplemap
+equiv_opt -undef -assert -multiclock opt_dff
+
+design -load orig
+opt_dff
+select -assert-count 0 t:$dffe
+select -assert-count 0 t:$adffe
+select -assert-count 0 t:$sdffe
+select -assert-count 0 t:$sdffce
+select -assert-count 0 t:$dffsre
+select -assert-count 0 t:$dlatch
+select -assert-count 0 t:$adlatch
+select -assert-count 0 t:$dlatchsr
+select -assert-count 1 t:$dff
+select -assert-count 2 t:$sdff
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$dffsr
+
+design -load orig
+simplemap
+opt_dff
+select -assert-count 0 t:$_DFFE_*
+select -assert-count 0 t:$_SDFFE_*
+select -assert-count 0 t:$_SDFFCE_*
+select -assert-count 0 t:$_DFFSRE_*
+select -assert-count 0 t:$_DLATCH*
+select -assert-count 2 t:$_DFF_P_
+select -assert-count 4 t:$_SDFF_PP?_
+select -assert-count 2 t:$_DFF_PP?_
+select -assert-count 2 t:$_DFFSR_PNP_
+
+design -reset
+
+
+
+### Never-active EN removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [1:0] D;
+(* init = 32'h55555555 *)
+output [31:0] Q;
+input SRST;
+input ARST;
+input [1:0] CLR;
+input [1:0] SET;
+
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(2)) ff0 (.CLK(CLK), .EN(1'b0), .D(D), .Q(Q[1:0]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff1 (.CLK(CLK), .EN(1'b1), .ARST(ARST), .D(D), .Q(Q[3:2]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(CLK), .EN(1'b0), .SRST(SRST), .D(D), .Q(Q[5:4]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(CLK), .EN(1'b0), .SRST(SRST), .D(D), .Q(Q[7:6]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff4 (.CLK(CLK), .EN(1'b1), .SET(SET), .CLR(CLR), .D(D), .Q(Q[9:8]));
+
+$dlatch #(.EN_POLARITY(1'b1), .WIDTH(2)) ff5 (.EN(1'b0), .D(D), .Q(Q[11:10]));
+$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff6 (.EN(1'b1), .ARST(ARST), .D(D), .Q(Q[13:12]));
+$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.EN(1'b1), .SET(SET), .CLR(CLR), .D(D), .Q(Q[15:14]));
+
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(2)) ff8 (.CLK(CLK), .EN(1'bx), .D(D), .Q(Q[17:16]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff9 (.CLK(CLK), .EN(1'bx), .ARST(ARST), .D(D), .Q(Q[19:18]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff10 (.CLK(CLK), .EN(1'bx), .SRST(SRST), .D(D), .Q(Q[21:20]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff11 (.CLK(CLK), .EN(1'bx), .SRST(SRST), .D(D), .Q(Q[23:22]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff12 (.CLK(CLK), .EN(1'bx), .SET(SET), .CLR(CLR), .D(D), .Q(Q[25:24]));
+
+$dlatch #(.EN_POLARITY(1'b1), .WIDTH(2)) ff13 (.EN(1'bx), .D(D), .Q(Q[27:26]));
+$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff14 (.EN(1'bx), .ARST(ARST), .D(D), .Q(Q[29:28]));
+$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff15 (.EN(1'bx), .SET(SET), .CLR(CLR), .D(D), .Q(Q[31:30]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-count 2 t:$dffe
+select -assert-count 4 t:$dlatch
+select -assert-count 4 t:$sr
+select -assert-none t:$dffe t:$dlatch t:$sr %% %n t:* %i
+
+design -load orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 2 t:$dffe
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$sdffe
+select -assert-count 1 t:$sdffce
+select -assert-count 1 t:$dffsre
+select -assert-count 3 t:$dlatch
+select -assert-count 1 t:$adlatch
+select -assert-count 1 t:$dlatchsr
+select -assert-count 2 t:$sr
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-count 4 t:$_DFFE_??_
+select -assert-count 8 t:$_DLATCH_?_
+select -assert-count 8 t:$_SR_??_
+select -assert-none t:$_DFFE_??_ t:$_DLATCH_?_ t:$_SR_??_ %% %n t:* %i
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 4 t:$_DFFE_??_
+select -assert-count 2 t:$_DFFE_????_
+select -assert-count 2 t:$_SDFFE_????_
+select -assert-count 2 t:$_SDFFCE_????_
+select -assert-count 2 t:$_DFFSRE_????_
+select -assert-count 6 t:$_DLATCH_?_
+select -assert-count 2 t:$_DLATCH_???_
+select -assert-count 2 t:$_DLATCHSR_???_
+select -assert-count 4 t:$_SR_??_
diff --git a/tests/opt/opt_dff_mux.ys b/tests/opt/opt_dff_mux.ys
new file mode 100644
index 000000000..ed01bed59
--- /dev/null
+++ b/tests/opt/opt_dff_mux.ys
@@ -0,0 +1,86 @@
+### CE and SRST matching.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input NE, NS;
+input EN;
+output [23:0] Q;
+input [23:0] D;
+input SRST;
+input ARST;
+input [1:0] CLR;
+input [1:0] SET;
+
+$dff #(.CLK_POLARITY(1'b1), .WIDTH(2)) ff0 (.CLK(CLK), .D(NS ? 2'h2 : NE ? D[1:0] : Q[1:0]), .Q(Q[1:0]));
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(2)) ff1 (.CLK(CLK), .EN(EN), .D(NS ? 2'h2 : NE ? D[3:2] : Q[3:2]), .Q(Q[3:2]));
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(CLK), .ARST(ARST), .D(NS ? 2'h2 : NE ? D[5:4] : Q[5:4]), .Q(Q[5:4]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(CLK), .EN(EN), .ARST(ARST), .D(NS ? 2'h2 : NE ? D[7:6] : Q[7:6]), .Q(Q[7:6]));
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff4 (.CLK(CLK), .SRST(SRST), .D(NS ? 2'h2 : NE ? D[9:8] : Q[9:8]), .Q(Q[9:8]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff5 (.CLK(CLK), .EN(EN), .SRST(SRST), .D(NS ? 2'h2 : NE ? D[11:10] : Q[11:10]), .Q(Q[11:10]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff6 (.CLK(CLK), .EN(EN), .SRST(SRST), .D(NS ? 2'h2 : NE ? D[13:12] : Q[13:12]), .Q(Q[13:12]));
+$dffsr #(.CLK_POLARITY(1'b1), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.CLK(CLK), .SET(SET), .CLR(CLR), .D(NS ? 2'h2 : NE ? D[15:14] : Q[15:14]), .Q(Q[15:14]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff8 (.CLK(CLK), .EN(EN), .SET(SET), .CLR(CLR), .D(NS ? 2'h2 : NE ? D[17:16] : Q[17:16]), .Q(Q[17:16]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+clean
+select -assert-count 0 t:$dff
+select -assert-count 0 t:$dffe
+select -assert-count 0 t:$adff
+select -assert-count 2 t:$adffe
+select -assert-count 0 t:$dffsr
+select -assert-count 2 t:$dffsre
+select -assert-count 0 t:$sdff
+select -assert-count 3 t:$sdffe
+select -assert-count 2 t:$sdffce
+
+design -load orig
+
+equiv_opt -undef -assert -multiclock opt_dff -nodffe -nosdff
+design -load postopt
+clean
+select -assert-count 1 t:$dff
+select -assert-count 1 t:$dffe
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsre
+select -assert-count 1 t:$sdff
+select -assert-count 1 t:$sdffe
+select -assert-count 1 t:$sdffce
+equiv_opt -undef -assert -multiclock opt_dff -nodffe
+design -load postopt
+clean
+select -assert-count 0 t:$dff
+select -assert-count 0 t:$dffe
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsre
+select -assert-count 2 t:$sdff
+select -assert-count 1 t:$sdffe
+select -assert-count 2 t:$sdffce
+
+design -load orig
+
+equiv_opt -undef -assert -multiclock opt_dff -nosdff
+design -load postopt
+clean
+select -assert-count 0 t:$dff
+select -assert-count 2 t:$dffe
+select -assert-count 0 t:$adff
+select -assert-count 2 t:$adffe
+select -assert-count 0 t:$dffsr
+select -assert-count 2 t:$dffsre
+select -assert-count 0 t:$sdff
+select -assert-count 2 t:$sdffe
+select -assert-count 1 t:$sdffce
diff --git a/tests/opt/opt_dff_qd.ys b/tests/opt/opt_dff_qd.ys
new file mode 100644
index 000000000..afc96c42f
--- /dev/null
+++ b/tests/opt/opt_dff_qd.ys
@@ -0,0 +1,56 @@
+### Q = D case.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input EN;
+(* init = 24'h555555 *)
+output [23:0] Q;
+input SRST;
+input ARST;
+input [1:0] CLR;
+input [1:0] SET;
+
+$dff #(.CLK_POLARITY(1'b1), .WIDTH(2)) ff0 (.CLK(CLK), .D(Q[1:0]), .Q(Q[1:0]));
+$dffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(2)) ff1 (.CLK(CLK), .EN(EN), .D(Q[3:2]), .Q(Q[3:2]));
+$adff #(.CLK_POLARITY(1'b1), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(CLK), .ARST(ARST), .D(Q[5:4]), .Q(Q[5:4]));
+$adffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(CLK), .EN(EN), .ARST(ARST), .D(Q[7:6]), .Q(Q[7:6]));
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff4 (.CLK(CLK), .SRST(SRST), .D(Q[9:8]), .Q(Q[9:8]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff5 (.CLK(CLK), .EN(EN), .SRST(SRST), .D(Q[11:10]), .Q(Q[11:10]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff6 (.CLK(CLK), .EN(EN), .SRST(SRST), .D(Q[13:12]), .Q(Q[13:12]));
+$dffsr #(.CLK_POLARITY(1'b1), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff7 (.CLK(CLK), .SET(SET), .CLR(CLR), .D(Q[15:14]), .Q(Q[15:14]));
+$dffsre #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff8 (.CLK(CLK), .EN(EN), .SET(SET), .CLR(CLR), .D(Q[17:16]), .Q(Q[17:16]));
+
+$dlatch #(.EN_POLARITY(1'b1), .WIDTH(2)) ff9 (.EN(EN), .D(Q[19:18]), .Q(Q[19:18]));
+$adlatch #(.EN_POLARITY(1'b0), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2), .WIDTH(2)) ff10 (.EN(EN), .ARST(ARST), .D(Q[21:20]), .Q(Q[21:20]));
+$dlatchsr #(.EN_POLARITY(1'b0), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0), .WIDTH(2)) ff11 (.EN(EN), .SET(SET), .CLR(CLR), .D(Q[23:22]), .Q(Q[23:22]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+# Equivalence check will fail for unmapped adlatch and dlatchsr due to negative hold hack.
+delete top/ff10 top/ff11
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+
+design -load orig
+opt_dff -keepdc
+select -assert-count 1 t:$and
+select -assert-count 3 t:$dffe
+select -assert-count 3 t:$dlatch
+select -assert-count 3 t:$sr
+select -assert-none t:$and t:$dffe t:$dlatch t:$sr %% %n t:* %i
+
+design -load orig
+simplemap
+opt_dff -keepdc
+select -assert-count 2 t:$_AND_
+select -assert-count 6 t:$_DFFE_??_
+select -assert-count 6 t:$_DLATCH_?_
+select -assert-count 6 t:$_SR_??_
+select -assert-none t:$_AND_ t:$_DFFE_??_ t:$_DLATCH_?_ t:$_SR_??_ %% %n t:* %i
+
diff --git a/tests/opt/opt_dff_sr.ys b/tests/opt/opt_dff_sr.ys
new file mode 100644
index 000000000..daedb115c
--- /dev/null
+++ b/tests/opt/opt_dff_sr.ys
@@ -0,0 +1,304 @@
+### Always-active SET/CLR removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [5:0] D;
+output [23:0] Q;
+input CLR;
+input SET;
+input EN;
+
+$dffsr #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b1), .CLR_POLARITY(1'b1), .WIDTH(6)) ff0 (.CLK(CLK), .CLR({CLR, CLR, CLR, 1'b1, 1'b0, 1'bx}), .SET({1'b1, 1'b0, 1'bx, SET, SET, SET}), .D(D), .Q(Q[5:0]));
+$dffsre #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b0), .CLR_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(6)) ff1 (.CLK(CLK), .EN(EN), .CLR({CLR, CLR, CLR, 1'b1, 1'b0, 1'bx}), .SET({1'b1, 1'b0, 1'bx, SET, SET, SET}), .D(D), .Q(Q[11:6]));
+$dlatchsr #(.SET_POLARITY(1'b0), .CLR_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(6)) ff2 (.EN(EN), .CLR({CLR, CLR, CLR, 1'b1, 1'b0, 1'bx}), .SET({1'b1, 1'b0, 1'bx, SET, SET, SET}), .D(D), .Q(Q[17:12]));
+$sr #(.SET_POLARITY(1'b1), .CLR_POLARITY(1'b0), .WIDTH(6)) ff3 (.CLR({CLR, CLR, CLR, 1'b1, 1'b0, 1'bx}), .SET({1'b1, 1'b0, 1'bx, SET, SET, SET}), .Q(Q[23:18]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsr r:WIDTH=2 %i
+select -assert-count 1 t:$dffsre
+select -assert-count 1 t:$dffsre r:WIDTH=2 %i
+select -assert-count 1 t:$dlatchsr
+select -assert-count 1 t:$dlatchsr r:WIDTH=2 %i
+select -assert-none t:$sr
+
+design -load orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsr r:WIDTH=4 %i
+select -assert-count 1 t:$dffsre
+select -assert-count 1 t:$dffsre r:WIDTH=4 %i
+select -assert-count 1 t:$dlatchsr
+select -assert-count 1 t:$dlatchsr r:WIDTH=4 %i
+select -assert-count 1 t:$sr
+select -assert-count 1 t:$sr r:WIDTH=4 %i
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-count 1 t:$_DFF_PP0_
+select -assert-count 1 t:$_DFF_PP1_
+select -assert-count 1 t:$_DFFE_PN0P_
+select -assert-count 1 t:$_DFFE_PN1P_
+select -assert-count 1 t:$_DLATCH_PP0_
+select -assert-count 1 t:$_DLATCH_PN1_
+select -assert-none t:$_DFF_PP0_ t:$_DFF_PP1_ t:$_DFFE_PN0P_ t:$_DFFE_PN1P_ t:$_DLATCH_PP0_ t:$_DLATCH_PN1_ t:$_NOT_ %% %n t:* %i
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 1 t:$_DFF_PP0_
+select -assert-count 1 t:$_DFF_PP1_
+select -assert-count 2 t:$_DFFSR_PPP_
+select -assert-count 1 t:$_DFFE_PN0P_
+select -assert-count 1 t:$_DFFE_PN1P_
+select -assert-count 2 t:$_DFFSRE_PNNP_
+select -assert-count 1 t:$_DLATCH_PP0_
+select -assert-count 1 t:$_DLATCH_PN1_
+select -assert-count 2 t:$_DLATCHSR_PNP_
+select -assert-count 1 t:$_DLATCH_P_
+select -assert-count 1 t:$_DLATCH_N_
+select -assert-count 2 t:$_SR_PN_
+select -assert-none t:$_DFF_PP0_ t:$_DFF_PP1_ t:$_DFFSR_PPP_ t:$_DFFE_PN0P_ t:$_DFFE_PN1P_ t:$_DFFSRE_PNNP_ t:$_DLATCH_PP0_ t:$_DLATCH_PN1_ t:$_DLATCHSR_PNP_ t:$_NOT_ t:$_DLATCH_N_ t:$_DLATCH_P_ t:$_SR_PN_ %% %n t:* %i
+
+design -reset
+
+
+
+### Never-active CLR removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [5:0] D;
+output [23:0] Q;
+input CLR;
+input SET;
+input EN;
+
+$dffsr #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b1), .CLR_POLARITY(1'b1), .WIDTH(6)) ff0 (.CLK(CLK), .CLR(6'h00), .SET({6{SET}}), .D(D), .Q(Q[5:0]));
+$dffsre #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b0), .CLR_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(6)) ff1 (.CLK(CLK), .EN(EN), .D(D), .CLR(6'h3f), .SET({6{SET}}), .Q(Q[11:6]));
+$dlatchsr #(.SET_POLARITY(1'b0), .CLR_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(6)) ff2 (.EN(EN), .D(D), .CLR(6'h00), .SET({6{SET}}), .Q(Q[17:12]));
+$sr #(.SET_POLARITY(1'b1), .CLR_POLARITY(1'b0), .WIDTH(6)) ff3 (.CLR(6'h3f), .SET({6{SET}}), .Q(Q[23:18]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 0 t:$dffsr
+select -assert-count 0 t:$dffsre
+select -assert-count 0 t:$dlatchsr
+select -assert-count 0 t:$sr
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$adlatch
+select -assert-count 1 t:$dlatch
+
+design -reset
+
+
+
+### Never-active CLR removal (not applicable).
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [5:0] D;
+output [23:0] Q;
+input CLR;
+input SET;
+input ALT;
+input EN;
+
+$dffsr #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b1), .CLR_POLARITY(1'b1), .WIDTH(6)) ff0 (.CLK(CLK), .CLR(6'h00), .SET({{5{SET}}, ALT}), .D(D), .Q(Q[5:0]));
+$dffsre #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b0), .CLR_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(6)) ff1 (.CLK(CLK), .EN(EN), .D(D), .CLR(6'h3f), .SET({{5{SET}}, ALT}), .Q(Q[11:6]));
+$dlatchsr #(.SET_POLARITY(1'b0), .CLR_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(6)) ff2 (.EN(EN), .D(D), .CLR(6'h00), .SET({{5{SET}}, ALT}), .Q(Q[17:12]));
+$sr #(.SET_POLARITY(1'b1), .CLR_POLARITY(1'b0), .WIDTH(6)) ff3 (.CLR(6'h3f), .SET({{5{SET}}, ALT}), .Q(Q[23:18]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsre
+select -assert-count 1 t:$dlatchsr
+select -assert-count 1 t:$sr
+select -assert-count 0 t:$adff
+select -assert-count 0 t:$adffe
+select -assert-count 0 t:$adlatch
+select -assert-count 0 t:$dlatch
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 0 t:$_DFFSR_*
+select -assert-count 0 t:$_DFFSRE_*
+select -assert-count 0 t:$_DLATCHSR_*
+select -assert-count 0 t:$_SR_*
+select -assert-count 6 t:$_DFF_PP1_
+select -assert-count 6 t:$_DFFE_PN1P_
+select -assert-count 6 t:$_DLATCH_PN1_
+select -assert-count 6 t:$_DLATCH_P_
+
+design -reset
+
+
+
+### Never-active SET removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [5:0] D;
+output [23:0] Q;
+input CLR;
+input SET;
+input EN;
+
+$dffsr #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b1), .CLR_POLARITY(1'b1), .WIDTH(6)) ff0 (.CLK(CLK), .CLR({6{CLR}}), .SET(6'h00), .D(D), .Q(Q[5:0]));
+$dffsre #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b0), .CLR_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(6)) ff1 (.CLK(CLK), .EN(EN), .D(D), .CLR({6{CLR}}), .SET(6'h3f), .Q(Q[11:6]));
+$dlatchsr #(.SET_POLARITY(1'b0), .CLR_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(6)) ff2 (.EN(EN), .D(D), .CLR({6{CLR}}), .SET(6'h3f), .Q(Q[17:12]));
+$sr #(.SET_POLARITY(1'b1), .CLR_POLARITY(1'b0), .WIDTH(6)) ff3 (.CLR({6{CLR}}), .SET(6'h00), .Q(Q[23:18]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 0 t:$dffsr
+select -assert-count 0 t:$dffsre
+select -assert-count 0 t:$dlatchsr
+select -assert-count 0 t:$sr
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$adlatch
+select -assert-count 1 t:$dlatch
+
+design -reset
+
+
+
+### Never-active CLR removal (not applicable).
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [5:0] D;
+output [23:0] Q;
+input CLR;
+input SET;
+input ALT;
+input EN;
+
+$dffsr #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b1), .CLR_POLARITY(1'b1), .WIDTH(6)) ff0 (.CLK(CLK), .CLR({{5{CLR}}, ALT}), .SET(6'h00), .D(D), .Q(Q[5:0]));
+$dffsre #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b0), .CLR_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(6)) ff1 (.CLK(CLK), .EN(EN), .D(D), .CLR({{5{CLR}}, ALT}), .SET(6'h3f), .Q(Q[11:6]));
+$dlatchsr #(.SET_POLARITY(1'b0), .CLR_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(6)) ff2 (.EN(EN), .D(D), .CLR({{5{CLR}}, ALT}), .SET(6'h3f), .Q(Q[17:12]));
+$sr #(.SET_POLARITY(1'b1), .CLR_POLARITY(1'b0), .WIDTH(6)) ff3 (.CLR({{5{CLR}}, ALT}), .SET(6'h00), .Q(Q[23:18]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsre
+select -assert-count 1 t:$dlatchsr
+select -assert-count 1 t:$sr
+select -assert-count 0 t:$adff
+select -assert-count 0 t:$adffe
+select -assert-count 0 t:$adlatch
+select -assert-count 0 t:$dlatch
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 0 t:$_DFFSR_*
+select -assert-count 0 t:$_DFFSRE_*
+select -assert-count 0 t:$_DLATCHSR_*
+select -assert-count 0 t:$_SR_*
+select -assert-count 6 t:$_DFF_PP0_
+select -assert-count 6 t:$_DFFE_PN0P_
+select -assert-count 6 t:$_DLATCH_PP0_
+select -assert-count 6 t:$_DLATCH_N_
+
+design -reset
+
+
+
+### SET/CLR merge into ARST.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [5:0] D;
+output [23:0] Q;
+input ARST;
+input EN;
+
+$dffsr #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b1), .CLR_POLARITY(1'b1), .WIDTH(6)) ff0 (.CLK(CLK), .CLR({ARST, 5'h00}), .SET({1'b0, {5{ARST}}}), .D(D), .Q(Q[5:0]));
+$dffsre #(.CLK_POLARITY(1'b1), .SET_POLARITY(1'b0), .CLR_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(6)) ff1 (.CLK(CLK), .EN(EN), .D(D), .CLR({ARST, 5'h1f}), .SET({1'b1, {5{ARST}}}), .Q(Q[11:6]));
+$dlatchsr #(.SET_POLARITY(1'b0), .CLR_POLARITY(1'b1), .EN_POLARITY(1'b1), .WIDTH(6)) ff2 (.EN(EN), .D(D), .CLR({ARST, 5'h00}), .SET({1'b1, {5{ARST}}}), .Q(Q[17:12]));
+$sr #(.SET_POLARITY(1'b1), .CLR_POLARITY(1'b0), .WIDTH(6)) ff3 (.CLR({ARST, 5'h1f}), .SET({1'b0, {5{ARST}}}), .Q(Q[23:18]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 0 t:$dffsr
+select -assert-count 0 t:$dffsre
+select -assert-count 1 t:$dlatchsr
+select -assert-count 1 t:$sr
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adff r:ARST_VALUE=6'h1f %i
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$adffe r:ARST_VALUE=6'h1f %i
+select -assert-count 0 t:$adlatch
+select -assert-count 0 t:$dlatch
diff --git a/tests/opt/opt_dff_srst.ys b/tests/opt/opt_dff_srst.ys
new file mode 100644
index 000000000..4a77de0b8
--- /dev/null
+++ b/tests/opt/opt_dff_srst.ys
@@ -0,0 +1,113 @@
+### Always-active SRST removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [1:0] D;
+(* init=12'h555 *)
+output [11:0] Q;
+input SRST;
+input EN;
+
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff0 (.CLK(CLK), .SRST(1'b1), .D(D), .Q(Q[1:0]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2), .WIDTH(2)) ff1 (.CLK(CLK), .SRST(1'b0), .EN(EN), .D(D), .Q(Q[3:2]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(CLK), .SRST(1'b0), .EN(EN), .D(D), .Q(Q[5:4]));
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff3 (.CLK(CLK), .SRST(1'bx), .D(D), .Q(Q[7:6]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2), .WIDTH(2)) ff4 (.CLK(CLK), .SRST(1'bx), .EN(EN), .D(D), .Q(Q[9:8]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2), .WIDTH(2)) ff5 (.CLK(CLK), .SRST(1'bx), .EN(EN), .D(D), .Q(Q[11:10]));
+
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-count 0 t:$sdff
+select -assert-count 0 t:$sdffe
+select -assert-count 0 t:$sdffce
+select -assert-count 4 t:$dff
+select -assert-count 2 t:$dffe
+
+design -load orig
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 1 t:$sdff
+select -assert-count 1 t:$sdffe
+select -assert-count 1 t:$sdffce
+select -assert-count 2 t:$dff
+select -assert-count 1 t:$dffe
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:$_SDFF_???_
+select -assert-none t:$_SDFFE_????_
+select -assert-none t:$_SDFFCE_????_
+select -assert-count 8 t:$_DFF_?_
+select -assert-count 4 t:$_DFFE_??_
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff -keepdc
+design -load postopt
+select -assert-count 2 t:$_SDFF_???_
+select -assert-count 2 t:$_SDFFE_????_
+select -assert-count 2 t:$_SDFFCE_????_
+select -assert-count 4 t:$_DFF_?_
+select -assert-count 2 t:$_DFFE_??_
+
+design -reset
+
+
+### Never-active SRST removal.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input CLK;
+input [1:0] D;
+output [5:0] Q;
+input SRST;
+input EN;
+
+$sdff #(.CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2), .WIDTH(2)) ff0 (.CLK(CLK), .SRST(1'b0), .D(D), .Q(Q[1:0]));
+$sdffe #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2), .WIDTH(2)) ff1 (.CLK(CLK), .SRST(1'b1), .EN(EN), .D(D), .Q(Q[3:2]));
+$sdffce #(.CLK_POLARITY(1'b1), .EN_POLARITY(1'b1), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2), .WIDTH(2)) ff2 (.CLK(CLK), .SRST(1'b1), .EN(EN), .D(D), .Q(Q[5:4]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:$sdff
+select -assert-none t:$sdffe
+select -assert-none t:$sdffce
+select -assert-count 1 t:$dff
+select -assert-count 2 t:$dffe
+
+design -load orig
+simplemap
+
+equiv_opt -undef -assert -multiclock opt_dff
+design -load postopt
+select -assert-none t:$_SDFF_???_
+select -assert-none t:$_SDFFE_????_
+select -assert-none t:$_SDFFCE_????_
+select -assert-count 2 t:$_DFF_P_
+select -assert-count 4 t:$_DFFE_PP_
+
+design -reset
+
diff --git a/tests/opt/opt_rmdff.ys b/tests/opt/opt_rmdff.ys
index 7e11bc73f..998414597 100644
--- a/tests/opt/opt_rmdff.ys
+++ b/tests/opt/opt_rmdff.ys
@@ -4,7 +4,7 @@ design -stash gold
read_verilog -icells opt_rmdff.v
proc
-opt_rmdff
+opt_dff
select -assert-count 0 c:remove*
select -assert-min 7 c:keep*
@@ -23,7 +23,6 @@ connect -port remove6 EN 1'b1
connect -port remove15 E 1'b1
cd ..
-dff2dffe -unmap
clk2fflogic
opt_clean
diff --git a/tests/opt/opt_rmdff_sat.ys b/tests/opt/opt_rmdff_sat.ys
index 1c3dd9c05..231c43ecb 100644
--- a/tests/opt/opt_rmdff_sat.ys
+++ b/tests/opt/opt_rmdff_sat.ys
@@ -1,5 +1,5 @@
read_verilog opt_rmdff_sat.v
prep -flatten
-opt_rmdff -sat
-synth
+opt_dff -sat -nosdff
+simplemap
select -assert-count 5 t:$_DFF_P_
diff --git a/tests/sat/dff.ys b/tests/sat/dff.ys
new file mode 100644
index 000000000..ba3625871
--- /dev/null
+++ b/tests/sat/dff.ys
@@ -0,0 +1,21 @@
+# Ensure all sync-only DFFs have usable SAT models.
+
+read_verilog -icells <<EOT
+
+module top(...);
+
+input C, D, R, E;
+output [4:0] Q;
+
+\$dff #(.WIDTH(1), .CLK_POLARITY(1'b1)) ff0 (.CLK(C), .D(D), .Q(Q[0]));
+\$dffe #(.WIDTH(1), .CLK_POLARITY(1'b1), .EN_POLARITY(1'b1)) ff1 (.CLK(C), .D(D), .EN(E), .Q(Q[1]));
+\$sdff #(.WIDTH(1), .CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(1'b0)) ff2 (.CLK(C), .D(D), .SRST(R), .Q(Q[2]));
+\$sdffe #(.WIDTH(1), .CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(1'b0), .EN_POLARITY(1'b1)) ff3 (.CLK(C), .D(D), .EN(E), .SRST(R), .Q(Q[3]));
+\$sdffce #(.WIDTH(1), .CLK_POLARITY(1'b1), .SRST_POLARITY(1'b1), .SRST_VALUE(1'b0), .EN_POLARITY(1'b1)) ff4 (.CLK(C), .D(D), .EN(E), .SRST(R), .Q(Q[4]));
+
+endmodule
+
+EOT
+
+# This ensures that 1) coarse cells have SAT models, 2) fine cells have SAT models, 3) they're equivalent
+equiv_opt -assert simplemap
diff --git a/tests/techmap/cellname.ys b/tests/techmap/cellname.ys
new file mode 100644
index 000000000..2edd6a9fd
--- /dev/null
+++ b/tests/techmap/cellname.ys
@@ -0,0 +1,41 @@
+read_verilog << EOT
+
+module sub (input i, output o);
+parameter _TECHMAP_CELLNAME_ = "";
+namedsub #(.name(_TECHMAP_CELLNAME_)) _TECHMAP_REPLACE_ (i, o);
+endmodule
+
+EOT
+
+design -stash map
+
+read_verilog << EOT
+
+(* blackbox *)
+module sub (input i, output o);
+endmodule
+
+(* blackbox *)
+module namedsub (input i, output o);
+parameter name = "";
+endmodule
+
+module top(input [3:0] i, output [3:0] o);
+
+sub s1 (i[0], o[0]);
+sub subsubsub (i[1], o[1]);
+sub s2 (i[2], o[2]);
+sub xxx (i[3], o[3]);
+
+endmodule
+
+EOT
+
+techmap -map %map
+
+select -assert-count 4 t:namedsub
+select -assert-count 0 t:sub
+select -assert-count 1 t:namedsub r:name=s1 %i
+select -assert-count 1 t:namedsub r:name=subsubsub %i
+select -assert-count 1 t:namedsub r:name=s2 %i
+select -assert-count 1 t:namedsub r:name=xxx %i
diff --git a/tests/techmap/dfflegalize_adff.ys b/tests/techmap/dfflegalize_adff.ys
index cf3e925a3..135ae0ab7 100644
--- a/tests/techmap/dfflegalize_adff.ys
+++ b/tests/techmap/dfflegalize_adff.ys
@@ -37,10 +37,10 @@ EOT
design -save orig
flatten
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ x
# Convert everything to ADFFs.
diff --git a/tests/techmap/dfflegalize_adff_init.ys b/tests/techmap/dfflegalize_adff_init.ys
index a10161701..7764e15a5 100644
--- a/tests/techmap/dfflegalize_adff_init.ys
+++ b/tests/techmap/dfflegalize_adff_init.ys
@@ -37,18 +37,18 @@ EOT
design -save orig
flatten
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 0 -cell $_DLATCH_P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 1 -cell $_DLATCH_P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP1_ 0 -cell $_DLATCH_P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP1_ 1 -cell $_DLATCH_P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ 0 -cell $_DLATCH_P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ 1 -cell $_DLATCH_P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP1P_ 0 -cell $_DLATCH_P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP1P_ 1 -cell $_DLATCH_P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 0 -cell $_DLATCH_P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 1 -cell $_DLATCH_P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP1_ 0 -cell $_DLATCH_P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP1_ 1 -cell $_DLATCH_P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ 0 -cell $_DLATCH_P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ 1 -cell $_DLATCH_P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP1P_ 0 -cell $_DLATCH_P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP1P_ 1 -cell $_DLATCH_P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 1
# Convert everything to ADFFs.
diff --git a/tests/techmap/dfflegalize_adlatch.ys b/tests/techmap/dfflegalize_adlatch.ys
index ea5aaa53c..b242cc809 100644
--- a/tests/techmap/dfflegalize_adlatch.ys
+++ b/tests/techmap/dfflegalize_adlatch.ys
@@ -21,8 +21,8 @@ EOT
design -save orig
flatten
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ x
# Convert everything to ADLATCHs.
diff --git a/tests/techmap/dfflegalize_adlatch_init.ys b/tests/techmap/dfflegalize_adlatch_init.ys
index 0a31d7736..7b22ea0c0 100644
--- a/tests/techmap/dfflegalize_adlatch_init.ys
+++ b/tests/techmap/dfflegalize_adlatch_init.ys
@@ -21,12 +21,12 @@ EOT
design -save orig
flatten
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 1
# Convert everything to ADLATCHs.
diff --git a/tests/techmap/dfflegalize_dff.ys b/tests/techmap/dfflegalize_dff.ys
index d71f4204e..63ab47865 100644
--- a/tests/techmap/dfflegalize_dff.ys
+++ b/tests/techmap/dfflegalize_dff.ys
@@ -66,15 +66,15 @@ EOT
design -save orig
flatten
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_P_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFF_PP0_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFE_PP0P_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFCE_PP0P_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_P_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFF_PP0_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFE_PP0P_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFCE_PP0P_ x
# Convert everything to DFFs.
diff --git a/tests/techmap/dfflegalize_dff_init.ys b/tests/techmap/dfflegalize_dff_init.ys
index 84848da1f..741ac39d0 100644
--- a/tests/techmap/dfflegalize_dff_init.ys
+++ b/tests/techmap/dfflegalize_dff_init.ys
@@ -66,34 +66,34 @@ EOT
design -save orig
flatten
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP1_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP1_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP1P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP1P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFF_PP0_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFF_PP0_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFF_PP1_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFF_PP1_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFE_PP0P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFE_PP0P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFE_PP1P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFE_PP1P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFCE_PP0P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFCE_PP0P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFCE_PP1P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFFCE_PP1P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP1_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP1_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP1P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP1P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFF_PP0_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFF_PP0_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFF_PP1_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFF_PP1_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFE_PP0P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFE_PP0P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFE_PP1P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFE_PP1P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFCE_PP0P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFCE_PP0P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFCE_PP1P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFFCE_PP1P_ 1
# Convert everything to DFFs.
diff --git a/tests/techmap/dfflegalize_dffsr.ys b/tests/techmap/dfflegalize_dffsr.ys
index 0cfb4950e..49a7237a2 100644
--- a/tests/techmap/dfflegalize_dffsr.ys
+++ b/tests/techmap/dfflegalize_dffsr.ys
@@ -24,10 +24,10 @@ EOT
design -save orig
flatten
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ x -cell $_SR_PP_ x
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ x -cell $_SR_PP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ x -cell $_SR_PP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ x -cell $_SR_PP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ x
# Convert everything to ADFFs.
diff --git a/tests/techmap/dfflegalize_dffsr_init.ys b/tests/techmap/dfflegalize_dffsr_init.ys
index a98bd0cfe..ce5a32f76 100644
--- a/tests/techmap/dfflegalize_dffsr_init.ys
+++ b/tests/techmap/dfflegalize_dffsr_init.ys
@@ -41,18 +41,18 @@ EOT
design -save orig
flatten
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 0 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 1 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP1_ 0 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP1_ 1 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ 0 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP0P_ 1 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP1P_ 0 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP1P_ 1 -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 0 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 1 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP1_ 0 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP1_ 1 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ 0 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP0P_ 1 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP1P_ 0 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP1P_ 1 -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 1
# Convert everything to ADFFs.
diff --git a/tests/techmap/dfflegalize_dlatch.ys b/tests/techmap/dfflegalize_dlatch.ys
index 8a5fad0da..b68ea741e 100644
--- a/tests/techmap/dfflegalize_dlatch.ys
+++ b/tests/techmap/dfflegalize_dlatch.ys
@@ -8,9 +8,9 @@ endmodule
EOT
design -save orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_P_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_P_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ x
# Convert everything to DFFs.
diff --git a/tests/techmap/dfflegalize_dlatch_const.ys b/tests/techmap/dfflegalize_dlatch_const.ys
index 0b5167a06..f30a534fd 100644
--- a/tests/techmap/dfflegalize_dlatch_const.ys
+++ b/tests/techmap/dfflegalize_dlatch_const.ys
@@ -14,10 +14,10 @@ endmodule
EOT
design -save orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP0_ 01
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_PP?_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP0_ 01
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_PP?_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 1
# Convert everything to ADFFs.
diff --git a/tests/techmap/dfflegalize_dlatch_init.ys b/tests/techmap/dfflegalize_dlatch_init.ys
index 3ec9d9b06..ccc9e41d7 100644
--- a/tests/techmap/dfflegalize_dlatch_init.ys
+++ b/tests/techmap/dfflegalize_dlatch_init.ys
@@ -8,14 +8,14 @@ endmodule
EOT
design -save orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_P_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_P_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_P_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_P_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 1
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 0
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 1
# Convert everything to DFFs.
diff --git a/tests/techmap/dfflegalize_dlatchsr.ys b/tests/techmap/dfflegalize_dlatchsr.ys
index 3476c0372..53d910723 100644
--- a/tests/techmap/dfflegalize_dlatchsr.ys
+++ b/tests/techmap/dfflegalize_dlatchsr.ys
@@ -10,8 +10,8 @@ endmodule
EOT
design -save orig
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ x -cell $_SR_PP_ x
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ x -cell $_SR_PP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ x
# Convert everything to ADLATCHs.
diff --git a/tests/techmap/dfflegalize_dlatchsr_init.ys b/tests/techmap/dfflegalize_dlatchsr_init.ys
index e922242d9..2d33634d1 100644
--- a/tests/techmap/dfflegalize_dlatchsr_init.ys
+++ b/tests/techmap/dfflegalize_dlatchsr_init.ys
@@ -23,12 +23,12 @@ EOT
design -save orig
flatten
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 1
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 0
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 1
# Convert everything to ADLATCHs.
diff --git a/tests/techmap/dfflegalize_inv.ys b/tests/techmap/dfflegalize_inv.ys
index 573393e7d..cb42e01a8 100644
--- a/tests/techmap/dfflegalize_inv.ys
+++ b/tests/techmap/dfflegalize_inv.ys
@@ -94,7 +94,7 @@ EOT
design -save orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_P_ x -cell $_DFFE_PP_ x -cell $_DFF_PP?_ x -cell $_DFFE_PP?P_ x -cell $_DFFSR_PPP_ x -cell $_DFFSRE_PPPP_ x -cell $_SDFF_PP?_ x -cell $_SDFFE_PP?P_ x -cell $_SDFFCE_PP?P_ x -cell $_DLATCH_P_ x -cell $_DLATCH_PP?_ x -cell $_DLATCHSR_PPP_ x -cell $_SR_PP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_P_ x -cell $_DFFE_PP_ x -cell $_DFF_PP?_ x -cell $_DFFE_PP?P_ x -cell $_DFFSR_PPP_ x -cell $_DFFSRE_PPPP_ x -cell $_SDFF_PP?_ x -cell $_SDFFE_PP?P_ x -cell $_SDFFCE_PP?P_ x -cell $_DLATCH_P_ x -cell $_DLATCH_PP?_ x -cell $_DLATCHSR_PPP_ x -cell $_SR_PP_ x
design -load postopt
select -assert-count 46 t:$_NOT_
@@ -123,7 +123,7 @@ select -assert-none t:$_DFF_P_ t:$_DFFE_PP_ t:$_DFF_PP?_ t:$_DFFE_PP?P_ t:$_DFFS
design -load orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFF_N_ x -cell $_DFFE_NN_ x -cell $_DFF_NN?_ x -cell $_DFFE_NN?N_ x -cell $_DFFSR_NNN_ x -cell $_DFFSRE_NNNN_ x -cell $_SDFF_NN?_ x -cell $_SDFFE_NN?N_ x -cell $_SDFFCE_NN?N_ x -cell $_DLATCH_N_ x -cell $_DLATCH_NN?_ x -cell $_DLATCHSR_NNN_ x -cell $_SR_NN_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFF_N_ x -cell $_DFFE_NN_ x -cell $_DFF_NN?_ x -cell $_DFFE_NN?N_ x -cell $_DFFSR_NNN_ x -cell $_DFFSRE_NNNN_ x -cell $_SDFF_NN?_ x -cell $_SDFFE_NN?N_ x -cell $_SDFFCE_NN?N_ x -cell $_DLATCH_N_ x -cell $_DLATCH_NN?_ x -cell $_DLATCHSR_NNN_ x -cell $_SR_NN_ x
design -load postopt
select -assert-count 122 t:$_NOT_
@@ -166,7 +166,7 @@ endmodule
EOT
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_NNNN_ x -cell $_DFFSRE_PPPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_NNNN_ x -cell $_DFFSRE_PPPP_ x
design -load postopt
select -assert-count 6 t:$_NOT_
diff --git a/tests/techmap/dfflegalize_mince.ys b/tests/techmap/dfflegalize_mince.ys
index 75069541b..31c8d04fc 100644
--- a/tests/techmap/dfflegalize_mince.ys
+++ b/tests/techmap/dfflegalize_mince.ys
@@ -22,7 +22,7 @@ endmodule
EOT
design -save orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFE_PP_ x -cell $_DFFE_PP?P_ x -cell $_DFFSRE_PPPP_ x -cell $_SDFFE_PP?P_ x -cell $_SDFFCE_PP?P_ x -mince 3
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFE_PP_ x -cell $_DFFE_PP?P_ x -cell $_DFFSRE_PPPP_ x -cell $_SDFFE_PP?P_ x -cell $_SDFFCE_PP?P_ x -mince 3
design -load postopt
select -assert-count 4 t:$_DFFE_PP_
diff --git a/tests/techmap/dfflegalize_minsrst.ys b/tests/techmap/dfflegalize_minsrst.ys
index b9bc3f1b9..0fc40dc08 100644
--- a/tests/techmap/dfflegalize_minsrst.ys
+++ b/tests/techmap/dfflegalize_minsrst.ys
@@ -18,7 +18,7 @@ endmodule
EOT
design -save orig
-equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SDFF_PP?_ x -cell $_SDFFE_PP?P_ x -cell $_SDFFCE_PP?P_ x -minsrst 3
+equiv_opt -assert -multiclock dfflegalize -cell $_SDFF_PP?_ x -cell $_SDFFE_PP?P_ x -cell $_SDFFCE_PP?P_ x -minsrst 3
design -load postopt
select -assert-count 5 t:$_SDFF_PP0_
diff --git a/tests/techmap/dfflegalize_sr.ys b/tests/techmap/dfflegalize_sr.ys
index b8c91f753..27e83be91 100644
--- a/tests/techmap/dfflegalize_sr.ys
+++ b/tests/techmap/dfflegalize_sr.ys
@@ -9,12 +9,12 @@ endmodule
EOT
design -save orig
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SR_PP_ x
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ x
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ x
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ x
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ x
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_SR_PP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ x
+equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ x
# Convert everything to SRs.
diff --git a/tests/techmap/dfflegalize_sr_init.ys b/tests/techmap/dfflegalize_sr_init.ys
index 5c52a0b28..52b797b9e 100644
--- a/tests/techmap/dfflegalize_sr_init.ys
+++ b/tests/techmap/dfflegalize_sr_init.ys
@@ -21,18 +21,18 @@ EOT
design -save orig
flatten
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SR_PP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_SR_PP_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP0_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCH_PP1_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DLATCHSR_PPP_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSR_PPP_ 1
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 0
-#equiv_opt -assert -multiclock -map +/simcells.v dfflegalize -cell $_DFFSRE_PPPP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_SR_PP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_SR_PP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP0_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCH_PP1_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DLATCHSR_PPP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSR_PPP_ 1
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 0
+#equiv_opt -assert -multiclock dfflegalize -cell $_DFFSRE_PPPP_ 1
# Convert everything to SRs.
diff --git a/tests/techmap/dffunmap.ys b/tests/techmap/dffunmap.ys
new file mode 100644
index 000000000..b813078ee
--- /dev/null
+++ b/tests/techmap/dffunmap.ys
@@ -0,0 +1,100 @@
+read_verilog -icells << EOT
+
+module top(...);
+
+input C, R, E, S;
+input [1:0] D;
+output [20:0] Q;
+
+$dff #(.CLK_POLARITY(1'b0), .WIDTH(2)) ff0 (.CLK(C), .D(D), .Q(Q[1:0]));
+$dffe #(.CLK_POLARITY(1'b0), .EN_POLARITY(1'b0), .WIDTH(2)) ff1 (.CLK(C), .EN(E), .D(D), .Q(Q[3:2]));
+$sdff #(.CLK_POLARITY(1'b0), .WIDTH(2), .SRST_POLARITY(1'b0), .SRST_VALUE(2'h2)) ff2 (.CLK(C), .SRST(R), .D(D), .Q(Q[5:4]));
+$sdffe #(.CLK_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(2), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2)) ff3 (.CLK(C), .EN(E), .SRST(R), .D(D), .Q(Q[7:6]));
+$sdffce #(.CLK_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(2), .SRST_POLARITY(1'b1), .SRST_VALUE(2'h2)) ff4 (.CLK(C), .EN(E), .SRST(R), .D(D), .Q(Q[9:8]));
+$adff #(.CLK_POLARITY(1'b0), .WIDTH(2), .ARST_POLARITY(1'b0), .ARST_VALUE(2'h2)) ff5 (.CLK(C), .ARST(R), .D(D), .Q(Q[11:10]));
+$adffe #(.CLK_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(2), .ARST_POLARITY(1'b1), .ARST_VALUE(2'h2)) ff6 (.CLK(C), .EN(E), .ARST(R), .D(D), .Q(Q[13:12]));
+$dffsr #(.CLK_POLARITY(1'b0), .WIDTH(2), .CLR_POLARITY(1'b0), .SET_POLARITY(1'b1)) ff7 (.CLK(C), .CLR({R, S}), .SET({S, R}), .D(D), .Q(Q[15:14]));
+$dffsre #(.CLK_POLARITY(1'b0), .EN_POLARITY(1'b1), .WIDTH(2), .CLR_POLARITY(1'b1), .SET_POLARITY(1'b0)) ff8 (.CLK(C), .EN(E), .CLR({R, R}), .SET({S, S}), .D(D), .Q(Q[17:16]));
+
+endmodule
+
+EOT
+
+design -save orig
+
+equiv_opt -assert -async2sync dffunmap
+design -load postopt
+select -assert-none t:$sdff t:$dffe t:$adffe t:$sdffe t:$sdffce t:$dffsre
+select -assert-count 5 t:$dff
+select -assert-count 2 t:$adff
+select -assert-count 2 t:$dffsr
+
+design -load orig
+
+equiv_opt -assert -async2sync dffunmap -ce-only
+design -load postopt
+select -assert-none t:$dffe t:$adffe t:$sdffe t:$sdffce t:$dffsre
+select -assert-count 3 t:$dff
+select -assert-count 2 t:$sdff
+select -assert-count 2 t:$adff
+select -assert-count 2 t:$dffsr
+
+design -load orig
+
+equiv_opt -assert -async2sync dffunmap -srst-only
+design -load postopt
+select -assert-none t:$sdff t:$sdffe t:$sdffce
+select -assert-count 3 t:$dff
+select -assert-count 2 t:$dffe
+select -assert-count 1 t:$adff
+select -assert-count 1 t:$adffe
+select -assert-count 1 t:$dffsr
+select -assert-count 1 t:$dffsre
+
+design -load orig
+simplemap
+
+equiv_opt -assert -async2sync dffunmap
+design -load postopt
+select -assert-none t:$_SDFF* t:$_DFFE_* t:$_DFFSRE_*
+select -assert-count 10 t:$_DFF_N_
+select -assert-count 1 t:$_DFF_NP0_
+select -assert-count 1 t:$_DFF_NN0_
+select -assert-count 1 t:$_DFF_NP1_
+select -assert-count 1 t:$_DFF_NN1_
+select -assert-count 2 t:$_DFFSR_NPN_
+select -assert-count 2 t:$_DFFSR_NNP_
+
+design -load orig
+simplemap
+
+equiv_opt -assert -async2sync dffunmap -ce-only
+design -load postopt
+select -assert-none t:$_SDFFE_* t:$_SDFFCE_* t:$_DFFE_* t:$_DFFSRE_*
+select -assert-count 6 t:$_DFF_N_
+select -assert-count 1 t:$_SDFF_NP0_
+select -assert-count 1 t:$_SDFF_NN0_
+select -assert-count 1 t:$_SDFF_NP1_
+select -assert-count 1 t:$_SDFF_NN1_
+select -assert-count 1 t:$_DFF_NP0_
+select -assert-count 1 t:$_DFF_NN0_
+select -assert-count 1 t:$_DFF_NP1_
+select -assert-count 1 t:$_DFF_NN1_
+select -assert-count 2 t:$_DFFSR_NPN_
+select -assert-count 2 t:$_DFFSR_NNP_
+
+design -load orig
+simplemap
+
+equiv_opt -assert -async2sync dffunmap -srst-only
+design -load postopt
+select -assert-none t:$sdff t:$sdffe t:$sdffce
+select -assert-count 6 t:$_DFF_N_
+select -assert-count 2 t:$_DFFE_NP_
+select -assert-count 2 t:$_DFFE_NN_
+select -assert-count 1 t:$_DFF_NN0_
+select -assert-count 1 t:$_DFF_NN1_
+select -assert-count 1 t:$_DFFE_NP0P_
+select -assert-count 1 t:$_DFFE_NP1P_
+select -assert-count 2 t:$_DFFSR_NPN_
+select -assert-count 2 t:$_DFFSRE_NNPP_
diff --git a/tests/techmap/zinit.ys b/tests/techmap/zinit.ys
index 3527840b9..1670573dd 100644
--- a/tests/techmap/zinit.ys
+++ b/tests/techmap/zinit.ys
@@ -95,7 +95,7 @@ EOT
zinit
select -assert-count 48 t:$_NOT_
-select -assert-count 1 w:Q a:init=24'bx %i
+select -assert-count 0 w:Q a:init %i
select -assert-count 4 c:dff0 c:dff2 c:dff4 c:dff6 %% t:$_DFFE_??1P_ %i
select -assert-count 4 c:dff1 c:dff3 c:dff5 c:dff7 %% t:$_DFFE_??0P_ %i
select -assert-count 4 c:dff8 c:dff10 c:dff12 c:dff14 %% t:$_SDFF_??1_ %i
@@ -142,7 +142,7 @@ EOT
zinit
select -assert-count 0 t:$_NOT_
-select -assert-count 1 w:Q a:init=24'bx %i
+select -assert-count 0 w:Q a:init %i
select -assert-count 4 c:dff0 c:dff2 c:dff4 c:dff6 %% t:$_DFFE_??0P_ %i
select -assert-count 4 c:dff1 c:dff3 c:dff5 c:dff7 %% t:$_DFFE_??1P_ %i
select -assert-count 4 c:dff8 c:dff10 c:dff12 c:dff14 %% t:$_SDFF_??0_ %i
diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh
index 4d3478628..72a3d51eb 100755
--- a/tests/tools/autotest.sh
+++ b/tests/tools/autotest.sh
@@ -193,13 +193,13 @@ do
elif [ "$frontend" = "verific_gates" ]; then
test_passes -p "verific -vlog2k ${bn}_ref.${refext}; verific -import -gates -all; opt; memory;;"
else
- test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.${refext}
+ test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt -nodffe -nosdff; fsm; opt; memory; opt -full -fine" ${bn}_ref.${refext}
test_passes -f "$frontend $include_opts" -p "hierarchy; synth -run coarse; techmap; opt; abc -dff" ${bn}_ref.${refext}
if [ -n "$firrtl2verilog" ]; then
if test -z "$xfirrtl" || ! grep "$fn" "$xfirrtl" ; then
- "$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt; memory; opt; fsm; opt -full -fine; pmuxtree" ${bn}_ref.${refext}
+ "$toolsdir"/../../yosys -b "firrtl" -o ${bn}_ref.fir -f "$frontend $include_opts" -p "prep -nordff; proc; opt -nodffe -nosdff; fsm; opt; memory; opt -full -fine; pmuxtree" ${bn}_ref.${refext}
$firrtl2verilog -i ${bn}_ref.fir -o ${bn}_ref.fir.v
- test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt; memory; opt; fsm; opt -full -fine" ${bn}_ref.fir.v
+ test_passes -f "$frontend $include_opts" -p "hierarchy; proc; opt -nodffe -nosdff; fsm; opt; memory; opt -full -fine" ${bn}_ref.fir.v
fi
fi
fi
diff --git a/tests/various/const_arg_loop.v b/tests/various/const_arg_loop.v
new file mode 100644
index 000000000..85318562f
--- /dev/null
+++ b/tests/various/const_arg_loop.v
@@ -0,0 +1,44 @@
+module top;
+ function automatic [31:0] operation1;
+ input [4:0] rounds;
+ input integer num;
+ integer i;
+ begin
+ begin : shadow
+ integer rounds;
+ rounds = 0;
+ end
+ for (i = 0; i < rounds; i = i + 1)
+ num = num * 2;
+ operation1 = num;
+ end
+ endfunction
+
+ function automatic [31:0] operation2;
+ input [4:0] var;
+ input integer num;
+ begin
+ var[0] = var[0] ^ 1;
+ operation2 = num * var;
+ end
+ endfunction
+
+ wire [31:0] a;
+ assign a = 2;
+
+ parameter A = 3;
+
+ wire [31:0] x1;
+ assign x1 = operation1(A, a);
+
+ wire [31:0] x2;
+ assign x2 = operation2(A, a);
+
+// `define VERIFY
+`ifdef VERIFY
+ assert property (a == 2);
+ assert property (A == 3);
+ assert property (x1 == 16);
+ assert property (x2 == 4);
+`endif
+endmodule
diff --git a/tests/various/const_arg_loop.ys b/tests/various/const_arg_loop.ys
new file mode 100644
index 000000000..b039bda10
--- /dev/null
+++ b/tests/various/const_arg_loop.ys
@@ -0,0 +1 @@
+read_verilog const_arg_loop.v
diff --git a/tests/various/equiv_opt_undef.ys b/tests/various/equiv_opt_undef.ys
new file mode 100644
index 000000000..5d2c60d0a
--- /dev/null
+++ b/tests/various/equiv_opt_undef.ys
@@ -0,0 +1,35 @@
+read_ilang << EOT
+
+module \top
+ wire $a
+ wire $b
+ wire input 1 \D
+ wire input 2 \EN
+ wire output 3 \Q
+ cell $mux $x
+ parameter \WIDTH 1
+ connect \A \Q
+ connect \B \D
+ connect \S \EN
+ connect \Y $a
+ end
+ cell $ff $y
+ parameter \WIDTH 1
+ connect \D $a
+ connect \Q $b
+ end
+ cell $and $z
+ parameter \A_SIGNED 0
+ parameter \A_WIDTH 1
+ parameter \B_SIGNED 0
+ parameter \B_WIDTH 1
+ parameter \Y_WIDTH 1
+ connect \A $b
+ connect \B 1'x
+ connect \Y \Q
+ end
+end
+
+EOT
+
+equiv_opt -assert -undef ls