diff options
Diffstat (limited to 'passes/techmap')
38 files changed, 3227 insertions, 997 deletions
diff --git a/passes/techmap/.gitignore b/passes/techmap/.gitignore deleted file mode 100644 index e6dcc6bc0..000000000 --- a/passes/techmap/.gitignore +++ /dev/null @@ -1 +0,0 @@ -techmap.inc diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 1802ba0de..e3b3d8dea 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -1,4 +1,5 @@ +OBJS += passes/techmap/flatten.o OBJS += passes/techmap/techmap.o OBJS += passes/techmap/simplemap.o OBJS += passes/techmap/dfflibmap.o @@ -40,23 +41,12 @@ OBJS += passes/techmap/insbuf.o OBJS += passes/techmap/attrmvcp.o OBJS += passes/techmap/attrmap.o OBJS += passes/techmap/zinit.o +OBJS += passes/techmap/dfflegalize.o OBJS += passes/techmap/dff2dffs.o OBJS += passes/techmap/flowmap.o OBJS += passes/techmap/extractinv.o endif -GENFILES += passes/techmap/techmap.inc - -passes/techmap/techmap.inc: techlibs/common/techmap.v - $(Q) mkdir -p $(dir $@) - $(P) echo "// autogenerated from $<" > $@.new - $(Q) echo "static char stdcells_code[] = {" >> $@.new - $(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new - $(Q) echo "0};" >> $@.new - $(Q) mv $@.new $@ - -passes/techmap/techmap.o: passes/techmap/techmap.inc - ifeq ($(DISABLE_SPAWN),0) TARGETS += $(PROGRAM_PREFIX)yosys-filterlib$(EXE) EXTRA_OBJS += passes/techmap/filterlib.o diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index fae8b2426..0a58fdcc0 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1276,7 +1276,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin struct AbcPass : public Pass { AbcPass() : Pass("abc", "use ABC for technology mapping") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1460,7 +1460,7 @@ struct AbcPass : public Pass { log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing ABC pass (technology mapping using ABC).\n"); log_push(); diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 1b3d5ff06..127f8934e 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -36,7 +36,7 @@ PRIVATE_NAMESPACE_BEGIN struct Abc9Pass : public ScriptPass { Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { } - void on_register() YS_OVERRIDE + void on_register() override { RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs"; RTLIL::constpad["abc9.script.default.area"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -a -v; &mfs"; @@ -81,7 +81,7 @@ struct Abc9Pass : public ScriptPass "&st; &if {C} -g -K 6; &synch2; &if {C} {W} {D} {R} -v; &save; &load;"\ "&mfs"; } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -151,8 +151,8 @@ struct Abc9Pass : public ScriptPass log(" specified).\n"); log("\n"); log(" -dff\n"); - log(" also pass $_ABC9_FF_ cells through to ABC. modules with many clock\n"); - log(" domains are marked as such and automatically partitioned by ABC.\n"); + log(" also pass $_DFF_[NP]_ cells through to ABC. modules with many clock\n"); + log(" domains are supported and automatically partitioned by ABC.\n"); log("\n"); log(" -nocleanup\n"); log(" when this option is used, the temporary files created by this pass\n"); @@ -184,7 +184,7 @@ struct Abc9Pass : public ScriptPass int maxlut; std::string box_file; - void clear_flags() YS_OVERRIDE + void clear_flags() override { exe_cmd.str(""); exe_cmd << "abc9_exe"; @@ -195,7 +195,7 @@ struct Abc9Pass : public ScriptPass box_file = ""; } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { std::string run_from, run_to; clear_flags(); @@ -272,42 +272,108 @@ struct Abc9Pass : public ScriptPass log_pop(); } - void script() YS_OVERRIDE + void script() override { + if (check_label("check")) { + if (help_mode) + run("abc9_ops -check [-dff]", "(option if -dff)"); + else + run(stringf("abc9_ops -check %s", dff_mode ? "-dff" : "")); + } + + if (check_label("map")) { + if (help_mode) + run("abc9_ops -prep_hier -prep_bypass [-prep_dff -dff]", "(option if -dff)"); + else + run(stringf("abc9_ops -prep_hier -prep_bypass %s", dff_mode ? "-prep_dff -dff" : "")); + if (dff_mode) { + run("design -copy-to $abc9_map @$abc9_flops", "(only if -dff)"); + run("select -unset $abc9_flops", " (only if -dff)"); + } + run("design -stash $abc9"); + run("design -load $abc9_map"); + run("proc"); + run("wbflip"); + run("techmap"); + run("opt"); + if (dff_mode || help_mode) { + if (!help_mode) + active_design->scratchpad_unset("abc9_ops.prep_dff_submod.did_something"); + run("abc9_ops -prep_dff_submod", " (only if -dff)"); // rewrite specify + bool did_something = help_mode || active_design->scratchpad_get_bool("abc9_ops.prep_dff_submod.did_something"); + if (did_something) { + // select all $_DFF_[NP]_ + // then select all its fanins + // then select all fanouts of all that + // lastly remove $_DFF_[NP]_ cells + run("setattr -set submod \"$abc9_flop\" t:$_DFF_?_ %ci* %co* t:$_DFF_?_ %d", " (only if -dff)"); + run("submod", " (only if -dff)"); + run("setattr -mod -set whitebox 1 -set abc9_flop 1 -set abc9_box 1 *_$abc9_flop", "(only if -dff)"); + if (help_mode) { + run("foreach module in design"); + run(" rename <module-name>_$abc9_flop _TECHMAP_REPLACE_", " (only if -dff)"); + } + else { + // Rename all submod-s to _TECHMAP_REPLACE_ to inherit name + attrs + for (auto module : active_design->selected_modules()) { + active_design->selected_active_module = module->name.str(); + if (module->cell(stringf("%s_$abc9_flop", module->name.c_str()))) + run(stringf("rename %s_$abc9_flop _TECHMAP_REPLACE_", module->name.c_str())); + } + active_design->selected_active_module.clear(); + } + run("abc9_ops -prep_dff_unmap", " (only if -dff)"); + run("design -copy-to $abc9 =*_$abc9_flop", " (only if -dff)"); // copy submod out + run("delete =*_$abc9_flop", " (only if -dff)"); + } + } + run("design -stash $abc9_map"); + run("design -load $abc9"); + run("design -delete $abc9"); + if (help_mode) + run("techmap -wb -max_iter 1 -map %$abc9_map -map +/abc9_map.v [-D DFF]", "(option if -dff)"); + else + run(stringf("techmap -wb -max_iter 1 -map %%$abc9_map -map +/abc9_map.v %s", dff_mode ? "-D DFF" : "")); + run("design -delete $abc9_map"); + } + if (check_label("pre")) { - run("abc9_ops -check"); + run("read_verilog -icells -lib -specify +/abc9_model.v"); run("scc -set_attr abc9_scc_id {}"); if (help_mode) run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)"); else - run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)"); + run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : "")); if (help_mode) run("abc9_ops -prep_lut <maxlut>", "(skip if -lut or -luts)"); else if (!lut_mode) run(stringf("abc9_ops -prep_lut %d", maxlut)); if (help_mode) - run("abc9_ops -prep_box [-dff]", "(skip if -box)"); + run("abc9_ops -prep_box", "(skip if -box)"); else if (box_file.empty()) - run(stringf("abc9_ops -prep_box %s", dff_mode ? "-dff" : "")); - run("select -set abc9_holes A:abc9_holes"); - run("flatten -wb @abc9_holes"); - run("techmap @abc9_holes"); - if (dff_mode || help_mode) - run("abc9_ops -prep_dff", "(only if -dff)"); - run("opt -purge @abc9_holes"); - run("aigmap"); - run("wbflip @abc9_holes"); + run("abc9_ops -prep_box"); + if (saved_designs.count("$abc9_holes") || help_mode) { + run("design -stash $abc9"); + run("design -load $abc9_holes"); + run("techmap -wb -map %$abc9 -map +/techmap.v"); + run("opt -purge"); + run("aigmap"); + run("design -stash $abc9_holes"); + run("design -load $abc9"); + run("design -delete $abc9"); + } } - if (check_label("map")) { + if (check_label("exe")) { + run("aigmap"); if (help_mode) { run("foreach module in selection"); run(" abc9_ops -write_lut <abc-temp-dir>/input.lut", "(skip if '-lut' or '-luts')"); - run(" abc9_ops -write_box <abc-temp-dir>/input.box"); - run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); - run(" abc9_exe [options] -cwd <abc-temp-dir> [-lut <abc-temp-dir>/input.lut] -box <abc-temp-dir>/input.box"); + run(" abc9_ops -write_box <abc-temp-dir>/input.box", "(skip if '-box')"); + run(" write_xaiger -map <abc-temp-dir>/input.sym [-dff] <abc-temp-dir>/input.xaig"); + run(" abc9_exe [options] -cwd <abc-temp-dir> -lut [<abc-temp-dir>/input.lut] -box [<abc-temp-dir>/input.box]"); run(" read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig"); - run(" abc9_ops -reintegrate"); + run(" abc9_ops -reintegrate [-dff]"); } else { auto selected_modules = active_design->selected_modules(); @@ -318,7 +384,6 @@ struct Abc9Pass : public ScriptPass log("Skipping module %s as it contains processes.\n", log_id(mod)); continue; } - log_assert(!mod->attributes.count(ID::abc9_box_id)); log_push(); active_design->selection().select(mod); @@ -333,8 +398,9 @@ struct Abc9Pass : public ScriptPass if (!lut_mode) run_nocheck(stringf("abc9_ops -write_lut %s/input.lut", tempdir_name.c_str())); - run_nocheck(stringf("abc9_ops -write_box %s/input.box", tempdir_name.c_str())); - run_nocheck(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); + if (box_file.empty()) + run_nocheck(stringf("abc9_ops -write_box %s/input.box", tempdir_name.c_str())); + run_nocheck(stringf("write_xaiger -map %s/input.sym %s %s/input.xaig", tempdir_name.c_str(), dff_mode ? "-dff" : "", tempdir_name.c_str())); int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); @@ -349,10 +415,13 @@ struct Abc9Pass : public ScriptPass abc9_exe_cmd += stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str()); if (!lut_mode) abc9_exe_cmd += stringf(" -lut %s/input.lut", tempdir_name.c_str()); - abc9_exe_cmd += stringf(" -box %s/input.box", tempdir_name.c_str()); + if (box_file.empty()) + abc9_exe_cmd += stringf(" -box %s/input.box", tempdir_name.c_str()); + else + abc9_exe_cmd += stringf(" -box %s", box_file.c_str()); run_nocheck(abc9_exe_cmd); run_nocheck(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str())); - run_nocheck("abc9_ops -reintegrate"); + run_nocheck(stringf("abc9_ops -reintegrate %s", dff_mode ? "-dff" : "")); } else log("Don't call ABC as there is nothing to map.\n"); @@ -369,6 +438,14 @@ struct Abc9Pass : public ScriptPass active_design->selection_stack.pop_back(); } } + + if (check_label("unmap")) { + run("techmap -wb -map %$abc9_unmap -map +/abc9_unmap.v"); // techmap user design from submod back to original cell + // ($_DFF_[NP]_ already shorted by -reintegrate) + run("design -delete $abc9_unmap"); + if (saved_designs.count("$abc9_holes") || help_mode) + run("design -delete $abc9_holes"); + } } } Abc9Pass; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index 0bf547921..7355840aa 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -293,7 +293,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe struct Abc9ExePass : public Pass { Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -375,7 +375,7 @@ struct Abc9ExePass : public Pass { log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing ABC9_EXE pass (technology mapping using ABC9).\n"); diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 1345188a4..98d0207c4 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -34,13 +34,10 @@ inline std::string remap_name(RTLIL::IdString abc9_name) return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); } -void check(RTLIL::Design *design) +void check(RTLIL::Design *design, bool dff_mode) { dict<IdString,IdString> box_lookup; for (auto m : design->modules()) { - if (m->name.begins_with("$paramod")) - continue; - auto flop = m->get_bool_attribute(ID::abc9_flop); auto it = m->attributes.find(ID::abc9_box_id); if (!flop) { @@ -88,6 +85,463 @@ void check(RTLIL::Design *design) log_error("Module '%s' with (* abc9_flop *) has %d outputs (expect 1).\n", log_id(m), num_outputs); } } + + if (dff_mode) { + static pool<IdString> unsupported{ + ID($adff), ID($dlatch), ID($dlatchsr), ID($sr), + ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_), + ID($_DLATCH_N_), ID($_DLATCH_P_), + ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_), + ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), + ID($_SR_NN_), ID($_SR_NP_), ID($_SR_PN_), ID($_SR_PP_) + }; + pool<IdString> processed; + for (auto module : design->selected_modules()) + for (auto cell : module->cells()) { + auto inst_module = design->module(cell->type); + if (!inst_module) + continue; + IdString derived_type; + Module *derived_module; + if (cell->parameters.empty()) { + derived_type = cell->type; + derived_module = inst_module; + } + else { + // Check potential (since its value may depend on a parameter, + // but not its existence) + if (!inst_module->has_attribute(ID::abc9_flop)) + continue; + derived_type = inst_module->derive(design, cell->parameters); + derived_module = design->module(derived_type); + log_assert(derived_module); + } + if (!derived_module->get_bool_attribute(ID::abc9_flop)) + continue; + if (derived_module->get_blackbox_attribute(true /* ignore_wb */)) + log_error("Module '%s' with (* abc9_flop *) is a blackbox.\n", log_id(derived_type)); + + if (derived_module->has_processes()) + Pass::call_on_module(design, derived_module, "proc"); + + bool found = false; + for (auto derived_cell : derived_module->cells()) { + if (derived_cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_))) { + if (found) + log_error("Whitebox '%s' with (* abc9_flop *) contains more than one $_DFF_[NP]_ cell.\n", log_id(derived_module)); + found = true; + + SigBit Q = derived_cell->getPort(ID::Q); + log_assert(GetSize(Q.wire) == 1); + + if (!Q.wire->port_output) + log_error("Whitebox '%s' with (* abc9_flop *) contains a %s cell where its 'Q' port does not drive a module output.\n", log_id(derived_module), log_id(derived_cell->type)); + + Const init = Q.wire->attributes.at(ID::init, State::Sx); + log_assert(GetSize(init) == 1); + } + else if (unsupported.count(derived_cell->type)) + log_error("Whitebox '%s' with (* abc9_flop *) contains a %s cell, which is not supported for sequential synthesis.\n", log_id(derived_module), log_id(derived_cell->type)); + } + } + } +} + +void prep_hier(RTLIL::Design *design, bool dff_mode) +{ + auto r = saved_designs.emplace("$abc9_unmap", nullptr); + if (r.second) + r.first->second = new Design; + Design *unmap_design = r.first->second; + + static const pool<IdString> seq_types{ + ID($dff), ID($dffsr), ID($adff), + ID($dlatch), ID($dlatchsr), ID($sr), + ID($mem), + ID($_DFF_N_), ID($_DFF_P_), + ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_), + ID($_DFF_N_), ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_P_), ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_), + ID($_DLATCH_N_), ID($_DLATCH_P_), + ID($_DLATCHSR_NNN_), ID($_DLATCHSR_NNP_), ID($_DLATCHSR_NPN_), ID($_DLATCHSR_NPP_), + ID($_DLATCHSR_PNN_), ID($_DLATCHSR_PNP_), ID($_DLATCHSR_PPN_), ID($_DLATCHSR_PPP_), + ID($_SR_NN_), ID($_SR_NP_), ID($_SR_PN_), ID($_SR_PP_) + }; + + for (auto module : design->selected_modules()) + for (auto cell : module->cells()) { + auto inst_module = design->module(cell->type); + if (!inst_module) + continue; + IdString derived_type; + Module *derived_module; + if (cell->parameters.empty()) { + derived_type = cell->type; + derived_module = inst_module; + } + else { + // Check potential for any one of those three + // (since its value may depend on a parameter, but not its existence) + if (!inst_module->has_attribute(ID::abc9_flop) && !inst_module->has_attribute(ID::abc9_box) && !inst_module->get_bool_attribute(ID::abc9_bypass)) + continue; + derived_type = inst_module->derive(design, cell->parameters); + derived_module = design->module(derived_type); + } + if (derived_module->get_blackbox_attribute(true /* ignore_wb */)) + continue; + + if (derived_module->get_bool_attribute(ID::abc9_flop)) { + if (!dff_mode) + continue; + } + else { + if (!derived_module->get_bool_attribute(ID::abc9_box) && !derived_module->get_bool_attribute(ID::abc9_bypass)) + continue; + } + + if (!unmap_design->module(derived_type)) { + if (derived_module->has_processes()) + Pass::call_on_module(design, derived_module, "proc"); + + if (derived_module->get_bool_attribute(ID::abc9_flop)) { + for (auto derived_cell : derived_module->cells()) + if (derived_cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_))) { + SigBit Q = derived_cell->getPort(ID::Q); + Const init = Q.wire->attributes.at(ID::init, State::Sx); + log_assert(GetSize(init) == 1); + + // Block sequential synthesis on cells with (* init *) != 1'b0 + // because ABC9 doesn't support them + if (init != State::S0) { + log_warning("Whitebox '%s' with (* abc9_flop *) contains a %s cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox.\n", log_id(derived_module), log_id(derived_cell->type)); + derived_module->set_bool_attribute(ID::abc9_flop, false); + } + break; + } + } + else if (derived_module->get_bool_attribute(ID::abc9_box)) { + for (auto derived_cell : derived_module->cells()) + if (seq_types.count(derived_cell->type)) { + derived_module->set_bool_attribute(ID::abc9_box, false); + derived_module->set_bool_attribute(ID::abc9_bypass); + break; + } + } + + if (derived_type != cell->type) { + auto unmap_module = unmap_design->addModule(derived_type); + for (auto port : derived_module->ports) { + auto w = unmap_module->addWire(port, derived_module->wire(port)); + // Do not propagate (* init *) values into the box, + // in fact, remove it from outside too + if (w->port_output) + w->attributes.erase(ID::init); + } + unmap_module->ports = derived_module->ports; + unmap_module->check(); + + auto replace_cell = unmap_module->addCell(ID::_TECHMAP_REPLACE_, cell->type); + for (const auto &conn : cell->connections()) { + auto w = unmap_module->wire(conn.first); + log_assert(w); + replace_cell->setPort(conn.first, w); + } + replace_cell->parameters = cell->parameters; + } + } + + cell->type = derived_type; + cell->parameters.clear(); + } +} + +void prep_bypass(RTLIL::Design *design) +{ + auto r = saved_designs.emplace("$abc9_map", nullptr); + if (r.second) + r.first->second = new Design; + Design *map_design = r.first->second; + + r = saved_designs.emplace("$abc9_unmap", nullptr); + if (r.second) + r.first->second = new Design; + Design *unmap_design = r.first->second; + + pool<IdString> processed; + for (auto module : design->selected_modules()) + for (auto cell : module->cells()) { + if (!processed.insert(cell->type).second) + continue; + auto inst_module = design->module(cell->type); + if (!inst_module) + continue; + if (!inst_module->get_bool_attribute(ID::abc9_bypass)) + continue; + log_assert(!inst_module->get_blackbox_attribute(true /* ignore_wb */)); + log_assert(cell->parameters.empty()); + + + // The idea is to create two techmap designs, one which maps: + // + // box u0 (.i(i), .o(o)); + // + // to + // + // wire $abc9$o; + // box u0 (.i(i), .o($abc9_byp$o)); + // box_$abc9_byp (.i(i), .$abc9_byp$o($abc9_byp$o), .o(o)); + // + // the purpose being to move the (* abc9_box *) status from 'box' + // (which is stateful) to 'box_$abc9_byp' (which becomes a new + // combinatorial black- (not white-) box with all state elements + // removed). This has the effect of preserving any combinatorial + // paths through an otherwise sequential primitive -- e.g. LUTRAMs. + // + // The unmap design performs the reverse: + // + // wire $abc9$o; + // box u0 (.i(i), .o($abc9_byp$o)); + // box_$abc9_byp (.i(i), .$abc9_byp$o($abc9_byp$o), .o(o)); + // + // to: + // + // wire $abc9$o; + // box u0 (.i(i), .o($abc9_byp$o)); + // assign o = $abc9_byp$o; + + + // Copy inst_module into map_design, with the same interface + // and duplicate $abc9$* wires for its output ports + auto map_module = map_design->addModule(cell->type); + for (auto port_name : inst_module->ports) { + auto w = map_module->addWire(port_name, inst_module->wire(port_name)); + if (w->port_output) + w->attributes.erase(ID::init); + } + map_module->ports = inst_module->ports; + map_module->check(); + map_module->set_bool_attribute(ID::whitebox); + + // Create the bypass module in the user design, which has the same + // interface as the derived module but with additional input + // ports driven by the outputs of the replaced cell + auto bypass_module = design->addModule(cell->type.str() + "_$abc9_byp"); + for (auto port_name : inst_module->ports) { + auto port = inst_module->wire(port_name); + if (!port->port_output) + continue; + auto dst = bypass_module->addWire(port_name, port); + auto src = bypass_module->addWire("$abc9byp$" + port_name.str(), GetSize(port)); + src->port_input = true; + // For these new input ports driven by the replaced + // cell, then create a new simple-path specify entry: + // (input => output) = 0 + auto specify = bypass_module->addCell(NEW_ID, ID($specify2)); + specify->setPort(ID::EN, State::S1); + specify->setPort(ID::SRC, src); + specify->setPort(ID::DST, dst); + specify->setParam(ID::FULL, 0); + specify->setParam(ID::SRC_WIDTH, GetSize(src)); + specify->setParam(ID::DST_WIDTH, GetSize(dst)); + specify->setParam(ID::SRC_DST_PEN, 0); + specify->setParam(ID::SRC_DST_POL, 0); + specify->setParam(ID::T_RISE_MIN, 0); + specify->setParam(ID::T_RISE_TYP, 0); + specify->setParam(ID::T_RISE_MAX, 0); + specify->setParam(ID::T_FALL_MIN, 0); + specify->setParam(ID::T_FALL_TYP, 0); + specify->setParam(ID::T_FALL_MAX, 0); + } + bypass_module->set_bool_attribute(ID::blackbox); + bypass_module->set_bool_attribute(ID::abc9_box); + + // Copy any 'simple' (combinatorial) specify paths from + // the derived module into the bypass module, if EN + // is not false and SRC/DST are driven only by + // module ports; create new input port if one doesn't + // already exist + for (auto cell : inst_module->cells()) { + if (cell->type != ID($specify2)) + continue; + auto EN = cell->getPort(ID::EN).as_bit(); + SigBit newEN; + if (!EN.wire && EN != State::S1) + continue; + auto SRC = cell->getPort(ID::SRC); + for (const auto &c : SRC.chunks()) + if (c.wire && !c.wire->port_input) { + SRC = SigSpec(); + break; + } + if (SRC.empty()) + continue; + auto DST = cell->getPort(ID::DST); + for (const auto &c : DST.chunks()) + if (c.wire && !c.wire->port_output) { + DST = SigSpec(); + break; + } + if (DST.empty()) + continue; + auto rw = [bypass_module](RTLIL::SigSpec &sig) + { + SigSpec new_sig; + for (auto c : sig.chunks()) { + if (c.wire) { + auto port = bypass_module->wire(c.wire->name); + if (!port) + port = bypass_module->addWire(c.wire->name, c.wire); + c.wire = port; + } + new_sig.append(std::move(c)); + } + sig = std::move(new_sig); + }; + auto specify = bypass_module->addCell(NEW_ID, cell); + specify->rewrite_sigspecs(rw); + } + bypass_module->fixup_ports(); + + // Create an _TECHMAP_REPLACE_ cell identical to the original cell, + // and a bypass cell that has the same inputs/outputs as the + // original cell, but with additional inputs taken from the + // replaced cell + auto replace_cell = map_module->addCell(ID::_TECHMAP_REPLACE_, cell->type); + auto bypass_cell = map_module->addCell(NEW_ID, cell->type.str() + "_$abc9_byp"); + for (const auto &conn : cell->connections()) { + auto port = map_module->wire(conn.first); + if (cell->input(conn.first)) { + replace_cell->setPort(conn.first, port); + if (bypass_module->wire(conn.first)) + bypass_cell->setPort(conn.first, port); + } + if (cell->output(conn.first)) { + bypass_cell->setPort(conn.first, port); + auto n = "$abc9byp$" + conn.first.str(); + auto w = map_module->addWire(n, GetSize(conn.second)); + replace_cell->setPort(conn.first, w); + bypass_cell->setPort(n, w); + } + } + + + // Lastly, create a new module in the unmap_design that shorts + // out the bypass cell back to leave the replace cell behind + // driving the outputs + auto unmap_module = unmap_design->addModule(cell->type.str() + "_$abc9_byp"); + for (auto port_name : inst_module->ports) { + auto w = unmap_module->addWire(port_name, inst_module->wire(port_name)); + if (w->port_output) { + w->attributes.erase(ID::init); + auto w2 = unmap_module->addWire("$abc9byp$" + port_name.str(), GetSize(w)); + w2->port_input = true; + unmap_module->connect(w, w2); + } + } + unmap_module->fixup_ports(); + } +} + +void prep_dff(RTLIL::Design *design) +{ + auto r = design->selection_vars.insert(std::make_pair(ID($abc9_flops), RTLIL::Selection(false))); + auto &modules_sel = r.first->second; + + for (auto module : design->selected_modules()) + for (auto cell : module->cells()) { + if (modules_sel.selected_whole_module(cell->type)) + continue; + auto inst_module = design->module(cell->type); + if (!inst_module) + continue; + if (!inst_module->get_bool_attribute(ID::abc9_flop)) + continue; + log_assert(!inst_module->get_blackbox_attribute(true /* ignore_wb */)); + log_assert(cell->parameters.empty()); + modules_sel.select(inst_module); + } +} + +void prep_dff_submod(RTLIL::Design *design) +{ + for (auto module : design->modules()) { + vector<Cell*> specify_cells; + SigBit Q; + Cell* dff_cell = nullptr; + + if (!module->get_bool_attribute(ID::abc9_flop)) + continue; + + for (auto cell : module->cells()) + if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) { + log_assert(!dff_cell); + dff_cell = cell; + Q = cell->getPort(ID::Q); + log_assert(GetSize(Q.wire) == 1); + } + else if (cell->type.in(ID($specify3), ID($specrule))) + specify_cells.emplace_back(cell); + log_assert(dff_cell); + + // Add an always-enabled CE mux that drives $_DFF_[NP]_.D so that: + // (a) flop box will have an output + // (b) $_DFF_[NP]_.Q will be present as an input + SigBit D = module->addWire(NEW_ID); + module->addMuxGate(NEW_ID, dff_cell->getPort(ID::D), Q, State::S0, D); + dff_cell->setPort(ID::D, D); + + // Rewrite $specify cells that end with $_DFF_[NP]_.Q + // to $_DFF_[NP]_.D since it will be moved into + // the submodule + for (auto cell : specify_cells) { + auto DST = cell->getPort(ID::DST); + DST.replace(Q, D); + cell->setPort(ID::DST, DST); + } + + design->scratchpad_set_bool("abc9_ops.prep_dff_submod.did_something", true); + } +} + +void prep_dff_unmap(RTLIL::Design *design) +{ + Design *unmap_design = saved_designs.at("$abc9_unmap"); + + for (auto module : design->modules()) { + if (!module->get_bool_attribute(ID::abc9_flop) || module->get_bool_attribute(ID::abc9_box)) + continue; + + // Make sure the box module has all the same ports present on flop cell + auto replace_cell = module->cell(ID::_TECHMAP_REPLACE_); + log_assert(replace_cell); + auto box_module = design->module(module->name.str() + "_$abc9_flop"); + log_assert(box_module); + for (auto port_name : module->ports) { + auto port = module->wire(port_name); + auto box_port = box_module->wire(port_name); + if (box_port) { + // Do not propagate init -- already captured by box + box_port->attributes.erase(ID::init); + continue; + } + log_assert(port->port_input); + box_module->addWire(port_name, port); + replace_cell->setPort(port_name, port); + } + box_module->fixup_ports(); + + auto unmap_module = unmap_design->addModule(box_module->name); + replace_cell = unmap_module->addCell(ID::_TECHMAP_REPLACE_, module->name); + for (auto port_name : box_module->ports) { + auto w = unmap_module->addWire(port_name, box_module->wire(port_name)); + if (module->wire(port_name)) + replace_cell->setPort(port_name, w); + } + unmap_module->ports = box_module->ports; + unmap_module->check(); + } } void mark_scc(RTLIL::Module *module) @@ -95,7 +549,7 @@ void mark_scc(RTLIL::Module *module) // For every unique SCC found, (arbitrarily) find the first // cell in the component, and replace its output connections // with a new wire driven by the old connection but with a - // special (* abc9_scc *) attribute set (which is used by + // special (* abc9_keep *) attribute set (which is used by // write_xaiger to break this wire into PI and POs) pool<RTLIL::Const> ids_seen; for (auto cell : module->cells()) { @@ -111,7 +565,7 @@ void mark_scc(RTLIL::Module *module) if (c.second.is_fully_const()) continue; if (cell->output(c.first)) { Wire *w = module->addWire(NEW_ID, GetSize(c.second)); - w->set_bool_attribute(ID::abc9_scc); + w->set_bool_attribute(ID::abc9_keep); module->connect(w, c.second); c.second = w; } @@ -119,80 +573,94 @@ void mark_scc(RTLIL::Module *module) } } -void prep_dff(RTLIL::Module *module) +void prep_delays(RTLIL::Design *design, bool dff_mode) { - auto design = module->design; - log_assert(design); + TimingInfo timing; - SigMap assign_map(module); + // Derive all Yosys blackbox modules that are not combinatorial abc9 boxes + // (e.g. DSPs, RAMs, etc.) nor abc9 flops and collect all such instantiations + std::vector<Cell*> cells; + for (auto module : design->selected_modules()) { + if (module->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(module)); + continue; + } - typedef SigSpec clkdomain_t; - dict<clkdomain_t, int> clk_to_mergeability; + for (auto cell : module->cells()) { + if (cell->type.in(ID($_AND_), ID($_NOT_), ID($_DFF_N_), ID($_DFF_P_))) + continue; + log_assert(!cell->type.begins_with("$paramod$__ABC9_DELAY\\DELAY=")); - for (auto cell : module->cells()) { - if (cell->type != ID($__ABC9_FF_)) - continue; + RTLIL::Module* inst_module = design->module(cell->type); + if (!inst_module) + continue; + if (!inst_module->get_blackbox_attribute()) + continue; + if (!cell->parameters.empty()) + continue; - Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str())); - if (abc9_clock_wire == NULL) - log_error("'%s.clock' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); - SigSpec abc9_clock = assign_map(abc9_clock_wire); + if (inst_module->get_bool_attribute(ID::abc9_box)) + continue; + if (inst_module->get_bool_attribute(ID::abc9_bypass)) + continue; - clkdomain_t key(abc9_clock); + if (dff_mode && inst_module->get_bool_attribute(ID::abc9_flop)) { + continue; // do not add $__ABC9_DELAY boxes to flops + // as delays will be captured in the flop box + } + + if (!timing.count(cell->type)) + timing.setup_module(inst_module); - auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); - auto r2 = cell->attributes.insert(ID::abc9_mergeability); - log_assert(r2.second); - r2.first->second = r.first->second; + cells.emplace_back(cell); + } } - RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str())); - if (holes_module) { - SigMap sigmap(holes_module); + // Insert $__ABC9_DELAY cells on all cells that instantiate blackboxes + // (or bypassed white-boxes with required times) + dict<int, IdString> box_cache; + Module *delay_module = design->module(ID($__ABC9_DELAY)); + log_assert(delay_module); + for (auto cell : cells) { + auto module = cell->module; + auto inst_module = design->module(cell->type); + log_assert(inst_module); - dict<SigSpec, SigSpec> replace; - for (auto cell : holes_module->cells().to_vector()) { - if (!cell->type.in(ID($_DFF_N_), ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), - ID($_DFF_P_), ID($_DFF_PN0_), ID($_DFF_PN1), ID($_DFF_PP0_), ID($_DFF_PP1_))) + auto &t = timing.at(cell->type).required; + for (auto &conn : cell->connections_) { + auto port_wire = inst_module->wire(conn.first); + if (!port_wire) + log_error("Port %s in cell %s (type %s) from module %s does not actually exist", + log_id(conn.first), log_id(cell), log_id(cell->type), log_id(module)); + if (!port_wire->port_input) + continue; + if (conn.second.is_fully_const()) continue; - SigBit D = cell->getPort(ID::D); - SigBit Q = cell->getPort(ID::Q); - // Emulate async control embedded inside $_DFF_* cell with mux in front of D - if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_PN0_))) - D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort(ID::R)); - else if (cell->type.in(ID($_DFF_NN1_), ID($_DFF_PN1_))) - D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort(ID::R)); - else if (cell->type.in(ID($_DFF_NP0_), ID($_DFF_PP0_))) - D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort(ID::R)); - else if (cell->type.in(ID($_DFF_NP1_), ID($_DFF_PP1_))) - D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort(ID::R)); - // Remove the $_DFF_* cell from what needs to be a combinatorial box - holes_module->remove(cell); - Wire *port; - if (GetSize(Q.wire) == 1) - port = holes_module->wire(stringf("$abc%s", Q.wire->name.c_str())); - else - port = holes_module->wire(stringf("$abc%s[%d]", Q.wire->name.c_str(), Q.offset)); - log_assert(port); - // Prepare to replace "assign <port> = $_DFF_*.Q;" with "assign <port> = $_DFF_*.D;" - // in order to extract just the combinatorial control logic that feeds the box - // (i.e. clock enable, synchronous reset, etc.) - replace.insert(std::make_pair(Q,D)); - // Since `flatten` above would have created wires named "<cell>.Q", - // extract the pre-techmap cell name - auto pos = Q.wire->name.str().rfind("."); - log_assert(pos != std::string::npos); - IdString driver = Q.wire->name.substr(0, pos); - // And drive the signal that was previously driven by "DFF.Q" (typically - // used to implement clock-enable functionality) with the "<cell>.$abc9_currQ" - // wire (which itself is driven an by input port) we inserted above - Wire *currQ = holes_module->wire(stringf("%s.abc9_ff.Q", driver.c_str())); - log_assert(currQ); - holes_module->connect(Q, currQ); - } - for (auto &conn : holes_module->connections_) - conn.second = replace.at(sigmap(conn.second), conn.second); + SigSpec O = module->addWire(NEW_ID, GetSize(conn.second)); + for (int i = 0; i < GetSize(conn.second); i++) { + auto d = t.at(TimingInfo::NameBit(conn.first,i), 0); + if (d == 0) + continue; + +#ifndef NDEBUG + if (ys_debug(1)) { + static std::set<std::tuple<IdString,IdString,int>> seen; + if (seen.emplace(cell->type, conn.first, i).second) log("%s.%s[%d] abc9_required = %d\n", + log_id(cell->type), log_id(conn.first), i, d); + } +#endif + auto r = box_cache.insert(d); + if (r.second) { + r.first->second = delay_module->derive(design, {{ID::DELAY, d}}); + log_assert(r.first->second.begins_with("$paramod$__ABC9_DELAY\\DELAY=")); + } + auto box = module->addCell(NEW_ID, r.first->second); + box->setPort(ID::I, conn.second[i]); + box->setPort(ID::O, O[i]); + conn.second[i] = O[i]; + } + } } } @@ -208,17 +676,17 @@ void prep_xaiger(RTLIL::Module *module, bool dff) dict<IdString, std::vector<IdString>> box_ports; for (auto cell : module->cells()) { - if (cell->type == ID($__ABC9_FF_)) + if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) continue; if (cell->has_keep_attr()) continue; - auto inst_module = module->design->module(cell->type); + auto inst_module = design->module(cell->type); bool abc9_flop = inst_module && inst_module->get_bool_attribute(ID::abc9_flop); if (abc9_flop && !dff) continue; - if ((inst_module && inst_module->get_bool_attribute(ID::abc9_box)) || abc9_flop) { + if (inst_module && inst_module->get_bool_attribute(ID::abc9_box)) { auto r = box_ports.insert(cell->type); if (r.second) { // Make carry in the last PI, and carry out the last PO @@ -253,8 +721,10 @@ void prep_xaiger(RTLIL::Module *module, bool dff) bit_users[bit].insert(cell->name); if (cell->output(conn.first) && !abc9_flop) - for (auto bit : sigmap(conn.second)) - bit_drivers[bit].insert(cell->name); + for (const auto &chunk : conn.second.chunks()) + if (!chunk.wire->get_bool_attribute(ID::abc9_keep)) + for (auto b : sigmap(SigSpec(chunk))) + bit_drivers[b].insert(cell->name); } toposort.node(cell->name); } @@ -271,7 +741,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff) if (ys_debug(1)) toposort.analyze_loops = true; - bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); + bool no_loops = toposort.sort(); if (ys_debug(1)) { unsigned i = 0; @@ -287,9 +757,13 @@ void prep_xaiger(RTLIL::Module *module, bool dff) log_assert(no_loops); - RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str())); + auto r = saved_designs.emplace("$abc9_holes", nullptr); + if (r.second) + r.first->second = new Design; + RTLIL::Design *holes_design = r.first->second; + log_assert(holes_design); + RTLIL::Module *holes_module = holes_design->addModule(module->name); log_assert(holes_module); - holes_module->set_bool_attribute(ID::abc9_holes); dict<IdString, Cell*> cell_cache; TimingInfo timing; @@ -300,22 +774,20 @@ void prep_xaiger(RTLIL::Module *module, bool dff) log_assert(cell); RTLIL::Module* box_module = design->module(cell->type); - if (!box_module || (!box_module->get_bool_attribute(ID::abc9_box) && !box_module->get_bool_attribute(ID::abc9_flop))) + if (!box_module) + continue; + if (!box_module->get_bool_attribute(ID::abc9_box)) continue; + log_assert(cell->parameters.empty()); + log_assert(box_module->get_blackbox_attribute()); cell->attributes[ID::abc9_box_seq] = box_count++; - IdString derived_type = box_module->derive(design, cell->parameters); - box_module = design->module(derived_type); - - auto r = cell_cache.insert(derived_type); + auto r = cell_cache.insert(cell->type); auto &holes_cell = r.first->second; if (r.second) { - if (box_module->has_processes()) - Pass::call_on_module(design, box_module, "proc"); - if (box_module->get_bool_attribute(ID::whitebox)) { - holes_cell = holes_module->addCell(cell->name, derived_type); + holes_cell = holes_module->addCell(cell->name, cell->type); if (box_module->has_processes()) Pass::call_on_module(design, box_module, "proc"); @@ -340,22 +812,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff) } } else if (w->port_output) - conn = holes_module->addWire(stringf("%s.%s", derived_type.c_str(), log_id(port_name)), GetSize(w)); - } - - // For flops only, create an extra 1-bit input that drives a new wire - // called "<cell>.abc9_ff.Q" that is used below - if (box_module->get_bool_attribute(ID::abc9_flop)) { - box_inputs++; - Wire *holes_wire = holes_module->wire(stringf("\\i%d", box_inputs)); - if (!holes_wire) { - holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs)); - holes_wire->port_input = true; - holes_wire->port_id = port_id++; - holes_module->ports.push_back(holes_wire->name); - } - Wire *Q = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str())); - holes_module->connect(Q, holes_wire); + conn = holes_module->addWire(stringf("%s.%s", cell->type.c_str(), log_id(port_name)), GetSize(w)); } } else // box_module is a blackbox @@ -379,90 +836,6 @@ void prep_xaiger(RTLIL::Module *module, bool dff) } } -void prep_delays(RTLIL::Design *design, bool dff_mode) -{ - TimingInfo timing; - - // Derive all Yosys blackbox modules that are not combinatorial abc9 boxes - // (e.g. DSPs, RAMs, etc.) nor abc9 flops and collect all such instantiations - pool<Module*> flops; - std::vector<Cell*> cells; - for (auto module : design->selected_modules()) { - if (module->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", log_id(module)); - continue; - } - - for (auto cell : module->cells()) { - if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_), ID($__ABC9_DELAY))) - continue; - - RTLIL::Module* inst_module = module->design->module(cell->type); - if (!inst_module) - continue; - if (!inst_module->get_blackbox_attribute()) - continue; - if (inst_module->attributes.count(ID::abc9_box)) - continue; - IdString derived_type = inst_module->derive(design, cell->parameters); - inst_module = design->module(derived_type); - log_assert(inst_module); - - if (dff_mode && inst_module->get_bool_attribute(ID::abc9_flop)) { - flops.insert(inst_module); - continue; // do not add $__ABC9_DELAY boxes to flops - // as delays will be captured in the flop box - } - - if (!timing.count(derived_type)) - timing.setup_module(inst_module); - - cells.emplace_back(cell); - } - } - - // Insert $__ABC9_DELAY cells on all cells that instantiate blackboxes - // with required times - for (auto cell : cells) { - auto module = cell->module; - RTLIL::Module* inst_module = module->design->module(cell->type); - log_assert(inst_module); - IdString derived_type = inst_module->derive(design, cell->parameters); - inst_module = design->module(derived_type); - log_assert(inst_module); - - auto &t = timing.at(derived_type).required; - for (auto &conn : cell->connections_) { - auto port_wire = inst_module->wire(conn.first); - if (!port_wire) - log_error("Port %s in cell %s (type %s) of module %s does not actually exist", - log_id(conn.first), log_id(cell->name), log_id(cell->type), log_id(module->name)); - if (!port_wire->port_input) - continue; - - SigSpec O = module->addWire(NEW_ID, GetSize(conn.second)); - for (int i = 0; i < GetSize(conn.second); i++) { - auto d = t.at(TimingInfo::NameBit(conn.first,i), 0); - if (d == 0) - continue; - -#ifndef NDEBUG - if (ys_debug(1)) { - static std::set<std::tuple<IdString,IdString,int>> seen; - if (seen.emplace(derived_type, conn.first, i).second) log("%s.%s[%d] abc9_required = %d\n", - log_id(cell->type), log_id(conn.first), i, d); - } -#endif - auto box = module->addCell(NEW_ID, ID($__ABC9_DELAY)); - box->setPort(ID::I, conn.second[i]); - box->setPort(ID::O, O[i]); - box->setParam(ID::DELAY, d); - conn.second[i] = O[i]; - } - } - } -} - void prep_lut(RTLIL::Design *design, int maxlut) { TimingInfo timing; @@ -540,7 +913,7 @@ void write_lut(RTLIL::Module *module, const std::string &dst) { ofs.close(); } -void prep_box(RTLIL::Design *design, bool dff_mode) +void prep_box(RTLIL::Design *design) { TimingInfo timing; @@ -555,165 +928,162 @@ void prep_box(RTLIL::Design *design, bool dff_mode) dict<IdString,std::vector<IdString>> box_ports; for (auto module : design->modules()) { - auto abc9_flop = module->get_bool_attribute(ID::abc9_flop); - if (abc9_flop) { - auto r = module->attributes.insert(ID::abc9_box_id); - if (!r.second) - continue; - r.first->second = abc9_box_id++; - - if (dff_mode) { - int num_inputs = 0, num_outputs = 0; - for (auto port_name : module->ports) { - auto wire = module->wire(port_name); - log_assert(GetSize(wire) == 1); - if (wire->port_input) num_inputs++; - if (wire->port_output) num_outputs++; - } - log_assert(num_outputs == 1); + auto it = module->attributes.find(ID::abc9_box); + if (it == module->attributes.end()) + continue; + bool box = it->second.as_bool(); + module->attributes.erase(it); + if (!box) + continue; - ss << log_id(module) << " " << r.first->second.as_int(); - ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0"); - ss << " " << num_inputs+1 << " " << num_outputs << std::endl; + auto r = module->attributes.insert(ID::abc9_box_id); + if (!r.second) + continue; + r.first->second = abc9_box_id++; + + if (module->get_bool_attribute(ID::abc9_flop)) { + int num_inputs = 0, num_outputs = 0; + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + log_assert(GetSize(wire) == 1); + if (wire->port_input) num_inputs++; + if (wire->port_output) num_outputs++; + } + log_assert(num_outputs == 1); + + ss << log_id(module) << " " << r.first->second.as_int(); + log_assert(module->get_bool_attribute(ID::whitebox)); + ss << " " << "1"; + ss << " " << num_inputs << " " << num_outputs << std::endl; + + ss << "#"; + bool first = true; + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + if (!wire->port_input) + continue; + if (first) + first = false; + else + ss << " "; + ss << log_id(wire); + } + ss << std::endl; - ss << "#"; - bool first = true; - for (auto port_name : module->ports) { - auto wire = module->wire(port_name); - if (!wire->port_input) - continue; - if (first) - first = false; - else - ss << " "; - ss << log_id(wire); - } - ss << " abc9_ff.Q" << std::endl; + auto &t = timing.setup_module(module).required; + if (t.empty()) + log_error("Module '%s' with (* abc9_flop *) has no clk-to-q timing (and thus no connectivity) information.\n", log_id(module)); - auto &t = timing.setup_module(module).required; - first = true; - for (auto port_name : module->ports) { - auto wire = module->wire(port_name); - if (!wire->port_input) - continue; - if (first) - first = false; - else - ss << " "; - log_assert(GetSize(wire) == 1); - auto it = t.find(TimingInfo::NameBit(port_name,0)); - if (it == t.end()) - // Assume that no setup time means zero - ss << 0; - else { - ss << it->second; + first = true; + for (auto port_name : module->ports) { + auto wire = module->wire(port_name); + if (!wire->port_input) + continue; + if (first) + first = false; + else + ss << " "; + log_assert(GetSize(wire) == 1); + auto it = t.find(TimingInfo::NameBit(port_name,0)); + if (it == t.end()) + // Assume no connectivity if no setup time + ss << "-"; + else { + ss << it->second; #ifndef NDEBUG - if (ys_debug(1)) { - static std::set<std::pair<IdString,IdString>> seen; - if (seen.emplace(module->name, port_name).second) log("%s.%s abc9_required = %d\n", log_id(module), - log_id(port_name), it->second); - } -#endif + if (ys_debug(1)) { + static std::set<std::pair<IdString,IdString>> seen; + if (seen.emplace(module->name, port_name).second) log("%s.%s abc9_required = %d\n", log_id(module), + log_id(port_name), it->second); } - +#endif } - // Last input is 'abc9_ff.Q' - ss << " 0" << std::endl << std::endl; - continue; } + ss << " # $_DFF_[NP]_.D" << std::endl; + ss << std::endl; } else { - if (!module->attributes.erase(ID::abc9_box)) - continue; - - auto r = module->attributes.insert(ID::abc9_box_id); - if (!r.second) - continue; - r.first->second = abc9_box_id++; - } + auto r2 = box_ports.insert(module->name); + if (r2.second) { + // Make carry in the last PI, and carry out the last PO + // since ABC requires it this way + IdString carry_in, carry_out; + for (const auto &port_name : module->ports) { + auto w = module->wire(port_name); + log_assert(w); + if (w->get_bool_attribute(ID::abc9_carry)) { + log_assert(w->port_input != w->port_output); + if (w->port_input) + carry_in = port_name; + else if (w->port_output) + carry_out = port_name; + } + else + r2.first->second.push_back(port_name); + } - auto r = box_ports.insert(module->name); - if (r.second) { - // Make carry in the last PI, and carry out the last PO - // since ABC requires it this way - IdString carry_in, carry_out; - for (const auto &port_name : module->ports) { - auto w = module->wire(port_name); - log_assert(w); - if (w->get_bool_attribute(ID::abc9_carry)) { - log_assert(w->port_input != w->port_output); - if (w->port_input) - carry_in = port_name; - else if (w->port_output) - carry_out = port_name; + if (carry_in != IdString()) { + r2.first->second.push_back(carry_in); + r2.first->second.push_back(carry_out); } - else - r.first->second.push_back(port_name); } - if (carry_in != IdString()) { - r.first->second.push_back(carry_in); - r.first->second.push_back(carry_out); + std::vector<SigBit> inputs, outputs; + for (auto port_name : r2.first->second) { + auto wire = module->wire(port_name); + if (wire->port_input) + for (int i = 0; i < GetSize(wire); i++) + inputs.emplace_back(wire, i); + if (wire->port_output) + for (int i = 0; i < GetSize(wire); i++) + outputs.emplace_back(wire, i); } - } - - std::vector<SigBit> inputs; - std::vector<SigBit> outputs; - for (auto port_name : r.first->second) { - auto wire = module->wire(port_name); - if (wire->port_input) - for (int i = 0; i < GetSize(wire); i++) - inputs.emplace_back(wire, i); - if (wire->port_output) - for (int i = 0; i < GetSize(wire); i++) - outputs.emplace_back(wire, i); - } - ss << log_id(module) << " " << module->attributes.at(ID::abc9_box_id).as_int(); - ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0"); - ss << " " << GetSize(inputs) << " " << GetSize(outputs) << std::endl; + ss << log_id(module) << " " << module->attributes.at(ID::abc9_box_id).as_int(); + ss << " " << (module->get_bool_attribute(ID::whitebox) ? "1" : "0"); + ss << " " << GetSize(inputs) << " " << GetSize(outputs) << std::endl; - bool first = true; - ss << "#"; - for (const auto &i : inputs) { - if (first) - first = false; - else - ss << " "; - if (GetSize(i.wire) == 1) - ss << log_id(i.wire); - else - ss << log_id(i.wire) << "[" << i.offset << "]"; - } - ss << std::endl; - - auto &t = timing.setup_module(module).comb; - if (!abc9_flop && t.empty()) - log_warning("(* abc9_box *) module '%s' has no timing (and thus no connectivity) information.\n", log_id(module)); - - for (const auto &o : outputs) { - first = true; + bool first = true; + ss << "#"; for (const auto &i : inputs) { if (first) first = false; else ss << " "; - auto jt = t.find(TimingInfo::BitBit(i,o)); - if (jt == t.end()) - ss << "-"; + if (GetSize(i.wire) == 1) + ss << log_id(i.wire); else - ss << jt->second; + ss << log_id(i.wire) << "[" << i.offset << "]"; } - ss << " # "; - if (GetSize(o.wire) == 1) - ss << log_id(o.wire); - else - ss << log_id(o.wire) << "[" << o.offset << "]"; ss << std::endl; + auto &t = timing.setup_module(module); + if (t.comb.empty()) + log_error("Module '%s' with (* abc9_box *) has no timing (and thus no connectivity) information.\n", log_id(module)); + + for (const auto &o : outputs) { + first = true; + for (const auto &i : inputs) { + if (first) + first = false; + else + ss << " "; + auto jt = t.comb.find(TimingInfo::BitBit(i,o)); + if (jt == t.comb.end()) + ss << "-"; + else + ss << jt->second; + } + ss << " # "; + if (GetSize(o.wire) == 1) + ss << log_id(o.wire); + else + ss << log_id(o.wire) << "[" << o.offset << "]"; + ss << std::endl; + } + ss << std::endl; } - ss << std::endl; } // ABC expects at least one box @@ -730,7 +1100,7 @@ void write_box(RTLIL::Module *module, const std::string &dst) { ofs.close(); } -void reintegrate(RTLIL::Module *module) +void reintegrate(RTLIL::Module *module, bool dff_mode) { auto design = module->design; log_assert(design); @@ -744,6 +1114,8 @@ void reintegrate(RTLIL::Module *module) for (auto w : mapped_mod->wires()) { auto nw = module->addWire(remap_name(w->name), GetSize(w)); nw->start_offset = w->start_offset; + // Remove all (* init *) since they only exist on $_DFF_[NP]_ + w->attributes.erase(ID::init); } dict<IdString,std::vector<IdString>> box_ports; @@ -779,11 +1151,38 @@ void reintegrate(RTLIL::Module *module) } } + SigMap initmap; + if (dff_mode) { + // Build a sigmap prioritising bits with (* init *) + initmap.set(module); + for (auto w : module->wires()) { + auto it = w->attributes.find(ID::init); + if (it == w->attributes.end()) + continue; + for (auto i = 0; i < GetSize(w); i++) + if (it->second[i] == State::S0 || it->second[i] == State::S1) + initmap.add(w); + } + } + std::vector<Cell*> boxes; for (auto cell : module->cells().to_vector()) { if (cell->has_keep_attr()) continue; - if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) + + // Short out (so that existing name can be preserved) and remove + // $_DFF_[NP]_ cells since flop box already has all the information + // we need to reconstruct them + if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep)) { + SigBit Q = cell->getPort(ID::Q); + module->connect(Q, cell->getPort(ID::D)); + module->remove(cell); + auto Qi = initmap(Q); + auto it = Qi.wire->attributes.find(ID::init); + if (it != Qi.wire->attributes.end()) + it->second[Qi.offset] = State::Sx; + } + else if (cell->type.in(ID($_AND_), ID($_NOT_))) module->remove(cell); else if (cell->attributes.erase(ID::abc9_box_seq)) boxes.emplace_back(cell); @@ -797,6 +1196,18 @@ void reintegrate(RTLIL::Module *module) std::map<IdString, int> cell_stats; for (auto mapped_cell : mapped_mod->cells()) { + // Short out $_FF_ cells since the flop box already has + // all the information we need to reconstruct cell + if (dff_mode && mapped_cell->type == ID($_FF_)) { + SigBit D = mapped_cell->getPort(ID::D); + SigBit Q = mapped_cell->getPort(ID::Q); + if (D.wire) + D.wire = module->wires_.at(remap_name(D.wire->name)); + Q.wire = module->wires_.at(remap_name(Q.wire->name)); + module->connect(Q, D); + continue; + } + // TODO: Speed up toposort -- we care about NOT ordering only toposort.node(mapped_cell->name); @@ -846,7 +1257,7 @@ void reintegrate(RTLIL::Module *module) continue; } - if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { + if (mapped_cell->type == ID($lut)) { RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); cell->parameters = mapped_cell->parameters; cell->attributes = mapped_cell->attributes; @@ -881,7 +1292,7 @@ void reintegrate(RTLIL::Module *module) if (!existing_cell) log_error("Cannot find existing box cell with name '%s' in original design.\n", log_id(mapped_cell)); - if (existing_cell->type == ID($__ABC9_DELAY)) { + if (existing_cell->type.begins_with("$paramod$__ABC9_DELAY\\DELAY=")) { SigBit I = mapped_cell->getPort(ID(i)); SigBit O = mapped_cell->getPort(ID(o)); if (I.wire) @@ -893,10 +1304,8 @@ void reintegrate(RTLIL::Module *module) } RTLIL::Module* box_module = design->module(existing_cell->type); - IdString derived_type = box_module->derive(design, existing_cell->parameters); - RTLIL::Module* derived_module = design->module(derived_type); - log_assert(derived_module); - log_assert(mapped_cell->type == stringf("$__boxid%d", derived_module->attributes.at(ID::abc9_box_id).as_int())); + log_assert(existing_cell->parameters.empty()); + log_assert(mapped_cell->type == stringf("$__boxid%d", box_module->attributes.at(ID::abc9_box_id).as_int())); mapped_cell->type = existing_cell->type; RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); @@ -913,8 +1322,26 @@ void reintegrate(RTLIL::Module *module) SigSpec outputs = std::move(jt->second); mapped_cell->connections_.erase(jt); - auto abc9_flop = box_module->attributes.count(ID::abc9_flop); - if (!abc9_flop) { + auto abc9_flop = box_module->get_bool_attribute(ID::abc9_flop); + if (abc9_flop) { + // Link this sole flop box output to the output of the existing + // flop box, so that any (public) signal it drives will be + // preserved + SigBit old_q; + for (const auto &port_name : box_ports.at(existing_cell->type)) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + if (!w->port_output) + continue; + log_assert(old_q == SigBit()); + log_assert(GetSize(w) == 1); + old_q = existing_cell->getPort(port_name); + } + auto new_q = outputs[0]; + new_q.wire = module->wires_.at(remap_name(new_q.wire->name)); + module->connect(old_q, new_q); + } + else { for (const auto &i : inputs) bit_users[i].insert(mapped_cell->name); for (const auto &i : outputs) @@ -924,7 +1351,7 @@ void reintegrate(RTLIL::Module *module) } int input_count = 0, output_count = 0; - for (const auto &port_name : box_ports.at(derived_type)) { + for (const auto &port_name : box_ports.at(existing_cell->type)) { RTLIL::Wire *w = box_module->wire(port_name); log_assert(w); @@ -947,11 +1374,12 @@ void reintegrate(RTLIL::Module *module) c.wire = module->wires_.at(remap_name(c.wire->name)); newsig.append(c); } - cell->setPort(port_name, newsig); if (w->port_input && !abc9_flop) for (const auto &i : newsig) bit2sinks[i].push_back(cell); + + cell->setPort(port_name, std::move(newsig)); } } @@ -988,7 +1416,7 @@ void reintegrate(RTLIL::Module *module) RTLIL::Wire *mapped_wire = mapped_mod->wire(port); RTLIL::Wire *wire = module->wire(port); log_assert(wire); - wire->attributes.erase(ID::abc9_scc); + wire->attributes.erase(ID::abc9_keep); RTLIL::Wire *remap_wire = module->wire(remap_name(port)); RTLIL::SigSpec signal(wire, remap_wire->start_offset-wire->start_offset, GetSize(remap_wire)); @@ -1013,7 +1441,7 @@ void reintegrate(RTLIL::Module *module) // treated as being "free"), in particular driving primary // outputs (real primary outputs, or cells treated as blackboxes) // or driving box inputs. - // Instead of just mapping those $_NOT_ gates into 2-input $lut-s + // Instead of just mapping those $_NOT_ gates into 1-input $lut-s // at an area and delay cost, see if it is possible to push // this $_NOT_ into the driving LUT, or into all sink LUTs. // When this is not possible, (i.e. this signal drives two primary @@ -1025,7 +1453,7 @@ void reintegrate(RTLIL::Module *module) for (auto driver_cell : bit_drivers.at(it.first)) for (auto user_cell : it.second) toposort.edge(driver_cell, user_cell); - bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); + bool no_loops = toposort.sort(); log_assert(no_loops); for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { @@ -1102,7 +1530,7 @@ clone_lut: struct Abc9OpsPass : public Pass { Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1116,6 +1544,37 @@ struct Abc9OpsPass : public Pass { log(" check that the design is valid, e.g. (* abc9_box_id *) values are unique,\n"); log(" (* abc9_carry *) is only given for one input/output port, etc.\n"); log("\n"); + log(" -prep_hier\n"); + log(" derive all used (* abc9_box *) or (* abc9_flop *) (if -dff option)\n"); + log(" whitebox modules. with (* abc9_flop *) modules, only those containing\n"); + log(" $dff/$_DFF_[NP]_ cells with zero initial state -- due to an ABC limitation\n"); + log(" -- will be derived.\n"); + log("\n"); + log(" -prep_bypass\n"); + log(" create techmap rules in the '$abc9_map' and '$abc9_unmap' designs for\n"); + log(" bypassing sequential (* abc9_box *) modules using a combinatorial box\n"); + log(" (named *_$abc9_byp). bypassing is necessary if sequential elements (e.g.\n"); + log(" $dff, $mem, etc.) are discovered inside so that any combinatorial paths\n"); + log(" will be correctly captured. this bypass box will only contain ports that\n"); + log(" are referenced by a simple path declaration ($specify2 cell) inside a\n"); + log(" specify block.\n"); + log("\n"); + log(" -prep_dff\n"); + log(" select all (* abc9_flop *) modules instantiated in the design and store\n"); + log(" in the named selection '$abc9_flops'.\n"); + log("\n"); + log(" -prep_dff_submod\n"); + log(" within (* abc9_flop *) modules, rewrite all edge-sensitive path\n"); + log(" declarations and $setup() timing checks ($specify3 and $specrule cells)\n"); + log(" that share a 'DST' port with the $_DFF_[NP]_.Q port from this 'Q' port to\n"); + log(" the DFF's 'D' port. this is to prepare such specify cells to be moved\n"); + log(" into the flop box.\n"); + log("\n"); + log(" -prep_dff_unmap\n"); + log(" populate the '$abc9_unmap' design with techmap rules for mapping *_$abc9_flop\n"); + log(" cells back into their derived cell types (where the rules created by\n"); + log(" -prep_hier will then map back to the original cell with parameters).\n"); + log("\n"); log(" -prep_delays\n"); log(" insert `$__ABC9_DELAY' blackbox cells into the design to account for\n"); log(" certain required times.\n"); @@ -1128,18 +1587,13 @@ struct Abc9OpsPass : public Pass { log("\n"); log(" -prep_xaiger\n"); log(" prepare the design for XAIGER output. this includes computing the\n"); - log(" topological ordering of ABC9 boxes, as well as preparing the\n"); - log(" '<module-name>$holes' module that contains the logic behaviour of ABC9\n"); - log(" whiteboxes.\n"); + log(" topological ordering of ABC9 boxes, as well as preparing the '$abc9_holes'\n"); + log(" design that contains the logic behaviour of ABC9 whiteboxes.\n"); log("\n"); log(" -dff\n"); log(" consider flop cells (those instantiating modules marked with (* abc9_flop *))\n"); log(" during -prep_{delays,xaiger,box}.\n"); log("\n"); - log(" -prep_dff\n"); - log(" compute the clock domain and initial value of each flop in the design.\n"); - log(" process the '$holes' module to support clock-enable functionality.\n"); - log("\n"); log(" -prep_lut <maxlut>\n"); log(" pre-compute the lut library by analysing all modules marked with\n"); log(" (* abc9_lut=<area> *).\n"); @@ -1160,14 +1614,16 @@ struct Abc9OpsPass : public Pass { log(" inputs and outputs.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n"); bool check_mode = false; bool prep_delays_mode = false; bool mark_scc_mode = false; - bool prep_dff_mode = false; + bool prep_hier_mode = false; + bool prep_bypass_mode = false; + bool prep_dff_mode = false, prep_dff_submod_mode = false, prep_dff_unmap_mode = false; bool prep_xaiger_mode = false; bool prep_lut_mode = false; bool prep_box_mode = false; @@ -1177,53 +1633,81 @@ struct Abc9OpsPass : public Pass { int maxlut = 0; std::string write_box_dst; + bool valid = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; if (arg == "-check") { check_mode = true; + valid = true; continue; } if (arg == "-mark_scc") { mark_scc_mode = true; + valid = true; + continue; + } + if (arg == "-prep_hier") { + prep_hier_mode = true; + valid = true; + continue; + } + if (arg == "-prep_bypass") { + prep_bypass_mode = true; + valid = true; continue; } if (arg == "-prep_dff") { prep_dff_mode = true; + valid = true; + continue; + } + if (arg == "-prep_dff_submod") { + prep_dff_submod_mode = true; + valid = true; + continue; + } + if (arg == "-prep_dff_unmap") { + prep_dff_unmap_mode = true; + valid = true; continue; } if (arg == "-prep_xaiger") { prep_xaiger_mode = true; + valid = true; continue; } if (arg == "-prep_delays") { prep_delays_mode = true; + valid = true; continue; } if (arg == "-prep_lut" && argidx+1 < args.size()) { prep_lut_mode = true; maxlut = atoi(args[++argidx].c_str()); - continue; - } - if (arg == "-maxlut" && argidx+1 < args.size()) { + valid = true; continue; } if (arg == "-write_lut" && argidx+1 < args.size()) { write_lut_dst = args[++argidx]; rewrite_filename(write_lut_dst); + valid = true; continue; } if (arg == "-prep_box") { prep_box_mode = true; + valid = true; continue; } if (arg == "-write_box" && argidx+1 < args.size()) { write_box_dst = args[++argidx]; rewrite_filename(write_box_dst); + valid = true; continue; } if (arg == "-reintegrate") { reintegrate_mode = true; + valid = true; continue; } if (arg == "-dff") { @@ -1234,25 +1718,32 @@ struct Abc9OpsPass : public Pass { } extra_args(args, argidx, design); - if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || prep_lut_mode || prep_box_mode || !write_lut_dst.empty() || !write_box_dst.empty() || reintegrate_mode)) - log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff,lut,box}, -write_{lut,box}, -reintegrate must be specified.\n"); + if (!valid) + log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate must be specified.\n"); - if (dff_mode && !prep_delays_mode && !prep_xaiger_mode && !prep_box_mode) - log_cmd_error("'-dff' option is only relevant for -prep_{delay,xaiger,box}.\n"); + if (dff_mode && !check_mode && !prep_hier_mode && !prep_delays_mode && !prep_xaiger_mode && !reintegrate_mode) + log_cmd_error("'-dff' option is only relevant for -prep_{hier,delay,xaiger} or -reintegrate.\n"); if (check_mode) - check(design); + check(design, dff_mode); + if (prep_hier_mode) + prep_hier(design, dff_mode); + if (prep_bypass_mode) + prep_bypass(design); + if (prep_dff_mode) + prep_dff(design); + if (prep_dff_submod_mode) + prep_dff_submod(design); + if (prep_dff_unmap_mode) + prep_dff_unmap(design); if (prep_delays_mode) prep_delays(design, dff_mode); if (prep_lut_mode) prep_lut(design, maxlut); if (prep_box_mode) - prep_box(design, dff_mode); + prep_box(design); for (auto mod : design->selected_modules()) { - if (mod->get_bool_attribute(ID::abc9_holes)) - continue; - if (mod->processes.size() > 0) { log("Skipping module %s as it contains processes.\n", log_id(mod)); continue; @@ -1267,12 +1758,10 @@ struct Abc9OpsPass : public Pass { write_box(mod, write_box_dst); if (mark_scc_mode) mark_scc(mod); - if (prep_dff_mode) - prep_dff(mod); if (prep_xaiger_mode) prep_xaiger(mod, dff_mode); if (reintegrate_mode) - reintegrate(mod); + reintegrate(mod, dff_mode); } } } Abc9OpsPass; diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index 2ecb2f35a..ce151c7f3 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct AigmapPass : public Pass { AigmapPass() : Pass("aigmap", "map logic to and-inverter-graph circuit") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -43,7 +43,7 @@ struct AigmapPass : public Pass { log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { bool nand_mode = false, select_mode = false; diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index 1925145d3..b16e9750e 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -550,7 +550,7 @@ struct AlumaccWorker struct AlumaccPass : public Pass { AlumaccPass() : Pass("alumacc", "extract ALU and MACC cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -560,7 +560,7 @@ struct AlumaccPass : public Pass { log("and $macc cells.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing ALUMACC pass (create $alu and $macc cells).\n"); diff --git a/passes/techmap/attrmap.cc b/passes/techmap/attrmap.cc index 5f30817d4..8643543c8 100644 --- a/passes/techmap/attrmap.cc +++ b/passes/techmap/attrmap.cc @@ -81,7 +81,7 @@ struct AttrmapAction { struct AttrmapTocase : AttrmapAction { string name; - bool apply(IdString &id, Const&) YS_OVERRIDE { + bool apply(IdString &id, Const&) override { if (match_name(name, id, true)) id = RTLIL::escape_id(name); return true; @@ -90,7 +90,7 @@ struct AttrmapTocase : AttrmapAction { struct AttrmapRename : AttrmapAction { string old_name, new_name; - bool apply(IdString &id, Const&) YS_OVERRIDE { + bool apply(IdString &id, Const&) override { if (match_name(old_name, id)) id = RTLIL::escape_id(new_name); return true; @@ -101,7 +101,7 @@ struct AttrmapMap : AttrmapAction { bool imap; string old_name, new_name; string old_value, new_value; - bool apply(IdString &id, Const &val) YS_OVERRIDE { + bool apply(IdString &id, Const &val) override { if (match_name(old_name, id) && match_value(old_value, val, true)) { id = RTLIL::escape_id(new_name); val = make_value(new_value); @@ -113,7 +113,7 @@ struct AttrmapMap : AttrmapAction { struct AttrmapRemove : AttrmapAction { bool has_value; string name, value; - bool apply(IdString &id, Const &val) YS_OVERRIDE { + bool apply(IdString &id, Const &val) override { return !(match_name(name, id) && (!has_value || match_value(value, val))); } }; @@ -221,7 +221,7 @@ bool parse_attrmap_paramap_options(size_t &argidx, std::vector<std::string> &arg struct AttrmapPass : public Pass { AttrmapPass() : Pass("attrmap", "renaming attributes") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -241,7 +241,7 @@ struct AttrmapPass : public Pass { log(" -imap keep=\"false\" keep=0 -remove keep=0\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing ATTRMAP pass (move or copy attributes).\n"); @@ -301,7 +301,7 @@ struct AttrmapPass : public Pass { struct ParamapPass : public Pass { ParamapPass() : Pass("paramap", "renaming cell parameters") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -317,7 +317,7 @@ struct ParamapPass : public Pass { log(" paramap -tocase INIT t:LUT4\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing PARAMAP pass (move or copy cell parameters).\n"); diff --git a/passes/techmap/attrmvcp.cc b/passes/techmap/attrmvcp.cc index e59aa6518..b3202c587 100644 --- a/passes/techmap/attrmvcp.cc +++ b/passes/techmap/attrmvcp.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct AttrmvcpPass : public Pass { AttrmvcpPass() : Pass("attrmvcp", "move or copy attributes from wires to driving cells") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" attrmvcp [options] [selection]\n"); @@ -53,7 +53,7 @@ struct AttrmvcpPass : public Pass { log(" multiple times.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing ATTRMVCP pass (move or copy attributes).\n"); diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc index 3f4b6aa66..451325fee 100644 --- a/passes/techmap/clkbufmap.cc +++ b/passes/techmap/clkbufmap.cc @@ -35,7 +35,7 @@ void split_portname_pair(std::string &port1, std::string &port2) struct ClkbufmapPass : public Pass { ClkbufmapPass() : Pass("clkbufmap", "insert global buffers on clock networks") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -76,7 +76,7 @@ struct ClkbufmapPass : public Pass { modules_processed.insert(module); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing CLKBUFMAP pass (inserting global clock buffers).\n"); diff --git a/passes/techmap/deminout.cc b/passes/techmap/deminout.cc index a7dce9c81..9a23cb90e 100644 --- a/passes/techmap/deminout.cc +++ b/passes/techmap/deminout.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct DeminoutPass : public Pass { DeminoutPass() : Pass("deminout", "demote inout ports to input or output") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" deminout [options] [selection]\n"); @@ -33,7 +33,7 @@ struct DeminoutPass : public Pass { log("\"Demote\" inout ports to input or output ports, if possible.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing DEMINOUT pass (demote inout ports to input or output).\n"); diff --git a/passes/techmap/dff2dffe.cc b/passes/techmap/dff2dffe.cc index aa9bbfe17..62ee3fea6 100644 --- a/passes/techmap/dff2dffe.cc +++ b/passes/techmap/dff2dffe.cc @@ -253,7 +253,7 @@ struct Dff2dffeWorker struct Dff2dffePass : public Pass { Dff2dffePass() : Pass("dff2dffe", "transform $dff cells to $dffe cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -282,13 +282,13 @@ struct Dff2dffePass : public Pass { log("\n"); log(" -direct-match <pattern>\n"); log(" like -direct for all DFF cell types matching the expression.\n"); - log(" this will use $__DFFE_* as <external_gate_type> matching the\n"); - log(" internal gate type $_DFF_*_, and $__DFFSE_* for those matching\n"); - log(" $_DFFS_*_, except for $_DFF_[NP]_, which is converted to \n"); + log(" this will use $_DFFE_* as <external_gate_type> matching the\n"); + log(" internal gate type $_DFF_*_, and $_SDFFE_* for those matching\n"); + log(" $_SDFF_*_, except for $_DFF_[NP]_, which is converted to \n"); log(" $_DFFE_[NP]_.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing DFF2DFFE pass (transform $dff to $dffe where applicable).\n"); @@ -318,23 +318,23 @@ struct Dff2dffePass : public Pass { const char *pattern = args[++argidx].c_str(); if (patmatch(pattern, "$_DFF_P_" )) found_match = true, direct_dict[ID($_DFF_P_) ] = ID($_DFFE_PP_); if (patmatch(pattern, "$_DFF_N_" )) found_match = true, direct_dict[ID($_DFF_N_) ] = ID($_DFFE_NP_); - if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict[ID($_DFF_NN0_)] = ID($__DFFE_NN0); - if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict[ID($_DFF_NN1_)] = ID($__DFFE_NN1); - if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict[ID($_DFF_NP0_)] = ID($__DFFE_NP0); - if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict[ID($_DFF_NP1_)] = ID($__DFFE_NP1); - if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict[ID($_DFF_PN0_)] = ID($__DFFE_PN0); - if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict[ID($_DFF_PN1_)] = ID($__DFFE_PN1); - if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict[ID($_DFF_PP0_)] = ID($__DFFE_PP0); - if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict[ID($_DFF_PP1_)] = ID($__DFFE_PP1); - - if (patmatch(pattern, "$__DFFS_NN0_")) found_match = true, direct_dict[ID($__DFFS_NN0_)] = ID($__DFFSE_NN0); - if (patmatch(pattern, "$__DFFS_NN1_")) found_match = true, direct_dict[ID($__DFFS_NN1_)] = ID($__DFFSE_NN1); - if (patmatch(pattern, "$__DFFS_NP0_")) found_match = true, direct_dict[ID($__DFFS_NP0_)] = ID($__DFFSE_NP0); - if (patmatch(pattern, "$__DFFS_NP1_")) found_match = true, direct_dict[ID($__DFFS_NP1_)] = ID($__DFFSE_NP1); - if (patmatch(pattern, "$__DFFS_PN0_")) found_match = true, direct_dict[ID($__DFFS_PN0_)] = ID($__DFFSE_PN0); - if (patmatch(pattern, "$__DFFS_PN1_")) found_match = true, direct_dict[ID($__DFFS_PN1_)] = ID($__DFFSE_PN1); - if (patmatch(pattern, "$__DFFS_PP0_")) found_match = true, direct_dict[ID($__DFFS_PP0_)] = ID($__DFFSE_PP0); - if (patmatch(pattern, "$__DFFS_PP1_")) found_match = true, direct_dict[ID($__DFFS_PP1_)] = ID($__DFFSE_PP1); + if (patmatch(pattern, "$_DFF_NN0_")) found_match = true, direct_dict[ID($_DFF_NN0_)] = ID($_DFFE_NN0P_); + if (patmatch(pattern, "$_DFF_NN1_")) found_match = true, direct_dict[ID($_DFF_NN1_)] = ID($_DFFE_NN1P_); + if (patmatch(pattern, "$_DFF_NP0_")) found_match = true, direct_dict[ID($_DFF_NP0_)] = ID($_DFFE_NP0P_); + if (patmatch(pattern, "$_DFF_NP1_")) found_match = true, direct_dict[ID($_DFF_NP1_)] = ID($_DFFE_NP1P_); + if (patmatch(pattern, "$_DFF_PN0_")) found_match = true, direct_dict[ID($_DFF_PN0_)] = ID($_DFFE_PN0P_); + if (patmatch(pattern, "$_DFF_PN1_")) found_match = true, direct_dict[ID($_DFF_PN1_)] = ID($_DFFE_PN1P_); + if (patmatch(pattern, "$_DFF_PP0_")) found_match = true, direct_dict[ID($_DFF_PP0_)] = ID($_DFFE_PP0P_); + if (patmatch(pattern, "$_DFF_PP1_")) found_match = true, direct_dict[ID($_DFF_PP1_)] = ID($_DFFE_PP1P_); + + if (patmatch(pattern, "$_SDFF_NN0_")) found_match = true, direct_dict[ID($_SDFF_NN0_)] = ID($_SDFFE_NN0P_); + if (patmatch(pattern, "$_SDFF_NN1_")) found_match = true, direct_dict[ID($_SDFF_NN1_)] = ID($_SDFFE_NN1P_); + if (patmatch(pattern, "$_SDFF_NP0_")) found_match = true, direct_dict[ID($_SDFF_NP0_)] = ID($_SDFFE_NP0P_); + if (patmatch(pattern, "$_SDFF_NP1_")) found_match = true, direct_dict[ID($_SDFF_NP1_)] = ID($_SDFFE_NP1P_); + if (patmatch(pattern, "$_SDFF_PN0_")) found_match = true, direct_dict[ID($_SDFF_PN0_)] = ID($_SDFFE_PN0P_); + if (patmatch(pattern, "$_SDFF_PN1_")) found_match = true, direct_dict[ID($_SDFF_PN1_)] = ID($_SDFFE_PN1P_); + if (patmatch(pattern, "$_SDFF_PP0_")) found_match = true, direct_dict[ID($_SDFF_PP0_)] = ID($_SDFFE_PP0P_); + if (patmatch(pattern, "$_SDFF_PP1_")) found_match = true, direct_dict[ID($_SDFF_PP1_)] = ID($_SDFFE_PP1P_); if (!found_match) log_cmd_error("No cell types matched pattern '%s'.\n", pattern); continue; diff --git a/passes/techmap/dff2dffs.cc b/passes/techmap/dff2dffs.cc index c155297d9..6c2cca4bc 100644 --- a/passes/techmap/dff2dffs.cc +++ b/passes/techmap/dff2dffs.cc @@ -26,12 +26,12 @@ PRIVATE_NAMESPACE_BEGIN struct Dff2dffsPass : public Pass { Dff2dffsPass() : Pass("dff2dffs", "process sync set/reset with SR over CE priority") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" dff2dffs [options] [selection]\n"); log("\n"); - log("Merge synchronous set/reset $_MUX_ cells to create $__DFFS_[NP][NP][01], to be run before\n"); + log("Merge synchronous set/reset $_MUX_ cells to create $_SDFF_[NP][NP][01]_, to be run before\n"); log("dff2dffe for SR over CE priority.\n"); log("\n"); log(" -match-init\n"); @@ -39,7 +39,7 @@ struct Dff2dffsPass : public Pass { log(" output wire's init attribute (if any).\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing dff2dffs pass (merge synchronous set/reset into FF cells).\n"); @@ -138,21 +138,21 @@ struct Dff2dffsPass : public Pass { if (sr_val == State::S1) { if (cell->type == ID($_DFF_N_)) { - if (invert_sr) cell->type = ID($__DFFS_NN1_); - else cell->type = ID($__DFFS_NP1_); + if (invert_sr) cell->type = ID($_SDFF_NN1_); + else cell->type = ID($_SDFF_NP1_); } else { log_assert(cell->type == ID($_DFF_P_)); - if (invert_sr) cell->type = ID($__DFFS_PN1_); - else cell->type = ID($__DFFS_PP1_); + if (invert_sr) cell->type = ID($_SDFF_PN1_); + else cell->type = ID($_SDFF_PP1_); } } else { if (cell->type == ID($_DFF_N_)) { - if (invert_sr) cell->type = ID($__DFFS_NN0_); - else cell->type = ID($__DFFS_NP0_); + if (invert_sr) cell->type = ID($_SDFF_NN0_); + else cell->type = ID($_SDFF_NP0_); } else { log_assert(cell->type == ID($_DFF_P_)); - if (invert_sr) cell->type = ID($__DFFS_PN0_); - else cell->type = ID($__DFFS_PP0_); + if (invert_sr) cell->type = ID($_SDFF_PN0_); + else cell->type = ID($_SDFF_PP0_); } } cell->setPort(ID::R, sr_sig); diff --git a/passes/techmap/dffinit.cc b/passes/techmap/dffinit.cc index 35645582b..c60a901c1 100644 --- a/passes/techmap/dffinit.cc +++ b/passes/techmap/dffinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct DffinitPass : public Pass { DffinitPass() : Pass("dffinit", "set INIT param on FF cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -54,7 +54,7 @@ struct DffinitPass : public Pass { log(" the already defined initial value.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing DFFINIT pass (set INIT param on FF cells).\n"); diff --git a/passes/techmap/dfflegalize.cc b/passes/techmap/dfflegalize.cc new file mode 100644 index 000000000..013f2d974 --- /dev/null +++ b/passes/techmap/dfflegalize.cc @@ -0,0 +1,1356 @@ +/* + * 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" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +enum FfType { + FF_DFF, + FF_DFFE, + FF_ADFF0, + FF_ADFF1, + FF_ADFFE0, + FF_ADFFE1, + FF_DFFSR, + FF_DFFSRE, + FF_SDFF0, + FF_SDFF1, + FF_SDFFE0, + FF_SDFFE1, + FF_SDFFCE0, + FF_SDFFCE1, + FF_SR, + FF_DLATCH, + FF_ADLATCH0, + FF_ADLATCH1, + FF_DLATCHSR, + NUM_FFTYPES, +}; + +enum FfNeg { + NEG_R = 0x1, + NEG_S = 0x2, + NEG_E = 0x4, + NEG_C = 0x8, + NUM_NEG = 0x10, +}; + +enum FfInit { + INIT_X = 0x1, + INIT_0 = 0x2, + INIT_1 = 0x4, +}; + +struct DffLegalizePass : public Pass { + DffLegalizePass() : Pass("dfflegalize", "convert FFs to types supported by the target") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" dfflegalize [options] [selection]\n"); + log("\n"); + log("Converts FFs to types supported by the target.\n"); + log("\n"); + log(" -cell <cell_type_pattern> <init_values>\n"); + log(" specifies a supported group of FF cells. <cell_type_pattern>\n"); + log(" is a yosys internal fine cell name, where ? characters can be\n"); + log(" as a wildcard matching any character. <init_values> specifies\n"); + log(" which initialization values these FF cells can support, and can\n"); + log(" be one of:\n"); + log("\n"); + log(" - x (no init value supported)\n"); + log(" - 0\n"); + log(" - 1\n"); + log(" - r (init value has to match reset value, only for some FF types)\n"); + log(" - 01 (both 0 and 1 supported).\n"); + log("\n"); + log(" -mince <num>\n"); + log(" specifies a minimum number of FFs that should be using any given\n"); + log(" clock enable signal. If a clock enable signal doesn't meet this\n"); + log(" threshold, it is unmapped into soft logic.\n"); + log("\n"); + log(" -minsrst <num>\n"); + log(" specifies a minimum number of FFs that should be using any given\n"); + log(" sync set/reset signal. If a sync set/reset signal doesn't meet this\n"); + log(" threshold, it is unmapped into soft logic.\n"); + log("\n"); + log("The following cells are supported by this pass (ie. will be ingested,\n"); + log("and can be specified as allowed targets):\n"); + log("\n"); + log("- $_DFF_[NP]_\n"); + log("- $_DFFE_[NP][NP]_\n"); + log("- $_DFF_[NP][NP][01]_\n"); + log("- $_DFFE_[NP][NP][01][NP]_\n"); + log("- $_DFFSR_[NP][NP][NP]_\n"); + log("- $_DFFSRE_[NP][NP][NP][NP]_\n"); + log("- $_SDFF_[NP][NP][01]_\n"); + log("- $_SDFFE_[NP][NP][01][NP]_\n"); + log("- $_SDFFCE_[NP][NP][01][NP]_\n"); + log("- $_SR_[NP][NP]_\n"); + log("- $_DLATCH_[NP]_\n"); + log("- $_DLATCH_[NP][NP][01]_\n"); + log("- $_DLATCHSR_[NP][NP][NP]_\n"); + log("\n"); + log("The following transformations are performed by this pass:"); + log(""); + log("- upconversion from a less capable cell to a more capable cell, if the less"); + log(" capable cell is not supported (eg. dff -> dffe, or adff -> dffsr)"); + log(""); + log("- unmapping FFs with clock enable (due to unsupported cell type or -mince)"); + log(""); + log("- unmapping FFs with sync reset (due to unsupported cell type or -minsrst)"); + log(""); + log("- adding inverters on the control pins (due to unsupported polarity)"); + log(""); + log("- adding inverters on the D and Q pins and inverting the init/reset values\n"); + log(" (due to unsupported init or reset value)"); + log(""); + log("- converting sr into adlatch (by tying D to 1 and using E as set input)"); + log(""); + log("- emulating unsupported dffsr cell by adff + adff + sr + mux"); + log(""); + log("- emulating unsupported dlatchsr cell by adlatch + adlatch + sr + mux"); + log(""); + log("- emulating adff when the (reset, init) value combination is unsupported by\n"); + log(" dff + adff + dlatch + mux"); + log(""); + log("- emulating adlatch when the (reset, init) value combination is unsupported by\n"); + log("- dlatch + adlatch + dlatch + mux"); + log(""); + log("If the pass is unable to realize a given cell type (eg. adff when only plain dff"); + log("is available), an error is raised."); + } + + // Table of all supported cell types. + // First index in the array is one of the FF_* values, second + // index is the set of negative-polarity inputs (OR of NEG_* + // values), and the value is the set of supported init values + // (OR of INIT_* values). + int supported_cells_neg[NUM_FFTYPES][NUM_NEG]; + // Aggregated table ignoring signal polarity. + int supported_cells[NUM_FFTYPES]; + // Aggregated for all *dff* cells. + int supported_dff; + // Aggregated for all dffsr* cells. + int supported_dffsr; + // Aggregated for all adff* cells. + int supported_adff0; + int supported_adff1; + // Aggregated for all sdff* cells. + int supported_sdff0; + int supported_sdff1; + // Aggregated for all ways to obtain a SR latch. + int supported_sr; + // Aggregated for all *dlatch* cells. + int supported_dlatch; + + int mince; + int minsrst; + + dict<SigBit, int> ce_used; + dict<SigBit, int> srst_used; + + SigMap sigmap; + dict<SigBit, std::pair<State,SigBit>> initbits; + + int flip_initmask(int mask) { + int res = mask & INIT_X; + if (mask & INIT_0) + res |= INIT_1; + if (mask & INIT_1) + res |= INIT_0; + return res; + } + + void handle_ff(Cell *cell) { + std::string type_str = cell->type.str(); + + FfType ff_type; + int ff_neg = 0; + SigSpec sig_d; + SigSpec sig_q; + SigSpec sig_c; + SigSpec sig_e; + SigSpec sig_r; + SigSpec sig_s; + bool has_srst = false; + + if (cell->hasPort(ID::D)) + sig_d = cell->getPort(ID::D); + if (cell->hasPort(ID::Q)) + sig_q = cell->getPort(ID::Q); + if (cell->hasPort(ID::C)) + sig_c = cell->getPort(ID::C); + if (cell->hasPort(ID::E)) + sig_e = cell->getPort(ID::E); + if (cell->hasPort(ID::R)) + sig_r = cell->getPort(ID::R); + if (cell->hasPort(ID::S)) + sig_s = cell->getPort(ID::S); + + if (type_str.substr(0, 5) == "$_SR_") { + ff_type = FF_SR; + if (type_str[5] == 'N') + ff_neg |= NEG_S; + if (type_str[6] == 'N') + ff_neg |= NEG_R; + } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 8) { + ff_type = FF_DFF; + if (type_str[6] == 'N') + ff_neg |= NEG_C; + } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 10) { + ff_type = FF_DFFE; + if (type_str[7] == 'N') + ff_neg |= NEG_C; + if (type_str[8] == 'N') + ff_neg |= NEG_E; + } else if (type_str.substr(0, 6) == "$_DFF_" && type_str.size() == 10) { + ff_type = type_str[8] == '1' ? FF_ADFF1 : FF_ADFF0; + if (type_str[6] == 'N') + ff_neg |= NEG_C; + if (type_str[7] == 'N') + ff_neg |= NEG_R; + } else if (type_str.substr(0, 7) == "$_DFFE_" && type_str.size() == 12) { + ff_type = type_str[9] == '1' ? FF_ADFFE1 : FF_ADFFE0; + if (type_str[7] == 'N') + ff_neg |= NEG_C; + if (type_str[8] == 'N') + ff_neg |= NEG_R; + if (type_str[10] == 'N') + ff_neg |= NEG_E; + } else if (type_str.substr(0, 8) == "$_DFFSR_" && type_str.size() == 12) { + ff_type = FF_DFFSR; + if (type_str[8] == 'N') + ff_neg |= NEG_C; + if (type_str[9] == 'N') + ff_neg |= NEG_S; + if (type_str[10] == 'N') + ff_neg |= NEG_R; + } else if (type_str.substr(0, 9) == "$_DFFSRE_" && type_str.size() == 14) { + ff_type = FF_DFFSRE; + if (type_str[9] == 'N') + ff_neg |= NEG_C; + if (type_str[10] == 'N') + ff_neg |= NEG_S; + if (type_str[11] == 'N') + ff_neg |= NEG_R; + if (type_str[12] == 'N') + ff_neg |= NEG_E; + } else if (type_str.substr(0, 7) == "$_SDFF_" && type_str.size() == 11) { + ff_type = type_str[9] == '1' ? FF_SDFF1 : FF_SDFF0; + if (type_str[7] == 'N') + ff_neg |= NEG_C; + if (type_str[8] == 'N') + ff_neg |= NEG_R; + has_srst = true; + } else if (type_str.substr(0, 8) == "$_SDFFE_" && type_str.size() == 13) { + ff_type = type_str[10] == '1' ? FF_SDFFE1 : FF_SDFFE0; + if (type_str[8] == 'N') + ff_neg |= NEG_C; + if (type_str[9] == 'N') + ff_neg |= NEG_R; + if (type_str[11] == 'N') + ff_neg |= NEG_E; + has_srst = true; + } else if (type_str.substr(0, 9) == "$_SDFFCE_" && type_str.size() == 14) { + ff_type = type_str[11] == '1' ? FF_SDFFCE1 : FF_SDFFCE0; + if (type_str[9] == 'N') + ff_neg |= NEG_C; + if (type_str[10] == 'N') + ff_neg |= NEG_R; + if (type_str[12] == 'N') + ff_neg |= NEG_E; + has_srst = true; + } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 11) { + ff_type = FF_DLATCH; + if (type_str[9] == 'N') + ff_neg |= NEG_E; + } else if (type_str.substr(0, 9) == "$_DLATCH_" && type_str.size() == 13) { + ff_type = type_str[11] == '1' ? FF_ADLATCH1 : FF_ADLATCH0; + if (type_str[9] == 'N') + ff_neg |= NEG_E; + if (type_str[10] == 'N') + ff_neg |= NEG_R; + } else if (type_str.substr(0, 11) == "$_DLATCHSR_" && type_str.size() == 15) { + ff_type = FF_DLATCHSR; + if (type_str[11] == 'N') + ff_neg |= NEG_E; + if (type_str[12] == 'N') + ff_neg |= NEG_S; + if (type_str[13] == 'N') + ff_neg |= NEG_R; + } else { + log_warning("Ignoring unknown ff type %s [%s.%s].\n", log_id(cell->type), log_id(cell->module->name), log_id(cell->name)); + 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; + } + + FfInit initmask = INIT_X; + if (initval == State::S0) + initmask = INIT_0; + else if (initval == State::S1) + initmask = INIT_1; + const char *reason; + + bool kill_ce = mince && GetSize(sig_c) && GetSize(sig_e) && sig_e[0].wire && ce_used[sig_e[0]] < mince; + bool kill_srst = minsrst && has_srst && sig_r[0].wire && srst_used[sig_r[0]] < minsrst; + + while (!(supported_cells[ff_type] & initmask) || kill_ce || kill_srst) { + // Well, cell is not directly supported. Decide how to deal with it. + + if (ff_type == FF_DFF || ff_type == FF_DFFE) { + if (kill_ce) { + ff_type = FF_DFF; + goto unmap_enable; + } + if (!(supported_dff & initmask)) { + // This init value is not supported at all... + if (supported_dff & flip_initmask(initmask)) { + // The other one is, though. Negate D, Q, and init. +flip_dqi: + if (initval == State::S0) { + initval = State::S1; + initmask = INIT_1; + } else if (initval == State::S1) { + initval = State::S0; + initmask = INIT_0; + } + if (ff_type != FF_SR) + 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); + } + sig_q = new_q; + continue; + } + if (!supported_dff) + reason = "dffs are not supported"; + else + reason = "initialized dffs are not supported"; + goto error; + } + + // Some DFF is supported with this init val. Just pick a type. + if (ff_type == FF_DFF) { + // Try adding a set or reset pin. + for (auto new_type: {FF_ADFF0, FF_ADFF1, FF_SDFF0, FF_SDFF1}) + if (supported_cells[new_type] & initmask) { + ff_type = new_type; + sig_r = State::S0; + goto cell_ok; + } + // Try adding both. + if (supported_cells[FF_DFFSR] & initmask) { + ff_type = FF_DFFSR; + sig_r = State::S0; + sig_s = State::S0; + break; + } + // Nope. Will need to add enable and go the DFFE route. + sig_e = State::S1; + if (supported_cells[FF_DFFE] & initmask) { + ff_type = FF_DFFE; + break; + } + } + // Try adding a set or reset pin. + for (auto new_type: {FF_SDFFE0, FF_SDFFE1, FF_SDFFCE0, FF_SDFFCE1, FF_ADFFE0, FF_ADFFE1}) + if (supported_cells[new_type] & initmask) { + ff_type = new_type; + sig_r = State::S0; + goto cell_ok; + } + // Try adding both. + if (supported_cells[FF_DFFSRE] & initmask) { + ff_type = FF_DFFSRE; + sig_r = State::S0; + sig_s = State::S0; + break; + } + + // Seems that no DFFs with enable are supported. + // The enable input needs to be unmapped. + // This should not be reached if we started from plain DFF. + log_assert(ff_type == FF_DFFE); + ff_type = FF_DFF; +unmap_enable: + if (ff_neg & NEG_E) + sig_d = cell->module->MuxGate(NEW_ID, sig_d[0], sig_q[0], sig_e[0]); + else + sig_d = cell->module->MuxGate(NEW_ID, sig_q[0], sig_d[0], sig_e[0]); + ff_neg &= ~NEG_E; + sig_e = SigSpec(); + kill_ce = false; + // Now try again as plain DFF. + continue; + } else if (ff_type == FF_ADFF0 || ff_type == FF_ADFF1 || ff_type == FF_ADFFE0 || ff_type == FF_ADFFE1) { + bool has_set = ff_type == FF_ADFF1 || ff_type == FF_ADFFE1; + bool has_en = ff_type == FF_ADFFE0 || ff_type == FF_ADFFE1; + if (kill_ce) { + ff_type = has_set ? FF_ADFF1 : FF_ADFF0; + goto unmap_enable; + } + if (!has_en && (supported_cells[has_set ? FF_ADFFE1 : FF_ADFFE0] & initmask)) { + // Just add enable. + sig_e = State::S1; + ff_type = has_set ? FF_ADFFE1 : FF_ADFFE0; + break; + } + if (supported_dffsr & initmask) { + // Throw in a set/reset, retry in DFFSR/DFFSRE branch. + if (has_set) { + sig_s = sig_r; + sig_r = State::S0; + if (ff_neg & NEG_R) { + ff_neg &= ~NEG_R; + ff_neg |= NEG_S; + } + } else { + sig_s = State::S0; + } + if (has_en) + ff_type = FF_DFFSRE; + else + ff_type = FF_DFFSR; + continue; + } + if (has_en && (supported_cells[has_set ? FF_ADFF1 : FF_ADFF0] & initmask)) { + // Unmap enable. + ff_type = has_set ? FF_ADFF1 : FF_ADFF0; + goto unmap_enable; + } + log_assert(!((has_set ? supported_adff1 : supported_adff0) & initmask)); + // Alright, so this particular combination of initval and + // resetval is not natively supported. First, try flipping + // them both to see whether this helps. + int flipmask = flip_initmask(initmask); + if ((has_set ? supported_adff0 : supported_adff1) & flipmask) { + // Checks out, do it. + ff_type = has_en ? (has_set ? FF_ADFFE0 : FF_ADFFE1) : (has_set ? FF_ADFF0 : FF_ADFF1); + goto flip_dqi; + } + + if (!supported_adff0 && !supported_adff1) { + reason = "dffs with async set or reset are not supported"; + goto error; + } + if (!(supported_dff & ~INIT_X)) { + reason = "initialized dffs are not supported"; + goto error; + } + // If we got here, initialized dff is supported, but not this + // particular reset+init combination (nor its negation). + // The only hope left is breaking down to adff + dff + dlatch + mux. + if (!(supported_dlatch & ~INIT_X)) { + reason = "unsupported initial value and async reset value combination"; + goto error; + } + + // If we have to unmap enable anyway, do it before breakdown. + if (has_en && !supported_cells[FF_ADFFE0] && !supported_cells[FF_ADFFE1]) { + ff_type = has_set ? FF_ADFF1 : FF_ADFF0; + goto 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; + 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)); + Cell *cell_dff; + Cell *cell_adff; + Cell *cell_sel; + if (!has_en) { + cell_dff = cell->module->addDffGate(NEW_ID, sig_c, sig_d, dff_q, !(ff_neg & NEG_C)); + cell_adff = cell->module->addAdffGate(NEW_ID, sig_c, sig_r, sig_d, adff_q, has_set, !(ff_neg & NEG_C), !(ff_neg & NEG_R)); + } else { + cell_dff = cell->module->addDffeGate(NEW_ID, sig_c, sig_e, sig_d, dff_q, !(ff_neg & NEG_C), !(ff_neg & NEG_E)); + cell_adff = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_r, sig_d, adff_q, has_set, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_R)); + } + cell_sel = cell->module->addDlatchGate(NEW_ID, sig_r, State::S1, sel_q, !(ff_neg & NEG_R)); + cell->module->addMuxGate(NEW_ID, dff_q, adff_q, sel_q, sig_q); + + // Bye, cell. + cell->module->remove(cell); + handle_ff(cell_dff); + handle_ff(cell_adff); + handle_ff(cell_sel); + return; + } else if (ff_type == FF_DFFSR || ff_type == FF_DFFSRE) { + if (kill_ce) { + ff_type = FF_DFFSR; + goto unmap_enable; + } + // First, see if mapping/unmapping enable will help. + if (supported_cells[FF_DFFSRE] & initmask) { + ff_type = FF_DFFSRE; + sig_e = State::S1; + break; + } + if (supported_cells[FF_DFFSR] & initmask) { + ff_type = FF_DFFSR; + goto unmap_enable; + } + if (supported_dffsr & flip_initmask(initmask)) { +flip_dqisr:; + log_warning("Flipping D/Q/init and inseerting set/reset fixup to handle init value on %s.%s [%s]\n", log_id(cell->module->name), log_id(cell->name), log_id(cell->type)); + SigSpec new_r; + bool neg_r = (ff_neg & NEG_R); + bool neg_s = (ff_neg & NEG_S); + if (!(ff_neg & NEG_S)) { + if (!(ff_neg & NEG_R)) + new_r = cell->module->AndnotGate(NEW_ID, sig_s, sig_r); + else + new_r = cell->module->AndGate(NEW_ID, sig_s, sig_r); + } else { + if (!(ff_neg & NEG_R)) + new_r = cell->module->OrGate(NEW_ID, sig_s, sig_r); + else + new_r = cell->module->OrnotGate(NEW_ID, sig_s, sig_r); + } + ff_neg &= ~(NEG_R | NEG_S); + if (neg_r) + ff_neg |= NEG_S; + if (neg_s) + ff_neg |= NEG_R; + sig_s = sig_r; + sig_r = new_r; + goto flip_dqi; + } + // No native DFFSR. However, if we can conjure + // a SR latch and ADFF, it can still be emulated. + int flipmask = flip_initmask(initmask); + bool init0 = true; + bool init1 = true; + State initsel = State::Sx; + if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && ((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && supported_sr) { + // OK + } else if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && (supported_sr & INIT_0)) { + init1 = false; + initsel = State::S0; + } else if (((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && (supported_sr & INIT_1)) { + init0 = false; + initsel = State::S1; + } else if (((supported_adff0 & initmask) || (supported_adff1 & flipmask)) && (supported_sr & INIT_1)) { + init1 = false; + initsel = State::S0; + } else if (((supported_adff1 & initmask) || (supported_adff0 & flipmask)) && (supported_sr & INIT_0)) { + init0 = false; + initsel = State::S1; + } else { + if (!supported_dffsr) + reason = "dffs with async set and reset are not supported"; + else + reason = "initialized dffs with async set and reset are not supported"; + goto error; + } + + // If we have to unmap enable anyway, do it before breakdown. + if (ff_type == FF_DFFSRE && !supported_cells[FF_ADFFE0] && !supported_cells[FF_ADFFE1]) { + ff_type = FF_DFFSR; + goto unmap_enable; + } + + 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; + 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)); + Cell *cell_adff0; + Cell *cell_adff1; + Cell *cell_sel; + if (ff_type == FF_DFFSR) { + cell_adff0 = cell->module->addAdffGate(NEW_ID, sig_c, sig_r, sig_d, adff0_q, false, !(ff_neg & NEG_C), !(ff_neg & NEG_R)); + cell_adff1 = cell->module->addAdffGate(NEW_ID, sig_c, sig_s, sig_d, adff1_q, true, !(ff_neg & NEG_C), !(ff_neg & NEG_S)); + } else { + cell_adff0 = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_r, sig_d, adff0_q, false, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_R)); + cell_adff1 = cell->module->addAdffeGate(NEW_ID, sig_c, sig_e, sig_s, sig_d, adff1_q, true, !(ff_neg & NEG_C), !(ff_neg & NEG_E), !(ff_neg & NEG_S)); + } + cell_sel = cell->module->addSrGate(NEW_ID, sig_s, sig_r, sel_q, !(ff_neg & NEG_S), !(ff_neg & NEG_R)); + cell->module->addMuxGate(NEW_ID, adff0_q, adff1_q, sel_q, sig_q); + + // Bye, cell. + cell->module->remove(cell); + handle_ff(cell_adff0); + handle_ff(cell_adff1); + handle_ff(cell_sel); + return; + } else if (ff_type == FF_SR) { + if (supported_cells[FF_ADLATCH0] & initmask || supported_cells[FF_ADLATCH1] & flip_initmask(initmask)) { + // Convert to ADLATCH0. May get converted to ADLATCH1. + ff_type = FF_ADLATCH0; + sig_e = sig_s; + sig_d = State::S1; + if (ff_neg & NEG_S) { + ff_neg &= ~NEG_S; + ff_neg |= NEG_E; + } + continue; + } else if (supported_cells[FF_DLATCHSR] & initmask) { + // Upgrade to DLATCHSR. + ff_type = FF_DLATCHSR; + sig_e = State::S0; + sig_d = State::Sx; + break; + } else if (supported_dffsr & initmask) { + // Upgrade to DFFSR. May get further upgraded to DFFSRE. + ff_type = FF_DFFSR; + sig_c = State::S0; + sig_d = State::Sx; + continue; + } else if (supported_sr & flip_initmask(initmask)) { + goto flip_dqisr; + } else { + if (!supported_sr) + reason = "sr latches are not supported"; + else + reason = "initialized sr latches are not supported"; + goto error; + } + } else if (ff_type == FF_DLATCH) { + if (!(supported_dlatch & initmask)) { + // This init value is not supported at all... + if (supported_dlatch & flip_initmask(initmask)) + goto flip_dqi; + if (!supported_dlatch) + reason = "dlatch are not supported"; + else + reason = "initialized dlatch are not supported"; + goto error; + } + + // Some DLATCH is supported with this init val. Just pick a type. + if (supported_cells[FF_ADLATCH0] & initmask) { + ff_type = FF_ADLATCH0; + sig_r = State::S0; + break; + } + if (supported_cells[FF_ADLATCH1] & initmask) { + ff_type = FF_ADLATCH1; + sig_r = State::S0; + break; + } + if (supported_cells[FF_DLATCHSR] & initmask) { + ff_type = FF_DLATCHSR; + sig_r = State::S0; + sig_s = State::S0; + break; + } + log_assert(0); + } else if (ff_type == FF_ADLATCH0 || ff_type == FF_ADLATCH1) { + if (supported_cells[FF_DLATCHSR] & initmask) { + if (ff_type == FF_ADLATCH1) { + sig_s = sig_r; + sig_r = State::S0; + if (ff_neg & NEG_R) { + ff_neg &= ~NEG_R; + ff_neg |= NEG_S; + } + } else { + sig_s = State::S0; + } + ff_type = FF_DLATCHSR; + break; + } + FfType flip_type = ff_type == FF_ADLATCH0 ? FF_ADLATCH1 : FF_ADLATCH0; + if ((supported_cells[flip_type] | supported_cells[FF_DLATCHSR]) & flip_initmask(initmask)) { + ff_type = flip_type; + goto flip_dqi; + } + + if (!supported_cells[FF_ADLATCH0] && !supported_cells[FF_ADLATCH1] && !supported_cells[FF_DLATCHSR]) { + reason = "dlatch with async set or reset are not supported"; + goto error; + } + if (!(supported_dlatch & ~INIT_X)) { + reason = "initialized dlatch are not supported"; + goto error; + } + + if (!(supported_dlatch & ~INIT_X)) { + reason = "initialized dlatch are not supported"; + goto error; + } + // If we got here, initialized dlatch is supported, but not this + // particular reset+init combination (nor its negation). + // 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; + 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)); + Cell *cell_dlatch; + Cell *cell_adlatch; + Cell *cell_sel; + cell_dlatch = cell->module->addDlatchGate(NEW_ID, sig_e, sig_d, dlatch_q, !(ff_neg & NEG_E)); + cell_adlatch = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_r, sig_d, adlatch_q, ff_type == FF_ADLATCH1, !(ff_neg & NEG_E), !(ff_neg & NEG_R)); + cell_sel = cell->module->addDlatchGate(NEW_ID, sig_r, State::S1, sel_q, !(ff_neg & NEG_R)); + cell->module->addMuxGate(NEW_ID, dlatch_q, adlatch_q, sel_q, sig_q); + + // Bye, cell. + cell->module->remove(cell); + handle_ff(cell_dlatch); + handle_ff(cell_adlatch); + handle_ff(cell_sel); + return; + } else if (ff_type == FF_DLATCHSR) { + if (supported_cells[FF_DLATCHSR] & flip_initmask(initmask)) { + goto flip_dqisr; + } + // No native DFFSR. However, if we can conjure + // a SR latch and ADFF, it can still be emulated. + int flipmask = flip_initmask(initmask); + bool init0 = true; + bool init1 = true; + State initsel = State::Sx; + if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && ((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && supported_sr) { + // OK + } else if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && (supported_sr & INIT_0)) { + init1 = false; + initsel = State::S0; + } else if (((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && (supported_sr & INIT_1)) { + init0 = false; + initsel = State::S1; + } else if (((supported_cells[FF_ADLATCH0] & initmask) || (supported_cells[FF_ADLATCH1] & flipmask)) && (supported_sr & INIT_1)) { + init1 = false; + initsel = State::S0; + } else if (((supported_cells[FF_ADLATCH1] & initmask) || (supported_cells[FF_ADLATCH0] & flipmask)) && (supported_sr & INIT_0)) { + init0 = false; + initsel = State::S1; + } else { + if (!supported_cells[FF_DLATCHSR]) + reason = "dlatch with async set and reset are not supported"; + else + reason = "initialized dlatch with async set and reset are not supported"; + goto error; + } + + 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; + 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)); + Cell *cell_adlatch0; + Cell *cell_adlatch1; + Cell *cell_sel; + cell_adlatch0 = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_r, sig_d, adlatch0_q, false, !(ff_neg & NEG_E), !(ff_neg & NEG_R)); + cell_adlatch1 = cell->module->addAdlatchGate(NEW_ID, sig_e, sig_s, sig_d, adlatch1_q, true, !(ff_neg & NEG_E), !(ff_neg & NEG_S)); + cell_sel = cell->module->addSrGate(NEW_ID, sig_s, sig_r, sel_q, !(ff_neg & NEG_S), !(ff_neg & NEG_R)); + cell->module->addMuxGate(NEW_ID, adlatch0_q, adlatch1_q, sel_q, sig_q); + + // Bye, cell. + cell->module->remove(cell); + handle_ff(cell_adlatch0); + handle_ff(cell_adlatch1); + handle_ff(cell_sel); + return; + } else if (ff_type == FF_SDFF0 || ff_type == FF_SDFF1 || ff_type == FF_SDFFE0 || ff_type == FF_SDFFE1 || ff_type == FF_SDFFCE0 || ff_type == FF_SDFFCE1) { + bool has_set = ff_type == FF_SDFF1 || ff_type == FF_SDFFE1 || ff_type == FF_SDFFCE1; + bool has_en = ff_type == FF_SDFFE0 || ff_type == FF_SDFFE1; + bool has_ce = ff_type == FF_SDFFCE0 || ff_type == FF_SDFFCE1; + + if (has_en) { + if (kill_ce || kill_srst) { + ff_type = has_set ? FF_SDFF1 : FF_SDFF0; + goto unmap_enable; + } + } else if (has_ce) { + if (kill_ce || kill_srst) + goto unmap_srst; + } else { + log_assert(!kill_ce); + if (kill_srst) + goto unmap_srst; + } + + if (!has_ce) { + if (!has_en && (supported_cells[has_set ? FF_SDFFE1 : FF_SDFFE0] & initmask)) { + // Just add enable. + sig_e = State::S1; + ff_type = has_set ? FF_SDFFE1 : FF_SDFFE0; + break; + } + if (!has_en && (supported_cells[has_set ? FF_SDFFCE1 : FF_SDFFCE0] & initmask)) { + // Just add enable. + sig_e = State::S1; + ff_type = has_set ? FF_SDFFCE1 : FF_SDFFCE0; + break; + } + if (has_en && (supported_cells[has_set ? FF_SDFFCE1 : FF_SDFFCE0] & initmask)) { + // Convert sdffe to sdffce + if (!(ff_neg & NEG_E)) { + if (!(ff_neg & NEG_R)) + sig_e = cell->module->OrGate(NEW_ID, sig_e, sig_r); + else + sig_e = cell->module->OrnotGate(NEW_ID, sig_e, sig_r); + } else { + if (!(ff_neg & NEG_R)) + sig_e = cell->module->AndnotGate(NEW_ID, sig_e, sig_r); + else + sig_e = cell->module->AndGate(NEW_ID, sig_e, sig_r); + } + ff_type = has_set ? FF_SDFFCE1 : FF_SDFFCE0; + break; + } + if (has_en && (supported_cells[has_set ? FF_SDFF1 : FF_SDFF0] & initmask)) { + // Unmap enable. + ff_type = has_set ? FF_SDFF1 : FF_SDFF0; + goto unmap_enable; + } + log_assert(!((has_set ? supported_sdff1 : supported_sdff0) & initmask)); + } else { + if ((has_set ? supported_sdff1 : supported_sdff0) & initmask) { + // Convert sdffce to sdffe, which may be further converted to sdff. + if (!(ff_neg & NEG_R)) { + if (!(ff_neg & NEG_E)) + sig_r = cell->module->AndGate(NEW_ID, sig_r, sig_e); + else + sig_r = cell->module->AndnotGate(NEW_ID, sig_r, sig_e); + } else { + if (!(ff_neg & NEG_E)) + sig_r = cell->module->OrnotGate(NEW_ID, sig_r, sig_e); + else + sig_r = cell->module->OrGate(NEW_ID, sig_r, sig_e); + } + ff_type = has_set ? FF_SDFFE1 : FF_SDFFE0; + continue; + } + } + // Alright, so this particular combination of initval and + // resetval is not natively supported. First, try flipping + // them both to see whether this helps. + if ((has_set ? supported_sdff0 : supported_sdff1) & flip_initmask(initmask)) { + // Checks out, do it. + ff_type = has_ce ? (has_set ? FF_SDFFCE0 : FF_SDFFCE1) : has_en ? (has_set ? FF_SDFFE0 : FF_SDFFE1) : (has_set ? FF_SDFF0 : FF_SDFF1); + goto flip_dqi; + } + + // Nope. No way to get SDFF* of the right kind, so unmap it. + // For SDFFE, the enable has to be unmapped first. + if (has_en) { + ff_type = has_set ? FF_SDFF1 : FF_SDFF0; + goto unmap_enable; + } +unmap_srst: + if (has_ce) + ff_type = FF_DFFE; + else + ff_type = FF_DFF; + if (ff_neg & NEG_R) + sig_d = cell->module->MuxGate(NEW_ID, has_set ? State::S1 : State::S0, sig_d[0], sig_r[0]); + else + sig_d = cell->module->MuxGate(NEW_ID, sig_d[0], has_set ? State::S1 : State::S0, sig_r[0]); + ff_neg &= ~NEG_R; + sig_r = SigSpec(); + kill_srst = false; + continue; + } else { + log_assert(0); + } + } +cell_ok: + + if (!(supported_cells_neg[ff_type][ff_neg] & initmask)) { + // Cell is supported, but not with those polarities. + // Will need to add some inverters. + + // Find the smallest value that xored with the neg mask + // results in a supported one — this results in preferentially + // inverting resets before clocks, etc. + int xneg; + for (xneg = 0; xneg < NUM_NEG; xneg++) + if (supported_cells_neg[ff_type][ff_neg ^ xneg] & initmask) + break; + log_assert(xneg < NUM_NEG); + if (xneg & NEG_R) + sig_r = cell->module->NotGate(NEW_ID, sig_r[0]); + if (xneg & NEG_S) + sig_s = cell->module->NotGate(NEW_ID, sig_s[0]); + if (xneg & NEG_E) + sig_e = cell->module->NotGate(NEW_ID, sig_e[0]); + if (xneg & NEG_C) + sig_c = cell->module->NotGate(NEW_ID, sig_c[0]); + ff_neg ^= xneg; + } + + cell->unsetPort(ID::D); + cell->unsetPort(ID::Q); + cell->unsetPort(ID::C); + cell->unsetPort(ID::E); + cell->unsetPort(ID::S); + cell->unsetPort(ID::R); + switch (ff_type) { + case FF_DFF: + cell->type = IdString(stringf("$_DFF_%c_", + (ff_neg & NEG_C) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + break; + case FF_DFFE: + cell->type = IdString(stringf("$_DFFE_%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_E) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::E, sig_e); + break; + case FF_ADFF0: + case FF_ADFF1: + cell->type = IdString(stringf("$_DFF_%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_type == FF_ADFF1) ? '1' : '0' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::R, sig_r); + break; + case FF_ADFFE0: + case FF_ADFFE1: + cell->type = IdString(stringf("$_DFFE_%c%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_type == FF_ADFFE1) ? '1' : '0', + (ff_neg & NEG_E) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::E, sig_e); + cell->setPort(ID::R, sig_r); + break; + case FF_DFFSR: + cell->type = IdString(stringf("$_DFFSR_%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_S) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::S, sig_s); + cell->setPort(ID::R, sig_r); + break; + case FF_DFFSRE: + cell->type = IdString(stringf("$_DFFSRE_%c%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_S) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_neg & NEG_E) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::E, sig_e); + cell->setPort(ID::S, sig_s); + cell->setPort(ID::R, sig_r); + break; + case FF_SDFF0: + case FF_SDFF1: + cell->type = IdString(stringf("$_SDFF_%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_type == FF_SDFF1) ? '1' : '0' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::R, sig_r); + break; + case FF_SDFFE0: + case FF_SDFFE1: + cell->type = IdString(stringf("$_SDFFE_%c%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_type == FF_SDFFE1) ? '1' : '0', + (ff_neg & NEG_E) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::E, sig_e); + cell->setPort(ID::R, sig_r); + break; + case FF_SDFFCE0: + case FF_SDFFCE1: + cell->type = IdString(stringf("$_SDFFCE_%c%c%c%c_", + (ff_neg & NEG_C) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_type == FF_SDFFCE1) ? '1' : '0', + (ff_neg & NEG_E) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::C, sig_c); + cell->setPort(ID::E, sig_e); + cell->setPort(ID::R, sig_r); + break; + case FF_DLATCH: + cell->type = IdString(stringf("$_DLATCH_%c_", + (ff_neg & NEG_E) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::E, sig_e); + break; + case FF_ADLATCH0: + case FF_ADLATCH1: + cell->type = IdString(stringf("$_DLATCH_%c%c%c_", + (ff_neg & NEG_E) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P', + (ff_type == FF_ADLATCH1) ? '1' : '0' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::E, sig_e); + cell->setPort(ID::R, sig_r); + break; + case FF_DLATCHSR: + cell->type = IdString(stringf("$_DLATCHSR_%c%c%c_", + (ff_neg & NEG_E) ? 'N' : 'P', + (ff_neg & NEG_S) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P' + )); + cell->setPort(ID::D, sig_d); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::E, sig_e); + cell->setPort(ID::S, sig_s); + cell->setPort(ID::R, sig_r); + break; + case FF_SR: + cell->type = IdString(stringf("$_SR_%c%c_", + (ff_neg & NEG_S) ? 'N' : 'P', + (ff_neg & NEG_R) ? 'N' : 'P' + )); + cell->setPort(ID::Q, sig_q); + cell->setPort(ID::S, sig_s); + cell->setPort(ID::R, sig_r); + break; + default: + log_assert(0); + } + return; + +error: + log_error("FF %s.%s (type %s) cannot be legalized: %s\n", log_id(cell->module->name), log_id(cell->name), log_id(cell->type), reason); + } + + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + + log_header(design, "Executing DFFLEGALIZE pass (convert FFs to types supported by the target).\n"); + + for (int i = 0; i < NUM_FFTYPES; i++) { + for (int j = 0; j < NUM_NEG; j++) + supported_cells_neg[i][j] = 0; + supported_cells[i] = 0; + } + mince = 0; + minsrst = 0; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-cell" && argidx + 2 < args.size()) { + std::string celltype = args[++argidx]; + std::string inittype = args[++argidx]; + enum FfType ff_type[2] = {NUM_FFTYPES, NUM_FFTYPES}; + char pol_c = 0; + char pol_e = 0; + char pol_s = 0; + char pol_r = 0; + char srval = 0; + if (celltype.substr(0, 5) == "$_SR_" && celltype.size() == 8 && celltype[7] == '_') { + ff_type[0] = FF_SR; + pol_s = celltype[5]; + pol_r = celltype[6]; + } else if (celltype.substr(0, 6) == "$_DFF_" && celltype.size() == 8 && celltype[7] == '_') { + ff_type[0] = FF_DFF; + pol_c = celltype[6]; + } else if (celltype.substr(0, 7) == "$_DFFE_" && celltype.size() == 10 && celltype[9] == '_') { + ff_type[0] = FF_DFFE; + pol_c = celltype[7]; + pol_e = celltype[8]; + } else if (celltype.substr(0, 6) == "$_DFF_" && celltype.size() == 10 && celltype[9] == '_') { + ff_type[0] = FF_ADFF0; + ff_type[1] = FF_ADFF1; + pol_c = celltype[6]; + pol_r = celltype[7]; + srval = celltype[8]; + } else if (celltype.substr(0, 7) == "$_DFFE_" && celltype.size() == 12 && celltype[11] == '_') { + ff_type[0] = FF_ADFFE0; + ff_type[1] = FF_ADFFE1; + pol_c = celltype[7]; + pol_r = celltype[8]; + srval = celltype[9]; + pol_e = celltype[10]; + } else if (celltype.substr(0, 8) == "$_DFFSR_" && celltype.size() == 12 && celltype[11] == '_') { + ff_type[0] = FF_DFFSR; + pol_c = celltype[8]; + pol_s = celltype[9]; + pol_r = celltype[10]; + } else if (celltype.substr(0, 9) == "$_DFFSRE_" && celltype.size() == 14 && celltype[13] == '_') { + ff_type[0] = FF_DFFSRE; + pol_c = celltype[9]; + pol_s = celltype[10]; + pol_r = celltype[11]; + pol_e = celltype[12]; + } else if (celltype.substr(0, 7) == "$_SDFF_" && celltype.size() == 11 && celltype[10] == '_') { + ff_type[0] = FF_SDFF0; + ff_type[1] = FF_SDFF1; + pol_c = celltype[7]; + pol_r = celltype[8]; + srval = celltype[9]; + } else if (celltype.substr(0, 8) == "$_SDFFE_" && celltype.size() == 13 && celltype[12] == '_') { + ff_type[0] = FF_SDFFE0; + ff_type[1] = FF_SDFFE1; + pol_c = celltype[8]; + pol_r = celltype[9]; + srval = celltype[10]; + pol_e = celltype[11]; + } else if (celltype.substr(0, 9) == "$_SDFFCE_" && celltype.size() == 14 && celltype[13] == '_') { + ff_type[0] = FF_SDFFCE0; + ff_type[1] = FF_SDFFCE1; + pol_c = celltype[9]; + pol_r = celltype[10]; + srval = celltype[11]; + pol_e = celltype[12]; + } else if (celltype.substr(0, 9) == "$_DLATCH_" && celltype.size() == 11 && celltype[10] == '_') { + ff_type[0] = FF_DLATCH; + pol_e = celltype[9]; + } else if (celltype.substr(0, 9) == "$_DLATCH_" && celltype.size() == 13 && celltype[12] == '_') { + ff_type[0] = FF_ADLATCH0; + ff_type[1] = FF_ADLATCH1; + pol_e = celltype[9]; + pol_r = celltype[10]; + srval = celltype[11]; + } else if (celltype.substr(0, 11) == "$_DLATCHSR_" && celltype.size() == 15 && celltype[14] == '_') { + ff_type[0] = FF_DLATCHSR; + pol_e = celltype[11]; + pol_s = celltype[12]; + pol_r = celltype[13]; + } else { +unrecognized: + log_error("unrecognized cell type %s.\n", celltype.c_str()); + } + int mask = 0; + int match = 0; + for (auto pair : { + std::make_pair(pol_c, NEG_C), + std::make_pair(pol_e, NEG_E), + std::make_pair(pol_s, NEG_S), + std::make_pair(pol_r, NEG_R), + }) { + if (pair.first == 'N') { + mask |= pair.second; + match |= pair.second; + } else if (pair.first == 'P' || pair.first == 0) { + mask |= pair.second; + } else if (pair.first != '?') { + goto unrecognized; + } + } + if (srval == '0') { + ff_type[1] = NUM_FFTYPES; + } else if (srval == '1') { + ff_type[0] = NUM_FFTYPES; + } else if (srval != 0 && srval != '?') { + goto unrecognized; + } + for (int i = 0; i < 2; i++) { + if (ff_type[i] == NUM_FFTYPES) + continue; + int initmask; + if (inittype == "x") { + initmask = INIT_X; + } else if (inittype == "0") { + initmask = INIT_X | INIT_0; + } else if (inittype == "1") { + initmask = INIT_X | INIT_1; + } else if (inittype == "r") { + if (srval == 0) + log_error("init type r not valid for cell type %s.\n", celltype.c_str()); + if (i == 0) + initmask = INIT_X | INIT_0; + else + initmask = INIT_X | INIT_1; + } else if (inittype == "01") { + initmask = INIT_X | INIT_0 | INIT_1; + } else { + log_error("unrecognized init type %s for cell type %s.\n", inittype.c_str(), celltype.c_str()); + } + for (int neg = 0; neg < NUM_NEG; neg++) + if ((neg & mask) == match) + supported_cells_neg[ff_type[i]][neg] |= initmask; + supported_cells[ff_type[i]] |= initmask; + } + continue; + } else if (args[argidx] == "-mince" && argidx + 1 < args.size()) { + mince = atoi(args[++argidx].c_str()); + continue; + } else if (args[argidx] == "-minsrst" && argidx + 1 < args.size()) { + minsrst = atoi(args[++argidx].c_str()); + continue; + } + break; + } + extra_args(args, argidx, design); + supported_dffsr = supported_cells[FF_DFFSR] | supported_cells[FF_DFFSRE]; + supported_adff0 = supported_cells[FF_ADFF0] | supported_cells[FF_ADFFE0] | supported_dffsr; + supported_adff1 = supported_cells[FF_ADFF1] | supported_cells[FF_ADFFE1] | supported_dffsr; + supported_sdff0 = supported_cells[FF_SDFF0] | supported_cells[FF_SDFFE0] | supported_cells[FF_SDFFCE0]; + supported_sdff1 = supported_cells[FF_SDFF1] | supported_cells[FF_SDFFE1] | supported_cells[FF_SDFFCE1]; + supported_dff = supported_cells[FF_DFF] | supported_cells[FF_DFFE] | supported_dffsr | supported_adff0 | supported_adff1 | supported_sdff0 | supported_sdff1; + supported_sr = supported_dffsr | supported_cells[FF_DLATCHSR] | supported_cells[FF_SR] | supported_cells[FF_ADLATCH0] | flip_initmask(supported_cells[FF_ADLATCH1]); + supported_dlatch = supported_cells[FF_DLATCH] | supported_cells[FF_ADLATCH0] | supported_cells[FF_ADLATCH1] | supported_cells[FF_DLATCHSR]; + + for (auto module : design->selected_modules()) + { + sigmap.set(module); + initbits.clear(); + + 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)); + } + } + + if (mince || minsrst) { + ce_used.clear(); + srst_used.clear(); + + for (auto cell : module->cells()) { + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; + + if (cell->hasPort(ID::C) && cell->hasPort(ID::E)) { + SigSpec sig = cell->getPort(ID::E); + // Do not count const enable signals. + if (GetSize(sig) == 1 && sig[0].wire) + ce_used[sig[0]]++; + } + if (cell->type.str().substr(0, 6) == "$_SDFF") { + SigSpec sig = cell->getPort(ID::R); + // Do not count const srst signals. + if (GetSize(sig) == 1 && sig[0].wire) + srst_used[sig[0]]++; + } + } + } + + // First gather FF cells, then iterate over them later. + // We may need to split an FF into several cells. + std::vector<Cell *> ff_cells; + + for (auto cell : module->selected_cells()) + { + // Early exit for non-FFs. + if (!RTLIL::builtin_ff_cell_types().count(cell->type)) + continue; + + ff_cells.push_back(cell); + } + + for (auto cell: ff_cells) + handle_ff(cell); + } + + sigmap.clear(); + initbits.clear(); + ce_used.clear(); + srst_used.clear(); + } +} DffLegalizePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/dfflibmap.cc b/passes/techmap/dfflibmap.cc index aa344cf8a..c189d649b 100644 --- a/passes/techmap/dfflibmap.cc +++ b/passes/techmap/dfflibmap.cc @@ -409,11 +409,11 @@ static void map_sr_to_arst(IdString from, IdString to) if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) return; - char from_clk_pol YS_ATTRIBUTE(unused) = from[8]; + char from_clk_pol = from[8]; char from_set_pol = from[9]; char from_clr_pol = from[10]; - char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; - char to_rst_pol YS_ATTRIBUTE(unused) = to[7]; + char to_clk_pol = to[6]; + char to_rst_pol = to[7]; char to_rst_val = to[8]; log_assert(from_clk_pol == to_clk_pol); @@ -455,9 +455,9 @@ static void map_adff_to_dff(IdString from, IdString to) if (!cell_mappings.count(from) || cell_mappings.count(to) > 0) return; - char from_clk_pol YS_ATTRIBUTE(unused) = from[6]; + char from_clk_pol = from[6]; char from_rst_pol = from[7]; - char to_clk_pol YS_ATTRIBUTE(unused) = to[6]; + char to_clk_pol = to[6]; log_assert(from_clk_pol == to_clk_pol); @@ -549,7 +549,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare struct DfflibmapPass : public Pass { DfflibmapPass() : Pass("dfflibmap", "technology mapping of flip-flops") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" dfflibmap [-prepare] -liberty <file> [selection]\n"); @@ -565,7 +565,7 @@ struct DfflibmapPass : public Pass { log("liberty file.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n"); diff --git a/passes/techmap/extract.cc b/passes/techmap/extract.cc index f29044790..7278cb680 100644 --- a/passes/techmap/extract.cc +++ b/passes/techmap/extract.cc @@ -345,7 +345,7 @@ bool compareSortNeedleList(RTLIL::Module *left, RTLIL::Module *right) struct ExtractPass : public Pass { ExtractPass() : Pass("extract", "find subcircuits and replace them with cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -433,7 +433,7 @@ struct ExtractPass : public Pass { log("See 'help techmap' for a pass that does the opposite thing.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing EXTRACT pass (map subcircuits to cells).\n"); log_push(); diff --git a/passes/techmap/extract_counter.cc b/passes/techmap/extract_counter.cc index 68b338143..56b2ea584 100644 --- a/passes/techmap/extract_counter.cc +++ b/passes/techmap/extract_counter.cc @@ -760,7 +760,7 @@ void counter_worker( struct ExtractCounterPass : public Pass { ExtractCounterPass() : Pass("extract_counter", "Extract GreenPak4 counter cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -788,18 +788,18 @@ struct ExtractCounterPass : public Pass { log("\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing EXTRACT_COUNTER pass (find counters in netlist).\n"); pool<RTLIL::IdString> _parallel_cells; CounterExtractionSettings settings { - .parallel_cells = _parallel_cells, - .maxwidth = 64, - .minwidth = 2, - .allow_arst = true, - .allowed_dirs = 0, + _parallel_cells, // parallel_cells + 64, // maxwidth + 2, // minwidth + true, // allow_arst + 0, // allowed_dirs }; size_t argidx; diff --git a/passes/techmap/extract_fa.cc b/passes/techmap/extract_fa.cc index 9023d8687..3fcff01c3 100644 --- a/passes/techmap/extract_fa.cc +++ b/passes/techmap/extract_fa.cc @@ -539,7 +539,7 @@ struct ExtractFaWorker struct ExtractFaPass : public Pass { ExtractFaPass() : Pass("extract_fa", "find and extract full/half adders") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -561,7 +561,7 @@ struct ExtractFaPass : public Pass { log(" Verbose output\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { ExtractFaConfig config; diff --git a/passes/techmap/extract_reduce.cc b/passes/techmap/extract_reduce.cc index 2d63e413f..07b4200cc 100644 --- a/passes/techmap/extract_reduce.cc +++ b/passes/techmap/extract_reduce.cc @@ -34,7 +34,7 @@ struct ExtractReducePass : public Pass ExtractReducePass() : Pass("extract_reduce", "converts gate chains into $reduce_* cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -63,7 +63,7 @@ struct ExtractReducePass : public Pass (cell->type == ID($_XOR_) && gt == GateType::Xor); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing EXTRACT_REDUCE pass.\n"); log_push(); diff --git a/passes/techmap/extractinv.cc b/passes/techmap/extractinv.cc index 269fe5c6c..9b350456f 100644 --- a/passes/techmap/extractinv.cc +++ b/passes/techmap/extractinv.cc @@ -35,7 +35,7 @@ void split_portname_pair(std::string &port1, std::string &port2) struct ExtractinvPass : public Pass { ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -57,7 +57,7 @@ struct ExtractinvPass : public Pass { log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n"); diff --git a/passes/techmap/flatten.cc b/passes/techmap/flatten.cc new file mode 100644 index 000000000..b5f55cffa --- /dev/null +++ b/passes/techmap/flatten.cc @@ -0,0 +1,333 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/utils.h" +#include "kernel/sigtools.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +IdString concat_name(RTLIL::Cell *cell, IdString object_name) +{ + if (object_name[0] == '\\') + return stringf("%s.%s", cell->name.c_str(), object_name.c_str() + 1); + else { + std::string object_name_str = object_name.str(); + if (object_name_str.substr(0, 8) == "$flatten") + object_name_str.erase(0, 8); + return stringf("$flatten%s.%s", cell->name.c_str(), object_name_str.c_str()); + } +} + +template<class T> +IdString map_name(RTLIL::Cell *cell, T *object) +{ + return cell->module->uniquify(concat_name(cell, object->name)); +} + +template<class T> +void map_attributes(RTLIL::Cell *cell, T *object, IdString orig_object_name) +{ + if (object->has_attribute(ID::src)) + object->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + + // Preserve original names via the hdlname attribute, but only for objects with a fully public name. + if (cell->name[0] == '\\' && (object->has_attribute(ID::hdlname) || orig_object_name[0] == '\\')) { + std::vector<std::string> hierarchy; + if (object->has_attribute(ID::hdlname)) + hierarchy = object->get_hdlname_attribute(); + else + hierarchy.push_back(orig_object_name.str().substr(1)); + hierarchy.insert(hierarchy.begin(), cell->name.str().substr(1)); + object->set_hdlname_attribute(hierarchy); + } +} + +void map_sigspec(const dict<RTLIL::Wire*, RTLIL::Wire*> &map, RTLIL::SigSpec &sig, RTLIL::Module *into = nullptr) +{ + vector<SigChunk> chunks = sig; + for (auto &chunk : chunks) + if (chunk.wire != nullptr && chunk.wire->module != into) + chunk.wire = map.at(chunk.wire); + sig = chunks; +} + +struct FlattenWorker +{ + bool ignore_wb = false; + + void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, std::vector<RTLIL::Cell*> &new_cells) + { + // Copy the contents of the flattened cell + + dict<IdString, IdString> memory_map; + for (auto &tpl_memory_it : tpl->memories) { + RTLIL::Memory *new_memory = module->addMemory(map_name(cell, tpl_memory_it.second), tpl_memory_it.second); + map_attributes(cell, new_memory, tpl_memory_it.second->name); + memory_map[tpl_memory_it.first] = new_memory->name; + design->select(module, new_memory); + } + + dict<RTLIL::Wire*, RTLIL::Wire*> wire_map; + dict<IdString, IdString> positional_ports; + for (auto tpl_wire : tpl->wires()) { + if (tpl_wire->port_id > 0) + positional_ports.emplace(stringf("$%d", tpl_wire->port_id), tpl_wire->name); + + RTLIL::Wire *new_wire = nullptr; + if (tpl_wire->name[0] == '\\') { + RTLIL::Wire *hier_wire = module->wire(concat_name(cell, tpl_wire->name)); + if (hier_wire != nullptr && hier_wire->get_bool_attribute(ID::hierconn)) { + hier_wire->attributes.erase(ID::hierconn); + if (GetSize(hier_wire) < GetSize(tpl_wire)) { + log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", + log_id(module), log_id(hier_wire), log_id(tpl), log_id(tpl_wire), log_id(module), log_id(cell)); + hier_wire->width = GetSize(tpl_wire); + } + new_wire = hier_wire; + } + } + if (new_wire == nullptr) { + new_wire = module->addWire(map_name(cell, tpl_wire), tpl_wire); + new_wire->port_input = new_wire->port_output = false; + new_wire->port_id = false; + } + + map_attributes(cell, new_wire, tpl_wire->name); + wire_map[tpl_wire] = new_wire; + design->select(module, new_wire); + } + + for (auto &tpl_proc_it : tpl->processes) { + RTLIL::Process *new_proc = module->addProcess(map_name(cell, tpl_proc_it.second), tpl_proc_it.second); + map_attributes(cell, new_proc, tpl_proc_it.second->name); + auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; + new_proc->rewrite_sigspecs(rewriter); + design->select(module, new_proc); + } + + for (auto tpl_cell : tpl->cells()) { + RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell), tpl_cell); + map_attributes(cell, new_cell, tpl_cell->name); + if (new_cell->type.in(ID($memrd), ID($memwr), ID($meminit))) { + IdString memid = new_cell->getParam(ID::MEMID).decode_string(); + new_cell->setParam(ID::MEMID, Const(memory_map.at(memid).str())); + } else if (new_cell->type == ID($mem)) { + IdString memid = new_cell->getParam(ID::MEMID).decode_string(); + new_cell->setParam(ID::MEMID, Const(concat_name(cell, memid).str())); + } + auto rewriter = [&](RTLIL::SigSpec &sig) { map_sigspec(wire_map, sig); }; + new_cell->rewrite_sigspecs(rewriter); + design->select(module, new_cell); + new_cells.push_back(new_cell); + } + + for (auto &tpl_conn_it : tpl->connections()) { + RTLIL::SigSig new_conn = tpl_conn_it; + map_sigspec(wire_map, new_conn.first); + map_sigspec(wire_map, new_conn.second); + module->connect(new_conn); + } + + // Attach port connections of the flattened cell + + SigMap tpl_sigmap(tpl); + pool<SigBit> tpl_driven; + for (auto tpl_cell : tpl->cells()) + for (auto &tpl_conn : tpl_cell->connections()) + if (tpl_cell->output(tpl_conn.first)) + for (auto bit : tpl_sigmap(tpl_conn.second)) + tpl_driven.insert(bit); + for (auto &tpl_conn : tpl->connections()) + for (auto bit : tpl_sigmap(tpl_conn.first)) + tpl_driven.insert(bit); + + SigMap sigmap(module); + for (auto &port_it : cell->connections()) + { + IdString port_name = port_it.first; + if (positional_ports.count(port_name) > 0) + port_name = positional_ports.at(port_name); + if (tpl->wire(port_name) == nullptr || tpl->wire(port_name)->port_id == 0) { + if (port_name.begins_with("$")) + log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", + port_name.c_str(), cell->name.c_str(), tpl->name.c_str()); + continue; + } + + if (GetSize(port_it.second) == 0) + continue; + + RTLIL::Wire *tpl_wire = tpl->wire(port_name); + RTLIL::SigSig new_conn; + if (tpl_wire->port_output && !tpl_wire->port_input) { + new_conn.first = port_it.second; + new_conn.second = tpl_wire; + } else if (!tpl_wire->port_output && tpl_wire->port_input) { + new_conn.first = tpl_wire; + new_conn.second = port_it.second; + } else { + SigSpec sig_tpl = tpl_wire, sig_mod = port_it.second; + for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) { + if (tpl_driven.count(tpl_sigmap(sig_tpl[i]))) { + new_conn.first.append(sig_mod[i]); + new_conn.second.append(sig_tpl[i]); + } else { + new_conn.first.append(sig_tpl[i]); + new_conn.second.append(sig_mod[i]); + } + } + } + map_sigspec(wire_map, new_conn.first, module); + map_sigspec(wire_map, new_conn.second, module); + + if (new_conn.second.size() > new_conn.first.size()) + new_conn.second.remove(new_conn.first.size(), new_conn.second.size() - new_conn.first.size()); + if (new_conn.second.size() < new_conn.first.size()) + new_conn.second.append(RTLIL::SigSpec(RTLIL::State::S0, new_conn.first.size() - new_conn.second.size())); + log_assert(new_conn.first.size() == new_conn.second.size()); + + if (sigmap(new_conn.first).has_const()) + log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", + log_id(module), log_id(cell), log_id(port_it.first), log_signal(new_conn.first), log_signal(new_conn.second)); + + module->connect(new_conn); + } + + module->remove(cell); + } + + void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules) + { + if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb)) + return; + + std::vector<RTLIL::Cell*> worklist = module->selected_cells(); + while (!worklist.empty()) + { + RTLIL::Cell *cell = worklist.back(); + worklist.pop_back(); + + if (!design->has(cell->type)) + continue; + + RTLIL::Module *tpl = design->module(cell->type); + if (tpl->get_blackbox_attribute(ignore_wb)) + continue; + + if (cell->get_bool_attribute(ID::keep_hierarchy) || tpl->get_bool_attribute(ID::keep_hierarchy)) { + log("Keeping %s.%s (found keep_hierarchy attribute).\n", log_id(module), log_id(cell)); + used_modules.insert(tpl); + continue; + } + + log_debug("Flattening %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + // If a design is fully selected and has a top module defined, topological sorting ensures that all cells + // added during flattening are black boxes, and flattening is finished in one pass. However, when flattening + // individual modules, this isn't the case, and the newly added cells might have to be flattened further. + flatten_cell(design, module, cell, tpl, worklist); + } + } +}; + +struct FlattenPass : public Pass { + FlattenPass() : Pass("flatten", "flatten design") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" flatten [options] [selection]\n"); + log("\n"); + log("This pass flattens the design by replacing cells by their implementation. This\n"); + log("pass is very similar to the 'techmap' pass. The only difference is that this\n"); + log("pass is using the current design as mapping library.\n"); + log("\n"); + log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n"); + log("flattened by this command.\n"); + log("\n"); + log(" -wb\n"); + log(" Ignore the 'whitebox' attribute on cell implementations.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) override + { + log_header(design, "Executing FLATTEN pass (flatten design).\n"); + log_push(); + + FlattenWorker worker; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-wb") { + worker.ignore_wb = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + RTLIL::Module *top = nullptr; + if (design->full_selection()) + for (auto module : design->modules()) + if (module->get_bool_attribute(ID::top)) + top = module; + + pool<RTLIL::Module*> used_modules; + if (top == nullptr) + used_modules = design->modules(); + else + used_modules.insert(top); + + TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules; + pool<RTLIL::Module*> worklist = used_modules; + while (!worklist.empty()) { + RTLIL::Module *module = worklist.pop(); + for (auto cell : module->selected_cells()) { + RTLIL::Module *tpl = design->module(cell->type); + if (tpl != nullptr) { + if (topo_modules.database.count(tpl) == 0) + worklist.insert(tpl); + topo_modules.edge(tpl, module); + } + } + } + + if (!topo_modules.sort()) + log_error("Cannot flatten a design containing recursive instantiations.\n"); + + for (auto module : topo_modules.sorted) + worker.flatten_module(design, module, used_modules); + + if (top != nullptr) + for (auto module : design->modules().to_vector()) + if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) { + log("Deleting now unused module %s.\n", log_id(module)); + design->remove(module); + } + + log_pop(); + } +} FlattenPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index 72947237b..dfdbe6b88 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -1470,7 +1470,7 @@ static void split(std::vector<std::string> &tokens, const std::string &text, cha struct FlowmapPass : public Pass { FlowmapPass() : Pass("flowmap", "pack LUTs with FlowMap") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1511,7 +1511,7 @@ struct FlowmapPass : public Pass { log(" explain decisions performed during depth relaxation.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { int order = 3; int minlut = 1; diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index 5aeb5ea79..b808a8d8e 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -55,7 +55,7 @@ void hilomap_worker(RTLIL::SigSpec &sig) struct HilomapPass : public Pass { HilomapPass() : Pass("hilomap", "technology mapping of constant hi- and/or lo-drivers") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" hilomap [options] [selection]\n"); @@ -74,7 +74,7 @@ struct HilomapPass : public Pass { log(" each constant bit.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing HILOMAP pass (mapping to constant drivers).\n"); diff --git a/passes/techmap/insbuf.cc b/passes/techmap/insbuf.cc index 0686c0f2b..a3b5b698d 100644 --- a/passes/techmap/insbuf.cc +++ b/passes/techmap/insbuf.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct InsbufPass : public Pass { InsbufPass() : Pass("insbuf", "insert buffer cells for connected wires") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" insbuf [options] [selection]\n"); @@ -37,7 +37,7 @@ struct InsbufPass : public Pass { log(" call to \"clean\" will remove all $_BUF_ in the design.)\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing INSBUF pass (insert buffer cells for connected wires).\n"); diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index a18d02652..e8530a034 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -34,7 +34,7 @@ void split_portname_pair(std::string &port1, std::string &port2) struct IopadmapPass : public Pass { IopadmapPass() : Pass("iopadmap", "technology mapping of i/o pads (or buffers)") { } - void help() YS_OVERRIDE + void help() override { log("\n"); log(" iopadmap [options] [selection]\n"); @@ -97,7 +97,7 @@ struct IopadmapPass : public Pass { modules_processed.insert(module); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing IOPADMAP pass (mapping inputs/outputs to IO-PAD cells).\n"); diff --git a/passes/techmap/lut2mux.cc b/passes/techmap/lut2mux.cc index 703bf6ff6..f56eff3e5 100644 --- a/passes/techmap/lut2mux.cc +++ b/passes/techmap/lut2mux.cc @@ -56,7 +56,7 @@ int lut2mux(Cell *cell) struct Lut2muxPass : public Pass { Lut2muxPass() : Pass("lut2mux", "convert $lut to $_MUX_") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -65,7 +65,7 @@ struct Lut2muxPass : public Pass { log("This pass converts $lut cells to $_MUX_ gates.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing LUT2MUX pass (convert $lut to $_MUX_).\n"); diff --git a/passes/techmap/maccmap.cc b/passes/techmap/maccmap.cc index 3bb929009..43f2d97f5 100644 --- a/passes/techmap/maccmap.cc +++ b/passes/techmap/maccmap.cc @@ -365,7 +365,7 @@ PRIVATE_NAMESPACE_BEGIN struct MaccmapPass : public Pass { MaccmapPass() : Pass("maccmap", "mapping macc cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -375,7 +375,7 @@ struct MaccmapPass : public Pass { log("is used then the $macc cell is mapped to $add, $sub, etc. cells instead.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { bool unmap_mode = false; diff --git a/passes/techmap/muxcover.cc b/passes/techmap/muxcover.cc index bd049d86d..24109b579 100644 --- a/passes/techmap/muxcover.cc +++ b/passes/techmap/muxcover.cc @@ -623,7 +623,7 @@ struct MuxcoverWorker struct MuxcoverPass : public Pass { MuxcoverPass() : Pass("muxcover", "cover trees of MUX cells with wider MUXes") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -656,7 +656,7 @@ struct MuxcoverPass : public Pass { log(" than <N> different signals.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing MUXCOVER pass (mapping to wider MUXes).\n"); diff --git a/passes/techmap/nlutmap.cc b/passes/techmap/nlutmap.cc index 798d82248..e1ebfcad8 100644 --- a/passes/techmap/nlutmap.cc +++ b/passes/techmap/nlutmap.cc @@ -129,7 +129,7 @@ struct NlutmapWorker struct NlutmapPass : public Pass { NlutmapPass() : Pass("nlutmap", "map to LUTs of different sizes") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -149,7 +149,7 @@ struct NlutmapPass : public Pass { log("to generic logic gates ($_AND_, etc.).\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { NlutmapConfig config; diff --git a/passes/techmap/pmuxtree.cc b/passes/techmap/pmuxtree.cc index 2810b7f2d..b937d3fb0 100644 --- a/passes/techmap/pmuxtree.cc +++ b/passes/techmap/pmuxtree.cc @@ -67,7 +67,7 @@ static SigSpec recursive_mux_generator(Module *module, const SigSpec &sig_data, struct PmuxtreePass : public Pass { PmuxtreePass() : Pass("pmuxtree", "transform $pmux cells to trees of $mux cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -76,7 +76,7 @@ struct PmuxtreePass : public Pass { log("This pass transforms $pmux cells to trees of $mux cells.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing PMUXTREE pass.\n"); diff --git a/passes/techmap/shregmap.cc b/passes/techmap/shregmap.cc index d7a381e0a..237c261ae 100644 --- a/passes/techmap/shregmap.cc +++ b/passes/techmap/shregmap.cc @@ -403,7 +403,7 @@ struct ShregmapWorker struct ShregmapPass : public Pass { ShregmapPass() : Pass("shregmap", "map shift registers") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -461,7 +461,7 @@ struct ShregmapPass : public Pass { log(" map to greenpak4 shift registers.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { ShregmapOptions opts; string clkpol, enpol; diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc index b65b3e972..b9d337da4 100644 --- a/passes/techmap/simplemap.cc +++ b/passes/techmap/simplemap.cc @@ -474,29 +474,93 @@ void simplemap_dffsr(RTLIL::Module *module, RTLIL::Cell *cell) } } -void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell) +void simplemap_dffsre(RTLIL::Module *module, RTLIL::Cell *cell) { int width = cell->parameters.at(ID::WIDTH).as_int(); char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; - char rst_pol = cell->parameters.at(ID::ARST_POLARITY).as_bool() ? 'P' : 'N'; + char set_pol = cell->parameters.at(ID::SET_POLARITY).as_bool() ? 'P' : 'N'; + char clr_pol = cell->parameters.at(ID::CLR_POLARITY).as_bool() ? 'P' : 'N'; + char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; - std::vector<RTLIL::State> rst_val = cell->parameters.at(ID::ARST_VALUE).bits; + RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); + RTLIL::SigSpec sig_s = cell->getPort(ID::SET); + RTLIL::SigSpec sig_r = cell->getPort(ID::CLR); + RTLIL::SigSpec sig_e = cell->getPort(ID::EN); + RTLIL::SigSpec sig_d = cell->getPort(ID::D); + RTLIL::SigSpec sig_q = cell->getPort(ID::Q); + + IdString gate_type = stringf("$_DFFSRE_%c%c%c%c_", clk_pol, set_pol, clr_pol, en_pol); + + for (int i = 0; i < width; i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + gate->setPort(ID::C, sig_clk); + gate->setPort(ID::S, sig_s[i]); + gate->setPort(ID::R, sig_r[i]); + gate->setPort(ID::E, sig_e); + gate->setPort(ID::D, sig_d[i]); + gate->setPort(ID::Q, sig_q[i]); + } +} + +void simplemap_adff_sdff(RTLIL::Module *module, RTLIL::Cell *cell) +{ + int width = cell->parameters.at(ID::WIDTH).as_int(); + bool is_async = cell->type == ID($adff); + char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; + char rst_pol = cell->parameters.at(is_async ? ID::ARST_POLARITY : ID::SRST_POLARITY).as_bool() ? 'P' : 'N'; + const char *type = is_async ? "DFF" : "SDFF"; + + std::vector<RTLIL::State> rst_val = cell->parameters.at(is_async ? ID::ARST_VALUE : ID::SRST_VALUE).bits; while (int(rst_val.size()) < width) rst_val.push_back(RTLIL::State::S0); RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); - RTLIL::SigSpec sig_rst = cell->getPort(ID::ARST); + RTLIL::SigSpec sig_rst = cell->getPort(is_async ? ID::ARST : ID::SRST); + RTLIL::SigSpec sig_d = cell->getPort(ID::D); + RTLIL::SigSpec sig_q = cell->getPort(ID::Q); + + IdString gate_type_0 = stringf("$_%s_%c%c0_", type, clk_pol, rst_pol); + IdString gate_type_1 = stringf("$_%s_%c%c1_", type, clk_pol, rst_pol); + + for (int i = 0; i < width; i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); + gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + gate->setPort(ID::C, sig_clk); + gate->setPort(ID::R, sig_rst); + gate->setPort(ID::D, sig_d[i]); + gate->setPort(ID::Q, sig_q[i]); + } +} + +void simplemap_adffe_sdffe_sdffce(RTLIL::Module *module, RTLIL::Cell *cell) +{ + int width = cell->parameters.at(ID::WIDTH).as_int(); + bool is_async = cell->type == ID($adffe); + char clk_pol = cell->parameters.at(ID::CLK_POLARITY).as_bool() ? 'P' : 'N'; + char rst_pol = cell->parameters.at(is_async ? ID::ARST_POLARITY : ID::SRST_POLARITY).as_bool() ? 'P' : 'N'; + char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; + const char *type = is_async ? "DFFE" : cell->type == ID($sdffe) ? "SDFFE" : "SDFFCE"; + + std::vector<RTLIL::State> rst_val = cell->parameters.at(is_async ? ID::ARST_VALUE : ID::SRST_VALUE).bits; + while (int(rst_val.size()) < width) + rst_val.push_back(RTLIL::State::S0); + + RTLIL::SigSpec sig_clk = cell->getPort(ID::CLK); + RTLIL::SigSpec sig_rst = cell->getPort(is_async ? ID::ARST : ID::SRST); + RTLIL::SigSpec sig_e = cell->getPort(ID::EN); RTLIL::SigSpec sig_d = cell->getPort(ID::D); RTLIL::SigSpec sig_q = cell->getPort(ID::Q); - IdString gate_type_0 = stringf("$_DFF_%c%c0_", clk_pol, rst_pol); - IdString gate_type_1 = stringf("$_DFF_%c%c1_", clk_pol, rst_pol); + IdString gate_type_0 = stringf("$_%s_%c%c0%c_", type, clk_pol, rst_pol, en_pol); + IdString gate_type_1 = stringf("$_%s_%c%c1%c_", type, clk_pol, rst_pol, en_pol); for (int i = 0; i < width; i++) { RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); gate->setPort(ID::C, sig_clk); gate->setPort(ID::R, sig_rst); + gate->setPort(ID::E, sig_e); gate->setPort(ID::D, sig_d[i]); gate->setPort(ID::Q, sig_q[i]); } @@ -522,7 +586,61 @@ void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell) } } -void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers) +void simplemap_adlatch(RTLIL::Module *module, RTLIL::Cell *cell) +{ + int width = cell->parameters.at(ID::WIDTH).as_int(); + char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; + char rst_pol = cell->parameters.at(ID::ARST_POLARITY).as_bool() ? 'P' : 'N'; + + std::vector<RTLIL::State> rst_val = cell->parameters.at(ID::ARST_VALUE).bits; + while (int(rst_val.size()) < width) + rst_val.push_back(RTLIL::State::S0); + + RTLIL::SigSpec sig_en = cell->getPort(ID::EN); + RTLIL::SigSpec sig_rst = cell->getPort(ID::ARST); + RTLIL::SigSpec sig_d = cell->getPort(ID::D); + RTLIL::SigSpec sig_q = cell->getPort(ID::Q); + + IdString gate_type_0 = stringf("$_DLATCH_%c%c0_", en_pol, rst_pol); + IdString gate_type_1 = stringf("$_DLATCH_%c%c1_", en_pol, rst_pol); + + for (int i = 0; i < width; i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, rst_val.at(i) == RTLIL::State::S1 ? gate_type_1 : gate_type_0); + gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + gate->setPort(ID::E, sig_en); + gate->setPort(ID::R, sig_rst); + gate->setPort(ID::D, sig_d[i]); + gate->setPort(ID::Q, sig_q[i]); + } +} + +void simplemap_dlatchsr(RTLIL::Module *module, RTLIL::Cell *cell) +{ + int width = cell->parameters.at(ID::WIDTH).as_int(); + char en_pol = cell->parameters.at(ID::EN_POLARITY).as_bool() ? 'P' : 'N'; + char set_pol = cell->parameters.at(ID::SET_POLARITY).as_bool() ? 'P' : 'N'; + char clr_pol = cell->parameters.at(ID::CLR_POLARITY).as_bool() ? 'P' : 'N'; + + RTLIL::SigSpec sig_en = cell->getPort(ID::EN); + RTLIL::SigSpec sig_s = cell->getPort(ID::SET); + RTLIL::SigSpec sig_r = cell->getPort(ID::CLR); + RTLIL::SigSpec sig_d = cell->getPort(ID::D); + RTLIL::SigSpec sig_q = cell->getPort(ID::Q); + + IdString gate_type = stringf("$_DLATCHSR_%c%c%c_", en_pol, set_pol, clr_pol); + + for (int i = 0; i < width; i++) { + RTLIL::Cell *gate = module->addCell(NEW_ID, gate_type); + gate->add_strpool_attribute(ID::src, cell->get_strpool_attribute(ID::src)); + gate->setPort(ID::E, sig_en); + gate->setPort(ID::S, sig_s[i]); + gate->setPort(ID::R, sig_r[i]); + gate->setPort(ID::D, sig_d[i]); + gate->setPort(ID::Q, sig_q[i]); + } +} + +void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers) { mappers[ID($not)] = simplemap_not; mappers[ID($pos)] = simplemap_pos; @@ -553,13 +671,20 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL mappers[ID($dff)] = simplemap_dff; mappers[ID($dffe)] = simplemap_dffe; mappers[ID($dffsr)] = simplemap_dffsr; - mappers[ID($adff)] = simplemap_adff; + mappers[ID($dffsre)] = simplemap_dffsre; + mappers[ID($adff)] = simplemap_adff_sdff; + mappers[ID($sdff)] = simplemap_adff_sdff; + mappers[ID($adffe)] = simplemap_adffe_sdffe_sdffce; + mappers[ID($sdffe)] = simplemap_adffe_sdffe_sdffce; + mappers[ID($sdffce)] = simplemap_adffe_sdffe_sdffce; mappers[ID($dlatch)] = simplemap_dlatch; + mappers[ID($adlatch)] = simplemap_adlatch; + mappers[ID($dlatchsr)] = simplemap_dlatchsr; } void simplemap(RTLIL::Module *module, RTLIL::Cell *cell) { - static std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; + static dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; static bool initialized_mappers = false; if (!initialized_mappers) { @@ -575,7 +700,7 @@ PRIVATE_NAMESPACE_BEGIN struct SimplemapPass : public Pass { SimplemapPass() : Pass("simplemap", "mapping simple coarse-grain cells") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -587,15 +712,15 @@ struct SimplemapPass : public Pass { log(" $not, $pos, $and, $or, $xor, $xnor\n"); log(" $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor, $reduce_bool\n"); log(" $logic_not, $logic_and, $logic_or, $mux, $tribuf\n"); - log(" $sr, $ff, $dff, $dffsr, $adff, $dlatch\n"); + log(" $sr, $ff, $dff, $dffe, $dffsr, $dffsre, $adff, $adffe, $sdff, $sdffe, $sdffce, $dlatch, $adlatch, $dlatchsr\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n"); extra_args(args, 1, design); - std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; + dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers; simplemap_get_mappers(mappers); for (auto mod : design->modules()) { diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h index c2d73ea79..5091050a1 100644 --- a/passes/techmap/simplemap.h +++ b/passes/techmap/simplemap.h @@ -42,7 +42,7 @@ extern void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell); extern void simplemap(RTLIL::Module *module, RTLIL::Cell *cell); -extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); +extern void simplemap_get_mappers(dict<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers); YOSYS_NAMESPACE_END diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index a554be257..f98d1564a 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -27,7 +27,6 @@ #include <string.h> #include "simplemap.h" -#include "passes/techmap/techmap.inc" YOSYS_NAMESPACE_BEGIN @@ -51,27 +50,23 @@ void apply_prefix(IdString prefix, RTLIL::SigSpec &sig, RTLIL::Module *module) { vector<SigChunk> chunks = sig; for (auto &chunk : chunks) - if (chunk.wire != NULL) { + if (chunk.wire != nullptr) { IdString wire_name = chunk.wire->name; apply_prefix(prefix, wire_name); - log_assert(module->wires_.count(wire_name) > 0); - chunk.wire = module->wires_[wire_name]; + log_assert(module->wire(wire_name) != nullptr); + chunk.wire = module->wire(wire_name); } sig = chunks; } struct TechmapWorker { - std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers; - std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; - std::map<RTLIL::Module*, bool> techmap_do_cache; - std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue; + dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers; + dict<std::pair<IdString, dict<IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache; + dict<RTLIL::Module*, bool> techmap_do_cache; + pool<RTLIL::Module*> module_queue; dict<Module*, SigMap> sigmaps; - pool<IdString> flatten_do_list; - pool<IdString> flatten_done_list; - pool<Cell*> flatten_keep_list; - pool<string> log_msg_cache; struct TechmapWireData { @@ -79,31 +74,20 @@ struct TechmapWorker RTLIL::SigSpec value; }; - typedef std::map<std::string, std::vector<TechmapWireData>> TechmapWires; + typedef dict<IdString, std::vector<TechmapWireData>> TechmapWires; - bool extern_mode; - bool assert_mode; - bool flatten_mode; - bool recursive_mode; - bool autoproc_mode; - bool ignore_wb; - - TechmapWorker() - { - extern_mode = false; - assert_mode = false; - flatten_mode = false; - recursive_mode = false; - autoproc_mode = false; - ignore_wb = false; - } + bool extern_mode = false; + bool assert_mode = false; + bool recursive_mode = false; + bool autoproc_mode = false; + bool ignore_wb = false; std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose) { std::string constmap_info; - std::map<RTLIL::SigBit, std::pair<RTLIL::IdString, int>> connbits_map; + dict<RTLIL::SigBit, std::pair<IdString, int>> connbits_map; - for (auto conn : cell->connections()) + for (auto &conn : cell->connections()) for (int i = 0; i < GetSize(conn.second); i++) { RTLIL::SigBit bit = sigmap(conn.second[i]); if (bit.wire == nullptr) { @@ -117,7 +101,7 @@ struct TechmapWorker constmap_info += stringf("|%s %d %s %d", log_id(conn.first), i, log_id(connbits_map.at(bit).first), connbits_map.at(bit).second); } else { - connbits_map[bit] = std::pair<RTLIL::IdString, int>(conn.first, i); + connbits_map.emplace(bit, std::make_pair(conn.first, i)); constmap_info += stringf("|%s %d", log_id(conn.first), i); } } @@ -129,24 +113,25 @@ struct TechmapWorker { TechmapWires result; - if (module == NULL) + if (module == nullptr) return result; - for (auto &it : module->wires_) { - const char *p = it.first.c_str(); + for (auto w : module->wires()) { + const char *p = w->name.c_str(); if (*p == '$') continue; const char *q = strrchr(p+1, '.'); - p = q ? q+1 : p+1; + if (q) + p = q; - if (!strncmp(p, "_TECHMAP_", 9)) { + if (!strncmp(p, "\\_TECHMAP_", 10)) { TechmapWireData record; - record.wire = it.second; - record.value = it.second; + record.wire = w; + record.value = w; result[p].push_back(record); - it.second->attributes[ID::keep] = RTLIL::Const(1); - it.second->attributes[ID::_techmap_special_] = RTLIL::Const(1); + w->set_bool_attribute(ID::keep); + w->set_bool_attribute(ID::_techmap_special_); } } @@ -165,7 +150,7 @@ struct TechmapWorker if (tpl->processes.size() != 0) { log("Technology map yielded processes:"); for (auto &it : tpl->processes) - log(" %s",RTLIL::id2cstr(it.first)); + log(" %s",log_id(it.first)); log("\n"); if (autoproc_mode) { Pass::call_on_module(tpl->design, tpl, "proc"); @@ -178,89 +163,71 @@ struct TechmapWorker pool<string> extra_src_attrs = cell->get_strpool_attribute(ID::src); orig_cell_name = cell->name.str(); - if (!flatten_mode) { - for (auto &it : tpl->cells_) - if (it.first == ID::_TECHMAP_REPLACE_) { - module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); - break; - } - } + for (auto tpl_cell : tpl->cells()) + if (tpl_cell->name == ID::_TECHMAP_REPLACE_) { + module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); + break; + } dict<IdString, IdString> memory_renames; for (auto &it : tpl->memories) { IdString m_name = it.first; apply_prefix(cell->name, m_name); - RTLIL::Memory *m = new RTLIL::Memory; - m->name = m_name; - m->width = it.second->width; - m->start_offset = it.second->start_offset; - m->size = it.second->size; - m->attributes = it.second->attributes; + RTLIL::Memory *m = module->addMemory(m_name, it.second); if (m->attributes.count(ID::src)) m->add_strpool_attribute(ID::src, extra_src_attrs); - module->memories[m->name] = m; memory_renames[it.first] = m->name; design->select(module, m); } - std::map<RTLIL::IdString, RTLIL::IdString> positional_ports; + dict<IdString, IdString> positional_ports; dict<Wire*, IdString> temp_renamed_wires; pool<SigBit> autopurge_tpl_bits; - for (auto &it : tpl->wires_) + for (auto tpl_w : tpl->wires()) { - if (it.second->port_id > 0) + if (tpl_w->port_id > 0) { - IdString posportname = stringf("$%d", it.second->port_id); - positional_ports[posportname] = it.first; + IdString posportname = stringf("$%d", tpl_w->port_id); + positional_ports.emplace(posportname, tpl_w->name); - if (!flatten_mode && it.second->get_bool_attribute(ID::techmap_autopurge) && - (!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) && + if (tpl_w->get_bool_attribute(ID::techmap_autopurge) && + (!cell->hasPort(tpl_w->name) || !GetSize(cell->getPort(tpl_w->name))) && (!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname)))) { if (sigmaps.count(tpl) == 0) sigmaps[tpl].set(tpl); - for (auto bit : sigmaps.at(tpl)(it.second)) + for (auto bit : sigmaps.at(tpl)(tpl_w)) if (bit.wire != nullptr) autopurge_tpl_bits.insert(bit); } } - IdString w_name = it.second->name; + IdString w_name = tpl_w->name; apply_prefix(cell->name, w_name); RTLIL::Wire *w = module->wire(w_name); if (w != nullptr) { - if (!flatten_mode || !w->get_bool_attribute(ID::hierconn)) { - temp_renamed_wires[w] = w->name; - module->rename(w, NEW_ID); - w = nullptr; - } else { - w->attributes.erase(ID::hierconn); - if (GetSize(w) < GetSize(it.second)) { - log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w), - log_id(tpl), log_id(it.second), log_id(module), log_id(cell)); - w->width = GetSize(it.second); - } - } + temp_renamed_wires[w] = w->name; + module->rename(w, NEW_ID); + w = nullptr; } if (w == nullptr) { - w = module->addWire(w_name, it.second); + w = module->addWire(w_name, tpl_w); w->port_input = false; w->port_output = false; w->port_id = 0; - if (!flatten_mode) - w->attributes.erase(ID::techmap_autopurge); - if (it.second->get_bool_attribute(ID::_techmap_special_)) + w->attributes.erase(ID::techmap_autopurge); + if (tpl_w->get_bool_attribute(ID::_techmap_special_)) w->attributes.clear(); if (w->attributes.count(ID::src)) w->add_strpool_attribute(ID::src, extra_src_attrs); } design->select(module, w); - if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) { - IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), it.second->name.c_str() + strlen("\\_TECHMAP_REPLACE_")); - Wire *replace_w = module->addWire(replace_name, it.second); + if (tpl_w->name.begins_with("\\_TECHMAP_REPLACE_.")) { + IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), tpl_w->name.c_str() + strlen("\\_TECHMAP_REPLACE_")); + Wire *replace_w = module->addWire(replace_name, tpl_w); module->connect(replace_w, w); } } @@ -268,24 +235,23 @@ struct TechmapWorker SigMap tpl_sigmap(tpl); pool<SigBit> tpl_written_bits; - for (auto &it1 : tpl->cells_) - for (auto &it2 : it1.second->connections_) - if (it1.second->output(it2.first)) - for (auto bit : tpl_sigmap(it2.second)) + for (auto tpl_cell : tpl->cells()) + for (auto &conn : tpl_cell->connections()) + if (tpl_cell->output(conn.first)) + for (auto bit : tpl_sigmap(conn.second)) tpl_written_bits.insert(bit); - for (auto &it1 : tpl->connections_) - for (auto bit : tpl_sigmap(it1.first)) + for (auto &conn : tpl->connections()) + for (auto bit : tpl_sigmap(conn.first)) tpl_written_bits.insert(bit); SigMap port_signal_map; - SigSig port_signal_assign; for (auto &it : cell->connections()) { - RTLIL::IdString portname = it.first; + IdString portname = it.first; if (positional_ports.count(portname) > 0) portname = positional_ports.at(portname); - if (tpl->wires_.count(portname) == 0 || tpl->wires_.at(portname)->port_id == 0) { + if (tpl->wire(portname) == nullptr || tpl->wire(portname)->port_id == 0) { if (portname.begins_with("$")) log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str()); continue; @@ -294,7 +260,7 @@ struct TechmapWorker if (GetSize(it.second) == 0) continue; - RTLIL::Wire *w = tpl->wires_.at(portname); + RTLIL::Wire *w = tpl->wire(portname); RTLIL::SigSig c, extra_connect; if (w->port_output && !w->port_input) { @@ -333,76 +299,58 @@ struct TechmapWorker log_assert(c.first.size() == c.second.size()); - if (flatten_mode) - { - // more conservative approach: - // connect internal and external wires - - if (sigmaps.count(module) == 0) - sigmaps[module].set(module); - - if (sigmaps.at(module)(c.first).has_const()) - log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n", - log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second)); + // replace internal wires that are connected to external wires + if (w->port_output && !w->port_input) { + port_signal_map.add(c.second, c.first); + } else + if (!w->port_output && w->port_input) { + port_signal_map.add(c.first, c.second); + } else { module->connect(c); + extra_connect = SigSig(); } - else - { - // approach that yields nicer outputs: - // replace internal wires that are connected to external wires - - if (w->port_output && !w->port_input) { - port_signal_map.add(c.second, c.first); - } else - if (!w->port_output && w->port_input) { - port_signal_map.add(c.first, c.second); - } else { - module->connect(c); - extra_connect = SigSig(); - } - for (auto &attr : w->attributes) { - if (attr.first == ID::src) - continue; - auto lhs = GetSize(extra_connect.first); - auto rhs = GetSize(extra_connect.second); - if (lhs > rhs) - extra_connect.first.remove(rhs, lhs-rhs); - else if (rhs > lhs) - extra_connect.second.remove(lhs, rhs-lhs); - module->connect(extra_connect); - break; - } + for (auto &attr : w->attributes) { + if (attr.first == ID::src) + continue; + auto lhs = GetSize(extra_connect.first); + auto rhs = GetSize(extra_connect.second); + if (lhs > rhs) + extra_connect.first.remove(rhs, lhs-rhs); + else if (rhs > lhs) + extra_connect.second.remove(lhs, rhs-lhs); + module->connect(extra_connect); + break; } } - for (auto &it : tpl->cells_) + for (auto tpl_cell : tpl->cells()) { - IdString c_name = it.second->name.str(); - bool techmap_replace_cell = (!flatten_mode) && (c_name == ID::_TECHMAP_REPLACE_); + IdString c_name = tpl_cell->name; + bool techmap_replace_cell = (c_name == ID::_TECHMAP_REPLACE_); if (techmap_replace_cell) c_name = orig_cell_name; - else if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) + else if (tpl_cell->name.begins_with("\\_TECHMAP_REPLACE_.")) c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_")); else apply_prefix(cell->name, c_name); - RTLIL::Cell *c = module->addCell(c_name, it.second); + RTLIL::Cell *c = module->addCell(c_name, tpl_cell); design->select(module, c); - if (!flatten_mode && c->type.begins_with("\\$")) + if (c->type.begins_with("\\$")) c->type = c->type.substr(1); vector<IdString> autopurge_ports; - for (auto &it2 : c->connections_) + for (auto &conn : c->connections()) { bool autopurge = false; if (!autopurge_tpl_bits.empty()) { - autopurge = GetSize(it2.second) != 0; - for (auto &bit : sigmaps.at(tpl)(it2.second)) + autopurge = GetSize(conn.second) != 0; + for (auto &bit : sigmaps.at(tpl)(conn.second)) if (!autopurge_tpl_bits.count(bit)) { autopurge = false; break; @@ -410,10 +358,12 @@ struct TechmapWorker } if (autopurge) { - autopurge_ports.push_back(it2.first); + autopurge_ports.push_back(conn.first); } else { - apply_prefix(cell->name, it2.second, module); - port_signal_map.apply(it2.second); + RTLIL::SigSpec new_conn = conn.second; + apply_prefix(cell->name, new_conn, module); + port_signal_map.apply(new_conn); + c->setPort(conn.first, std::move(new_conn)); } } @@ -463,8 +413,8 @@ struct TechmapWorker } } - bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells, - const std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> &celltypeMap, bool in_recursion) + bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, pool<RTLIL::Cell*> &handled_cells, + const dict<IdString, pool<IdString>> &celltypeMap, bool in_recursion) { std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping"; @@ -489,13 +439,13 @@ struct TechmapWorker } } - TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells; - std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit; - std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell; + TopoSort<RTLIL::Cell*, IdString::compare_ptr_by_name<RTLIL::Cell>> cells; + dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_inbit; + dict<RTLIL::SigBit, pool<RTLIL::Cell*>> outbit_to_cell; - for (auto cell : module->cells()) + for (auto cell : module->selected_cells()) { - if (!design->selected(module, cell) || handled_cells.count(cell) > 0) + if (handled_cells.count(cell) > 0) continue; std::string cell_type = cell->type.str(); @@ -508,22 +458,6 @@ struct TechmapWorker continue; } - if (flatten_mode) { - bool keepit = cell->get_bool_attribute(ID::keep_hierarchy); - for (auto &tpl_name : celltypeMap.at(cell_type)) - if (map->modules_[tpl_name]->get_bool_attribute(ID::keep_hierarchy)) - keepit = true; - if (keepit) { - if (!flatten_keep_list[cell]) { - log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell)); - flatten_keep_list.insert(cell); - } - if (!flatten_done_list[cell->type]) - flatten_do_list.insert(cell->type); - continue; - } - } - for (auto &conn : cell->connections()) { RTLIL::SigSpec sig = sigmap(conn.second); @@ -533,7 +467,7 @@ struct TechmapWorker continue; for (auto &tpl_name : celltypeMap.at(cell_type)) { - RTLIL::Module *tpl = map->modules_[tpl_name]; + RTLIL::Module *tpl = map->module(tpl_name); RTLIL::Wire *port = tpl->wire(conn.first); if (port && port->port_input) cell_to_inbit[cell].insert(sig.begin(), sig.end()); @@ -566,189 +500,188 @@ struct TechmapWorker for (auto &tpl_name : celltypeMap.at(cell_type)) { - RTLIL::IdString derived_name = tpl_name; - RTLIL::Module *tpl = map->modules_[tpl_name]; - std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end()); + IdString derived_name = tpl_name; + RTLIL::Module *tpl = map->module(tpl_name); + dict<IdString, RTLIL::Const> parameters(cell->parameters); if (tpl->get_blackbox_attribute(ignore_wb)) continue; - if (!flatten_mode) - { - std::string extmapper_name; + std::string extmapper_name; - if (tpl->get_bool_attribute(ID::techmap_simplemap)) - extmapper_name = "simplemap"; + if (tpl->get_bool_attribute(ID::techmap_simplemap)) + extmapper_name = "simplemap"; - if (tpl->get_bool_attribute(ID::techmap_maccmap)) - extmapper_name = "maccmap"; + if (tpl->get_bool_attribute(ID::techmap_maccmap)) + extmapper_name = "maccmap"; - if (tpl->attributes.count(ID::techmap_wrap)) - extmapper_name = "wrap"; + if (tpl->attributes.count(ID::techmap_wrap)) + extmapper_name = "wrap"; - if (!extmapper_name.empty()) - { - cell->type = cell_type; - - if ((extern_mode && !in_recursion) || extmapper_name == "wrap") - { - std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type)); - - for (auto &c : cell->parameters) - m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second)); - - if (extmapper_name == "wrap") - m_name += ":" + sha1(tpl->attributes.at(ID::techmap_wrap).decode_string()); - - RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design; - RTLIL::Module *extmapper_module = extmapper_design->module(m_name); + if (!extmapper_name.empty()) + { + cell->type = cell_type; - if (extmapper_module == nullptr) - { - extmapper_module = extmapper_design->addModule(m_name); - RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell); - - extmapper_cell->set_src_attribute(cell->get_src_attribute()); - - int port_counter = 1; - for (auto &c : extmapper_cell->connections_) { - RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second)); - if (w->name.in(ID::Y, ID::Q)) - w->port_output = true; - else - w->port_input = true; - w->port_id = port_counter++; - c.second = w; - } + if ((extern_mode && !in_recursion) || extmapper_name == "wrap") + { + std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type)); - extmapper_module->fixup_ports(); - extmapper_module->check(); + for (auto &c : cell->parameters) + m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second)); - if (extmapper_name == "simplemap") { - log("Creating %s with simplemap.\n", log_id(extmapper_module)); - if (simplemap_mappers.count(extmapper_cell->type) == 0) - log_error("No simplemap mapper for cell type %s found!\n", log_id(extmapper_cell->type)); - simplemap_mappers.at(extmapper_cell->type)(extmapper_module, extmapper_cell); - extmapper_module->remove(extmapper_cell); - } + if (extmapper_name == "wrap") + m_name += ":" + sha1(tpl->attributes.at(ID::techmap_wrap).decode_string()); - if (extmapper_name == "maccmap") { - log("Creating %s with maccmap.\n", log_id(extmapper_module)); - if (extmapper_cell->type != ID($macc)) - log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type)); - maccmap(extmapper_module, extmapper_cell); - extmapper_module->remove(extmapper_cell); - } + RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design; + RTLIL::Module *extmapper_module = extmapper_design->module(m_name); - if (extmapper_name == "wrap") { - std::string cmd_string = tpl->attributes.at(ID::techmap_wrap).decode_string(); - log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module)); - mkdebug.on(); - Pass::call_on_module(extmapper_design, extmapper_module, cmd_string); - log_continue = true; - } + if (extmapper_module == nullptr) + { + extmapper_module = extmapper_design->addModule(m_name); + RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell); + + extmapper_cell->set_src_attribute(cell->get_src_attribute()); + + int port_counter = 1; + for (auto &c : extmapper_cell->connections_) { + RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second)); + if (w->name.in(ID::Y, ID::Q)) + w->port_output = true; + else + w->port_input = true; + w->port_id = port_counter++; + c.second = w; } - cell->type = extmapper_module->name; - cell->parameters.clear(); + extmapper_module->fixup_ports(); + extmapper_module->check(); - if (!extern_mode || in_recursion) { - tpl = extmapper_module; - goto use_wrapper_tpl; + if (extmapper_name == "simplemap") { + log("Creating %s with simplemap.\n", log_id(extmapper_module)); + if (simplemap_mappers.count(extmapper_cell->type) == 0) + log_error("No simplemap mapper for cell type %s found!\n", log_id(extmapper_cell->type)); + simplemap_mappers.at(extmapper_cell->type)(extmapper_module, extmapper_cell); + extmapper_module->remove(extmapper_cell); } - auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type)); - if (!log_msg_cache.count(msg)) { - log_msg_cache.insert(msg); - log("%s\n", msg.c_str()); - } - log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module)); - } - else - { - auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type)); - if (!log_msg_cache.count(msg)) { - log_msg_cache.insert(msg); - log("%s\n", msg.c_str()); + if (extmapper_name == "maccmap") { + log("Creating %s with maccmap.\n", log_id(extmapper_module)); + if (extmapper_cell->type != ID($macc)) + log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type)); + maccmap(extmapper_module, extmapper_cell); + extmapper_module->remove(extmapper_cell); } - log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str()); - if (extmapper_name == "simplemap") { - if (simplemap_mappers.count(cell->type) == 0) - log_error("No simplemap mapper for cell type %s found!\n", RTLIL::id2cstr(cell->type)); - simplemap_mappers.at(cell->type)(module, cell); + if (extmapper_name == "wrap") { + std::string cmd_string = tpl->attributes.at(ID::techmap_wrap).decode_string(); + log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module)); + mkdebug.on(); + Pass::call_on_module(extmapper_design, extmapper_module, cmd_string); + log_continue = true; } + } - if (extmapper_name == "maccmap") { - if (cell->type != ID($macc)) - log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type)); - maccmap(module, cell); - } + cell->type = extmapper_module->name; + cell->parameters.clear(); - module->remove(cell); - cell = NULL; + if (!extern_mode || in_recursion) { + tpl = extmapper_module; + goto use_wrapper_tpl; } - did_something = true; - mapped_cell = true; - break; + auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type)); + if (!log_msg_cache.count(msg)) { + log_msg_cache.insert(msg); + log("%s\n", msg.c_str()); + } + log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module)); } + else + { + auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type)); + if (!log_msg_cache.count(msg)) { + log_msg_cache.insert(msg); + log("%s\n", msg.c_str()); + } + log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str()); + + if (extmapper_name == "simplemap") { + if (simplemap_mappers.count(cell->type) == 0) + log_error("No simplemap mapper for cell type %s found!\n", log_id(cell->type)); + simplemap_mappers.at(cell->type)(module, cell); + } - for (auto conn : cell->connections()) { - if (conn.first.begins_with("$")) - continue; - if (tpl->wires_.count(conn.first) > 0 && tpl->wires_.at(conn.first)->port_id > 0) - continue; - if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0) - goto next_tpl; - parameters[conn.first] = conn.second.as_const(); + if (extmapper_name == "maccmap") { + if (cell->type != ID($macc)) + log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type)); + maccmap(module, cell); + } + + module->remove(cell); + cell = nullptr; } - if (0) { - next_tpl: + did_something = true; + mapped_cell = true; + break; + } + + for (auto &conn : cell->connections()) { + if (conn.first.begins_with("$")) continue; - } + if (tpl->wire(conn.first) != nullptr && tpl->wire(conn.first)->port_id > 0) + continue; + if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0) + goto next_tpl; + parameters[conn.first] = conn.second.as_const(); + } + + if (0) { + next_tpl: + continue; + } - if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0) - parameters[ID::_TECHMAP_CELLTYPE_] = RTLIL::unescape_id(cell->type); + if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0) + parameters.emplace(ID::_TECHMAP_CELLTYPE_, RTLIL::unescape_id(cell->type)); - for (auto conn : cell->connections()) { - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))) != 0) { - std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector(); - for (auto &bit : v) - bit = RTLIL::SigBit(bit.wire == NULL ? RTLIL::State::S1 : RTLIL::State::S0); - parameters[stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); - } - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))) != 0) { - std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector(); - for (auto &bit : v) - if (bit.wire != NULL) - bit = RTLIL::SigBit(RTLIL::State::Sx); - parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); - } - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(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; - } + for (auto &conn : cell->connections()) { + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first))) != 0) { + std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector(); + for (auto &bit : v) + bit = RTLIL::SigBit(bit.wire == nullptr ? RTLIL::State::S1 : RTLIL::State::S0); + parameters.emplace(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const()); + } + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first))) != 0) { + std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector(); + for (auto &bit : v) + if (bit.wire != nullptr) + bit = RTLIL::SigBit(RTLIL::State::Sx); + 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[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value; } + parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), value); } + } + { int unique_bit_id_counter = 0; - std::map<RTLIL::SigBit, int> unique_bit_id; + dict<RTLIL::SigBit, int> unique_bit_id; unique_bit_id[RTLIL::State::S0] = unique_bit_id_counter++; unique_bit_id[RTLIL::State::S1] = unique_bit_id_counter++; unique_bit_id[RTLIL::State::Sx] = unique_bit_id_counter++; unique_bit_id[RTLIL::State::Sz] = unique_bit_id_counter++; - for (auto conn : cell->connections()) - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) { - for (auto &bit : sigmap(conn.second).to_sigbit_vector()) + for (auto &conn : cell->connections()) + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first))) != 0) { + for (auto &bit : sigmap(conn.second)) if (unique_bit_id.count(bit) == 0) unique_bit_id[bit] = unique_bit_id_counter++; } @@ -763,14 +696,17 @@ struct TechmapWorker if (tpl->avail_parameters.count(ID::_TECHMAP_BITS_CONNMAP_)) parameters[ID::_TECHMAP_BITS_CONNMAP_] = bits; - for (auto conn : cell->connections()) - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) { + for (auto &conn : cell->connections()) + if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first))) != 0) { RTLIL::Const value; - for (auto &bit : sigmap(conn.second).to_sigbit_vector()) { - RTLIL::Const chunk(unique_bit_id.at(bit), bits); - value.bits.insert(value.bits.end(), chunk.bits.begin(), chunk.bits.end()); + for (auto &bit : sigmap(conn.second)) { + int val = unique_bit_id.at(bit); + for (int i = 0; i < bits; i++) { + value.bits.push_back((val & 1) != 0 ? State::S1 : State::S0); + val = val >> 1; + } } - parameters[stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))] = value; + parameters.emplace(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first)), value); } } @@ -778,34 +714,31 @@ struct TechmapWorker use_wrapper_tpl:; // do not register techmap_wrap modules with techmap_cache } else { - std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters); - if (techmap_cache.count(key) > 0) { - tpl = techmap_cache[key]; + std::pair<IdString, dict<IdString, RTLIL::Const>> key(tpl_name, parameters); + auto it = techmap_cache.find(key); + if (it != techmap_cache.end()) { + tpl = it->second; } else { if (parameters.size() != 0) { mkdebug.on(); - derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end())); + derived_name = tpl->derive(map, parameters); tpl = map->module(derived_name); log_continue = true; } - techmap_cache[key] = tpl; + techmap_cache.emplace(std::move(key), tpl); } } - if (flatten_mode) { - techmap_do_cache[tpl] = true; - } else { - RTLIL::Module *constmapped_tpl = map->module(constmap_tpl_name(sigmap, tpl, cell, false)); - if (constmapped_tpl != nullptr) - tpl = constmapped_tpl; - } + RTLIL::Module *constmapped_tpl = map->module(constmap_tpl_name(sigmap, tpl, cell, false)); + if (constmapped_tpl != nullptr) + tpl = constmapped_tpl; if (techmap_do_cache.count(tpl) == 0) { bool keep_running = true; techmap_do_cache[tpl] = true; - std::set<std::string> techmap_wire_names; + pool<IdString> techmap_wire_names; while (keep_running) { @@ -815,11 +748,11 @@ struct TechmapWorker for (auto &it : twd) techmap_wire_names.insert(it.first); - for (auto &it : twd["_TECHMAP_FAIL_"]) { + for (auto &it : twd[ID::_TECHMAP_FAIL_]) { RTLIL::SigSpec value = it.value; if (value.is_fully_const() && value.as_bool()) { log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", - derived_name.c_str(), RTLIL::id2cstr(it.wire->name), log_signal(value)); + derived_name.c_str(), log_id(it.wire->name), log_signal(value)); techmap_do_cache[tpl] = false; } } @@ -829,13 +762,13 @@ struct TechmapWorker for (auto &it : twd) { - if (it.first.compare(0, 12, "_TECHMAP_DO_") != 0 || it.second.empty()) + if (!it.first.begins_with("\\_TECHMAP_DO_") || it.second.empty()) continue; auto &data = it.second.front(); if (!data.value.is_fully_const()) - log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(data.wire->name), log_signal(data.value)); + log_error("Techmap yielded config wire %s with non-const value %s.\n", log_id(data.wire->name), log_signal(data.value)); techmap_wire_names.erase(it.first); @@ -851,7 +784,7 @@ struct TechmapWorker cmd_string = cmd_string.substr(strlen("CONSTMAP; ")); log("Analyzing pattern of constant bits for this cell:\n"); - RTLIL::IdString new_tpl_name = constmap_tpl_name(sigmap, tpl, cell, true); + IdString new_tpl_name = constmap_tpl_name(sigmap, tpl, cell, true); log("Creating constmapped module `%s'.\n", log_id(new_tpl_name)); log_assert(map->module(new_tpl_name) == nullptr); @@ -862,16 +795,16 @@ struct TechmapWorker techmap_do_cache[new_tpl] = true; tpl = new_tpl; - std::map<RTLIL::SigBit, RTLIL::SigBit> port_new2old_map; - std::map<RTLIL::SigBit, RTLIL::SigBit> port_connmap; - std::map<RTLIL::SigBit, RTLIL::SigBit> cellbits_to_tplbits; + dict<RTLIL::SigBit, RTLIL::SigBit> port_new2old_map; + dict<RTLIL::SigBit, RTLIL::SigBit> port_connmap; + dict<RTLIL::SigBit, RTLIL::SigBit> cellbits_to_tplbits; for (auto wire : tpl->wires().to_vector()) { if (!wire->port_input || wire->port_output) continue; - RTLIL::IdString port_name = wire->name; + IdString port_name = wire->name; tpl->rename(wire, NEW_ID); RTLIL::Wire *new_wire = tpl->addWire(port_name, wire); @@ -879,12 +812,12 @@ struct TechmapWorker wire->port_id = 0; for (int i = 0; i < wire->width; i++) { - port_new2old_map[RTLIL::SigBit(new_wire, i)] = RTLIL::SigBit(wire, i); - port_connmap[RTLIL::SigBit(wire, i)] = RTLIL::SigBit(new_wire, i); + port_new2old_map.emplace(RTLIL::SigBit(new_wire, i), RTLIL::SigBit(wire, i)); + port_connmap.emplace(RTLIL::SigBit(wire, i), RTLIL::SigBit(new_wire, i)); } } - for (auto conn : cell->connections()) + for (auto &conn : cell->connections()) for (int i = 0; i < GetSize(conn.second); i++) { RTLIL::SigBit bit = sigmap(conn.second[i]); @@ -926,7 +859,7 @@ struct TechmapWorker log_assert(!strncmp(q, "_TECHMAP_DO_", 12)); std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12); - while (tpl->wires_.count(new_name)) + while (tpl->wire(new_name) != nullptr) new_name += "_"; tpl->rename(data.wire->name, new_name); @@ -937,17 +870,17 @@ struct TechmapWorker TechmapWires twd = techmap_find_special_wires(tpl); for (auto &it : twd) { - if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_") - log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str()); + if (it.first != ID::_TECHMAP_FAIL_ && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.begins_with("\\_TECHMAP_DO_") && !it.first.begins_with("\\_TECHMAP_DONE_")) + log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first)); if (techmap_do_cache[tpl]) for (auto &it2 : it.second) if (!it2.value.is_fully_const()) - log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value)); + log_error("Techmap yielded config wire %s with non-const value %s.\n", log_id(it2.wire->name), log_signal(it2.value)); techmap_wire_names.erase(it.first); } for (auto &it : techmap_wire_names) - log_error("Techmap special wire %s disappeared. This is considered a fatal error.\n", RTLIL::id2cstr(it)); + log_error("Techmap special wire %s disappeared. This is considered a fatal error.\n", log_id(it)); if (recursive_mode) { if (log_continue) { @@ -970,10 +903,10 @@ struct TechmapWorker TechmapWires twd = techmap_find_special_wires(tpl); for (auto &it : twd) { - if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") { + if (it.first.begins_with("\\_TECHMAP_REMOVEINIT_")) { for (auto &it2 : it.second) { auto val = it2.value.as_const(); - auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1)); + auto wirename = RTLIL::escape_id(it.first.substr(21, it.first.size() - 21 - 1)); auto it = cell->connections().find(wirename); if (it != cell->connections().end()) { auto sig = sigmap(it->second); @@ -1015,7 +948,7 @@ struct TechmapWorker } log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(tpl)); techmap_module_worker(design, module, cell, tpl); - cell = NULL; + cell = nullptr; } did_something = true; mapped_cell = true; @@ -1059,7 +992,7 @@ struct TechmapWorker struct TechmapPass : public Pass { TechmapPass() : Pass("techmap", "generic technology mapper") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1219,7 +1152,7 @@ struct TechmapPass : public Pass { log("essentially techmap but using the design itself as map library).\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); @@ -1275,8 +1208,7 @@ struct TechmapPass : public Pass { RTLIL::Design *map = new RTLIL::Design; if (map_files.empty()) { - std::istringstream f(stdcells_code); - Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend); + Frontend::frontend_call(map, nullptr, "+/techmap.v", verilog_frontend); } else { for (auto &fn : map_files) if (fn.compare(0, 1, "%") == 0) { @@ -1285,35 +1217,30 @@ struct TechmapPass : public Pass { log_cmd_error("Can't open saved design `%s'.\n", fn.c_str()+1); } for (auto mod : saved_designs.at(fn.substr(1))->modules()) - if (!map->has(mod->name)) + if (!map->module(mod->name)) map->add(mod->clone()); } else { - std::ifstream f; - rewrite_filename(fn); - f.open(fn.c_str()); - yosys_input_files.insert(fn); - if (f.fail()) - log_cmd_error("Can't open map file `%s'\n", fn.c_str()); - Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "ilang" : verilog_frontend)); + Frontend::frontend_call(map, nullptr, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "ilang" : verilog_frontend)); } } log_header(design, "Continuing TECHMAP pass.\n"); - std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; - for (auto &it : map->modules_) { - if (it.second->attributes.count(ID::techmap_celltype) && !it.second->attributes.at(ID::techmap_celltype).bits.empty()) { - char *p = strdup(it.second->attributes.at(ID::techmap_celltype).decode_string().c_str()); - for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n")) - celltypeMap[RTLIL::escape_id(q)].insert(it.first); + dict<IdString, pool<IdString>> celltypeMap; + for (auto module : map->modules()) { + if (module->attributes.count(ID::techmap_celltype) && !module->attributes.at(ID::techmap_celltype).bits.empty()) { + char *p = strdup(module->attributes.at(ID::techmap_celltype).decode_string().c_str()); + for (char *q = strtok(p, " \t\r\n"); q; q = strtok(nullptr, " \t\r\n")) + celltypeMap[RTLIL::escape_id(q)].insert(module->name); free(p); } else { - string module_name = it.first.str(); - if (it.first.begins_with("\\$")) - module_name = module_name.substr(1); - celltypeMap[module_name].insert(it.first); + IdString module_name = module->name.begins_with("\\$") ? + module->name.substr(1) : module->name.str(); + celltypeMap[module_name].insert(module->name); } } + for (auto &i : celltypeMap) + i.second.sort(RTLIL::sort_by_id_str()); for (auto module : design->modules()) worker.module_queue.insert(module); @@ -1325,7 +1252,7 @@ struct TechmapPass : public Pass { int module_max_iter = max_iter; bool did_something = true; - std::set<RTLIL::Cell*> handled_cells; + pool<RTLIL::Cell*> handled_cells; while (did_something) { did_something = false; if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false)) @@ -1344,100 +1271,4 @@ struct TechmapPass : public Pass { } } TechmapPass; -struct FlattenPass : public Pass { - FlattenPass() : Pass("flatten", "flatten design") { } - void help() YS_OVERRIDE - { - // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| - log("\n"); - log(" flatten [options] [selection]\n"); - log("\n"); - log("This pass flattens the design by replacing cells by their implementation. This\n"); - log("pass is very similar to the 'techmap' pass. The only difference is that this\n"); - log("pass is using the current design as mapping library.\n"); - log("\n"); - log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n"); - log("flattened by this command.\n"); - log("\n"); - log(" -wb\n"); - log(" Ignore the 'whitebox' attribute on cell implementations.\n"); - log("\n"); - } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE - { - log_header(design, "Executing FLATTEN pass (flatten design).\n"); - log_push(); - - TechmapWorker worker; - worker.flatten_mode = true; - - size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-wb") { - worker.ignore_wb = true; - continue; - } - break; - } - extra_args(args, argidx, design); - - - std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; - for (auto module : design->modules()) - celltypeMap[module->name].insert(module->name); - - RTLIL::Module *top_mod = NULL; - if (design->full_selection()) - for (auto mod : design->modules()) - if (mod->get_bool_attribute(ID::top)) - top_mod = mod; - - std::set<RTLIL::Cell*> handled_cells; - if (top_mod != NULL) { - worker.flatten_do_list.insert(top_mod->name); - while (!worker.flatten_do_list.empty()) { - auto mod = design->module(*worker.flatten_do_list.begin()); - while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } - worker.flatten_done_list.insert(mod->name); - worker.flatten_do_list.erase(mod->name); - } - } else { - for (auto mod : vector<Module*>(design->modules())) { - while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } - } - } - - log_suppressed(); - log("No more expansions possible.\n"); - - if (top_mod != NULL) - { - pool<RTLIL::IdString> used_modules, new_used_modules; - new_used_modules.insert(top_mod->name); - while (!new_used_modules.empty()) { - pool<RTLIL::IdString> queue; - queue.swap(new_used_modules); - for (auto modname : queue) - used_modules.insert(modname); - for (auto modname : queue) - for (auto cell : design->module(modname)->cells()) - if (design->module(cell->type) && !used_modules[cell->type]) - new_used_modules.insert(cell->type); - } - - dict<RTLIL::IdString, RTLIL::Module*> new_modules; - for (auto mod : vector<Module*>(design->modules())) - if (used_modules[mod->name] || mod->get_blackbox_attribute(worker.ignore_wb)) { - new_modules[mod->name] = mod; - } else { - log("Deleting now unused module %s.\n", log_id(mod)); - delete mod; - } - design->modules_.swap(new_modules); - } - - log_pop(); - } -} FlattenPass; - PRIVATE_NAMESPACE_END diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index 90f3a9d6f..79ddb4bd7 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -143,7 +143,7 @@ struct TribufWorker { struct TribufPass : public Pass { TribufPass() : Pass("tribuf", "infer tri-state buffers") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -160,7 +160,7 @@ struct TribufPass : public Pass { log(" to non-tristate logic. this option implies -merge.\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { TribufConfig config; diff --git a/passes/techmap/zinit.cc b/passes/techmap/zinit.cc index 74604ba3b..cc0b26bcc 100644 --- a/passes/techmap/zinit.cc +++ b/passes/techmap/zinit.cc @@ -25,7 +25,7 @@ PRIVATE_NAMESPACE_BEGIN struct ZinitPass : public Pass { ZinitPass() : Pass("zinit", "add inverters so all FF are zero-initialized") { } - void help() YS_OVERRIDE + void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -37,7 +37,7 @@ struct ZinitPass : public Pass { log(" also add zero initialization to uninitialized FFs\n"); log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + void execute(std::vector<std::string> args, RTLIL::Design *design) override { bool all_mode = false; @@ -91,20 +91,29 @@ struct ZinitPass : public Pass { // FIXME: It would appear that supporting // $dffsr/$_DFFSR_* would require a new // cell type where S has priority over R - ID($ff), ID($dff), ID($dffe), /*ID($dffsr),*/ ID($adff), + ID($ff), ID($dff), ID($dffe), /*ID($dffsr),*/ ID($adff), ID($adffe), + ID($sdff), ID($sdffe), ID($sdffce), ID($_FF_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), /*ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_),*/ ID($_DFF_N_), ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), ID($_DFF_P_), ID($_DFF_PN0_), ID($_DFF_PN1_), ID($_DFF_PP0_), ID($_DFF_PP1_), // Async set/reset - ID($__DFFE_NN0), ID($__DFFE_NN1), ID($__DFFE_NP0), ID($__DFFE_NP1), - ID($__DFFE_PN0), ID($__DFFE_PN1), ID($__DFFE_PP0), ID($__DFFE_PP1), + ID($_DFFE_NN0P_), ID($_DFFE_NN1P_), ID($_DFFE_NP0P_), ID($_DFFE_NP1P_), + ID($_DFFE_PN0P_), ID($_DFFE_PN1P_), ID($_DFFE_PP0P_), ID($_DFFE_PP1P_), + ID($_DFFE_NN0N_), ID($_DFFE_NN1N_), ID($_DFFE_NP0N_), ID($_DFFE_NP1N_), + ID($_DFFE_PN0N_), ID($_DFFE_PN1N_), ID($_DFFE_PP0N_), ID($_DFFE_PP1N_), // Sync set/reset - ID($__DFFS_NN0_), ID($__DFFS_NN1_), ID($__DFFS_NP0_), ID($__DFFS_NP1_), - ID($__DFFS_PN0_), ID($__DFFS_PN1_), ID($__DFFS_PP0_), ID($__DFFS_PP1_), - ID($__DFFSE_NN0), ID($__DFFSE_NN1), ID($__DFFSE_NP0), ID($__DFFSE_NP1), - ID($__DFFSE_PN0), ID($__DFFSE_PN1), ID($__DFFSE_PP0), ID($__DFFSE_PP1) + ID($_SDFF_NN0_), ID($_SDFF_NN1_), ID($_SDFF_NP0_), ID($_SDFF_NP1_), + ID($_SDFF_PN0_), ID($_SDFF_PN1_), ID($_SDFF_PP0_), ID($_SDFF_PP1_), + ID($_SDFFE_NN0P_), ID($_SDFFE_NN1P_), ID($_SDFFE_NP0P_), ID($_SDFFE_NP1P_), + ID($_SDFFE_PN0P_), ID($_SDFFE_PN1P_), ID($_SDFFE_PP0P_), ID($_SDFFE_PP1P_), + ID($_SDFFE_NN0N_), ID($_SDFFE_NN1N_), ID($_SDFFE_NP0N_), ID($_SDFFE_NP1N_), + ID($_SDFFE_PN0N_), ID($_SDFFE_PN1N_), ID($_SDFFE_PP0N_), ID($_SDFFE_PP1N_), + ID($_SDFFCE_NN0P_), ID($_SDFFCE_NN1P_), ID($_SDFFCE_NP0P_), ID($_SDFFCE_NP1P_), + ID($_SDFFCE_PN0P_), ID($_SDFFCE_PN1P_), ID($_SDFFCE_PP0P_), ID($_SDFFCE_PP1P_), + ID($_SDFFCE_NN0N_), ID($_SDFFCE_NN1N_), ID($_SDFFCE_NP0N_), ID($_SDFFCE_NP1N_), + ID($_SDFFCE_PN0N_), ID($_SDFFCE_PN1N_), ID($_SDFFCE_PP0N_), ID($_SDFFCE_PP1N_) }; for (auto cell : module->selected_cells()) @@ -151,13 +160,20 @@ struct ZinitPass : public Pass { cell->setPort(ID::D, sig_d); cell->setPort(ID::Q, initwire); - if (cell->type == ID($adff)) { + if (cell->type.in(ID($adff), ID($adffe))) { auto val = cell->getParam(ID::ARST_VALUE); for (int i = 0; i < GetSize(initwire); i++) if (initval[i] == State::S1) val[i] = (val[i] == State::S1 ? State::S0 : State::S1); cell->setParam(ID::ARST_VALUE, std::move(val)); } + else if (cell->type.in(ID($sdff), ID($sdffe), ID($sdffce))) { + auto val = cell->getParam(ID::SRST_VALUE); + for (int i = 0; i < GetSize(initwire); i++) + if (initval[i] == State::S1) + val[i] = (val[i] == State::S1 ? State::S0 : State::S1); + cell->setParam(ID::SRST_VALUE, std::move(val)); + } else if (initval == State::S1) { std::string t = cell->type.str(); if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), @@ -165,15 +181,29 @@ struct ZinitPass : public Pass { { t[8] = (t[8] == '0' ? '1' : '0'); } - else if (cell->type.in(ID($__DFFE_NN0), ID($__DFFE_NN1), ID($__DFFE_NP0), ID($__DFFE_NP1), - ID($__DFFE_PN0), ID($__DFFE_PN1), ID($__DFFE_PP0), ID($__DFFE_PP1), - ID($__DFFS_NN0_), ID($__DFFS_NN1_), ID($__DFFS_NP0_), ID($__DFFS_NP1_), - ID($__DFFS_PN0_), ID($__DFFS_PN1_), ID($__DFFS_PP0_), ID($__DFFS_PP1_))) + else if (cell->type.in(ID($_SDFF_NN0_), ID($_SDFF_NN1_), ID($_SDFF_NP0_), ID($_SDFF_NP1_), + ID($_SDFF_PN0_), ID($_SDFF_PN1_), ID($_SDFF_PP0_), ID($_SDFF_PP1_))) + { + t[9] = (t[9] == '0' ? '1' : '0'); + } + else if (cell->type.in(ID($_DFFE_NN0P_), ID($_DFFE_NN1P_), ID($_DFFE_NP0P_), ID($_DFFE_NP1P_), + ID($_DFFE_PN0P_), ID($_DFFE_PN1P_), ID($_DFFE_PP0P_), ID($_DFFE_PP1P_), + ID($_DFFE_NN0N_), ID($_DFFE_NN1N_), ID($_DFFE_NP0N_), ID($_DFFE_NP1N_), + ID($_DFFE_PN0N_), ID($_DFFE_PN1N_), ID($_DFFE_PP0N_), ID($_DFFE_PP1N_))) + { + t[9] = (t[9] == '0' ? '1' : '0'); + } + else if (cell->type.in(ID($_SDFFE_NN0P_), ID($_SDFFE_NN1P_), ID($_SDFFE_NP0P_), ID($_SDFFE_NP1P_), + ID($_SDFFE_PN0P_), ID($_SDFFE_PN1P_), ID($_SDFFE_PP0P_), ID($_SDFFE_PP1P_), + ID($_SDFFE_NN0N_), ID($_SDFFE_NN1N_), ID($_SDFFE_NP0N_), ID($_SDFFE_NP1N_), + ID($_SDFFE_PN0N_), ID($_SDFFE_PN1N_), ID($_SDFFE_PP0N_), ID($_SDFFE_PP1N_))) { t[10] = (t[10] == '0' ? '1' : '0'); } - else if (cell->type.in(ID($__DFFSE_NN0), ID($__DFFSE_NN1), ID($__DFFSE_NP0), ID($__DFFSE_NP1), - ID($__DFFSE_PN0), ID($__DFFSE_PN1), ID($__DFFSE_PP0), ID($__DFFSE_PP1))) + else if (cell->type.in(ID($_SDFFCE_NN0P_), ID($_SDFFCE_NN1P_), ID($_SDFFCE_NP0P_), ID($_SDFFCE_NP1P_), + ID($_SDFFCE_PN0P_), ID($_SDFFCE_PN1P_), ID($_SDFFCE_PP0P_), ID($_SDFFCE_PP1P_), + ID($_SDFFCE_NN0N_), ID($_SDFFCE_NN1N_), ID($_SDFFCE_NP0N_), ID($_SDFFCE_NP1N_), + ID($_SDFFCE_PN0N_), ID($_SDFFCE_PN1N_), ID($_SDFFCE_PP0N_), ID($_SDFFCE_PP1N_))) { t[11] = (t[11] == '0' ? '1' : '0'); } |