diff options
Diffstat (limited to 'passes')
-rw-r--r-- | passes/hierarchy/hierarchy.cc | 76 | ||||
-rw-r--r-- | passes/opt/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/opt/opt_lut_ins.cc | 278 | ||||
-rw-r--r-- | passes/opt/opt_merge.cc | 17 | ||||
-rw-r--r-- | passes/pmgen/xilinx_dsp.cc | 7 | ||||
-rw-r--r-- | passes/sat/clk2fflogic.cc | 83 | ||||
-rw-r--r-- | passes/techmap/Makefile.inc | 1 | ||||
-rw-r--r-- | passes/techmap/abc9.cc | 22 | ||||
-rw-r--r-- | passes/techmap/abc9_exe.cc | 5 | ||||
-rw-r--r-- | passes/techmap/abc9_ops.cc | 437 |
10 files changed, 836 insertions, 91 deletions
diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index d8a628448..fa4a8ea29 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -548,6 +548,19 @@ RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod) return NULL; } +// Find a matching wire for an implicit port connection; traversing generate block scope +RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port) +{ + const std::string &cellname = cell->name.str(); + size_t idx = cellname.size(); + while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) { + Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1)); + if (found != nullptr) + return found; + } + return module->wire(port); +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } void help() YS_OVERRIDE @@ -970,15 +983,71 @@ struct HierarchyPass : public Pass { } } + // Determine default values + dict<IdString, dict<IdString, Const>> defaults_db; if (!nodefaults) { - dict<IdString, dict<IdString, Const>> defaults_db; - for (auto module : design->modules()) for (auto wire : module->wires()) if (wire->port_input && wire->attributes.count("\\defaultvalue")) defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue"); + } + // Process SV implicit wildcard port connections + std::set<Module*> blackbox_derivatives; + std::vector<Module*> design_modules = design->modules(); + for (auto module : design_modules) + { + for (auto cell : module->cells()) + { + if (!cell->get_bool_attribute(ID(wildcard_port_conns))) + continue; + Module *m = design->module(cell->type); + + if (m == nullptr) + log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n", + RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + + // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths + if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } + + auto old_connections = cell->connections(); + for (auto wire : m->wires()) { + // Find ports of the module that aren't explicitly connected + if (!wire->port_input && !wire->port_output) + continue; + if (old_connections.count(wire->name)) + continue; + // Make sure a wire of correct name exists in the parent + Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str()); + + // Missing wires are OK when a default value is set + if (!nodefaults && parent_wire == nullptr && defaults_db.count(cell->type) && defaults_db.at(cell->type).count(wire->name)) + continue; + + if (parent_wire == nullptr) + log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n", + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + if (parent_wire->width != wire->width) + log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n", + parent_wire->width, wire->width, + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + cell->setPort(wire->name, parent_wire); + } + cell->attributes.erase(ID(wildcard_port_conns)); + } + } + + if (!nodefaults) + { for (auto module : design->modules()) for (auto cell : module->cells()) { @@ -1000,9 +1069,6 @@ struct HierarchyPass : public Pass { } } - std::set<Module*> blackbox_derivatives; - std::vector<Module*> design_modules = design->modules(); - for (auto module : design_modules) { pool<Wire*> wand_wor_index; diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 002c1a6a1..3133927bb 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -15,6 +15,7 @@ OBJS += passes/opt/wreduce.o OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o +OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o endif diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc new file mode 100644 index 000000000..cf5248ced --- /dev/null +++ b/passes/opt/opt_lut_ins.cc @@ -0,0 +1,278 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptLutInsPass : public Pass { + OptLutInsPass() : Pass("opt_lut_ins", "discard unused LUT inputs") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_lut_ins [options] [selection]\n"); + log("\n"); + log("This pass removes unused inputs from LUT cells (that is, inputs that can not\n"); + log("influence the output signal given this LUT's value). While such LUTs cannot\n"); + log("be directly emitted by ABC, they can be a result of various post-ABC\n"); + log("transformations, such as mapping wide LUTs (not all sub-LUTs will use the\n"); + log("full set of inputs) or optimizations such as xilinx_dffopt.\n"); + log("\n"); + log(" -tech <technology>\n"); + log(" Instead of generic $lut cells, operate on LUT cells specific\n"); + log(" to the given technology. Valid values are: xilinx, ecp5, gowin.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_LUT_INS pass (discard unused LUT inputs).\n"); + string techname; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-tech" && argidx+1 < args.size()) { + techname = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (techname != "" && techname != "xilinx" && techname != "ecp5" && techname != "gowin") + log_cmd_error("Unsupported technology: '%s'\n", techname.c_str()); + + for (auto module : design->selected_modules()) + { + log("Optimizing LUTs in %s.\n", log_id(module)); + + std::vector<Cell *> remove_cells; + // Gather LUTs. + for (auto cell : module->selected_cells()) + { + if (cell->get_bool_attribute(ID::keep)) + continue; + Const lut; + std::vector<SigBit> inputs; + std::vector<SigBit> output; + bool ignore_const = false; + if (techname == "") { + if (cell->type != ID($lut)) + continue; + inputs = cell->getPort(ID::A).bits(); + output = cell->getPort(ID::Y); + lut = cell->getParam(ID(LUT)); + } else if (techname == "xilinx" || techname == "gowin") { + if (cell->type == ID(LUT1)) { + inputs = { + cell->getPort(ID(I0)), + }; + } else if (cell->type == ID(LUT2)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + }; + } else if (cell->type == ID(LUT3)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + }; + } else if (cell->type == ID(LUT4)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + }; + } else if (cell->type == ID(LUT5)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + cell->getPort(ID(I4)), + }; + } else if (cell->type == ID(LUT6)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + cell->getPort(ID(I4)), + cell->getPort(ID(I5)), + }; + } else { + // Not a LUT. + continue; + } + lut = cell->getParam(ID(INIT)); + if (techname == "xilinx") + output = cell->getPort(ID(O)); + else + output = cell->getPort(ID(F)); + } else if (techname == "ecp5") { + if (cell->type == ID(LUT4)) { + inputs = { + cell->getPort(ID::A), + cell->getPort(ID::B), + cell->getPort(ID(C)), + cell->getPort(ID(D)), + }; + lut = cell->getParam(ID(INIT)); + output = cell->getPort(ID(Z)); + ignore_const = true; + } else { + // Not a LUT. + continue; + } + } + std::vector<int> swizzle; + std::vector<SigBit> new_inputs; + bool doit = false; + for (int i = 0; i < GetSize(inputs); i++) { + SigBit input = inputs[i]; + if (!input.wire) { + if (input.data == State::S1) + swizzle.push_back(-2); + else + swizzle.push_back(-1); + // For ECP5, smaller LUTs are + // implemented as LUT4s with + // extra const inputs. Do not + // consider that to be a reason + // to redo a LUT. + if (!ignore_const) + doit = true; + } else { + bool redundant = true; + for (int j = 0; j < GetSize(lut); j++) { + if (lut[j] != lut[j ^ 1 << i]) + redundant = false; + } + if (redundant) { + swizzle.push_back(-1); + doit = true; + } else { + swizzle.push_back(GetSize(new_inputs)); + new_inputs.push_back(input); + } + } + } + if (!doit) + continue; + log(" Optimizing lut %s (%d -> %d)\n", log_id(cell), GetSize(inputs), GetSize(new_inputs)); + if (techname == "ecp5") { + // Pad the LUT to 4 inputs, adding consts from the front. + int extra = 4 - GetSize(new_inputs); + log_assert(extra >= 0); + if (extra) { + for (int i = 0; i < extra; i++) + new_inputs.insert(new_inputs.begin(), State::S0); + for (auto &swz : swizzle) + if (swz >= 0) + swz += extra; + } + } + Const new_lut(0, 1 << GetSize(new_inputs)); + for (int i = 0; i < GetSize(new_lut); i++) { + int lidx = 0; + for (int j = 0; j < GetSize(inputs); j++) { + int val; + if (swizzle[j] == -2) { + val = 1; + } else if (swizzle[j] == -1) { + val = 0; + } else { + val = (i >> swizzle[j]) & 1; + } + lidx |= val << j; + } + new_lut[i] = lut[lidx]; + } + // For ecp5, do not replace with a const driver — the nextpnr + // packer requires a complete set of LUTs for wide LUT muxes. + if (new_inputs.empty() && techname != "ecp5") { + // const driver. + remove_cells.push_back(cell); + module->connect(output, new_lut[0]); + } else { + if (techname == "") { + cell->setParam(ID(LUT), new_lut); + cell->setParam(ID(WIDTH), GetSize(new_inputs)); + cell->setPort(ID::A, new_inputs); + } else if (techname == "ecp5") { + log_assert(GetSize(new_inputs) == 4); + cell->setParam(ID(INIT), new_lut); + cell->setPort(ID::A, new_inputs[0]); + cell->setPort(ID::B, new_inputs[1]); + cell->setPort(ID(C), new_inputs[2]); + cell->setPort(ID(D), new_inputs[3]); + } else { + // xilinx, gowin + cell->setParam(ID(INIT), new_lut); + if (techname == "xilinx") + log_assert(GetSize(new_inputs) <= 6); + else + log_assert(GetSize(new_inputs) <= 4); + if (GetSize(new_inputs) == 1) + cell->type = ID(LUT1); + else if (GetSize(new_inputs) == 2) + cell->type = ID(LUT2); + else if (GetSize(new_inputs) == 3) + cell->type = ID(LUT3); + else if (GetSize(new_inputs) == 4) + cell->type = ID(LUT4); + else if (GetSize(new_inputs) == 5) + cell->type = ID(LUT5); + else if (GetSize(new_inputs) == 6) + cell->type = ID(LUT6); + else + log_assert(0); + cell->unsetPort(ID(I0)); + cell->unsetPort(ID(I1)); + cell->unsetPort(ID(I2)); + cell->unsetPort(ID(I3)); + cell->unsetPort(ID(I4)); + cell->unsetPort(ID(I5)); + cell->setPort(ID(I0), new_inputs[0]); + if (GetSize(new_inputs) >= 2) + cell->setPort(ID(I1), new_inputs[1]); + if (GetSize(new_inputs) >= 3) + cell->setPort(ID(I2), new_inputs[2]); + if (GetSize(new_inputs) >= 4) + cell->setPort(ID(I3), new_inputs[3]); + if (GetSize(new_inputs) >= 5) + cell->setPort(ID(I4), new_inputs[4]); + if (GetSize(new_inputs) >= 6) + cell->setPort(ID(I5), new_inputs[5]); + } + } + } + for (auto cell : remove_cells) + module->remove(cell); + } + } +} XilinxDffOptPass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index aaea6159e..8823a9061 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -222,7 +222,9 @@ struct OptMergeWorker return true; } - if (cell1->type.begins_with("$") && conn1.count(ID(Q)) != 0) { + if (conn1.count(ID(Q)) != 0 && (cell1->type.begins_with("$dff") || cell1->type.begins_with("$dlatch") || + cell1->type.begins_with("$_DFF") || cell1->type.begins_with("$_DLATCH") || cell1->type.begins_with("$_SR_") || + cell1->type.in("$adff", "$sr", "$ff", "$_FF_"))) { std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort(ID(Q))).to_sigbit_vector(); std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort(ID(Q))).to_sigbit_vector(); for (size_t i = 0; i < q1.size(); i++) @@ -323,6 +325,19 @@ struct OptMergeWorker log_signal(it.second), log_signal(other_sig)); module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); + + if (it.first == ID(Q) && (cell->type.begins_with("$dff") || cell->type.begins_with("$dlatch") || + cell->type.begins_with("$_DFF") || cell->type.begins_with("$_DLATCH") || cell->type.begins_with("$_SR_") || + cell->type.in("$adff", "$sr", "$ff", "$_FF_"))) { + for (auto c : it.second.chunks()) { + auto jt = c.wire->attributes.find(ID(init)); + if (jt == c.wire->attributes.end()) + continue; + for (int i = c.offset; i < c.offset + c.width; i++) + jt->second[i] = State::Sx; + } + dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second))); + } } } log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc index 81c3c57c4..ae7967d7c 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/passes/pmgen/xilinx_dsp.cc @@ -767,6 +767,9 @@ struct XilinxDspPass : public Pass { log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n"); log("device.\n"); log("\n"); + log("This pass is a no-op if the scratchpad variable 'xilinx_dsp.multonly' is set\n"); + log("to 1.\n"); + log("\n"); log("\n"); log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n"); log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n"); @@ -805,6 +808,10 @@ struct XilinxDspPass : public Pass { family = "xcu"; for (auto module : design->selected_modules()) { + + if (design->scratchpad_get_bool("xilinx_dsp.multonly")) + continue; + // Experimental feature: pack $add/$sub cells with // (* use_dsp48="simd" *) into DSP48E1's using its // SIMD feature diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 4bb4aa047..f9e7783a9 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -214,14 +214,38 @@ struct Clk2fflogicPass : public Pass { continue; } - if (cell->type.in("$dff", "$adff", "$dffsr")) + bool word_dff = cell->type.in("$dff", "$adff", "$dffsr"); + if (word_dff || cell->type.in(ID($_DFF_N_), ID($_DFF_P_), + ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_), + ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) { - bool clkpol = cell->parameters["\\CLK_POLARITY"].as_bool(); + bool clkpol; + SigSpec clk; + if (word_dff) { + clkpol = cell->parameters["\\CLK_POLARITY"].as_bool(); + clk = cell->getPort("\\CLK"); + } + else { + if (cell->type.in(ID($_DFF_P_), ID($_DFF_N_), + ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_))) + clkpol = cell->type[6] == 'P'; + else if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) + clkpol = cell->type[8] == 'P'; + else log_abort(); + clk = cell->getPort("\\C"); + } - SigSpec clk = cell->getPort("\\CLK"); Wire *past_clk = module->addWire(NEW_ID); past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0; - module->addFf(NEW_ID, clk, past_clk); + + if (word_dff) + module->addFf(NEW_ID, clk, past_clk); + else + module->addFfGate(NEW_ID, clk, past_clk); SigSpec sig_d = cell->getPort("\\D"); SigSpec sig_q = cell->getPort("\\Q"); @@ -244,8 +268,14 @@ struct Clk2fflogicPass : public Pass { Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d)); Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q)); - module->addFf(NEW_ID, sig_d, past_d); - module->addFf(NEW_ID, sig_q, past_q); + if (word_dff) { + module->addFf(NEW_ID, sig_d, past_d); + module->addFf(NEW_ID, sig_q, past_q); + } + else { + module->addFfGate(NEW_ID, sig_d, past_d); + module->addFfGate(NEW_ID, sig_q, past_q); + } if (cell->type == "$adff") { @@ -266,6 +296,26 @@ struct Clk2fflogicPass : public Pass { module->addMux(NEW_ID, rstval, qval, arst, sig_q); } else + if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_))) + { + SigSpec arst = cell->getPort("\\R"); + SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); + SigBit rstval = (cell->type[8] == '1'); + + Wire *past_arst = module->addWire(NEW_ID); + module->addFfGate(NEW_ID, arst, past_arst); + if (cell->type[7] == 'P') + arst = module->OrGate(NEW_ID, arst, past_arst); + else + arst = module->AndGate(NEW_ID, arst, past_arst); + + if (cell->type[7] == 'P') + module->addMuxGate(NEW_ID, qval, rstval, arst, sig_q); + else + module->addMuxGate(NEW_ID, rstval, qval, arst, sig_q); + } + else if (cell->type == "$dffsr") { SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); @@ -282,9 +332,30 @@ struct Clk2fflogicPass : public Pass { module->addAnd(NEW_ID, qval, clrval, sig_q); } else + if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) + { + SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); + SigSpec setval = cell->getPort("\\S"); + SigSpec clrval = cell->getPort("\\R"); + + if (cell->type[9] != 'P') + setval = module->Not(NEW_ID, setval); + + if (cell->type[10] == 'P') + clrval = module->Not(NEW_ID, clrval); + + qval = module->OrGate(NEW_ID, qval, setval); + module->addAndGate(NEW_ID, qval, clrval, sig_q); + } + else if (cell->type == "$dff") { module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q); } + else + { + module->addMuxGate(NEW_ID, past_q, past_d, clock_edge, sig_q); + } Const initval; bool assign_initval = false; diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 369c9de64..c16db0d57 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -13,6 +13,7 @@ OBJS += passes/techmap/abc9_ops.o ifneq ($(ABCEXTERNAL),) passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif endif diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 2aeda16d6..5ae2fb22a 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -175,6 +175,7 @@ struct Abc9Pass : public ScriptPass std::stringstream exe_cmd; bool dff_mode, cleanup; + std::string box_file; void clear_flags() YS_OVERRIDE { @@ -182,6 +183,7 @@ struct Abc9Pass : public ScriptPass exe_cmd << "abc9_exe"; dff_mode = false; cleanup = true; + box_file.clear(); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -203,7 +205,7 @@ struct Abc9Pass : public ScriptPass std::string arg = args[argidx]; if ((arg == "-exe" || arg == "-script" || arg == "-D" || /* arg == "-S" || */ arg == "-lut" || arg == "-luts" || - arg == "-box" || arg == "-W") && + /*arg == "-box" ||*/ arg == "-W") && argidx+1 < args.size()) { exe_cmd << " " << arg << " " << args[++argidx]; continue; @@ -222,6 +224,10 @@ struct Abc9Pass : public ScriptPass cleanup = false; continue; } + if (arg == "-box" && argidx+1 < args.size()) { + box_file = args[++argidx]; + continue; + } if (arg == "-run" && argidx+1 < args.size()) { size_t pos = args[argidx+1].find(':'); if (pos == std::string::npos) @@ -251,11 +257,12 @@ struct Abc9Pass : public ScriptPass void script() YS_OVERRIDE { if (check_label("pre")) { + run("abc9_ops -check"); run("scc -set_attr abc9_scc_id {}"); if (help_mode) - run("abc9_ops -mark_scc -prep_xaiger [-dff]", "(option for -dff)"); + run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)"); else - run("abc9_ops -mark_scc -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)"); + run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)"); run("select -set abc9_holes A:abc9_holes"); run("flatten -wb @abc9_holes"); run("techmap @abc9_holes"); @@ -269,8 +276,9 @@ struct Abc9Pass : public ScriptPass if (check_label("map")) { if (help_mode) { run("foreach module in selection"); + run(" abc9_ops -write_box [<value from -box>|(null)] <abc-temp-dir>/input.box"); run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); - run(" abc9_exe -cwd <abc-temp-dir> [options]"); + run(" abc9_exe [options] -cwd <abc-temp-dir> -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"); } @@ -296,6 +304,10 @@ struct Abc9Pass : public ScriptPass tempdir_name[0] = tempdir_name[4] = '_'; tempdir_name = make_temp_dir(tempdir_name); + if (box_file.empty()) + run(stringf("abc9_ops -write_box (null) %s/input.box", tempdir_name.c_str())); + else + run(stringf("abc9_ops -write_box %s %s/input.box", box_file.c_str(), tempdir_name.c_str())); run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); @@ -307,7 +319,7 @@ struct Abc9Pass : public ScriptPass active_design->scratchpad_get_int("write_xaiger.num_inputs"), num_outputs); if (num_outputs) { - run(stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str())); + run(stringf("%s -cwd %s -box %s/input.box", exe_cmd.str().c_str(), tempdir_name.c_str(), tempdir_name.c_str())); run(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("abc9_ops -reintegrate"); } diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index 01bf46539..d3db0065c 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -471,7 +471,7 @@ struct Abc9ExePass : public Pass { // handle -lut / -luts args if (!lut_arg.empty()) { string arg = lut_arg; - if (arg.find_first_not_of("0123456789:") == std::string::npos) { + if (arg.find_first_not_of("0123456789:,") == std::string::npos) { size_t pos = arg.find_first_of(':'); int lut_mode = 0, lut_mode2 = 0; if (pos != string::npos) { @@ -510,9 +510,8 @@ struct Abc9ExePass : public Pass { } } - // ABC expects a box file for XAIG if (box_file.empty()) - box_file = "+/dummy.box"; + log_cmd_error("abc9_exe '-box' option is mandatory.\n"); rewrite_filename(box_file); if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 9ad29a8f6..7071f0de4 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -23,6 +23,9 @@ #include "kernel/utils.h" #include "kernel/celltypes.h" +#define ABC9_FLOPS_BASE_ID 8000 +#define ABC9_DELAY_BASE_ID 9000 + USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -33,6 +36,110 @@ 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) +{ + 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) { + if (it == m->attributes.end()) + continue; + auto id = it->second.as_int(); + auto r = box_lookup.insert(std::make_pair(stringf("$__boxid%d", id), m->name)); + if (!r.second) + log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n", + log_id(m), id, log_id(r.first->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 : m->ports) { + auto w = m->wire(port_name); + log_assert(w); + if (w->get_bool_attribute("\\abc9_carry")) { + if (w->port_input) { + if (carry_in != IdString()) + log_error("Module '%s' contains more than one (* abc9_carry *) input port.\n", log_id(m)); + carry_in = port_name; + } + if (w->port_output) { + if (carry_out != IdString()) + log_error("Module '%s' contains more than one (* abc9_carry *) output port.\n", log_id(m)); + carry_out = port_name; + } + } + + auto it = w->attributes.find("\\abc9_arrival"); + if (it != w->attributes.end()) { + int count = 0; + if (it->second.flags == 0) { + if (it->second.as_int() < 0) + log_error("%s.%s has negative arrival value %d!\n", log_id(m), log_id(port_name), + it->second.as_int()); + count++; + } + else + for (const auto &tok : split_tokens(it->second.decode_string())) { + if (tok.find_first_not_of("0123456789") != std::string::npos) + log_error("%s.%s has non-integer arrival value '%s'!\n", log_id(m), log_id(port_name), + tok.c_str()); + if (atoi(tok.c_str()) < 0) + log_error("%s.%s has negative arrival value %s!\n", log_id(m), log_id(port_name), + tok.c_str()); + count++; + } + if (count > 1 && count != GetSize(w)) + log_error("%s.%s is %d bits wide but abc9_arrival = %s has %d value(s)!\n", log_id(m), log_id(port_name), + GetSize(w), log_signal(it->second), count); + } + + it = w->attributes.find("\\abc9_required"); + if (it != w->attributes.end()) { + int count = 0; + if (it->second.flags == 0) { + if (it->second.as_int() < 0) + log_error("%s.%s has negative required value %d!\n", log_id(m), log_id(port_name), + it->second.as_int()); + count++; + } + else + for (const auto &tok : split_tokens(it->second.decode_string())) { + if (tok.find_first_not_of("0123456789") != std::string::npos) + log_error("%s.%s has non-integer required value '%s'!\n", log_id(m), log_id(port_name), + tok.c_str()); + if (atoi(tok.c_str()) < 0) + log_error("%s.%s has negative required value %s!\n", log_id(m), log_id(port_name), + tok.c_str()); + count++; + } + if (count > 1 && count != GetSize(w)) + log_error("%s.%s is %d bits wide but abc9_required = %s has %d value(s)!\n", log_id(m), log_id(port_name), + GetSize(w), log_signal(it->second), count); + } + } + + if (carry_in != IdString() && carry_out == IdString()) + log_error("Module '%s' contains an (* abc9_carry *) input port but no output port.\n", log_id(m)); + if (carry_in == IdString() && carry_out != IdString()) + log_error("Module '%s' contains an (* abc9_carry *) output port but no input port.\n", log_id(m)); + + if (flop) { + int num_outputs = 0; + for (auto port_name : m->ports) { + auto wire = m->wire(port_name); + if (wire->port_output) num_outputs++; + } + if (num_outputs != 1) + log_error("Module '%s' with (* abc9_flop *) has %d outputs (expect 1).\n", log_id(m), num_outputs); + } + } +} + void mark_scc(RTLIL::Module *module) { // For every unique SCC found, (arbitrarily) find the first @@ -169,13 +276,11 @@ void prep_xaiger(RTLIL::Module *module, bool dff) continue; auto inst_module = module->design->module(cell->type); - bool abc9_box = inst_module && inst_module->attributes.count("\\abc9_box_id"); - bool abc9_flop = false; - if (abc9_box) { - abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); - if (abc9_flop && !dff) - continue; + bool abc9_flop = inst_module && inst_module->get_bool_attribute("\\abc9_flop"); + if (abc9_flop && !dff) + continue; + if ((inst_module && inst_module->attributes.count("\\abc9_box_id")) || abc9_flop) { auto r = box_ports.insert(cell->type); if (r.second) { // Make carry in the last PI, and carry out the last PO @@ -185,25 +290,15 @@ void prep_xaiger(RTLIL::Module *module, bool dff) auto w = inst_module->wire(port_name); log_assert(w); if (w->get_bool_attribute("\\abc9_carry")) { - if (w->port_input) { - if (carry_in != IdString()) - log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(inst_module)); + log_assert(w->port_input != w->port_output); + if (w->port_input) carry_in = port_name; - } - if (w->port_output) { - if (carry_out != IdString()) - log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(inst_module)); + else if (w->port_output) carry_out = port_name; - } } else r.first->second.push_back(port_name); } - - if (carry_in != IdString() && carry_out == IdString()) - log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(inst_module)); - if (carry_in == IdString() && carry_out != IdString()) - log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(inst_module)); if (carry_in != IdString()) { r.first->second.push_back(carry_in); r.first->second.push_back(carry_out); @@ -266,22 +361,25 @@ void prep_xaiger(RTLIL::Module *module, bool dff) log_assert(cell); RTLIL::Module* box_module = design->module(cell->type); - if (!box_module || !box_module->attributes.count("\\abc9_box_id")) + if (!box_module || (!box_module->attributes.count("\\abc9_box_id") && !box_module->get_bool_attribute("\\abc9_flop"))) continue; cell->attributes["\\abc9_box_seq"] = box_count++; - IdString derived_name = box_module->derive(design, cell->parameters); - box_module = design->module(derived_name); + IdString derived_type = box_module->derive(design, cell->parameters); + box_module = design->module(derived_type); - auto r = cell_cache.insert(derived_name); + auto r = cell_cache.insert(derived_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("\\whitebox")) { - holes_cell = holes_module->addCell(cell->name, derived_name); + holes_cell = holes_module->addCell(cell->name, derived_type); + + if (box_module->has_processes()) + Pass::call_on_module(design, box_module, "proc"); int box_inputs = 0; for (auto port_name : box_ports.at(cell->type)) { @@ -303,7 +401,7 @@ void prep_xaiger(RTLIL::Module *module, bool dff) } } else if (w->port_output) - conn = holes_module->addWire(stringf("%s.%s", derived_name.c_str(), log_id(port_name)), GetSize(w)); + 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 @@ -342,6 +440,169 @@ void prep_xaiger(RTLIL::Module *module, bool dff) } } +void prep_delays(RTLIL::Design *design) +{ + std::set<int> delays; + pool<Module*> flops; + std::vector<Cell*> cells; + dict<IdString,dict<IdString,std::vector<int>>> requireds_cache; + for (auto module : design->selected_modules()) { + if (module->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(module)); + continue; + } + + cells.clear(); + 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->get_bool_attribute(ID(abc9_flop))) { + IdString derived_type = inst_module->derive(design, cell->parameters); + inst_module = design->module(derived_type); + log_assert(inst_module); + flops.insert(inst_module); + continue; // because all flop required times + // will be captured in the flop box + } + if (inst_module->attributes.count(ID(abc9_box_id))) + continue; + cells.emplace_back(cell); + } + + delays.clear(); + for (auto cell : cells) { + RTLIL::Module* inst_module = module->design->module(cell->type); + log_assert(inst_module); + auto &cell_requireds = requireds_cache[cell->type]; + for (auto &conn : cell->connections_) { + auto port_wire = inst_module->wire(conn.first); + if (!port_wire->port_input) + continue; + + auto r = cell_requireds.insert(conn.first); + auto &requireds = r.first->second; + if (r.second) { + auto it = port_wire->attributes.find("\\abc9_required"); + if (it == port_wire->attributes.end()) + continue; + if (it->second.flags == 0) { + int delay = it->second.as_int(); + delays.insert(delay); + requireds.emplace_back(delay); + } + else + for (const auto &tok : split_tokens(it->second.decode_string())) { + int delay = atoi(tok.c_str()); + delays.insert(delay); + requireds.push_back(delay); + } + } + + if (requireds.empty()) + continue; + + SigSpec O = module->addWire(NEW_ID, GetSize(conn.second)); + auto it = requireds.begin(); + for (int i = 0; i < GetSize(conn.second); ++i) { +#ifndef NDEBUG + if (ys_debug(1)) { + static std::set<std::pair<IdString,IdString>> seen; + if (seen.emplace(cell->type, conn.first).second) log("%s.%s abc9_required = %d\n", log_id(cell->type), log_id(conn.first), requireds[i]); + } +#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), *it); + if (requireds.size() > 1) + it++; + conn.second[i] = O[i]; + } + } + } + + std::stringstream ss; + bool first = true; + for (auto d : delays) { + if (first) + first = false; + else + ss << " "; + ss << d; + } + module->attributes[ID(abc9_delays)] = ss.str(); + } + + int flops_id = ABC9_FLOPS_BASE_ID; + std::stringstream ss; + for (auto flop_module : flops) { + int num_inputs = 0, num_outputs = 0; + for (auto port_name : flop_module->ports) { + auto wire = flop_module->wire(port_name); + if (wire->port_input) num_inputs++; + if (wire->port_output) num_outputs++; + } + log_assert(num_outputs == 1); + + auto r = flop_module->attributes.insert(ID(abc9_box_id)); + if (r.second) + r.first->second = flops_id++; + + ss << log_id(flop_module) << " " << r.first->second.as_int(); + ss << " 1 " << num_inputs+1 << " " << num_outputs << std::endl; + bool first = true; + for (auto port_name : flop_module->ports) { + auto wire = flop_module->wire(port_name); + if (!wire->port_input) + continue; + if (first) + first = false; + else + ss << " "; + ss << wire->attributes.at("\\abc9_required", 0).as_int(); + } + // Last input is 'abc9_ff.Q' + ss << " 0" << std::endl << std::endl; + } + design->scratchpad_set_string("abc9_ops.box.flops", ss.str()); +} + +void write_box(RTLIL::Module *module, const std::string &src, const std::string &dst) { + std::ofstream ofs(dst); + log_assert(ofs.is_open()); + + // Since ABC can only accept one box file, we have to copy + // over the existing box file + if (src != "(null)") { + std::ifstream ifs(src); + ofs << ifs.rdbuf() << std::endl; + ifs.close(); + } + + ofs << module->design->scratchpad_get_string("abc9_ops.box.flops"); + + auto it = module->attributes.find(ID(abc9_delays)); + if (it != module->attributes.end()) { + for (const auto &tok : split_tokens(it->second.decode_string())) { + int d = atoi(tok.c_str()); + ofs << "$__ABC9_DELAY@" << d << " " << ABC9_DELAY_BASE_ID + d << " 0 1 1" << std::endl; + ofs << d << std::endl; + } + module->attributes.erase(it); + } + + if (ofs.tellp() == 0) + ofs << "(dummy) 1 0 0 0"; + + ofs.close(); +} + void reintegrate(RTLIL::Module *module) { auto design = module->design; @@ -363,37 +624,29 @@ void reintegrate(RTLIL::Module *module) continue; auto r = box_ports.insert(m->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 : m->ports) { - auto w = m->wire(port_name); - log_assert(w); - if (w->get_bool_attribute("\\abc9_carry")) { - if (w->port_input) { - if (carry_in != IdString()) - log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m)); - carry_in = port_name; - } - if (w->port_output) { - if (carry_out != IdString()) - log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m)); - carry_out = port_name; - } - } - else - r.first->second.push_back(port_name); - } + if (!r.second) + continue; - if (carry_in != IdString() && carry_out == IdString()) - log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m)); - if (carry_in == IdString() && carry_out != IdString()) - log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m)); - if (carry_in != IdString()) { - r.first->second.push_back(carry_in); - r.first->second.push_back(carry_out); + // 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 : m->ports) { + auto w = m->wire(port_name); + log_assert(w); + if (w->get_bool_attribute("\\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 + 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); } } @@ -465,16 +718,6 @@ void reintegrate(RTLIL::Module *module) } if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { - // Convert buffer into direct connection - if (mapped_cell->type == ID($lut) && - GetSize(mapped_cell->getPort(ID::A)) == 1 && - mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { - SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name)); - SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name)); - module->connect(my_y, my_a); - log_abort(); - continue; - } RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); cell->parameters = mapped_cell->parameters; cell->attributes = mapped_cell->attributes; @@ -506,12 +749,27 @@ void reintegrate(RTLIL::Module *module) } else { RTLIL::Cell *existing_cell = module->cell(mapped_cell->name); - log_assert(existing_cell); + 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)) { + SigBit I = mapped_cell->getPort(ID(i)); + SigBit O = mapped_cell->getPort(ID(o)); + if (I.wire) + I.wire = module->wires_.at(remap_name(I.wire->name)); + log_assert(O.wire); + O.wire = module->wires_.at(remap_name(O.wire->name)); + module->connect(O, I); + continue; + } +#ifndef NDEBUG RTLIL::Module* box_module = design->module(existing_cell->type); - auto it = box_module->attributes.find(ID(abc9_box_id)); - log_assert(it != box_module->attributes.end()); - log_assert(mapped_cell->type == stringf("$__boxid%d", it->second.as_int())); + 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("\\abc9_box_id").as_int())); +#endif mapped_cell->type = existing_cell->type; RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); @@ -539,7 +797,7 @@ void reintegrate(RTLIL::Module *module) } int input_count = 0, output_count = 0; - for (const auto &port_name : box_ports.at(cell->type)) { + for (const auto &port_name : box_ports.at(derived_type)) { RTLIL::Wire *w = box_module->wire(port_name); log_assert(w); @@ -729,6 +987,14 @@ struct Abc9OpsPass : public Pass { log("mapping, and is expected to be called in conjunction with other operations from\n"); log("the `abc9' script pass. Only fully-selected modules are supported.\n"); log("\n"); + log(" -check\n"); + 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_delays\n"); + log(" insert `$__ABC9_DELAY' blackbox cells into the design to account for\n"); + log(" certain delays, e.g. (* abc9_required *) values.\n"); + log("\n"); log(" -mark_scc\n"); log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n"); log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n"); @@ -749,6 +1015,10 @@ struct Abc9OpsPass : public Pass { 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(" -write_box (<src>|(null)) <dst>\n"); + log(" copy the existing box file from <src> (skip if '(null)') and append any\n"); + log(" new box definitions.\n"); + log("\n"); log(" -reintegrate\n"); log(" for each selected module, re-intergrate the module '<module-name>$abc9'\n"); log(" by first recovering ABC9 boxes, and then stitching in the remaining primary\n"); @@ -759,15 +1029,22 @@ struct Abc9OpsPass : public Pass { { 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_xaiger_mode = false; bool reintegrate_mode = false; bool dff_mode = false; + std::string write_box_src, write_box_dst; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; + if (arg == "-check") { + check_mode = true; + continue; + } if (arg == "-mark_scc") { mark_scc_mode = true; continue; @@ -780,6 +1057,17 @@ struct Abc9OpsPass : public Pass { prep_xaiger_mode = true; continue; } + if (arg == "-prep_delays") { + prep_delays_mode = true; + continue; + } + if (arg == "-write_box" && argidx+2 < args.size()) { + write_box_src = args[++argidx]; + write_box_dst = args[++argidx]; + rewrite_filename(write_box_src); + rewrite_filename(write_box_dst); + continue; + } if (arg == "-reintegrate") { reintegrate_mode = true; continue; @@ -792,12 +1080,17 @@ struct Abc9OpsPass : public Pass { } extra_args(args, argidx, design); - if (!(mark_scc_mode || prep_dff_mode || reintegrate_mode)) - log_cmd_error("At least one of -mark_scc, -prep_{xaiger,dff}, -reintegrate must be specified.\n"); + if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || !write_box_src.empty() || reintegrate_mode)) + log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff}, -write_box, -reintegrate must be specified.\n"); if (dff_mode && !prep_xaiger_mode) log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n"); + if (check_mode) + check(design); + if (prep_delays_mode) + prep_delays(design); + for (auto mod : design->selected_modules()) { if (mod->get_bool_attribute("\\abc9_holes")) continue; @@ -810,6 +1103,8 @@ struct Abc9OpsPass : public Pass { if (!design->selected_whole_module(mod)) log_error("Can't handle partially selected module %s!\n", log_id(mod)); + if (!write_box_src.empty()) + write_box(mod, write_box_src, write_box_dst); if (mark_scc_mode) mark_scc(mod); if (prep_dff_mode) |