diff options
116 files changed, 5912 insertions, 3928 deletions
@@ -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 ---------------------- @@ -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 |