diff options
50 files changed, 2833 insertions, 1660 deletions
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index a6c87159d..b72dd6890 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -93,7 +93,6 @@ struct XAigerWriter dict<SigBit, int> ordered_outputs; vector<Cell*> box_list; - dict<IdString, std::vector<IdString>> box_ports; int mkgate(int a0, int a1) { @@ -157,7 +156,6 @@ struct XAigerWriter if (wire->get_bool_attribute(ID::keep)) sigmap.add(wire); - for (auto wire : module->wires()) for (int i = 0; i < GetSize(wire); i++) { @@ -175,108 +173,96 @@ struct XAigerWriter undriven_bits.insert(bit); unused_bits.insert(bit); - if (wire->port_input) + bool keep = wire->get_bool_attribute(ID::keep); + if (wire->port_input || keep) input_bits.insert(bit); - if (wire->port_output) { + if (wire->port_output || keep) { if (bit != wirebit) alias_map[wirebit] = bit; output_bits.insert(wirebit); } } - // TODO: Speed up toposort -- ultimately we care about - // box ordering, but not individual AIG cells - dict<SigBit, pool<IdString>> bit_drivers, bit_users; - TopoSort<IdString, RTLIL::sort_by_id_str> toposort; - bool abc9_box_seen = false; - - for (auto cell : module->selected_cells()) { - if (cell->type == "$_NOT_") - { - SigBit A = sigmap(cell->getPort("\\A").as_bit()); - SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); - unused_bits.erase(A); - undriven_bits.erase(Y); - not_map[Y] = A; - toposort.node(cell->name); - bit_users[A].insert(cell->name); - bit_drivers[Y].insert(cell->name); - continue; - } - - if (cell->type == "$_AND_") - { - SigBit A = sigmap(cell->getPort("\\A").as_bit()); - SigBit B = sigmap(cell->getPort("\\B").as_bit()); - SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); - unused_bits.erase(A); - unused_bits.erase(B); - undriven_bits.erase(Y); - and_map[Y] = make_pair(A, B); - toposort.node(cell->name); - bit_users[A].insert(cell->name); - bit_users[B].insert(cell->name); - bit_drivers[Y].insert(cell->name); - continue; - } + dict<IdString,dict<IdString,int>> arrival_cache; + for (auto cell : module->cells()) { + RTLIL::Module* inst_module = module->design->module(cell->type); + if (!cell->has_keep_attr()) { + if (cell->type == "$_NOT_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + undriven_bits.erase(Y); + not_map[Y] = A; + continue; + } - if (cell->type == "$__ABC9_FF_" && - // The presence of an abc9_mergeability attribute indicates - // that we do want to pass this flop to ABC - cell->attributes.count("\\abc9_mergeability")) - { - SigBit D = sigmap(cell->getPort("\\D").as_bit()); - SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); - unused_bits.erase(D); - undriven_bits.erase(Q); - alias_map[Q] = D; - auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell)); - log_assert(r.second); - continue; - } + if (cell->type == "$_AND_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit B = sigmap(cell->getPort("\\B").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + unused_bits.erase(B); + undriven_bits.erase(Y); + and_map[Y] = make_pair(A, B); + continue; + } - RTLIL::Module* inst_module = module->design->module(cell->type); - if (inst_module) { - bool abc9_box = inst_module->attributes.count("\\abc9_box_id"); - bool abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); - if (abc9_box && cell->get_bool_attribute("\\abc9_keep")) - abc9_box = false; - - for (const auto &conn : cell->connections()) { - auto port_wire = inst_module->wire(conn.first); - - if (abc9_box) { - // Ignore inout for the sake of topographical ordering - if (port_wire->port_input && !port_wire->port_output) - for (auto bit : sigmap(conn.second)) - bit_users[bit].insert(cell->name); - if (port_wire->port_output) - for (auto bit : sigmap(conn.second)) - bit_drivers[bit].insert(cell->name); + if (cell->type == "$__ABC9_FF_" && + // The presence of an abc9_mergeability attribute indicates + // that we do want to pass this flop to ABC + cell->attributes.count("\\abc9_mergeability")) + { + SigBit D = sigmap(cell->getPort("\\D").as_bit()); + SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); + unused_bits.erase(D); + undriven_bits.erase(Q); + alias_map[Q] = D; + auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell)); + log_assert(r.second); + if (input_bits.erase(Q)) + log_assert(Q.wire->attributes.count(ID::keep)); + continue; + } + if (inst_module) { + bool abc9_flop = false; + auto it = cell->attributes.find("\\abc9_box_seq"); + if (it != cell->attributes.end()) { + int abc9_box_seq = it->second.as_int(); + if (GetSize(box_list) <= abc9_box_seq) + box_list.resize(abc9_box_seq+1); + box_list[abc9_box_seq] = cell; + // Only flop boxes may have arrival times + abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); if (!abc9_flop) continue; } - if (port_wire->port_output) { - int arrival = 0; - auto it = port_wire->attributes.find("\\abc9_arrival"); - if (it != port_wire->attributes.end()) { - if (it->second.flags != 0) - log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); - arrival = it->second.as_int(); + auto &cell_arrivals = arrival_cache[cell->type]; + for (const auto &conn : cell->connections()) { + auto r = cell_arrivals.insert(conn.first); + auto &arrival = r.first->second; + if (r.second) { + auto port_wire = inst_module->wire(conn.first); + if (port_wire->port_output) { + auto it = port_wire->attributes.find("\\abc9_arrival"); + if (it != port_wire->attributes.end()) { + if (it->second.flags != 0) + log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); + arrival = it->second.as_int(); + } + } } if (arrival) for (auto bit : sigmap(conn.second)) arrival_times[bit] = arrival; } - } - if (abc9_box) { - abc9_box_seen = true; - toposort.node(cell->name); - continue; + if (abc9_flop) + continue; } } @@ -293,6 +279,9 @@ struct XAigerWriter for (auto b : c.second) { Wire *w = b.wire; if (!w) continue; + // Do not add as PO if bit is already a PI + if (input_bits.count(b)) + continue; if (!w->port_output || !cell_known) { SigBit I = sigmap(b); if (I != b) @@ -305,138 +294,54 @@ struct XAigerWriter //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); } - if (abc9_box_seen) { - for (auto &it : bit_users) - if (bit_drivers.count(it.first)) - for (auto driver_cell : bit_drivers.at(it.first)) - for (auto user_cell : it.second) - toposort.edge(driver_cell, user_cell); - -#if 0 - toposort.analyze_loops = true; -#endif - bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); -#if 0 - unsigned i = 0; - for (auto &it : toposort.loops) { - log(" loop %d\n", i++); - for (auto cell_name : it) { - auto cell = module->cell(cell_name); - log_assert(cell); - log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); - } - } -#endif - log_assert(no_loops); - - for (auto cell_name : toposort.sorted) { - RTLIL::Cell *cell = module->cell(cell_name); - log_assert(cell); - - RTLIL::Module* box_module = module->design->module(cell->type); - if (!box_module || !box_module->attributes.count("\\abc9_box_id")) - continue; + dict<IdString, std::vector<IdString>> box_ports; + for (auto cell : box_list) { + log_assert(cell); - bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); + RTLIL::Module* box_module = module->design->module(cell->type); + log_assert(box_module); + log_assert(box_module->attributes.count("\\abc9_box_id")); - auto r = box_ports.insert(cell->type); - 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 : box_module->ports) { - auto w = box_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(box_module)); - 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(box_module)); - 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(box_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(box_module)); - if (carry_in != IdString()) { - r.first->second.push_back(carry_in); - r.first->second.push_back(carry_out); - } - } - - // Fully pad all unused input connections of this box cell with S0 - // Fully pad all undriven output connections of this box cell with anonymous wires - for (auto port_name : r.first->second) { + auto r = box_ports.insert(cell->type); + 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 : box_module->ports) { auto w = box_module->wire(port_name); log_assert(w); - auto it = cell->connections_.find(port_name); - if (w->port_input) { - RTLIL::SigSpec rhs; - if (it != cell->connections_.end()) { - if (GetSize(it->second) < GetSize(w)) - it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second))); - rhs = it->second; - } - else { - rhs = RTLIL::SigSpec(State::S0, GetSize(w)); - cell->setPort(port_name, rhs); + 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(box_module)); + carry_in = port_name; } - - for (auto b : rhs) { - SigBit I = sigmap(b); - if (b == RTLIL::Sx) - b = State::S0; - else if (I != b) { - if (I == RTLIL::Sx) - alias_map[b] = State::S0; - else - alias_map[b] = I; - } - co_bits.emplace_back(b); - unused_bits.erase(I); - } - } - if (w->port_output) { - RTLIL::SigSpec rhs; - auto it = cell->connections_.find(port_name); - if (it != cell->connections_.end()) { - if (GetSize(it->second) < GetSize(w)) - it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second))); - rhs = it->second; - } - else { - Wire *wire = module->addWire(NEW_ID, GetSize(w)); - if (blackbox) - wire->set_bool_attribute(ID(abc9_padding)); - rhs = wire; - cell->setPort(port_name, rhs); - } - - for (const auto &b : rhs.bits()) { - SigBit O = sigmap(b); - if (O != b) - alias_map[O] = b; - ci_bits.emplace_back(b); - undriven_bits.erase(O); + if (w->port_output) { + if (carry_out != IdString()) + log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module)); + carry_out = port_name; } } + else + r.first->second.push_back(port_name); } - // Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box - if (box_module->get_bool_attribute("\\abc9_flop")) { - SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str())); - if (rhs.empty()) - log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); + if (carry_in != IdString() && carry_out == IdString()) + log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_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(box_module)); + if (carry_in != IdString()) { + r.first->second.push_back(carry_in); + r.first->second.push_back(carry_out); + } + } + for (auto port_name : r.first->second) { + auto w = box_module->wire(port_name); + log_assert(w); + auto rhs = cell->connections_.at(port_name, SigSpec()); + rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs))); + if (w->port_input) for (auto b : rhs) { SigBit I = sigmap(b); if (b == RTLIL::Sx) @@ -450,12 +355,41 @@ struct XAigerWriter co_bits.emplace_back(b); unused_bits.erase(I); } - } - - box_list.emplace_back(cell); + if (w->port_output) + for (const auto &b : rhs) { + SigBit O = sigmap(b); + if (O != b) + alias_map[O] = b; + ci_bits.emplace_back(b); + undriven_bits.erase(O); + // If PI and CI, then must be a (* keep *) wire + if (input_bits.erase(O)) { + log_assert(output_bits.count(O)); + log_assert(O.wire->get_bool_attribute(ID::keep)); + } + } } - // TODO: Free memory from toposort, bit_drivers, bit_users + // Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box + if (box_module->get_bool_attribute("\\abc9_flop")) { + SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str())); + if (rhs.empty()) + log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); + + for (auto b : rhs) { + SigBit I = sigmap(b); + if (b == RTLIL::Sx) + b = State::S0; + else if (I != b) { + if (I == RTLIL::Sx) + alias_map[b] = State::S0; + else + alias_map[b] = I; + } + co_bits.emplace_back(b); + unused_bits.erase(I); + } + } } for (auto bit : input_bits) @@ -501,6 +435,10 @@ struct XAigerWriter for (auto &bit : ci_bits) { aig_m++, aig_i++; + // 1'bx may exist here due to a box output + // that has been padded to its full width + if (bit == State::Sx) + continue; log_assert(!aig_map.count(bit)); aig_map[bit] = 2*aig_m; } @@ -512,7 +450,27 @@ struct XAigerWriter for (const auto &bit : output_bits) { ordered_outputs[bit] = aig_o++; - aig_outputs.push_back(bit2aig(bit)); + int aig; + // Unlike bit2aig() which checks aig_map first, for + // inout/keep bits, since aig_map will point to + // the PI, first attempt to find the NOT/AND driver + // before resorting to an aig_map lookup (which + // could be another PO) + if (input_bits.count(bit)) { + if (not_map.count(bit)) { + aig = bit2aig(not_map.at(bit)) ^ 1; + } else if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + aig = mkgate(a0, a1); + } + else + aig = aig_map.at(bit); + } + else + aig = bit2aig(bit); + aig_outputs.push_back(aig); } for (auto &i : ff_bits) { @@ -612,106 +570,41 @@ struct XAigerWriter // write_o_buffer(0); if (!box_list.empty() || !ff_bits.empty()) { - RTLIL::Module *holes_module = module->design->addModule("$__holes__"); - log_assert(holes_module); + dict<IdString, std::tuple<int,int,int>> cell_cache; - dict<IdString, std::tuple<Cell*,int,int,int>> cell_cache; - - int port_id = 1; int box_count = 0; for (auto cell : box_list) { - RTLIL::Module* orig_box_module = module->design->module(cell->type); - log_assert(orig_box_module); - IdString derived_name = orig_box_module->derive(module->design, cell->parameters); - RTLIL::Module* box_module = module->design->module(derived_name); + log_assert(cell); + + RTLIL::Module* box_module = module->design->module(cell->type); + log_assert(box_module); - auto r = cell_cache.insert(derived_name); + auto r = cell_cache.insert(cell->type); auto &v = r.first->second; if (r.second) { - if (box_module->has_processes()) - Pass::call_on_module(module->design, box_module, "proc"); - int box_inputs = 0, box_outputs = 0; - if (box_module->get_bool_attribute("\\whitebox")) { - auto holes_cell = holes_module->addCell(cell->name, derived_name); - for (auto port_name : box_ports.at(cell->type)) { - RTLIL::Wire *w = box_module->wire(port_name); - log_assert(w); - log_assert(!w->port_input || !w->port_output); - auto &conn = holes_cell->connections_[port_name]; - if (w->port_input) { - for (int i = 0; i < GetSize(w); i++) { - box_inputs++; - RTLIL::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); - } - conn.append(holes_wire); - } - } - else if (w->port_output) { - box_outputs += GetSize(w); - conn = holes_module->addWire(stringf("%s.%s", derived_name.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("\\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); - } - - std::get<0>(v) = holes_cell; - } - else { - for (auto port_name : box_ports.at(cell->type)) { - RTLIL::Wire *w = box_module->wire(port_name); - log_assert(w); - log_assert(!w->port_input || !w->port_output); - if (w->port_input) - box_inputs += GetSize(w); - else if (w->port_output) - box_outputs += GetSize(w); - } - log_assert(std::get<0>(v) == nullptr); + for (auto port_name : box_module->ports) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + if (w->port_input) + box_inputs += GetSize(w); + if (w->port_output) + box_outputs += GetSize(w); } - std::get<1>(v) = box_inputs; - std::get<2>(v) = box_outputs; - std::get<3>(v) = box_module->attributes.at("\\abc9_box_id").as_int(); - } + // 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("\\abc9_flop")) + box_inputs++; - auto holes_cell = std::get<0>(v); - for (auto port_name : box_ports.at(cell->type)) { - RTLIL::Wire *w = box_module->wire(port_name); - log_assert(w); - if (!w->port_output) - continue; - Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w)); - holes_wire->port_output = true; - holes_wire->port_id = port_id++; - holes_module->ports.push_back(holes_wire->name); - if (holes_cell) // whitebox - holes_module->connect(holes_wire, holes_cell->getPort(port_name)); - else // blackbox - holes_module->connect(holes_wire, Const(State::S0, GetSize(w))); + std::get<0>(v) = box_inputs; + std::get<1>(v) = box_outputs; + std::get<2>(v) = box_module->attributes.at("\\abc9_box_id").as_int(); } + write_h_buffer(std::get<0>(v)); write_h_buffer(std::get<1>(v)); write_h_buffer(std::get<2>(v)); - write_h_buffer(std::get<3>(v)); write_h_buffer(box_count++); } @@ -759,82 +652,17 @@ struct XAigerWriter f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); f.write(buffer_str.data(), buffer_str.size()); + RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str())); if (holes_module) { - log_push(); - - // NB: fixup_ports() will sort ports by name - //holes_module->fixup_ports(); - holes_module->check(); - - // Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger - // since boxes may contain parameters in which case `flatten` would have - // created a new $paramod ... - Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap"); - - SigMap holes_sigmap(holes_module); - - dict<SigSpec, SigSpec> replace; - for (auto it = holes_module->cells_.begin(); it != holes_module->cells_.end(); ) { - auto cell = it->second; - if (cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", - "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) { - SigBit D = cell->getPort("\\D"); - SigBit Q = cell->getPort("\\Q"); - // Remove the $_DFF_* cell from what needs to be a combinatorial box - it = holes_module->cells_.erase(it); - 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_ff.Q" - // wire (which itself is driven by an 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); - continue; - } - else if (!cell->type.in("$_NOT_", "$_AND_")) - log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n"); - ++it; - } - - for (auto &conn : holes_module->connections_) { - auto it = replace.find(sigmap(conn.second)); - if (it != replace.end()) - conn.second = it->second; - } - - // Move into a new (temporary) design so that "clean" will only - // operate (and run checks on) this one module - RTLIL::Design *holes_design = new RTLIL::Design; - module->design->modules_.erase(holes_module->name); - holes_design->add(holes_module); - Pass::call(holes_design, "opt -purge"); - std::stringstream a_buffer; XAigerWriter writer(holes_module, true /* holes_mode */); writer.write_aiger(a_buffer, false /*ascii_mode*/); - delete holes_design; f << "a"; std::string buffer_str = a_buffer.str(); int32_t buffer_size_be = to_big_endian(buffer_str.size()); f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); f.write(buffer_str.data(), buffer_str.size()); - - log_pop(); } } @@ -917,7 +745,8 @@ struct XAigerBackend : public Backend { log("Write the top module (according to the (* top *) attribute or if only one module\n"); log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or"); log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n"); - log("pseudo-outputs.\n"); + log("pseudo-outputs. Whitebox contents will be taken from the '<module-name>$holes'\n"); + log("module, if it exists.\n"); log("\n"); log(" -ascii\n"); log(" write ASCII version of AIGER format\n"); diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 60a098917..616b754ce 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -326,7 +326,7 @@ struct EdifBackend : public Backend { continue; SigMap sigmap(module); - std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db; + std::map<RTLIL::SigSpec, std::set<std::pair<std::string, bool>>> net_join_db; *f << stringf(" (cell %s\n", EDIF_DEF(module->name)); *f << stringf(" (cellType GENERIC)\n"); @@ -349,7 +349,7 @@ struct EdifBackend : public Backend { add_prop(p.first, p.second); *f << ")\n"; RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire)); - net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name))); + net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input)); } else { int b[2]; b[wire->upto ? 0 : 1] = wire->start_offset; @@ -362,7 +362,7 @@ struct EdifBackend : public Backend { *f << ")\n"; for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); - net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1)); + net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input)); } } } @@ -391,7 +391,7 @@ struct EdifBackend : public Backend { log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); else if (sig.size() == 1) - net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name))); + net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first))); else { int member_idx = GetSize(sig)-i-1; auto m = design->module(cell->type); @@ -400,8 +400,8 @@ struct EdifBackend : public Backend { if (w) member_idx = GetSize(w)-i-1; } - net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", - EDIF_REF(p.first), member_idx, EDIF_REF(cell->name))); + net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))", + EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first))); } } } @@ -410,13 +410,13 @@ struct EdifBackend : public Backend { if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) { if (sig == RTLIL::State::Sx) { for (auto &ref : it.second) - log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str()); + log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str()); sig = RTLIL::State::S0; } else if (sig == RTLIL::State::Sz) { continue; } else { for (auto &ref : it.second) - log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str()); + log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str()); log_abort(); } } @@ -433,7 +433,7 @@ struct EdifBackend : public Backend { } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) - *f << stringf(" %s\n", ref.c_str()); + *f << stringf(" %s\n", ref.first.c_str()); if (sig.wire == NULL) { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); @@ -448,6 +448,31 @@ struct EdifBackend : public Backend { add_prop(p.first, p.second); *f << stringf("\n )\n"); } + for (auto &wire_it : module->wires_) { + RTLIL::Wire *wire = wire_it.second; + if (!wire->get_bool_attribute(ID::keep)) + continue; + for(int i = 0; i < wire->width; i++) { + SigBit raw_sig = RTLIL::SigSpec(wire, i); + SigBit mapped_sig = sigmap(raw_sig); + if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0) + continue; + std::string netname = log_signal(raw_sig); + for (size_t i = 0; i < netname.size(); i++) + if (netname[i] == ' ' || netname[i] == '\\') + netname.erase(netname.begin() + i--); + *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); + auto &refs = net_join_db.at(mapped_sig); + for (auto &ref : refs) + if (ref.second) + *f << stringf(" %s\n", ref.first.c_str()); + *f << stringf(" )"); + if (attr_properties && raw_sig.wire != NULL) + for (auto &p : raw_sig.wire->attributes) + add_prop(p.first, p.second); + *f << stringf("\n )\n"); + } + } *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" )\n"); diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 1df996aa7..34bf7ef38 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -304,7 +304,11 @@ class SmtIo: def p_open(self): assert self.p is None - self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + try: + self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except FileNotFoundError: + print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True) + sys.exit(1) running_solvers[self.p_index] = self.p self.p_running = True self.p_next = None diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index a4b1e6fec..a42569301 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -393,21 +393,6 @@ void AigerReader::parse_xaiger() if (f.peek() == '\n') f.get(); - dict<int,IdString> box_lookup; - for (auto m : design->modules()) { - auto it = m->attributes.find(ID(abc9_box_id)); - if (it == m->attributes.end()) - continue; - if (m->name.begins_with("$paramod")) - continue; - auto id = it->second.as_int(); - auto r = box_lookup.insert(std::make_pair(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)); - log_assert(r.second); - } - // Parse footer (symbol table, comments, etc.) std::string s; for (int c = f.get(); c != EOF; c = f.get()) { @@ -429,17 +414,23 @@ void AigerReader::parse_xaiger() for (unsigned j = 0; j < cutLeavesM; ++j) { nodeID = parse_xaiger_literal(f); log_debug2("\t%u\n", nodeID); + if (nodeID == 0) { + log_debug("\tLUT '$lut$aiger%d$%d' input %d is constant!\n", aiger_autoidx, rootNodeID, cutLeavesM); + continue; + } RTLIL::Wire *wire = module->wire(stringf("$aiger%d$%d", aiger_autoidx, nodeID)); log_assert(wire); input_sig.append(wire); } + // Reverse input order as fastest input is returned first + input_sig.reverse(); // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size()) ce.clear(); ce.compute_deps(output_sig, input_sig.to_sigbit_pool()); - RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); - for (int j = 0; j < (1 << cutLeavesM); ++j) { + RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << GetSize(input_sig)); + for (int j = 0; j < GetSize(lut_mask); ++j) { int gray = j ^ (j >> 1); - ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)}); + ce.set_incremental(input_sig, RTLIL::Const{gray, GetSize(input_sig)}); RTLIL::SigBit o(output_sig); bool success YS_ATTRIBUTE(unused) = ce.eval(o); log_assert(success); @@ -453,11 +444,13 @@ void AigerReader::parse_xaiger() } } else if (c == 'r') { - uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + uint32_t dataSize = parse_xaiger_literal(f); flopNum = parse_xaiger_literal(f); log_debug("flopNum = %u\n", flopNum); log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); - f.ignore(flopNum * sizeof(uint32_t)); + mergeability.reserve(flopNum); + for (unsigned i = 0; i < flopNum; i++) + mergeability.emplace_back(parse_xaiger_literal(f)); } else if (c == 'n') { parse_xaiger_literal(f); @@ -479,11 +472,15 @@ void AigerReader::parse_xaiger() uint32_t boxNum = parse_xaiger_literal(f); log_debug("boxNum = %u\n", boxNum); for (unsigned i = 0; i < boxNum; i++) { - f.ignore(2*sizeof(uint32_t)); + uint32_t boxInputs = parse_xaiger_literal(f); + uint32_t boxOutputs = parse_xaiger_literal(f); uint32_t boxUniqueId = parse_xaiger_literal(f); log_assert(boxUniqueId > 0); uint32_t oldBoxNum = parse_xaiger_literal(f); - RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), box_lookup.at(boxUniqueId)); + RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), stringf("$__boxid%u", boxUniqueId)); + cell->setPort("\\i", SigSpec(State::S0, boxInputs)); + cell->setPort("\\o", SigSpec(State::S0, boxOutputs)); + cell->attributes["\\abc9_box_seq"] = oldBoxNum; boxes.emplace_back(cell); } } @@ -568,25 +565,18 @@ void AigerReader::parse_aiger_ascii() } // Parse outputs + digits = ceil(log10(O)); for (unsigned i = 0; i < O; ++i, ++line_count) { if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); log_debug2("%d is an output\n", l1); - const unsigned variable = l1 >> 1; - const bool invert = l1 & 1; - RTLIL::IdString wire_name(stringf("$%d%s", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? - RTLIL::Wire *wire = module->wire(wire_name); - if (!wire) - wire = createWireIfNotExists(module, l1); - else if (wire->port_input || wire->port_output) { - RTLIL::Wire *new_wire = module->addWire(NEW_ID); - module->connect(new_wire, wire); - wire = new_wire; - } + RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i)); wire->port_output = true; + module->connect(wire, createWireIfNotExists(module, l1)); outputs.push_back(wire); } + //std::getline(f, line); // Ignore up to start of next line // Parse bad properties for (unsigned i = 0; i < B; ++i, ++line_count) { @@ -598,6 +588,8 @@ void AigerReader::parse_aiger_ascii() wire->port_output = true; bad_properties.push_back(wire); } + //if (B > 0) + // std::getline(f, line); // Ignore up to start of next line // TODO: Parse invariant constraints for (unsigned i = 0; i < C; ++i, ++line_count) @@ -753,84 +745,41 @@ void AigerReader::parse_aiger_binary() void AigerReader::post_process() { - dict<IdString, std::vector<IdString>> box_ports; - unsigned ci_count = 0, co_count = 0, flop_count = 0; + unsigned ci_count = 0, co_count = 0; for (auto cell : boxes) { - RTLIL::Module* box_module = design->module(cell->type); - log_assert(box_module); - - auto r = box_ports.insert(cell->type); - 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 : box_module->ports) { - auto w = box_module->wire(port_name); - log_assert(w); - if (w->get_bool_attribute("\\abc9_carry")) { - if (w->port_input) - carry_in = port_name; - if (w->port_output) - carry_out = port_name; - } - else - r.first->second.push_back(port_name); - } - if (carry_in != IdString()) { - log_assert(carry_out != IdString()); - r.first->second.push_back(carry_in); - r.first->second.push_back(carry_out); - } + for (auto &bit : cell->connections_.at("\\i")) { + log_assert(bit == State::S0); + log_assert(co_count < outputs.size()); + bit = outputs[co_count++]; + log_assert(bit.wire && GetSize(bit.wire) == 1); + log_assert(bit.wire->port_output); + bit.wire->port_output = false; } - - for (auto port_name : box_ports.at(cell->type)) { - RTLIL::Wire* port = box_module->wire(port_name); - log_assert(port); - RTLIL::SigSpec rhs; - for (int i = 0; i < GetSize(port); i++) { - RTLIL::Wire* wire = nullptr; - if (port->port_input) { - log_assert(co_count < outputs.size()); - wire = outputs[co_count++]; - log_assert(wire); - log_assert(wire->port_output); - wire->port_output = false; - } - if (port->port_output) { - log_assert((piNum + ci_count) < inputs.size()); - wire = inputs[piNum + ci_count++]; - log_assert(wire); - log_assert(wire->port_input); - wire->port_input = false; - } - rhs.append(wire); - } - cell->setPort(port_name, rhs); + for (auto &bit : cell->connections_.at("\\o")) { + log_assert(bit == State::S0); + log_assert((piNum + ci_count) < inputs.size()); + bit = inputs[piNum + ci_count++]; + log_assert(bit.wire && GetSize(bit.wire) == 1); + log_assert(bit.wire->port_input); + bit.wire->port_input = false; } + } - if (box_module->attributes.count("\\abc9_flop")) { - log_assert(co_count < outputs.size()); - Wire *wire = outputs[co_count++]; - log_assert(wire); - log_assert(wire->port_output); - wire->port_output = false; - - RTLIL::Wire *d = outputs[outputs.size() - flopNum + flop_count]; - log_assert(d); - log_assert(d->port_output); - d->port_output = false; - - RTLIL::Wire *q = inputs[piNum - flopNum + flop_count]; - log_assert(q); - log_assert(q->port_input); - q->port_input = false; - - auto ff = module->addCell(NEW_ID, "$__ABC9_FF_"); - ff->setPort("\\D", d); - ff->setPort("\\Q", q); - flop_count++; - continue; - } + for (uint32_t i = 0; i < flopNum; i++) { + RTLIL::Wire *d = outputs[outputs.size() - flopNum + i]; + log_assert(d); + log_assert(d->port_output); + d->port_output = false; + + RTLIL::Wire *q = inputs[piNum - flopNum + i]; + log_assert(q); + log_assert(q->port_input); + q->port_input = false; + + auto ff = module->addCell(NEW_ID, "$__ABC9_FF_"); + ff->setPort("\\D", d); + ff->setPort("\\Q", q); + ff->attributes["\\abc9_mergeability"] = mergeability[i]; } dict<RTLIL::IdString, int> wideports_cache; @@ -859,6 +808,7 @@ void AigerReader::post_process() wire->port_input = false; module->connect(wire, existing); } + log_debug(" -> %s\n", log_id(escaped_s)); } else if (index > 0) { std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); @@ -872,18 +822,14 @@ void AigerReader::post_process() module->connect(wire, existing); wire->port_input = false; } + log_debug(" -> %s\n", log_id(indexed_name)); } - log_debug(" -> %s\n", log_id(wire)); } else if (type == "output") { log_assert(static_cast<unsigned>(variable + co_count) < outputs.size()); RTLIL::Wire* wire = outputs[variable + co_count]; log_assert(wire); log_assert(wire->port_output); - if (escaped_s == "$__dummy__") { - wire->port_output = false; - continue; - } log_debug("Renaming output %s", log_id(wire)); if (index == 0) { @@ -896,9 +842,11 @@ void AigerReader::post_process() } else { wire->port_output = false; + existing->port_output = true; module->connect(wire, existing); wire = existing; } + log_debug(" -> %s\n", log_id(escaped_s)); } else if (index > 0) { std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); @@ -909,11 +857,12 @@ void AigerReader::post_process() wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); } else { - module->connect(wire, existing); wire->port_output = false; + existing->port_output = true; + module->connect(wire, existing); } + log_debug(" -> %s\n", log_id(indexed_name)); } - log_debug(" -> %s\n", log_id(wire)); int init; mf >> init; if (init < 2) @@ -921,26 +870,8 @@ void AigerReader::post_process() } else if (type == "box") { RTLIL::Cell* cell = module->cell(stringf("$box%d", variable)); - if (cell) { // ABC could have optimised this box away + if (cell) // ABC could have optimised this box away module->rename(cell, escaped_s); - for (const auto &i : cell->connections()) { - RTLIL::IdString port_name = i.first; - RTLIL::SigSpec rhs = i.second; - int index = 0; - for (auto bit : rhs.bits()) { - RTLIL::Wire* wire = bit.wire; - RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name))); - if (index == 0) - module->rename(wire, escaped_s); - else if (index > 0) { - module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index)); - if (wideports) - wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index); - } - index++; - } - } - } } else log_error("Symbol type '%s' not recognised.\n", type.c_str()); @@ -1018,18 +949,21 @@ struct AigerFrontend : public Frontend { log("Load module from an AIGER file into the current design.\n"); log("\n"); log(" -module_name <module_name>\n"); - log(" Name of module to be created (default: <filename>)\n"); + log(" name of module to be created (default: <filename>)\n"); log("\n"); log(" -clk_name <wire_name>\n"); - log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); - log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n"); + log(" if specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); + log(" clocked by wire of this name. otherwise, $_FF_ cells will be used\n"); log("\n"); log(" -map <filename>\n"); log(" read file with port and latch symbols\n"); log("\n"); log(" -wideports\n"); - log(" Merge ports that match the pattern 'name[int]' into a single\n"); - log(" multi-bit port 'name'.\n"); + log(" merge ports that match the pattern 'name[int]' into a single\n"); + log(" multi-bit port 'name'\n"); + log("\n"); + log(" -xaiger\n"); + log(" read XAIGER extensions\n"); log("\n"); } void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -1039,7 +973,7 @@ struct AigerFrontend : public Frontend { RTLIL::IdString clk_name; RTLIL::IdString module_name; std::string map_filename; - bool wideports = false; + bool wideports = false, xaiger = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { @@ -1060,6 +994,10 @@ struct AigerFrontend : public Frontend { wideports = true; continue; } + if (arg == "-xaiger") { + xaiger = true; + continue; + } break; } extra_args(f, filename, args, argidx, true); @@ -1079,7 +1017,10 @@ struct AigerFrontend : public Frontend { } AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); - reader.parse_aiger(); + if (xaiger) + reader.parse_xaiger(); + else + reader.parse_aiger(); } } AigerFrontend; diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 722f1e472..46ac81212 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -45,6 +45,7 @@ struct AigerReader std::vector<RTLIL::Wire*> outputs; std::vector<RTLIL::Wire*> bad_properties; std::vector<RTLIL::Cell*> boxes; + std::vector<int> mergeability; AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); diff --git a/kernel/driver.cc b/kernel/driver.cc index f0d495b46..9040408bc 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -295,6 +295,9 @@ int main(int argc, char **argv) printf(" -E <depsfile>\n"); printf(" write a Makefile dependencies file with in- and output file names\n"); printf("\n"); + printf(" -x <feature>\n"); + printf(" do not print warnings for the specified experimental feature\n"); + printf("\n"); printf(" -g\n"); printf(" globally enable debug log messages\n"); printf("\n"); @@ -317,8 +320,14 @@ int main(int argc, char **argv) exit(0); } + if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version"))) + { + printf("%s\n", yosys_version_str); + exit(0); + } + int opt; - while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1) { switch (opt) { @@ -449,6 +458,9 @@ int main(int argc, char **argv) case 'E': depsfile = optarg; break; + case 'x': + log_experimentals_ignored.insert(optarg); + break; default: fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); @@ -561,6 +573,10 @@ int main(int argc, char **argv) if (log_warnings_count) log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count); + + if (!log_experimentals.empty()) + log("Warnings: %d experimental features used (not excluded with -x).\n", GetSize(log_experimentals)); + #ifdef _WIN32 log("End of script. Logfile hash: %s\n", hash.c_str()); #else diff --git a/kernel/log.cc b/kernel/log.cc index c5ba0d10d..f5d6c488e 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -42,7 +42,7 @@ std::vector<FILE*> log_files; std::vector<std::ostream*> log_streams; std::map<std::string, std::set<std::string>> log_hdump; std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; -std::set<std::string> log_warnings; +std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; bool log_hdump_all = false; FILE *log_errfile = NULL; @@ -377,6 +377,19 @@ void log_warning(const char *format, ...) va_end(ap); } +void log_experimental(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + string s = vstringf(format, ap); + va_end(ap); + + if (log_experimentals_ignored.count(s) == 0 && log_experimentals.count(s) == 0) { + log_warning("Feature '%s' is experimental.\n", s.c_str()); + log_experimentals.insert(s); + } +} + void log_warning_noprefix(const char *format, ...) { va_list ap; diff --git a/kernel/log.h b/kernel/log.h index 1f15f3459..9db8efaa5 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -50,7 +50,7 @@ extern std::vector<FILE*> log_files; extern std::vector<std::ostream*> log_streams; extern std::map<std::string, std::set<std::string>> log_hdump; extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; -extern std::set<std::string> log_warnings; +extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; extern int log_warnings_count; extern bool log_hdump_all; extern FILE *log_errfile; @@ -77,6 +77,7 @@ YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noretur void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3)); void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); +void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); // Log with filename to report a problem in a source file. void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); diff --git a/kernel/register.cc b/kernel/register.cc index 5d0fb3c8c..e59d59654 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -304,6 +304,9 @@ void Pass::call(RTLIL::Design *design, std::vector<std::string> args) if (pass_register.count(args[0]) == 0) log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str()); + if (pass_register[args[0]]->experimental_flag) + log_experimental("%s", args[0].c_str()); + size_t orig_sel_stack_pos = design->selection_stack.size(); auto state = pass_register[args[0]]->pre_execute(); pass_register[args[0]]->execute(args, design); @@ -824,6 +827,11 @@ struct HelpPass : public Pass { log("="); log("\n"); it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } } } else if (args[1] == "-cells") { @@ -846,6 +854,11 @@ struct HelpPass : public Pass { std::ostringstream buf; log_streams.push_back(&buf); it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } log_streams.pop_back(); write_tex(f, it.first, it.second->short_help, buf.str()); } @@ -858,6 +871,11 @@ struct HelpPass : public Pass { std::ostringstream buf; log_streams.push_back(&buf); it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } log_streams.pop_back(); write_html(f, it.first, it.second->short_help, buf.str()); } @@ -865,6 +883,11 @@ struct HelpPass : public Pass { } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); + if (pass_register.at(args[1])->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str()); + log("\n"); + } } else if (cell_help_messages.cell_help.count(args[1])) { log("%s", cell_help_messages.cell_help.at(args[1]).c_str()); diff --git a/kernel/register.h b/kernel/register.h index 821faff3e..4622845b6 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -36,6 +36,11 @@ struct Pass int call_counter; int64_t runtime_ns; + bool experimental_flag = false; + + void experimental() { + experimental_flag = true; + } struct pre_post_exec_state_t { Pass *parent_pass; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 6251d265d..58c5d9674 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -851,6 +851,8 @@ public: RTLIL::SigSpec repeat(int num) const; + void reverse() { inline_unpack(); std::reverse(bits_.begin(), bits_.end()); } + bool operator <(const RTLIL::SigSpec &other) const; bool operator ==(const RTLIL::SigSpec &other) const; inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); } diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 00a88cc82..55abd9b96 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -198,8 +198,8 @@ calculated signal and a constant zero with an {\tt \$and} gate). \subsection{Registers} -D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK}, -an input port \B{D} and an output port \B{Q}. The following parameters are available for \$dff +D-type flip-flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK}, +an input port \B{D} and an output port \B{Q}. The following parameters are available for {\tt \$dff} cells: \begin{itemize} @@ -211,13 +211,23 @@ Clock is active on the positive edge if this parameter has the value {\tt 1'b1} edge if this parameter is {\tt 1'b0}. \end{itemize} -D-Type Flip-Flops with asynchronous resets are represented by {\tt \$adff} cells. As the {\tt \$dff} +D-type flip-flops with enable are represented by {\tt \$dffe} cells. As the {\tt \$dff} +cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{EN} +input port for the enable pin and the following parameter: + +\begin{itemize} +\item \B{EN\_POLARITY} \\ +The enable input is active-high if this parameter has the value {\tt 1'b1} and active-low +if this parameter is {\tt 1'b0}. +\end{itemize} + +D-type flip-flops with asynchronous reset are represented by {\tt \$adff} cells. As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST} input port for the reset pin and the following additional two parameters: \begin{itemize} \item \B{ARST\_POLARITY} \\ -The asynchronous reset is high-active if this parameter has the value {\tt 1'b1} and low-active +The asynchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low if this parameter is {\tt 1'b0}. \item \B{ARST\_VALUE} \\ @@ -231,8 +241,27 @@ Usually these cells are generated by the {\tt proc} pass using the information in the designs RTLIL::Process objects. \end{sloppypar} +D-type flip-flops with asynchronous set and reset are represented by {\tt \$dffsr} cells. +As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have +a single-bit \B{SET} input port for the set pin, a single-bit \B{CLR} input port for the reset pin, +and the following two parameters: + +\begin{itemize} +\item \B{SET\_POLARITY} \\ +The set input is active-high if this parameter has the value {\tt 1'b1} and active-low +if this parameter is {\tt 1'b0}. + +\item \B{CLR\_POLARITY} \\ +The reset input is active-high if this parameter has the value {\tt 1'b1} and active-low +if this parameter is {\tt 1'b0}. +\end{itemize} + +When both the set and reset inputs of a {\tt \$dffsr} cell are active, the reset input takes +precedence. + \begin{fixme} -Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches. +Add information about {\tt \$sr} cells (set-reset flip-flops), {\tt \$dlatch} cells (d-type latches), +and {\tt \$dlatchsr} cells (d-type latches with set/reset). \end{fixme} \subsection{Memories} @@ -451,6 +480,30 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\ \lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_} \\ \lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_} \\ \end{tabular} +% FIXME: the layout of this is broken and I have no idea how to fix it +\hfil +\begin{tabular}[t]{lll} +$ClkEdge$ & $EnLvl$ & Cell Type \\ +\hline +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP\_} \\ +\end{tabular} +% FIXME: the layout of this is broken too +\hfil +\begin{tabular}[t]{llll} +$ClkEdge$ & $SetLvl$ & $RstLvl$ & Cell Type \\ +\hline +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NNN\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NNP\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NPN\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NPP\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PNN\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PNP\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PPN\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PPP\_} \\ +\end{tabular} \caption{Cell types for gate level logic networks} \label{tab:CellLib_gates} \end{table} @@ -459,11 +512,22 @@ Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. Th {\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_}, {\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic. The cell type {\tt \$\_TBUF\_} is used to model tristate logic. + The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops. +The cell types {\tt \$\_DFFE\_NN\_}, {\tt \$\_DFFE\_NP\_}, {\tt \$\_DFFE\_PN\_} and {\tt \$\_DFFE\_PP\_} +implement d-type flip-flops with enable. The values in the table for these cell types relate to the +following Verilog code template. + +\begin{lstlisting}[mathescape,language=Verilog] + always @($ClkEdge$ C) + if (EN == $EnLvl$) + Q <= D; +\end{lstlisting} + The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_}, {\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement -d-type flip-flops with asynchronous resets. The values in the table for these cell types relate to the +d-type flip-flops with asynchronous reset. The values in the table for these cell types relate to the following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge; otherwise. @@ -476,6 +540,25 @@ otherwise. Q <= D; \end{lstlisting} +The cell types {\tt \$\_DFFSR\_NNN\_}, {\tt \$\_DFFSR\_NNP\_}, {\tt \$\_DFFSR\_NPN\_}, {\tt \$\_DFFSR\_NPP\_}, +{\tt \$\_DFFSR\_PNN\_}, {\tt \$\_DFFSR\_PNP\_}, {\tt \$\_DFFSR\_PPN\_} and {\tt \$\_DFFSR\_PPP\_} implement +d-type flip-flops with asynchronous set and reset. The values in the table for these cell types relate to the +following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; +if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; +otherwise, and \lstinline[mathescape,language=Verilog];$SetEdge$; is \lstinline[language=Verilog];posedge; +if \lstinline[mathescape,language=Verilog];$SetLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; +otherwise. + +\begin{lstlisting}[mathescape,language=Verilog] + always @($ClkEdge$ C, $RstEdge$ R, $SetEdge$ S) + if (R == $RstLvl$) + Q <= 0; + else if (S == $SetLvl$) + Q <= 1; + else + Q <= D; +\end{lstlisting} + In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap} pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC} @@ -507,11 +590,7 @@ Add information about {\tt \$ff} and {\tt \$\_FF\_} cells. \end{fixme} \begin{fixme} -Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells. -\end{fixme} - -\begin{fixme} -Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells. +Add information about {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells. \end{fixme} \begin{fixme} diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index c58c3f66a..9b4e644c0 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -1935,6 +1935,19 @@ def parse_header(source): line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }") ugly_line = unpretty_string(line) + # for anonymous unions, ignore union enclosure by skipping start line and replacing end line with new line + if 'union {' in line: + j = i+1 + while j < len(source_text): + union_line = source_text[j] + if '};' in union_line: + source_text[j] = '\n' + break + j += 1 + if j != len(source_text): + i += 1 + continue + if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1: namespace_name = ugly_line[10:].replace("{","").strip() namespaces.append((namespace_name, ugly_line.count("{"))) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index a3e969ef1..eeef24bde 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -873,7 +873,7 @@ struct ShowPass : public Pass { #ifdef __APPLE__ std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str()); #else - std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); + std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); #endif log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 6a8d8cabd..f74655d1c 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -44,9 +44,10 @@ struct OptReduceWorker cells.erase(cell); RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); + sig_a.sort_and_unify(); pool<RTLIL::SigBit> new_sig_a_bits; - for (auto &bit : sig_a.to_sigbit_set()) + for (auto &bit : sig_a) { if (bit == RTLIL::State::S0) { if (cell->type == ID($reduce_and)) { @@ -86,6 +87,7 @@ struct OptReduceWorker } RTLIL::SigSpec new_sig_a(new_sig_a_bits); + new_sig_a.sort_and_unify(); if (new_sig_a != sig_a || sig_a.size() != cell->getPort(ID::A).size()) { log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a)); @@ -235,7 +237,6 @@ struct OptReduceWorker log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); module->connect(old_sig_conn); - module->check(); did_something = true; total_count++; @@ -324,6 +325,8 @@ struct OptReduceWorker opt_mux(cell); } } + + module->check(); } }; diff --git a/passes/pmgen/ice40_wrapcarry.cc b/passes/pmgen/ice40_wrapcarry.cc index 6e154147f..0053c8872 100644 --- a/passes/pmgen/ice40_wrapcarry.cc +++ b/passes/pmgen/ice40_wrapcarry.cc @@ -42,11 +42,19 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm) cell->setPort("\\A", st.carry->getPort("\\I0")); cell->setPort("\\B", st.carry->getPort("\\I1")); - cell->setPort("\\CI", st.carry->getPort("\\CI")); + auto CI = st.carry->getPort("\\CI"); + cell->setPort("\\CI", CI); cell->setPort("\\CO", st.carry->getPort("\\CO")); cell->setPort("\\I0", st.lut->getPort("\\I0")); - cell->setPort("\\I3", st.lut->getPort("\\I3")); + auto I3 = st.lut->getPort("\\I3"); + if (pm.sigmap(CI) == pm.sigmap(I3)) { + cell->setParam("\\I3_IS_CI", State::S1); + I3 = State::Sx; + } + else + cell->setParam("\\I3_IS_CI", State::S0); + cell->setPort("\\I3", I3); cell->setPort("\\O", st.lut->getPort("\\O")); cell->setParam("\\LUT", st.lut->getParam("\\LUT_INIT")); @@ -118,7 +126,8 @@ struct Ice40WrapCarryPass : public Pass { auto lut = module->addCell(lut_name, ID($lut)); lut->setParam(ID(WIDTH), 4); lut->setParam(ID(LUT), cell->getParam(ID(LUT))); - lut->setPort(ID(A), {cell->getPort(ID(I0)), cell->getPort(ID(A)), cell->getPort(ID(B)), cell->getPort(ID(I3)) }); + auto I3 = cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3)); + lut->setPort(ID(A), { I3, cell->getPort(ID(B)), cell->getPort(ID(A)), cell->getPort(ID(I0)) }); lut->setPort(ID(Y), cell->getPort(ID(O))); Const src; diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 430bba1e8..436ac1b01 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -269,7 +269,8 @@ struct SatHelper for (int i = 0; i < lhs.size(); i++) { RTLIL::SigSpec bit = lhs.extract(i, 1); if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) { - removed_bits.append(bit); + if (rhs[i] != State::Sx) + removed_bits.append(bit); lhs.remove(i, 1); rhs.remove(i, 1); i--; diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cd357d72a..369c9de64 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -8,6 +8,8 @@ OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) OBJS += passes/techmap/abc.o OBJS += passes/techmap/abc9.o +OBJS += passes/techmap/abc9_exe.o +OBJS += passes/techmap/abc9_ops.o ifneq ($(ABCEXTERNAL),) passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 2568a6cd1..2aeda16d6 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -2,7 +2,7 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * 2019 Eddie Hung <eddie@fpgeh.com> + * (C) 2019 Eddie Hung <eddie@fpgeh.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,714 +23,19 @@ // http://www.eecs.berkeley.edu/~alanmi/abc/ #include "kernel/register.h" -#include "kernel/sigtools.h" #include "kernel/celltypes.h" -#include "kernel/cost.h" +#include "kernel/rtlil.h" #include "kernel/log.h" -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <cerrno> -#include <sstream> -#include <climits> -#ifndef _WIN32 -# include <unistd.h> -# include <dirent.h> -#endif - -#include "frontends/aiger/aigerparse.h" -#include "kernel/utils.h" - -#ifdef YOSYS_LINK_ABC -extern "C" int Abc_RealMain(int argc, char *argv[]); -#endif +// abc9_exe.cc +std::string fold_abc9_cmd(std::string str); USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -int map_autoidx; - -inline std::string remap_name(RTLIL::IdString abc9_name) -{ - return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); -} - -void handle_loops(RTLIL::Design *design, RTLIL::Module *module) -{ - Pass::call(design, "scc -set_attr abc9_scc_id {} % w:*"); - - // For every unique SCC found, (arbitrarily) find the first - // cell in the component, and select (and mark) all its output - // wires - pool<RTLIL::Const> ids_seen; - for (auto cell : module->cells()) { - auto it = cell->attributes.find(ID(abc9_scc_id)); - if (it != cell->attributes.end()) { - auto r = ids_seen.insert(it->second); - if (r.second) { - for (auto &c : cell->connections_) { - if (c.second.is_fully_const()) continue; - if (cell->output(c.first)) { - SigBit b = c.second.as_bit(); - Wire *w = b.wire; - if (w->port_input) { - // In this case, hopefully the loop break has been already created - // Get the non-prefixed wire - Wire *wo = module->wire(stringf("%s.abco", b.wire->name.c_str())); - log_assert(wo != nullptr); - log_assert(wo->port_output); - log_assert(b.offset < GetSize(wo)); - c.second = RTLIL::SigBit(wo, b.offset); - } - else { - // Create a new output/input loop break - w->port_input = true; - w = module->wire(stringf("%s.abco", w->name.c_str())); - if (!w) { - w = module->addWire(stringf("%s.abco", b.wire->name.c_str()), GetSize(b.wire)); - w->port_output = true; - } - else { - log_assert(w->port_input); - log_assert(b.offset < GetSize(w)); - } - w->set_bool_attribute(ID(abc9_scc_break)); - c.second = RTLIL::SigBit(w, b.offset); - } - } - } - } - cell->attributes.erase(it); - } - } - - module->fixup_ports(); -} - -std::string add_echos_to_abc9_cmd(std::string str) -{ - std::string new_str, token; - for (size_t i = 0; i < str.size(); i++) { - token += str[i]; - if (str[i] == ';') { - while (i+1 < str.size() && str[i+1] == ' ') - i++; - new_str += "echo + " + token + " " + token + " "; - token.clear(); - } - } - - if (!token.empty()) { - if (!new_str.empty()) - new_str += "echo + " + token + "; "; - new_str += token; - } - - return new_str; -} - -std::string fold_abc9_cmd(std::string str) -{ - std::string token, new_str = " "; - int char_counter = 10; - - for (size_t i = 0; i <= str.size(); i++) { - if (i < str.size()) - token += str[i]; - if (i == str.size() || str[i] == ';') { - if (char_counter + token.size() > 75) - new_str += "\n ", char_counter = 14; - new_str += token, char_counter += token.size(); - token.clear(); - } - } - - return new_str; -} - -std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) -{ - if (show_tempdir) - return text; - - while (1) { - size_t pos = text.find(tempdir_name); - if (pos == std::string::npos) - break; - text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); - } - - std::string selfdir_name = proc_self_dirname(); - if (selfdir_name != "/") { - while (1) { - size_t pos = text.find(selfdir_name); - if (pos == std::string::npos) - break; - text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); - } - } - - return text; -} - -struct abc9_output_filter -{ - bool got_cr; - int escape_seq_state; - std::string linebuf; - std::string tempdir_name; - bool show_tempdir; - - abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) - { - got_cr = false; - escape_seq_state = 0; - } - - void next_char(char ch) - { - if (escape_seq_state == 0 && ch == '\033') { - escape_seq_state = 1; - return; - } - if (escape_seq_state == 1) { - escape_seq_state = ch == '[' ? 2 : 0; - return; - } - if (escape_seq_state == 2) { - if ((ch < '0' || '9' < ch) && ch != ';') - escape_seq_state = 0; - return; - } - escape_seq_state = 0; - if (ch == '\r') { - got_cr = true; - return; - } - if (ch == '\n') { - log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); - got_cr = false, linebuf.clear(); - return; - } - if (got_cr) - got_cr = false, linebuf.clear(); - linebuf += ch; - } - - void next_line(const std::string &line) - { - //int pi, po; - //if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { - // log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", - // pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", - // po, po_map.count(po) ? po_map.at(po).c_str() : "???"); - // return; - //} - - for (char ch : line) - next_char(ch); - } -}; - -void abc9_module(RTLIL::Design *design, RTLIL::Module *module, std::string script_file, std::string exe_file, - bool cleanup, vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, - bool show_tempdir, std::string box_file, std::string lut_file, - std::string wire_delay -) +struct Abc9Pass : public ScriptPass { - map_autoidx = autoidx++; - - std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; - if (!cleanup) - tempdir_name[0] = tempdir_name[4] = '_'; - tempdir_name = make_temp_dir(tempdir_name); - log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", - module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); - - std::string abc9_script; - - if (!lut_costs.empty()) { - abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); - if (!box_file.empty()) - abc9_script += stringf("read_box %s; ", box_file.c_str()); - } - else - if (!lut_file.empty()) { - abc9_script += stringf("read_lut %s; ", lut_file.c_str()); - if (!box_file.empty()) - abc9_script += stringf("read_box %s; ", box_file.c_str()); - } - else - log_abort(); - - abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); - - if (!script_file.empty()) { - if (script_file[0] == '+') { - for (size_t i = 1; i < script_file.size(); i++) - if (script_file[i] == '\'') - abc9_script += "'\\''"; - else if (script_file[i] == ',') - abc9_script += " "; - else - abc9_script += script_file[i]; - } else - abc9_script += stringf("source %s", script_file.c_str()); - } else if (!lut_costs.empty() || !lut_file.empty()) { - abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) - : RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); - } else - log_abort(); - - for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) - abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); - - //for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) - // abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); - - for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) - abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); - - std::string C; - if (design->scratchpad.count("abc9.if.C")) - C = "-C " + design->scratchpad_get_string("abc9.if.C"); - for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) - abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); - - std::string R; - if (design->scratchpad.count("abc9.if.R")) - R = "-R " + design->scratchpad_get_string("abc9.if.R"); - for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) - abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); - - abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str()); - if (design->scratchpad_get_bool("abc9.verify")) { - if (dff_mode) - abc9_script += "; &verify -s"; - else - abc9_script += "; &verify"; - } - abc9_script += "; time"; - abc9_script = add_echos_to_abc9_cmd(abc9_script); - - for (size_t i = 0; i+1 < abc9_script.size(); i++) - if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') - abc9_script[i+1] = '\n'; - - FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); - fprintf(f, "%s\n", abc9_script.c_str()); - fclose(f); - - log_push(); - - handle_loops(design, module); - - Pass::call(design, "aigmap -select"); - - Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); - - int count_outputs = design->scratchpad_get_int("write_xaiger.num_outputs"); - log("Extracted %d AND gates and %d wires to a netlist network with %d inputs and %d outputs.\n", - design->scratchpad_get_int("write_xaiger.num_ands"), - design->scratchpad_get_int("write_xaiger.num_wires"), - design->scratchpad_get_int("write_xaiger.num_inputs"), - count_outputs); - - if (count_outputs > 0) { - std::string buffer; - std::ifstream ifs; -#if 0 - buffer = stringf("%s/%s", tempdir_name.c_str(), "input.xaig"); - ifs.open(buffer); - if (ifs.fail()) - log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); - log_assert(!design->module(ID($__abc9__))); - { - AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); - reader.parse_xaiger(); - } - ifs.close(); - Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected")); - design->remove(design->module(ID($__abc9__))); -#endif - - log_header(design, "Executing ABC9.\n"); - - if (!lut_costs.empty()) { - buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); - f = fopen(buffer.c_str(), "wt"); - if (f == NULL) - log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); - for (int i = 0; i < GetSize(lut_costs); i++) - fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); - fclose(f); - } - - buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); - log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); - -#ifndef YOSYS_LINK_ABC - abc9_output_filter filt(tempdir_name, show_tempdir); - int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); -#else - // These needs to be mutable, supposedly due to getopt - char *abc9_argv[5]; - string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); - abc9_argv[0] = strdup(exe_file.c_str()); - abc9_argv[1] = strdup("-s"); - abc9_argv[2] = strdup("-f"); - abc9_argv[3] = strdup(tmp_script_name.c_str()); - abc9_argv[4] = 0; - int ret = Abc_RealMain(4, abc9_argv); - free(abc9_argv[0]); - free(abc9_argv[1]); - free(abc9_argv[2]); - free(abc9_argv[3]); -#endif - if (ret != 0) - log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); - - buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig"); - ifs.open(buffer, std::ifstream::binary); - if (ifs.fail()) - log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); - - buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); - log_assert(!design->module(ID($__abc9__))); - - AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); - reader.parse_xaiger(); - ifs.close(); - -#if 0 - Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected")); -#endif - - log_header(design, "Re-integrating ABC9 results.\n"); - RTLIL::Module *mapped_mod = design->module(ID($__abc9__)); - if (mapped_mod == NULL) - log_error("ABC output file does not contain a module `$__abc9__'.\n"); - - for (auto w : mapped_mod->wires()) - module->addWire(remap_name(w->name), GetSize(w)); - - dict<IdString, bool> abc9_box; - vector<RTLIL::Cell*> boxes; - for (auto cell : module->cells().to_vector()) { - if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) { - module->remove(cell); - continue; - } - RTLIL::Module* box_module = design->module(cell->type); - auto jt = abc9_box.find(cell->type); - if (jt == abc9_box.end()) - jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; - if (jt->second) { - if (box_module->get_bool_attribute("\\abc9_flop")) { - if (dff_mode) - boxes.emplace_back(cell); - else - box_module->set_bool_attribute("\\abc9_keep", false); - } - else - boxes.emplace_back(cell); - } - } - - dict<SigBit, pool<IdString>> bit_drivers, bit_users; - TopoSort<IdString, RTLIL::sort_by_id_str> toposort; - dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; - dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; - - std::map<IdString, int> cell_stats; - for (auto mapped_cell : mapped_mod->cells()) - { - toposort.node(mapped_cell->name); - - RTLIL::Cell *cell = nullptr; - if (mapped_cell->type == ID($_NOT_)) { - RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); - RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); - bit_users[a_bit].insert(mapped_cell->name); - bit_drivers[y_bit].insert(mapped_cell->name); - - if (!a_bit.wire) { - mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); - RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); - log_assert(wire); - module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); - } - else if (!lut_costs.empty() || !lut_file.empty()) { - RTLIL::Cell* driver_lut = nullptr; - // ABC can return NOT gates that drive POs - if (!a_bit.wire->port_input) { - // If it's not a NOT gate that that comes from a PI directly, - // find the driver LUT and clone that to guarantee that we won't - // increase the max logic depth - // (TODO: Optimise by not cloning unless will increase depth) - RTLIL::IdString driver_name; - if (GetSize(a_bit.wire) == 1) - driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); - else - driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); - driver_lut = mapped_mod->cell(driver_name); - } - - if (!driver_lut) { - // If a driver couldn't be found (could be from PI or box CI) - // then implement using a LUT - cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), - RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), - RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), - RTLIL::Const::from_string("01")); - bit2sinks[cell->getPort(ID::A)].push_back(cell); - cell_stats[ID($lut)]++; - } - else - not2drivers[mapped_cell] = driver_lut; - continue; - } - else - log_abort(); - continue; - } - cell_stats[mapped_cell->type]++; - - RTLIL::Cell *existing_cell = nullptr; - if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) { - 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; - } - cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); - } - else { - existing_cell = module->cell(mapped_cell->name); - log_assert(existing_cell); - cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); - } - - if (existing_cell) { - cell->parameters = existing_cell->parameters; - cell->attributes = existing_cell->attributes; - } - else { - cell->parameters = mapped_cell->parameters; - cell->attributes = mapped_cell->attributes; - } - - RTLIL::Module* box_module = design->module(mapped_cell->type); - auto abc9_flop = box_module && box_module->get_bool_attribute("\\abc9_flop"); - for (auto &conn : mapped_cell->connections()) { - RTLIL::SigSpec newsig; - for (auto c : conn.second.chunks()) { - if (c.width == 0) - continue; - //log_assert(c.width == 1); - if (c.wire) - c.wire = module->wires_.at(remap_name(c.wire->name)); - newsig.append(c); - } - cell->setPort(conn.first, newsig); - - if (!abc9_flop) { - if (cell->input(conn.first)) { - for (auto i : newsig) - bit2sinks[i].push_back(cell); - for (auto i : conn.second) - bit_users[i].insert(mapped_cell->name); - } - if (cell->output(conn.first)) - for (auto i : conn.second) - bit_drivers[i].insert(mapped_cell->name); - } - } - } - - for (auto existing_cell : boxes) { - Cell *cell = module->cell(remap_name(existing_cell->name)); - if (cell) { - for (auto &conn : existing_cell->connections()) { - if (!conn.second.is_wire()) - continue; - Wire *wire = conn.second.as_wire(); - if (!wire->get_bool_attribute(ID(abc9_padding))) - continue; - cell->unsetPort(conn.first); - log_debug("Dropping padded port connection for %s (%s) .%s (%s )\n", log_id(cell), cell->type.c_str(), log_id(conn.first), log_signal(conn.second)); - } - module->swap_names(cell, existing_cell); - } - module->remove(existing_cell); - } - - // Copy connections (and rename) from mapped_mod to module - for (auto conn : mapped_mod->connections()) { - if (!conn.first.is_fully_const()) { - auto chunks = conn.first.chunks(); - for (auto &c : chunks) - c.wire = module->wires_.at(remap_name(c.wire->name)); - conn.first = std::move(chunks); - } - if (!conn.second.is_fully_const()) { - auto chunks = conn.second.chunks(); - for (auto &c : chunks) - if (c.wire) - c.wire = module->wires_.at(remap_name(c.wire->name)); - conn.second = std::move(chunks); - } - module->connect(conn); - } - - for (auto &it : cell_stats) - log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); - int in_wires = 0, out_wires = 0; - - // Stitch in mapped_mod's inputs/outputs into module - for (auto port : mapped_mod->ports) { - RTLIL::Wire *w = mapped_mod->wire(port); - RTLIL::Wire *wire = module->wire(port); - log_assert(wire); - RTLIL::Wire *remap_wire = module->wire(remap_name(port)); - RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire)); - log_assert(GetSize(signal) >= GetSize(remap_wire)); - - RTLIL::SigSig conn; - if (w->port_output) { - conn.first = signal; - conn.second = remap_wire; - out_wires++; - module->connect(conn); - } - else if (w->port_input) { - conn.first = remap_wire; - conn.second = signal; - in_wires++; - module->connect(conn); - } - } - - for (auto &it : bit_users) - if (bit_drivers.count(it.first)) - 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(); - log_assert(no_loops); - - for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { - RTLIL::Cell *not_cell = mapped_mod->cell(*ii); - log_assert(not_cell); - if (not_cell->type != ID($_NOT_)) - continue; - auto it = not2drivers.find(not_cell); - if (it == not2drivers.end()) - continue; - RTLIL::Cell *driver_lut = it->second; - RTLIL::SigBit a_bit = not_cell->getPort(ID::A); - RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); - RTLIL::Const driver_mask; - - a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); - y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); - - auto jt = bit2sinks.find(a_bit); - if (jt == bit2sinks.end()) - goto clone_lut; - - for (auto sink_cell : jt->second) - if (sink_cell->type != ID($lut)) - goto clone_lut; - - // Push downstream LUTs past inverter - for (auto sink_cell : jt->second) { - SigSpec A = sink_cell->getPort(ID::A); - RTLIL::Const mask = sink_cell->getParam(ID(LUT)); - int index = 0; - for (; index < GetSize(A); index++) - if (A[index] == a_bit) - break; - log_assert(index < GetSize(A)); - int i = 0; - while (i < GetSize(mask)) { - for (int j = 0; j < (1 << index); j++) - std::swap(mask[i+j], mask[i+j+(1 << index)]); - i += 1 << (index+1); - } - A[index] = y_bit; - sink_cell->setPort(ID::A, A); - sink_cell->setParam(ID(LUT), mask); - } - - // Since we have rewritten all sinks (which we know - // to be only LUTs) to be after the inverter, we can - // go ahead and clone the LUT with the expectation - // that the original driving LUT will become dangling - // and get cleaned away -clone_lut: - driver_mask = driver_lut->getParam(ID(LUT)); - for (auto &b : driver_mask.bits) { - if (b == RTLIL::State::S0) b = RTLIL::State::S1; - else if (b == RTLIL::State::S1) b = RTLIL::State::S0; - } - auto cell = module->addLut(NEW_ID, - driver_lut->getPort(ID::A), - y_bit, - driver_mask); - for (auto &bit : cell->connections_.at(ID::A)) { - bit.wire = module->wires_.at(remap_name(bit.wire->name)); - bit2sinks[bit].push_back(cell); - } - } - - // Now 'unexpose' those wires by undoing - // the expose operation -- remove them from PO/PI - // and re-connecting them back together - for (auto wire : module->wires()) { - auto it = wire->attributes.find(ID(abc9_scc_break)); - if (it != wire->attributes.end()) { - wire->attributes.erase(it); - log_assert(wire->port_output); - wire->port_output = false; - std::string name = wire->name.str(); - RTLIL::Wire *i_wire = module->wire(name.substr(0, GetSize(name) - 5)); - log_assert(i_wire); - log_assert(i_wire->port_input); - i_wire->port_input = false; - module->connect(i_wire, wire); - } - } - module->fixup_ports(); - - //log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); - log("ABC RESULTS: input signals: %8d\n", in_wires); - log("ABC RESULTS: output signals: %8d\n", out_wires); - - design->remove(mapped_mod); - } - else - { - log("Don't call ABC as there is nothing to map.\n"); - } - - if (cleanup) - { - log("Removing temp directory.\n"); - remove_directory(tempdir_name); - } - - log_pop(); -} - -struct Abc9Pass : public Pass { - Abc9Pass() : Pass("abc9", "use ABC9 for technology mapping") { } + Abc9Pass() : ScriptPass("abc9", "use ABC9 for technology mapping") { } void on_register() YS_OVERRIDE { RTLIL::constpad["abc9.script.default"] = "+&scorr; &sweep; &dc2; &dch -f; &ps; &if {C} {W} {D} {R} -v; &mfs"; @@ -782,8 +87,14 @@ struct Abc9Pass : public Pass { log("\n"); log(" abc9 [options] [selection]\n"); log("\n"); - log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); - log("library to a target architecture. Only fully-selected modules are supported.\n"); + log("This script pass performs a sequence of commands to facilitate the use of the ABC\n"); + log("tool [1] for technology mapping of the current design to a target FPGA\n"); + log("architecture. Only fully-selected modules are supported.\n"); + log("\n"); + log(" -run <from_label>:<to_label>\n"); + log(" only run the commands between the labels (see below). an empty\n"); + log(" from label is synonymous to 'begin', and empty to label is\n"); + log(" synonymous to the end of the command list.\n"); log("\n"); log(" -exe <command>\n"); #ifdef ABCEXTERNAL @@ -802,15 +113,11 @@ struct Abc9Pass : public Pass { log(" replaced with blanks before the string is passed to ABC.\n"); log("\n"); log(" if no -script parameter is given, the following scripts are used:\n"); - log("\n"); - log(" for -lut/-luts:\n"); log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str()); log("\n"); log(" -fast\n"); log(" use different default scripts that are slightly faster (at the cost\n"); log(" of output quality):\n"); - log("\n"); - log(" for -lut/-luts:\n"); log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str()); log("\n"); log(" -D <picoseconds>\n"); @@ -851,7 +158,7 @@ struct Abc9Pass : public Pass { log(" command output is identical across runs.\n"); log("\n"); log(" -box <file>\n"); - log(" pass this file with box library to ABC. Use with -lut.\n"); + log(" pass this file with box library to ABC.\n"); log("\n"); log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); @@ -862,234 +169,163 @@ struct Abc9Pass : public Pass { log("\n"); log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("\n"); + help_script(); + log("\n"); } - void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE - { - log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\n"); - log_push(); -#ifdef ABCEXTERNAL - std::string exe_file = ABCEXTERNAL; -#else - std::string exe_file = proc_self_dirname() + "yosys-abc"; -#endif - std::string script_file, clk_str, box_file, lut_file; - std::string delay_target, lutin_shared = "-S 1", wire_delay; - bool fast_mode = false, dff_mode = false, cleanup = true; - bool show_tempdir = false; - vector<int> lut_costs; + std::stringstream exe_cmd; + bool dff_mode, cleanup; -#if 0 - cleanup = false; - show_tempdir = true; -#endif + void clear_flags() YS_OVERRIDE + { + exe_cmd.str(""); + exe_cmd << "abc9_exe"; + dff_mode = false; + cleanup = true; + } -#ifdef _WIN32 -#ifndef ABCEXTERNAL - if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) - exe_file = proc_self_dirname() + "..\\yosys-abc"; -#endif -#endif + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + std::string run_from, run_to; + clear_flags(); // get arguments from scratchpad first, then override by command arguments - std::string lut_arg, luts_arg; - exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); - script_file = design->scratchpad_get_string("abc9.script", script_file); - if (design->scratchpad.count("abc9.D")) { - delay_target = "-D " + design->scratchpad_get_string("abc9.D"); - } - lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); - luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); - fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode); dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode); cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup); - show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); - box_file = design->scratchpad_get_string("abc9.box", box_file); - if (design->scratchpad.count("abc9.W")) { - wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); - } if (design->scratchpad_get_bool("abc9.debug")) { cleanup = false; - show_tempdir = true; + exe_cmd << " -showtmp"; } size_t argidx; - char pwd [PATH_MAX]; - if (!getcwd(pwd, sizeof(pwd))) { - log_cmd_error("getcwd failed: %s\n", strerror(errno)); - log_abort(); - } for (argidx = 1; argidx < args.size(); argidx++) { std::string arg = args[argidx]; - if (arg == "-exe" && argidx+1 < args.size()) { - exe_file = args[++argidx]; - continue; - } - if (arg == "-script" && argidx+1 < args.size()) { - script_file = args[++argidx]; - continue; - } - if (arg == "-D" && argidx+1 < args.size()) { - delay_target = "-D " + args[++argidx]; - continue; - } - //if (arg == "-S" && argidx+1 < args.size()) { - // lutin_shared = "-S " + args[++argidx]; - // continue; - //} - if (arg == "-lut" && argidx+1 < args.size()) { - lut_arg = args[++argidx]; + if ((arg == "-exe" || arg == "-script" || arg == "-D" || + /* arg == "-S" || */ arg == "-lut" || arg == "-luts" || + arg == "-box" || arg == "-W") && + argidx+1 < args.size()) { + exe_cmd << " " << arg << " " << args[++argidx]; continue; } - if (arg == "-luts" && argidx+1 < args.size()) { - luts_arg = args[++argidx]; - continue; - } - if (arg == "-fast") { - fast_mode = true; + if (arg == "-fast" || /* arg == "-dff" || */ + /* arg == "-nocleanup" || */ arg == "-showtmp") { + exe_cmd << " " << arg; continue; } if (arg == "-dff") { dff_mode = true; + exe_cmd << " " << arg; continue; } if (arg == "-nocleanup") { cleanup = false; continue; } - if (arg == "-showtmp") { - show_tempdir = true; - continue; - } - if (arg == "-box" && argidx+1 < args.size()) { - box_file = args[++argidx]; - continue; - } - if (arg == "-W" && argidx+1 < args.size()) { - wire_delay = "-W " + args[++argidx]; + if (arg == "-run" && argidx+1 < args.size()) { + size_t pos = args[argidx+1].find(':'); + if (pos == std::string::npos) + break; + run_from = args[++argidx].substr(0, pos); + run_to = args[argidx].substr(pos+1); continue; } break; } extra_args(args, argidx, design); - rewrite_filename(script_file); - if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') - script_file = std::string(pwd) + "/" + script_file; - - // handle -lut / -luts args - if (!lut_arg.empty()) { - string arg = lut_arg; - 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) { - lut_mode = atoi(arg.substr(0, pos).c_str()); - lut_mode2 = atoi(arg.substr(pos+1).c_str()); - } else { - lut_mode = atoi(arg.c_str()); - lut_mode2 = lut_mode; - } - lut_costs.clear(); - for (int i = 0; i < lut_mode; i++) - lut_costs.push_back(1); - for (int i = lut_mode; i < lut_mode2; i++) - lut_costs.push_back(2 << (i - lut_mode)); - } - else { - lut_file = arg; - rewrite_filename(lut_file); - if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') - lut_file = std::string(pwd) + "/" + lut_file; - } - } - if (!luts_arg.empty()) { - lut_costs.clear(); - for (auto &tok : split_tokens(luts_arg, ",")) { - auto parts = split_tokens(tok, ":"); - if (GetSize(parts) == 0 && !lut_costs.empty()) - lut_costs.push_back(lut_costs.back()); - else if (GetSize(parts) == 1) - lut_costs.push_back(atoi(parts.at(0).c_str())); - else if (GetSize(parts) == 2) - while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) - lut_costs.push_back(atoi(parts.at(1).c_str())); - else - log_cmd_error("Invalid -luts syntax.\n"); - } + log_assert(design); + if (design->selected_modules().empty()) { + log_warning("No modules selected for ABC9 techmapping.\n"); + return; } - // ABC expects a box file for XAIG - if (box_file.empty()) - box_file = "+/dummy.box"; + log_header(design, "Executing ABC9 pass.\n"); + log_push(); - rewrite_filename(box_file); - if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') - box_file = std::string(pwd) + "/" + box_file; + run_script(design, run_from, run_to); - SigMap assign_map; - CellTypes ct(design); - for (auto module : design->selected_modules()) - { - if (module->processes.size() > 0) { - log("Skipping module %s as it contains processes.\n", log_id(module)); - continue; + log_pop(); + } + + void script() YS_OVERRIDE + { + if (check_label("pre")) { + run("scc -set_attr abc9_scc_id {}"); + if (help_mode) + run("abc9_ops -mark_scc -prep_xaiger [-dff]", "(option for -dff)"); + else + run("abc9_ops -mark_scc -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"); + if (dff_mode || help_mode) + run("abc9_ops -prep_dff", "(only if -dff)"); + run("opt -purge @abc9_holes"); + run("aigmap"); + run("wbflip @abc9_holes"); + } + + if (check_label("map")) { + if (help_mode) { + run("foreach module in selection"); + run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); + run(" abc9_exe -cwd <abc-temp-dir> [options]"); + 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"); } - log_assert(!module->attributes.count(ID(abc9_box_id))); + else { + auto selected_modules = active_design->selected_modules(); + active_design->selection_stack.emplace_back(false); - if (!design->selected_whole_module(module)) - log_error("Can't handle partially selected module %s!\n", log_id(module)); + for (auto mod : selected_modules) { + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); + continue; + } + log_assert(!mod->attributes.count(ID(abc9_box_id))); - assign_map.set(module); + log_push(); + active_design->selection().select(mod); - typedef SigSpec clkdomain_t; - dict<clkdomain_t, int> clk_to_mergeability; + if (!active_design->selected_whole_module(mod)) + log_error("Can't handle partially selected module %s!\n", log_id(mod)); - if (dff_mode) - for (auto cell : module->cells()) { - if (cell->type != "$__ABC9_FF_") - continue; + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; + if (!cleanup) + tempdir_name[0] = tempdir_name[4] = '_'; + tempdir_name = make_temp_dir(tempdir_name); - 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); + run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); - clkdomain_t key(abc9_clock); + int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); - auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); - auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second)); - log_assert(r2.second); + log("Extracted %d AND gates and %d wires from module `%s' to a netlist network with %d inputs and %d outputs.\n", + active_design->scratchpad_get_int("write_xaiger.num_ands"), + active_design->scratchpad_get_int("write_xaiger.num_wires"), + log_id(mod), + 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("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"); + } + else + log("Don't call ABC as there is nothing to map.\n"); - Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); - if (abc9_init_wire == NULL) - log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); - log_assert(GetSize(abc9_init_wire) == 1); - SigSpec abc9_init = assign_map(abc9_init_wire); - if (!abc9_init.is_fully_const()) - log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); - if (abc9_init == State::S1) - log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module)); - r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); - log_assert(r2.second); - } - else - for (auto cell : module->cells()) { - auto inst_module = design->module(cell->type); - if (!inst_module || !inst_module->get_bool_attribute("\\abc9_flop")) - continue; - cell->set_bool_attribute("\\abc9_keep"); + if (cleanup) { + log("Removing temp directory.\n"); + remove_directory(tempdir_name); + } + + active_design->selection().selected_modules.clear(); + log_pop(); } - design->selected_active_module = module->name.str(); - abc9_module(design, module, script_file, exe_file, cleanup, lut_costs, dff_mode, - delay_target, lutin_shared, fast_mode, show_tempdir, - box_file, lut_file, wire_delay); - design->selected_active_module.clear(); + active_design->selection_stack.pop_back(); + } } - - log_pop(); } } Abc9Pass; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc new file mode 100644 index 000000000..01bf46539 --- /dev/null +++ b/passes/techmap/abc9_exe.cc @@ -0,0 +1,531 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] ABC +// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification +// http://www.eecs.berkeley.edu/~alanmi/abc/ + +#include "kernel/register.h" +#include "kernel/log.h" + +#ifndef _WIN32 +# include <unistd.h> +# include <dirent.h> +#endif + +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + +std::string fold_abc9_cmd(std::string str) +{ + std::string token, new_str = " "; + int char_counter = 10; + + for (size_t i = 0; i <= str.size(); i++) { + if (i < str.size()) + token += str[i]; + if (i == str.size() || str[i] == ';') { + if (char_counter + token.size() > 75) + new_str += "\n ", char_counter = 14; + new_str += token, char_counter += token.size(); + token.clear(); + } + } + + return new_str; +} + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +std::string add_echos_to_abc9_cmd(std::string str) +{ + std::string new_str, token; + for (size_t i = 0; i < str.size(); i++) { + token += str[i]; + if (str[i] == ';') { + while (i+1 < str.size() && str[i+1] == ' ') + i++; + new_str += "echo + " + token + " " + token + " "; + token.clear(); + } + } + + if (!token.empty()) { + if (!new_str.empty()) + new_str += "echo + " + token + "; "; + new_str += token; + } + + return new_str; +} + +std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir) +{ + if (show_tempdir) + return text; + + while (1) { + size_t pos = text.find(tempdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name)); + } + + std::string selfdir_name = proc_self_dirname(); + if (selfdir_name != "/") { + while (1) { + size_t pos = text.find(selfdir_name); + if (pos == std::string::npos) + break; + text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name)); + } + } + + return text; +} + +struct abc9_output_filter +{ + bool got_cr; + int escape_seq_state; + std::string linebuf; + std::string tempdir_name; + bool show_tempdir; + + abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir) + { + got_cr = false; + escape_seq_state = 0; + } + + void next_char(char ch) + { + if (escape_seq_state == 0 && ch == '\033') { + escape_seq_state = 1; + return; + } + if (escape_seq_state == 1) { + escape_seq_state = ch == '[' ? 2 : 0; + return; + } + if (escape_seq_state == 2) { + if ((ch < '0' || '9' < ch) && ch != ';') + escape_seq_state = 0; + return; + } + escape_seq_state = 0; + if (ch == '\r') { + got_cr = true; + return; + } + if (ch == '\n') { + log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str()); + got_cr = false, linebuf.clear(); + return; + } + if (got_cr) + got_cr = false, linebuf.clear(); + linebuf += ch; + } + + void next_line(const std::string &line) + { + //int pi, po; + //if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) { + // log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n", + // pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???", + // po, po_map.count(po) ? po_map.at(po).c_str() : "???"); + // return; + //} + + for (char ch : line) + next_char(ch); + } +}; + +void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file, + vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, + bool show_tempdir, std::string box_file, std::string lut_file, + std::string wire_delay, std::string tempdir_name +) +{ + std::string abc9_script; + + if (!lut_costs.empty()) + abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); + else if (!lut_file.empty()) + abc9_script += stringf("read_lut %s; ", lut_file.c_str()); + else + log_abort(); + + log_assert(!box_file.empty()); + abc9_script += stringf("read_box %s; ", box_file.c_str()); + abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); + + if (!script_file.empty()) { + if (script_file[0] == '+') { + for (size_t i = 1; i < script_file.size(); i++) + if (script_file[i] == '\'') + abc9_script += "'\\''"; + else if (script_file[i] == ',') + abc9_script += " "; + else + abc9_script += script_file[i]; + } else + abc9_script += stringf("source %s", script_file.c_str()); + } else if (!lut_costs.empty() || !lut_file.empty()) { + abc9_script += fast_mode ? RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos) + : RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos); + } else + log_abort(); + + for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) + abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3); + + //for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos)) + // abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3); + + for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos)) + abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3); + + std::string C; + if (design->scratchpad.count("abc9.if.C")) + C = "-C " + design->scratchpad_get_string("abc9.if.C"); + for (size_t pos = abc9_script.find("{C}"); pos != std::string::npos; pos = abc9_script.find("{C}", pos)) + abc9_script = abc9_script.substr(0, pos) + C + abc9_script.substr(pos+3); + + std::string R; + if (design->scratchpad.count("abc9.if.R")) + R = "-R " + design->scratchpad_get_string("abc9.if.R"); + for (size_t pos = abc9_script.find("{R}"); pos != std::string::npos; pos = abc9_script.find("{R}", pos)) + abc9_script = abc9_script.substr(0, pos) + R + abc9_script.substr(pos+3); + + abc9_script += stringf("; &ps -l; &write -n %s/output.aig", tempdir_name.c_str()); + if (design->scratchpad_get_bool("abc9.verify")) { + if (dff_mode) + abc9_script += "; verify -s"; + else + abc9_script += "; verify"; + } + abc9_script += "; time"; + abc9_script = add_echos_to_abc9_cmd(abc9_script); + + for (size_t i = 0; i+1 < abc9_script.size(); i++) + if (abc9_script[i] == ';' && abc9_script[i+1] == ' ') + abc9_script[i+1] = '\n'; + + FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); + fprintf(f, "%s\n", abc9_script.c_str()); + fclose(f); + + std::string buffer; + + log_header(design, "Executing ABC9.\n"); + + if (!lut_costs.empty()) { + buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str()); + f = fopen(buffer.c_str(), "wt"); + if (f == NULL) + log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno)); + for (int i = 0; i < GetSize(lut_costs); i++) + fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i)); + fclose(f); + } + + buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str()); + log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str()); + +#ifndef YOSYS_LINK_ABC + abc9_output_filter filt(tempdir_name, show_tempdir); + int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1)); +#else + // These needs to be mutable, supposedly due to getopt + char *abc9_argv[5]; + string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); + abc9_argv[0] = strdup(exe_file.c_str()); + abc9_argv[1] = strdup("-s"); + abc9_argv[2] = strdup("-f"); + abc9_argv[3] = strdup(tmp_script_name.c_str()); + abc9_argv[4] = 0; + int ret = Abc_RealMain(4, abc9_argv); + free(abc9_argv[0]); + free(abc9_argv[1]); + free(abc9_argv[2]); + free(abc9_argv[3]); +#endif + if (ret != 0) + log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret); +} + +struct Abc9ExePass : public Pass { + Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" abc9_exe [options]\n"); + log("\n"); + log(" \n"); + log("This pass uses the ABC tool [1] for technology mapping of the top module\n"); + log("(according to the (* top *) attribute or if only one module is currently selected)\n"); + log("to a target FPGA architecture.\n"); + log("\n"); + log(" -exe <command>\n"); +#ifdef ABCEXTERNAL + log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n"); +#else + log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n"); +#endif + log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n"); + log("\n"); + log(" -script <file>\n"); + log(" use the specified ABC script file instead of the default script.\n"); + log("\n"); + log(" if <file> starts with a plus sign (+), then the rest of the filename\n"); + log(" string is interpreted as the command string to be passed to ABC. The\n"); + log(" leading plus sign is removed and all commas (,) in the string are\n"); + log(" replaced with blanks before the string is passed to ABC.\n"); + log("\n"); + log(" if no -script parameter is given, the following scripts are used:\n"); + log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default").substr(1,std::string::npos)).c_str()); + log("\n"); + log(" -fast\n"); + log(" use different default scripts that are slightly faster (at the cost\n"); + log(" of output quality):\n"); + log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str()); + log("\n"); + log(" -D <picoseconds>\n"); + log(" set delay target. the string {D} in the default scripts above is\n"); + log(" replaced by this option when used, and an empty string otherwise\n"); + log(" (indicating best possible delay).\n"); + log("\n"); +// log(" -S <num>\n"); +// log(" maximum number of LUT inputs shared.\n"); +// log(" (replaces {S} in the default scripts above, default: -S 1)\n"); +// log("\n"); + log(" -lut <width>\n"); + log(" generate netlist using luts of (max) the specified width.\n"); + log("\n"); + log(" -lut <w1>:<w2>\n"); + log(" generate netlist using luts of (max) the specified width <w2>. All\n"); + log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n"); + log(" the area cost doubles with each additional input bit. the delay cost\n"); + log(" is still constant for all lut widths.\n"); + log("\n"); + log(" -lut <file>\n"); + log(" pass this file with lut library to ABC.\n"); + log("\n"); + log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n"); + log(" generate netlist using luts. Use the specified costs for luts with 1,\n"); + log(" 2, 3, .. inputs.\n"); + log("\n"); + log(" -showtmp\n"); + log(" print the temp dir name in log. usually this is suppressed so that the\n"); + log(" command output is identical across runs.\n"); + log("\n"); + log(" -box <file>\n"); + log(" pass this file with box library to ABC.\n"); + log("\n"); + log(" -cwd <dir>\n"); + log(" use this as the current working directory, inside which the 'input.xaig'\n"); + log(" file is expected. temporary files will be created in this directory, and\n"); + log(" the mapped result will be written to 'output.aig'.\n"); + log("\n"); + log("Note that this is a logic optimization pass within Yosys that is calling ABC\n"); + log("internally. This is not going to \"run ABC on your design\". It will instead run\n"); + log("ABC on logic snippets extracted from your design. You will not get any useful\n"); + log("output when passing an ABC script that writes a file. Instead write your full\n"); + log("design as BLIF file with write_blif and then load that into ABC externally if\n"); + log("you want to use ABC to convert your design into another format.\n"); + log("\n"); + log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n"); + +#ifdef ABCEXTERNAL + std::string exe_file = ABCEXTERNAL; +#else + std::string exe_file = proc_self_dirname() + "yosys-abc"; +#endif + std::string script_file, clk_str, box_file, lut_file; + std::string delay_target, lutin_shared = "-S 1", wire_delay; + std::string tempdir_name; + bool fast_mode = false, dff_mode = false; + bool show_tempdir = false; + vector<int> lut_costs; + +#if 0 + cleanup = false; + show_tempdir = true; +#endif + +#ifdef _WIN32 +#ifndef ABCEXTERNAL + if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) + exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif +#endif + + std::string lut_arg, luts_arg; + exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */); + script_file = design->scratchpad_get_string("abc9.script", script_file); + if (design->scratchpad.count("abc9.D")) { + delay_target = "-D " + design->scratchpad_get_string("abc9.D"); + } + lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); + luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); + fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode); + dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode); + show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); + box_file = design->scratchpad_get_string("abc9.box", box_file); + if (design->scratchpad.count("abc9.W")) { + wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); + } + + size_t argidx; + char pwd [PATH_MAX]; + if (!getcwd(pwd, sizeof(pwd))) { + log_cmd_error("getcwd failed: %s\n", strerror(errno)); + log_abort(); + } + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-exe" && argidx+1 < args.size()) { + exe_file = args[++argidx]; + continue; + } + if (arg == "-script" && argidx+1 < args.size()) { + script_file = args[++argidx]; + continue; + } + if (arg == "-D" && argidx+1 < args.size()) { + delay_target = "-D " + args[++argidx]; + continue; + } + //if (arg == "-S" && argidx+1 < args.size()) { + // lutin_shared = "-S " + args[++argidx]; + // continue; + //} + if (arg == "-lut" && argidx+1 < args.size()) { + lut_arg = args[++argidx]; + continue; + } + if (arg == "-luts" && argidx+1 < args.size()) { + lut_arg = args[++argidx]; + continue; + } + if (arg == "-fast") { + fast_mode = true; + continue; + } + if (arg == "-dff") { + dff_mode = true; + continue; + } + if (arg == "-showtmp") { + show_tempdir = true; + continue; + } + if (arg == "-box" && argidx+1 < args.size()) { + box_file = args[++argidx]; + continue; + } + if (arg == "-W" && argidx+1 < args.size()) { + wire_delay = "-W " + args[++argidx]; + continue; + } + if (arg == "-cwd" && argidx+1 < args.size()) { + tempdir_name = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + rewrite_filename(script_file); + if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+') + script_file = std::string(pwd) + "/" + script_file; + + // handle -lut / -luts args + if (!lut_arg.empty()) { + string arg = lut_arg; + 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) { + lut_mode = atoi(arg.substr(0, pos).c_str()); + lut_mode2 = atoi(arg.substr(pos+1).c_str()); + } else { + lut_mode = atoi(arg.c_str()); + lut_mode2 = lut_mode; + } + lut_costs.clear(); + for (int i = 0; i < lut_mode; i++) + lut_costs.push_back(1); + for (int i = lut_mode; i < lut_mode2; i++) + lut_costs.push_back(2 << (i - lut_mode)); + } + else { + lut_file = arg; + rewrite_filename(lut_file); + if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+') + lut_file = std::string(pwd) + "/" + lut_file; + } + } + if (!luts_arg.empty()) { + lut_costs.clear(); + for (auto &tok : split_tokens(luts_arg, ",")) { + auto parts = split_tokens(tok, ":"); + if (GetSize(parts) == 0 && !lut_costs.empty()) + lut_costs.push_back(lut_costs.back()); + else if (GetSize(parts) == 1) + lut_costs.push_back(atoi(parts.at(0).c_str())); + else if (GetSize(parts) == 2) + while (GetSize(lut_costs) < atoi(parts.at(0).c_str())) + lut_costs.push_back(atoi(parts.at(1).c_str())); + else + log_cmd_error("Invalid -luts syntax.\n"); + } + } + + // ABC expects a box file for XAIG + if (box_file.empty()) + box_file = "+/dummy.box"; + + rewrite_filename(box_file); + if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') + box_file = std::string(pwd) + "/" + box_file; + + if (tempdir_name.empty()) + log_cmd_error("abc9_exe '-cwd' option is mandatory.\n"); + + + abc9_module(design, script_file, exe_file, lut_costs, dff_mode, + delay_target, lutin_shared, fast_mode, show_tempdir, + box_file, lut_file, wire_delay, tempdir_name); + } +} Abc9ExePass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc new file mode 100644 index 000000000..9ad29a8f6 --- /dev/null +++ b/passes/techmap/abc9_ops.cc @@ -0,0 +1,825 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" +#include "kernel/celltypes.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +int map_autoidx; + +inline std::string remap_name(RTLIL::IdString abc9_name) +{ + return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); +} + +void mark_scc(RTLIL::Module *module) +{ + // For every unique SCC found, (arbitrarily) find the first + // cell in the component, and convert all wires driven by + // its output ports into a new PO, and drive its previous + // sinks with a new PI + pool<RTLIL::Const> ids_seen; + for (auto cell : module->cells()) { + auto it = cell->attributes.find(ID(abc9_scc_id)); + if (it == cell->attributes.end()) + continue; + auto id = it->second; + auto r = ids_seen.insert(id); + cell->attributes.erase(it); + if (!r.second) + continue; + for (auto &c : cell->connections_) { + if (c.second.is_fully_const()) continue; + if (cell->output(c.first)) { + SigBit b = c.second.as_bit(); + Wire *w = b.wire; + w->set_bool_attribute(ID::keep); + w->attributes[ID(abc9_scc_id)] = id.as_int(); + } + } + } + + module->fixup_ports(); +} + +void prep_dff(RTLIL::Module *module) +{ + auto design = module->design; + log_assert(design); + + SigMap assign_map(module); + + typedef SigSpec clkdomain_t; + dict<clkdomain_t, int> clk_to_mergeability; + + for (auto cell : module->cells()) { + if (cell->type != "$__ABC9_FF_") + 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); + + clkdomain_t key(abc9_clock); + + auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1)); + auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second)); + log_assert(r2.second); + + Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str())); + if (abc9_init_wire == NULL) + log_error("'%s.init' is not a wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); + log_assert(GetSize(abc9_init_wire) == 1); + SigSpec abc9_init = assign_map(abc9_init_wire); + if (!abc9_init.is_fully_const()) + log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module)); + if (abc9_init == State::S1) + log_error("'%s.init' in module '%s' has value 1'b1 which is not supported by 'abc9 -dff'.\n", cell->name.c_str(), log_id(module)); + r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const())); + log_assert(r2.second); + } + + RTLIL::Module *holes_module = design->module(stringf("%s$holes", module->name.c_str())); + if (holes_module) { + SigMap sigmap(holes_module); + + dict<SigSpec, SigSpec> replace; + for (auto cell : holes_module->cells().to_vector()) { + if (!cell->type.in("$_DFF_N_", "$_DFF_NN0_", "$_DFF_NN1_", "$_DFF_NP0_", "$_DFF_NP1_", + "$_DFF_P_", "$_DFF_PN0_", "$_DFF_PN1", "$_DFF_PP0_", "$_DFF_PP1_")) + continue; + SigBit D = cell->getPort("\\D"); + SigBit Q = cell->getPort("\\Q"); + // Emulate async control embedded inside $_DFF_* cell with mux in front of D + if (cell->type.in("$_DFF_NN0_", "$_DFF_PN0_")) + D = holes_module->MuxGate(NEW_ID, State::S0, D, cell->getPort("\\R")); + else if (cell->type.in("$_DFF_NN1_", "$_DFF_PN1_")) + D = holes_module->MuxGate(NEW_ID, State::S1, D, cell->getPort("\\R")); + else if (cell->type.in("$_DFF_NP0_", "$_DFF_PP0_")) + D = holes_module->MuxGate(NEW_ID, D, State::S0, cell->getPort("\\R")); + else if (cell->type.in("$_DFF_NP1_", "$_DFF_PP1_")) + D = holes_module->MuxGate(NEW_ID, D, State::S1, cell->getPort("\\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); + } +} + +void prep_xaiger(RTLIL::Module *module, bool dff) +{ + auto design = module->design; + log_assert(design); + + SigMap sigmap(module); + + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + dict<IdString, std::vector<IdString>> box_ports; + + for (auto cell : module->cells()) { + if (cell->type == "$__ABC9_FF_") + continue; + if (cell->has_keep_attr()) + 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; + + auto r = box_ports.insert(cell->type); + 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 : inst_module->ports) { + 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)); + 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)); + 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); + } + } + } + else if (!yosys_celltypes.cell_known(cell->type)) + continue; + + // TODO: Speed up toposort -- we care about box ordering only + for (auto conn : cell->connections()) { + if (cell->input(conn.first)) + for (auto bit : sigmap(conn.second)) + 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); + } + toposort.node(cell->name); + } + + if (box_ports.empty()) + return; + + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + for (auto driver_cell : bit_drivers.at(it.first)) + for (auto user_cell : it.second) + toposort.edge(driver_cell, user_cell); + + if (ys_debug(1)) + toposort.analyze_loops = true; + + bool no_loops YS_ATTRIBUTE(unused) = toposort.sort(); + + if (ys_debug(1)) { + unsigned i = 0; + for (auto &it : toposort.loops) { + log(" loop %d\n", i++); + for (auto cell_name : it) { + auto cell = module->cell(cell_name); + log_assert(cell); + log("\t%s (%s @ %s)\n", log_id(cell), log_id(cell->type), cell->get_src_attribute().c_str()); + } + } + } + + log_assert(no_loops); + + RTLIL::Module *holes_module = design->addModule(stringf("%s$holes", module->name.c_str())); + log_assert(holes_module); + holes_module->set_bool_attribute("\\abc9_holes"); + + dict<IdString, Cell*> cell_cache; + + int port_id = 1, box_count = 0; + for (auto cell_name : toposort.sorted) { + RTLIL::Cell *cell = module->cell(cell_name); + log_assert(cell); + + RTLIL::Module* box_module = design->module(cell->type); + if (!box_module || !box_module->attributes.count("\\abc9_box_id")) + continue; + + cell->attributes["\\abc9_box_seq"] = box_count++; + + IdString derived_name = box_module->derive(design, cell->parameters); + box_module = design->module(derived_name); + + auto r = cell_cache.insert(derived_name); + 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); + + int box_inputs = 0; + for (auto port_name : box_ports.at(cell->type)) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + log_assert(!w->port_input || !w->port_output); + auto &conn = holes_cell->connections_[port_name]; + if (w->port_input) { + for (int i = 0; i < GetSize(w); i++) { + box_inputs++; + RTLIL::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); + } + conn.append(holes_wire); + } + } + else if (w->port_output) + conn = holes_module->addWire(stringf("%s.%s", derived_name.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("\\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); + } + } + else // box_module is a blackbox + log_assert(holes_cell == nullptr); + } + + for (auto port_name : box_ports.at(cell->type)) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + if (!w->port_output) + continue; + Wire *holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(port_name)), GetSize(w)); + holes_wire->port_output = true; + holes_wire->port_id = port_id++; + holes_module->ports.push_back(holes_wire->name); + if (holes_cell) // whitebox + holes_module->connect(holes_wire, holes_cell->getPort(port_name)); + else // blackbox + holes_module->connect(holes_wire, Const(State::S0, GetSize(w))); + } + } +} + +void reintegrate(RTLIL::Module *module) +{ + auto design = module->design; + log_assert(design); + + map_autoidx = autoidx++; + + RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str())); + if (mapped_mod == NULL) + log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module)); + + for (auto w : mapped_mod->wires()) + module->addWire(remap_name(w->name), GetSize(w)); + + dict<IdString,std::vector<IdString>> box_ports; + + for (auto m : design->modules()) { + if (!m->attributes.count(ID(abc9_box_id))) + 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 (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); + } + } + } + + 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_))) + module->remove(cell); + else if (cell->attributes.erase("\\abc9_box_seq")) + boxes.emplace_back(cell); + } + + dict<SigBit, pool<IdString>> bit_drivers, bit_users; + TopoSort<IdString, RTLIL::sort_by_id_str> toposort; + dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers; + dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; + + std::map<IdString, int> cell_stats; + for (auto mapped_cell : mapped_mod->cells()) + { + // TODO: Speed up toposort -- we care about NOT ordering only + toposort.node(mapped_cell->name); + + if (mapped_cell->type == ID($_NOT_)) { + RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A); + RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y); + bit_users[a_bit].insert(mapped_cell->name); + // Ignore inouts for topo ordering + if (y_bit.wire && !(y_bit.wire->port_input && y_bit.wire->port_output)) + bit_drivers[y_bit].insert(mapped_cell->name); + + if (!a_bit.wire) { + mapped_cell->setPort(ID::Y, module->addWire(NEW_ID)); + RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); + log_assert(wire); + module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); + } + else { + RTLIL::Cell* driver_lut = nullptr; + // ABC can return NOT gates that drive POs + if (!a_bit.wire->port_input) { + // If it's not a NOT gate that that comes from a PI directly, + // find the driver LUT and clone that to guarantee that we won't + // increase the max logic depth + // (TODO: Optimise by not cloning unless will increase depth) + RTLIL::IdString driver_name; + if (GetSize(a_bit.wire) == 1) + driver_name = stringf("$lut%s", a_bit.wire->name.c_str()); + else + driver_name = stringf("$lut%s[%d]", a_bit.wire->name.c_str(), a_bit.offset); + driver_lut = mapped_mod->cell(driver_name); + } + + if (!driver_lut) { + // If a driver couldn't be found (could be from PI or box CI) + // then implement using a LUT + RTLIL::Cell *cell = module->addLut(remap_name(stringf("$lut%s", mapped_cell->name.c_str())), + RTLIL::SigBit(module->wires_.at(remap_name(a_bit.wire->name)), a_bit.offset), + RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), + RTLIL::Const::from_string("01")); + bit2sinks[cell->getPort(ID::A)].push_back(cell); + cell_stats[ID($lut)]++; + } + else + not2drivers[mapped_cell] = driver_lut; + } + continue; + } + + 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; + + for (auto &mapped_conn : mapped_cell->connections()) { + RTLIL::SigSpec newsig; + for (auto c : mapped_conn.second.chunks()) { + if (c.width == 0) + continue; + //log_assert(c.width == 1); + if (c.wire) + c.wire = module->wires_.at(remap_name(c.wire->name)); + newsig.append(c); + } + cell->setPort(mapped_conn.first, newsig); + + if (cell->input(mapped_conn.first)) { + for (auto i : newsig) + bit2sinks[i].push_back(cell); + for (auto i : mapped_conn.second) + bit_users[i].insert(mapped_cell->name); + } + if (cell->output(mapped_conn.first)) + for (auto i : mapped_conn.second) + // Ignore inouts for topo ordering + if (i.wire && !(i.wire->port_input && i.wire->port_output)) + bit_drivers[i].insert(mapped_cell->name); + } + } + else { + RTLIL::Cell *existing_cell = module->cell(mapped_cell->name); + log_assert(existing_cell); + + 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())); + mapped_cell->type = existing_cell->type; + + RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); + cell->parameters = existing_cell->parameters; + cell->attributes = existing_cell->attributes; + module->swap_names(cell, existing_cell); + + auto jt = mapped_cell->connections_.find("\\i"); + log_assert(jt != mapped_cell->connections_.end()); + SigSpec inputs = std::move(jt->second); + mapped_cell->connections_.erase(jt); + jt = mapped_cell->connections_.find("\\o"); + log_assert(jt != mapped_cell->connections_.end()); + SigSpec outputs = std::move(jt->second); + mapped_cell->connections_.erase(jt); + + auto abc9_flop = box_module->attributes.count("\\abc9_flop"); + if (!abc9_flop) { + for (const auto &i : inputs) + bit_users[i].insert(mapped_cell->name); + for (const auto &i : outputs) + // Ignore inouts for topo ordering + if (i.wire && !(i.wire->port_input && i.wire->port_output)) + bit_drivers[i].insert(mapped_cell->name); + } + + int input_count = 0, output_count = 0; + for (const auto &port_name : box_ports.at(cell->type)) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + + SigSpec sig; + if (w->port_input) { + sig = inputs.extract(input_count, GetSize(w)); + input_count += GetSize(w); + } + if (w->port_output) { + sig = outputs.extract(output_count, GetSize(w)); + output_count += GetSize(w); + } + + SigSpec newsig; + for (auto c : sig.chunks()) { + if (c.width == 0) + continue; + //log_assert(c.width == 1); + if (c.wire) + 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_stats[mapped_cell->type]++; + } + + for (auto cell : boxes) + module->remove(cell); + + // Copy connections (and rename) from mapped_mod to module + for (auto conn : mapped_mod->connections()) { + if (!conn.first.is_fully_const()) { + auto chunks = conn.first.chunks(); + for (auto &c : chunks) + c.wire = module->wires_.at(remap_name(c.wire->name)); + conn.first = std::move(chunks); + } + if (!conn.second.is_fully_const()) { + auto chunks = conn.second.chunks(); + for (auto &c : chunks) + if (c.wire) + c.wire = module->wires_.at(remap_name(c.wire->name)); + conn.second = std::move(chunks); + } + module->connect(conn); + } + + for (auto &it : cell_stats) + log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); + int in_wires = 0, out_wires = 0; + + // Stitch in mapped_mod's inputs/outputs into module + for (auto port : mapped_mod->ports) { + RTLIL::Wire *mapped_wire = mapped_mod->wire(port); + RTLIL::Wire *wire = module->wire(port); + log_assert(wire); + if (wire->attributes.erase(ID(abc9_scc_id))) { + auto r YS_ATTRIBUTE(unused) = wire->attributes.erase(ID::keep); + log_assert(r); + } + RTLIL::Wire *remap_wire = module->wire(remap_name(port)); + RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire)); + log_assert(GetSize(signal) >= GetSize(remap_wire)); + + RTLIL::SigSig conn; + if (mapped_wire->port_output) { + conn.first = signal; + conn.second = remap_wire; + out_wires++; + module->connect(conn); + } + else if (mapped_wire->port_input) { + conn.first = remap_wire; + conn.second = signal; + in_wires++; + module->connect(conn); + } + } + + // ABC9 will return $_NOT_ gates in its mapping (since they are + // 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 + // 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 + // outputs, only one of which is complemented) and when the driver + // is a LUT, then clone the LUT so that it can be inverted without + // increasing depth/delay. + for (auto &it : bit_users) + if (bit_drivers.count(it.first)) + 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(); + log_assert(no_loops); + + for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) { + RTLIL::Cell *not_cell = mapped_mod->cell(*ii); + log_assert(not_cell); + if (not_cell->type != ID($_NOT_)) + continue; + auto it = not2drivers.find(not_cell); + if (it == not2drivers.end()) + continue; + RTLIL::Cell *driver_lut = it->second; + RTLIL::SigBit a_bit = not_cell->getPort(ID::A); + RTLIL::SigBit y_bit = not_cell->getPort(ID::Y); + RTLIL::Const driver_mask; + + a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name)); + y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name)); + + auto jt = bit2sinks.find(a_bit); + if (jt == bit2sinks.end()) + goto clone_lut; + + for (auto sink_cell : jt->second) + if (sink_cell->type != ID($lut)) + goto clone_lut; + + // Push downstream LUTs past inverter + for (auto sink_cell : jt->second) { + SigSpec A = sink_cell->getPort(ID::A); + RTLIL::Const mask = sink_cell->getParam(ID(LUT)); + int index = 0; + for (; index < GetSize(A); index++) + if (A[index] == a_bit) + break; + log_assert(index < GetSize(A)); + int i = 0; + while (i < GetSize(mask)) { + for (int j = 0; j < (1 << index); j++) + std::swap(mask[i+j], mask[i+j+(1 << index)]); + i += 1 << (index+1); + } + A[index] = y_bit; + sink_cell->setPort(ID::A, A); + sink_cell->setParam(ID(LUT), mask); + } + + // Since we have rewritten all sinks (which we know + // to be only LUTs) to be after the inverter, we can + // go ahead and clone the LUT with the expectation + // that the original driving LUT will become dangling + // and get cleaned away +clone_lut: + driver_mask = driver_lut->getParam(ID(LUT)); + for (auto &b : driver_mask.bits) { + if (b == RTLIL::State::S0) b = RTLIL::State::S1; + else if (b == RTLIL::State::S1) b = RTLIL::State::S0; + } + auto cell = module->addLut(NEW_ID, + driver_lut->getPort(ID::A), + y_bit, + driver_mask); + for (auto &bit : cell->connections_.at(ID::A)) { + bit.wire = module->wires_.at(remap_name(bit.wire->name)); + bit2sinks[bit].push_back(cell); + } + } + + //log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires); + log("ABC RESULTS: input signals: %8d\n", in_wires); + log("ABC RESULTS: output signals: %8d\n", out_wires); + + design->remove(mapped_mod); +} + +struct Abc9OpsPass : public Pass { + Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" abc9_ops [options] [selection]\n"); + log("\n"); + log("This pass contains a set of supporting operations for use during ABC technology\n"); + 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(" -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"); + log(" wires driven by this cell's outputs with a (* keep *) attribute in order\n"); + log(" to break the SCC. this temporary attribute will be removed on -reintegrate.\n"); + 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("\n"); + log(" -dff\n"); + log(" consider flop cells (those instantiating modules marked with (* abc9_flop *)\n"); + log(" during -prep_xaiger.\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(" -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"); + log(" inputs and outputs.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ABC9_OPS pass (helper functions for ABC9).\n"); + + bool mark_scc_mode = false; + bool prep_dff_mode = false; + bool prep_xaiger_mode = false; + bool reintegrate_mode = false; + bool dff_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-mark_scc") { + mark_scc_mode = true; + continue; + } + if (arg == "-prep_dff") { + prep_dff_mode = true; + continue; + } + if (arg == "-prep_xaiger") { + prep_xaiger_mode = true; + continue; + } + if (arg == "-reintegrate") { + reintegrate_mode = true; + continue; + } + if (arg == "-dff") { + dff_mode = true; + continue; + } + break; + } + 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 (dff_mode && !prep_xaiger_mode) + log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n"); + + for (auto mod : design->selected_modules()) { + if (mod->get_bool_attribute("\\abc9_holes")) + continue; + + if (mod->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(mod)); + continue; + } + + if (!design->selected_whole_module(mod)) + log_error("Can't handle partially selected module %s!\n", log_id(mod)); + + 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); + } + } +} Abc9OpsPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index 41fdc8f3d..decf9a202 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -86,6 +86,7 @@ struct TribufWorker { cell->unsetPort(ID(S)); cell->type = tri_type; tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); + module->design->scratchpad_set_bool("tribuf.added_something", true); continue; } @@ -95,6 +96,7 @@ struct TribufWorker { cell->unsetPort(ID(S)); cell->type = tri_type; tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); + module->design->scratchpad_set_bool("tribuf.added_something", true); continue; } } @@ -130,8 +132,10 @@ struct TribufWorker { if (no_tribuf) module->connect(it.first, muxout); - else + else { module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first); + module->design->scratchpad_set_bool("tribuf.added_something", true); + } } } } diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v index 10e89a3e0..c031703a9 100644 --- a/techlibs/ecp5/cells_map.v +++ b/techlibs/ecp5/cells_map.v @@ -73,102 +73,80 @@ module \$lut (A, Y); input [WIDTH-1:0] A; output Y; - // Need to swap input ordering, and fix init accordingly, - // to match ABC's expectation of LUT inputs in non-decreasing - // delay order - localparam P_WIDTH = WIDTH < 4 ? 4 : WIDTH; - function [P_WIDTH-1:0] permute_index; - input [P_WIDTH-1:0] i; - integer j; - begin - permute_index = 0; - for (j = 0; j < P_WIDTH; j = j + 1) - permute_index[P_WIDTH-1 - j] = i[j]; - end - endfunction - - function [2**P_WIDTH-1:0] permute_init; - integer i; - begin - permute_init = 0; - for (i = 0; i < 2**P_WIDTH; i = i + 1) - permute_init[i] = LUT[permute_index(i)]; - end - endfunction - - parameter [2**P_WIDTH-1:0] P_LUT = permute_init(); - generate if (WIDTH == 1) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), + localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}}; + LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y), .A(1'b0), .B(1'b0), .C(1'b0), .D(A[0])); end else if (WIDTH == 2) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(1'b0), .B(1'b0), .C(A[1]), .D(A[0])); + localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}}; + LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(1'b0), .C(A[0]), .D(A[1])); end else if (WIDTH == 3) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(1'b0), .B(A[2]), .C(A[1]), .D(A[0])); + localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}}; + LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(A[0]), .C(A[1]), .D(A[2])); end else if (WIDTH == 4) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(A[3]), .B(A[2]), .C(A[1]), .D(A[0])); + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); `ifndef NO_PFUMUX end else if (WIDTH == 5) begin wire f0, f1; - LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), - .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1])); - LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), - .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1])); - PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y)); + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y)); end else if (WIDTH == 6) begin wire f0, f1, f2, f3, g0, g1; - LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - - LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - - PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0)); - PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1)); - L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y)); + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); + L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y)); end else if (WIDTH == 7) begin wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1; - LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0)); - PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1)); - PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2)); - PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3)); - L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0)); - L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1)); - L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y)); + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); + PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2)); + PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3)); + L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0)); + L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1)); + L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y)); `endif end else begin wire _TECHMAP_FAIL_ = 1; diff --git a/techlibs/ice40/abc9_model.v b/techlibs/ice40/abc9_model.v index 26cf6cc22..a5e5f4372 100644 --- a/techlibs/ice40/abc9_model.v +++ b/techlibs/ice40/abc9_model.v @@ -9,6 +9,8 @@ module \$__ICE40_CARRY_WRAPPER ( input I0, I3 ); parameter LUT = 0; + parameter I3_IS_CI = 0; + wire I3_OR_CI = I3_IS_CI ? CI : I3; SB_CARRY carry ( .I0(A), .I1(B), @@ -21,7 +23,7 @@ module \$__ICE40_CARRY_WRAPPER ( .I0(I0), .I1(A), .I2(B), - .I3(I3), + .I3(I3_OR_CI), .O(O) ); endmodule diff --git a/techlibs/ice40/abc9_u.box b/techlibs/ice40/abc9_u.box index 48a51463e..3d4b93834 100644 --- a/techlibs/ice40/abc9_u.box +++ b/techlibs/ice40/abc9_u.box @@ -6,13 +6,12 @@ # Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve # SB_LUT4+SB_CARRY) -# Outputs: O, CO # (Exception: carry chain input/output must be the # last input and output and the entire bus has been # moved there overriding the otherwise # alphabetical ordering) # name ID w/b ins outs $__ICE40_CARRY_WRAPPER 1 1 5 2 -#A B I0 I3 CI +#A B I0 I3 CI 1231 1205 1285 874 874 # O 675 609 - - 278 # CO diff --git a/techlibs/ice40/arith_map.v b/techlibs/ice40/arith_map.v index 00a07247b..ed4140e44 100644 --- a/techlibs/ice40/arith_map.v +++ b/techlibs/ice40/arith_map.v @@ -49,13 +49,14 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO); // A[1]: 1100 1100 1100 1100 // A[2]: 1111 0000 1111 0000 // A[3]: 1111 1111 0000 0000 - .LUT(16'b 0110_1001_1001_0110) + .LUT(16'b 0110_1001_1001_0110), + .I3_IS_CI(1'b1) ) carry ( .A(AA[i]), .B(BB[i]), .CI(C[i]), .I0(1'b0), - .I3(C[i]), + .I3(1'bx), .CO(CO[i]), .O(Y[i]) ); diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v index 759549e30..d5362eb83 100644 --- a/techlibs/ice40/cells_map.v +++ b/techlibs/ice40/cells_map.v @@ -42,19 +42,18 @@ module \$lut (A, Y); .I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0])); end else if (WIDTH == 2) begin - localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}}; + localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}}; SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0])); + .I0(1'b0), .I1(1'b0), .I2(A[0]), .I3(A[1])); end else if (WIDTH == 3) begin - localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[3]}}, {2{LUT[5]}}, {2{LUT[1]}}, {2{LUT[6]}}, {2{LUT[2]}}, {2{LUT[4]}}, {2{LUT[0]}}}; + localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}}; SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0])); + .I0(1'b0), .I1(A[0]), .I2(A[1]), .I3(A[2])); end else if (WIDTH == 4) begin - localparam [15:0] INIT = {LUT[15], LUT[7], LUT[11], LUT[3], LUT[13], LUT[5], LUT[9], LUT[1], LUT[14], LUT[6], LUT[10], LUT[2], LUT[12], LUT[4], LUT[8], LUT[0]}; - SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0])); + SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3])); end else begin wire _TECHMAP_FAIL_ = 1; end diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 7d1b37fd6..50eab5dde 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -1126,6 +1126,7 @@ module SB_SPRAM256KA ( input [15:0] DATAIN, input [3:0] MASKWREN, input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF, + `ABC9_ARRIVAL_U(1821) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13207 output reg [15:0] DATAOUT ); `ifndef BLACKBOX diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc index 9bee0444b..925ab31bb 100644 --- a/techlibs/ice40/ice40_opt.cc +++ b/techlibs/ice40/ice40_opt.cc @@ -139,7 +139,8 @@ static void run_ice40_opts(Module *module) log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n", log_id(module), log_id(cell), log_signal(replacement_output)); cell->type = "$lut"; - cell->setPort("\\A", { cell->getPort("\\I0"), inbit[0], inbit[1], cell->getPort("\\I3") }); + auto I3 = get_bit_or_zero(cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3))); + cell->setPort("\\A", { I3, inbit[1], inbit[0], get_bit_or_zero(cell->getPort("\\I0")) }); cell->setPort("\\Y", cell->getPort("\\O")); cell->unsetPort("\\B"); cell->unsetPort("\\CI"); @@ -148,6 +149,7 @@ static void run_ice40_opts(Module *module) cell->unsetPort("\\CO"); cell->unsetPort("\\O"); cell->setParam("\\WIDTH", 4); + cell->unsetParam("\\I3_IS_CI"); } continue; } diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index 2ebb8bf50..3689df70e 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -26,7 +26,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SynthIntelPass : public ScriptPass { - SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") {} + SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") { experimental(); } void help() YS_OVERRIDE { diff --git a/techlibs/xilinx/abc9_map.v b/techlibs/xilinx/abc9_map.v index 0652064cb..7dc027176 100644 --- a/techlibs/xilinx/abc9_map.v +++ b/techlibs/xilinx/abc9_map.v @@ -74,7 +74,7 @@ // (e) a special _TECHMAP_REPLACE_.abc9_ff.Q wire that will be used for feedback // into the (combinatorial) FD* cell to facilitate clock-enable behaviour -module FDRE (output Q, input C, CE, D, R); +module FDRE (output Q, (* techmap_autopurge *) input C, CE, D, R); parameter [0:0] INIT = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -110,7 +110,7 @@ module FDRE (output Q, input C, CE, D, R); wire [0:0] abc9_ff.init = 1'b0; wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ; endmodule -module FDRE_1 (output Q, input C, CE, D, R); +module FDRE_1 (output Q, (* techmap_autopurge *) input C, CE, D, R); parameter [0:0] INIT = 1'b0; wire QQ, $Q; generate if (INIT == 1'b1) begin @@ -138,7 +138,7 @@ module FDRE_1 (output Q, input C, CE, D, R); wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ; endmodule -module FDSE (output Q, input C, CE, D, S); +module FDSE (output Q, (* techmap_autopurge *) input C, CE, D, S); parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -173,7 +173,7 @@ module FDSE (output Q, input C, CE, D, S); wire [0:0] abc9_ff.init = 1'b0; wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ; endmodule -module FDSE_1 (output Q, input C, CE, D, S); +module FDSE_1 (output Q, (* techmap_autopurge *) input C, CE, D, S); parameter [0:0] INIT = 1'b1; wire QQ, $Q; generate if (INIT == 1'b1) begin @@ -200,7 +200,7 @@ module FDSE_1 (output Q, input C, CE, D, S); wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = QQ; endmodule -module FDCE (output Q, input C, CE, D, CLR); +module FDCE (output Q, (* techmap_autopurge *) input C, CE, D, CLR); parameter [0:0] INIT = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -249,7 +249,7 @@ module FDCE (output Q, input C, CE, D, CLR); wire [0:0] abc9_ff.init = 1'b0; wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ; endmodule -module FDCE_1 (output Q, input C, CE, D, CLR); +module FDCE_1 (output Q, (* techmap_autopurge *) input C, CE, D, CLR); parameter [0:0] INIT = 1'b0; wire QQ, $Q, $QQ; generate if (INIT == 1'b1) begin @@ -288,7 +288,7 @@ module FDCE_1 (output Q, input C, CE, D, CLR); wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ; endmodule -module FDPE (output Q, input C, CE, D, PRE); +module FDPE (output Q, (* techmap_autopurge *) input C, CE, D, PRE); parameter [0:0] INIT = 1'b1; parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_D_INVERTED = 1'b0; @@ -335,7 +335,7 @@ module FDPE (output Q, input C, CE, D, PRE); wire [0:0] abc9_ff.init = 1'b0; wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ; endmodule -module FDPE_1 (output Q, input C, CE, D, PRE); +module FDPE_1 (output Q, (* techmap_autopurge *) input C, CE, D, PRE); parameter [0:0] INIT = 1'b1; wire QQ, $Q, $QQ; generate if (INIT == 1'b1) begin diff --git a/techlibs/xilinx/abc9_model.v b/techlibs/xilinx/abc9_model.v index 204fa883f..15d12c89f 100644 --- a/techlibs/xilinx/abc9_model.v +++ b/techlibs/xilinx/abc9_model.v @@ -42,7 +42,7 @@ endmodule // Box to emulate async behaviour of FDP* (* abc9_box_id = 1001, lib_whitebox *) module \$__ABC9_ASYNC1 (input A, S, output Y); - assign Y = S ? 1'b0 : A; + assign Y = S ? 1'b1 : A; endmodule // Box to emulate comb/seq behaviour of RAM{32,64} and SRL{16,32} diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 22dca3c47..eb145593e 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -2155,7 +2155,235 @@ assign PCOUT = P; endmodule -// TODO: DSP48 (Virtex 4). +module DSP48 ( + input signed [17:0] A, + input signed [17:0] B, + input signed [47:0] C, + input signed [17:0] BCIN, + input signed [47:0] PCIN, + input CARRYIN, + input [6:0] OPMODE, + input SUBTRACT, + input [1:0] CARRYINSEL, + output signed [47:0] P, + output signed [17:0] BCOUT, + output signed [47:0] PCOUT, + (* clkbuf_sink *) + input CLK, + input CEA, + input CEB, + input CEC, + input CEM, + input CECARRYIN, + input CECINSUB, + input CECTRL, + input CEP, + input RSTA, + input RSTB, + input RSTC, + input RSTM, + input RSTCARRYIN, + input RSTCTRL, + input RSTP +); + +parameter integer AREG = 1; +parameter integer BREG = 1; +parameter integer CREG = 1; +parameter integer MREG = 1; +parameter integer PREG = 1; +parameter integer CARRYINREG = 1; +parameter integer CARRYINSELREG = 1; +parameter integer OPMODEREG = 1; +parameter integer SUBTRACTREG = 1; +parameter B_INPUT = "DIRECT"; +parameter LEGACY_MODE = "MULT18X18S"; + +wire signed [17:0] A_OUT; +wire signed [17:0] B_OUT; +wire signed [47:0] C_OUT; +wire signed [35:0] M_MULT; +wire signed [35:0] M_OUT; +wire signed [47:0] P_IN; +wire [6:0] OPMODE_OUT; +wire [1:0] CARRYINSEL_OUT; +wire CARRYIN_OUT; +wire SUBTRACT_OUT; +reg INT_CARRYIN_XY; +reg INT_CARRYIN_Z; +reg signed [47:0] XMUX; +reg signed [47:0] YMUX; +wire signed [47:0] XYMUX; +reg signed [47:0] ZMUX; +reg CIN; + +// The B input multiplexer. +wire signed [17:0] B_MUX; +assign B_MUX = (B_INPUT == "DIRECT") ? B : BCIN; + +// The cascade output. +assign BCOUT = B_OUT; +assign PCOUT = P; + +// The registers. +reg signed [17:0] A0_REG; +reg signed [17:0] A1_REG; +reg signed [17:0] B0_REG; +reg signed [17:0] B1_REG; +reg signed [47:0] C_REG; +reg signed [35:0] M_REG; +reg signed [47:0] P_REG; +reg [6:0] OPMODE_REG; +reg [1:0] CARRYINSEL_REG; +reg SUBTRACT_REG; +reg CARRYIN_REG; +reg INT_CARRYIN_XY_REG; + +initial begin + A0_REG = 0; + A1_REG = 0; + B0_REG = 0; + B1_REG = 0; + C_REG = 0; + M_REG = 0; + P_REG = 0; + OPMODE_REG = 0; + CARRYINSEL_REG = 0; + SUBTRACT_REG = 0; + CARRYIN_REG = 0; + INT_CARRYIN_XY_REG = 0; +end + +always @(posedge CLK) begin + if (RSTA) begin + A0_REG <= 0; + A1_REG <= 0; + end else if (CEA) begin + A0_REG <= A; + A1_REG <= A0_REG; + end + if (RSTB) begin + B0_REG <= 0; + B1_REG <= 0; + end else if (CEB) begin + B0_REG <= B_MUX; + B1_REG <= B0_REG; + end + if (RSTC) begin + C_REG <= 0; + end else if (CEC) begin + C_REG <= C; + end + if (RSTM) begin + M_REG <= 0; + end else if (CEM) begin + M_REG <= M_MULT; + end + if (RSTP) begin + P_REG <= 0; + end else if (CEP) begin + P_REG <= P_IN; + end + if (RSTCTRL) begin + OPMODE_REG <= 0; + CARRYINSEL_REG <= 0; + SUBTRACT_REG <= 0; + end else begin + if (CECTRL) begin + OPMODE_REG <= OPMODE; + CARRYINSEL_REG <= CARRYINSEL; + end + if (CECINSUB) + SUBTRACT_REG <= SUBTRACT; + end + if (RSTCARRYIN) begin + CARRYIN_REG <= 0; + INT_CARRYIN_XY_REG <= 0; + end else begin + if (CECINSUB) + CARRYIN_REG <= CARRYIN; + if (CECARRYIN) + INT_CARRYIN_XY_REG <= INT_CARRYIN_XY; + end +end + +// The register enables. +assign A_OUT = (AREG == 2) ? A1_REG : (AREG == 1) ? A0_REG : A; +assign B_OUT = (BREG == 2) ? B1_REG : (BREG == 1) ? B0_REG : B_MUX; +assign C_OUT = (CREG == 1) ? C_REG : C; +assign M_OUT = (MREG == 1) ? M_REG : M_MULT; +assign P = (PREG == 1) ? P_REG : P_IN; +assign OPMODE_OUT = (OPMODEREG == 1) ? OPMODE_REG : OPMODE; +assign SUBTRACT_OUT = (SUBTRACTREG == 1) ? SUBTRACT_REG : SUBTRACT; +assign CARRYINSEL_OUT = (CARRYINSELREG == 1) ? CARRYINSEL_REG : CARRYINSEL; +assign CARRYIN_OUT = (CARRYINREG == 1) ? CARRYIN_REG : CARRYIN; + +// The multiplier. +assign M_MULT = A_OUT * B_OUT; + +// The post-adder inputs. +always @* begin + case (OPMODE_OUT[1:0]) + 2'b00: XMUX <= 0; + 2'b10: XMUX <= P; + 2'b11: XMUX <= {{12{A_OUT[17]}}, A_OUT, B_OUT}; + default: XMUX <= 48'hxxxxxxxxxxxx; + endcase + case (OPMODE_OUT[1:0]) + 2'b01: INT_CARRYIN_XY <= A_OUT[17] ~^ B_OUT[17]; + 2'b11: INT_CARRYIN_XY <= ~A_OUT[17]; + // TODO: not tested in hardware. + default: INT_CARRYIN_XY <= A_OUT[17] ~^ B_OUT[17]; + endcase +end + +always @* begin + case (OPMODE_OUT[3:2]) + 2'b00: YMUX <= 0; + 2'b11: YMUX <= C_OUT; + default: YMUX <= 48'hxxxxxxxxxxxx; + endcase +end + +assign XYMUX = (OPMODE_OUT[3:0] == 4'b0101) ? M_OUT : (XMUX + YMUX); + +always @* begin + case (OPMODE_OUT[6:4]) + 3'b000: ZMUX <= 0; + 3'b001: ZMUX <= PCIN; + 3'b010: ZMUX <= P; + 3'b011: ZMUX <= C_OUT; + 3'b101: ZMUX <= {{17{PCIN[47]}}, PCIN[47:17]}; + 3'b110: ZMUX <= {{17{P[47]}}, P[47:17]}; + default: ZMUX <= 48'hxxxxxxxxxxxx; + endcase + // TODO: check how all this works on actual hw. + if (OPMODE_OUT[1:0] == 2'b10) + INT_CARRYIN_Z <= ~P[47]; + else + case (OPMODE_OUT[6:4]) + 3'b001: INT_CARRYIN_Z <= ~PCIN[47]; + 3'b010: INT_CARRYIN_Z <= ~P[47]; + 3'b101: INT_CARRYIN_Z <= ~PCIN[47]; + 3'b110: INT_CARRYIN_Z <= ~P[47]; + default: INT_CARRYIN_Z <= 1'bx; + endcase +end + +always @* begin + case (CARRYINSEL_OUT) + 2'b00: CIN <= CARRYIN_OUT; + 2'b01: CIN <= INT_CARRYIN_Z; + 2'b10: CIN <= INT_CARRYIN_XY; + 2'b11: CIN <= INT_CARRYIN_XY_REG; + default: CIN <= 1'bx; + endcase +end + +// The post-adder. +assign P_IN = SUBTRACT_OUT ? (ZMUX - (XYMUX + CIN)) : (ZMUX + XYMUX + CIN); + +endmodule // TODO: DSP48E (Virtex 5). diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py index d5c58c5d7..06e982a0e 100644 --- a/techlibs/xilinx/cells_xtra.py +++ b/techlibs/xilinx/cells_xtra.py @@ -209,7 +209,7 @@ CELLS = [ # Cell('MULT18X18SIO', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 3E # Cell('DSP48A', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 3A DSP # Cell('DSP48A1', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 6 - Cell('DSP48', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 4 + # Cell('DSP48', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 4 Cell('DSP48E', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 5 #Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 6 / Series 7 Cell('DSP48E2', port_attrs={'CLK': ['clkbuf_sink']}), # Ultrascale diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index c3e5c72f9..54e48f1a6 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -5476,49 +5476,6 @@ module URAM288_BASE (...); input SLEEP; endmodule -module DSP48 (...); - parameter integer AREG = 1; - parameter integer BREG = 1; - parameter B_INPUT = "DIRECT"; - parameter integer CARRYINREG = 1; - parameter integer CARRYINSELREG = 1; - parameter integer CREG = 1; - parameter LEGACY_MODE = "MULT18X18S"; - parameter integer MREG = 1; - parameter integer OPMODEREG = 1; - parameter integer PREG = 1; - parameter integer SUBTRACTREG = 1; - output [17:0] BCOUT; - output [47:0] P; - output [47:0] PCOUT; - input [17:0] A; - input [17:0] B; - input [17:0] BCIN; - input [47:0] C; - input CARRYIN; - input [1:0] CARRYINSEL; - input CEA; - input CEB; - input CEC; - input CECARRYIN; - input CECINSUB; - input CECTRL; - input CEM; - input CEP; - (* clkbuf_sink *) - input CLK; - input [6:0] OPMODE; - input [47:0] PCIN; - input RSTA; - input RSTB; - input RSTC; - input RSTCARRYIN; - input RSTCTRL; - input RSTM; - input RSTP; - input SUBTRACT; -endmodule - module DSP48E (...); parameter SIM_MODE = "SAFE"; parameter integer ACASCREG = 1; diff --git a/techlibs/xilinx/lut_map.v b/techlibs/xilinx/lut_map.v index 62d501632..718ec42f1 100644 --- a/techlibs/xilinx/lut_map.v +++ b/techlibs/xilinx/lut_map.v @@ -29,90 +29,65 @@ module \$lut (A, Y); input [WIDTH-1:0] A; output Y; - // Need to swap input ordering, and fix init accordingly, - // to match ABC's expectation of LUT inputs in non-decreasing - // delay order - function [WIDTH-1:0] permute_index; - input [WIDTH-1:0] i; - integer j; - begin - permute_index = 0; - for (j = 0; j < WIDTH; j = j + 1) - permute_index[WIDTH-1 - j] = i[j]; - end - endfunction - - function [2**WIDTH-1:0] permute_init; - input [2**WIDTH-1:0] orig; - integer i; - begin - permute_init = 0; - for (i = 0; i < 2**WIDTH; i = i + 1) - permute_init[i] = orig[permute_index(i)]; - end - endfunction - - parameter [2**WIDTH-1:0] P_LUT = permute_init(LUT); - generate if (WIDTH == 1) begin - if (P_LUT == 2'b01) begin + if (LUT == 2'b01) begin INV _TECHMAP_REPLACE_ (.O(Y), .I(A[0])); end else begin - LUT1 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0])); end end else if (WIDTH == 2) begin - LUT2 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[1]), .I1(A[0])); + LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1])); end else if (WIDTH == 3) begin - LUT3 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[2]), .I1(A[1]), .I2(A[0])); + LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2])); end else if (WIDTH == 4) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[3]), .I1(A[2]), .I2(A[1]), - .I3(A[0])); + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3])); end else if (WIDTH == 5) begin - LUT5 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[4]), .I1(A[3]), .I2(A[2]), - .I3(A[1]), .I4(A[0])); + LUT5 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4])); end else if (WIDTH == 6) begin - LUT6 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[5]), .I1(A[4]), .I2(A[3]), - .I3(A[2]), .I4(A[1]), .I5(A[0])); + LUT6 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); end else if (WIDTH == 7) begin wire T0, T1; - LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0), - .I0(A[6]), .I1(A[5]), .I2(A[4]), - .I3(A[3]), .I4(A[2]), .I5(A[1])); - LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1), - .I0(A[6]), .I1(A[5]), .I2(A[4]), - .I3(A[3]), .I4(A[2]), .I5(A[1])); - MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[0])); + LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[6])); end else if (WIDTH == 8) begin wire T0, T1, T2, T3, T4, T5; - LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - LUT6 #(.INIT(P_LUT[191:128])) fpga_lut_2 (.O(T2), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - LUT6 #(.INIT(P_LUT[255:192])) fpga_lut_3 (.O(T3), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[1])); - MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[1])); - MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[0])); + LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[191:128])) fpga_lut_2 (.O(T2), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[255:192])) fpga_lut_3 (.O(T3), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[6])); + MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[6])); + MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[7])); end else begin wire _TECHMAP_FAIL_ = 1; end diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 5c3b5179d..8119d307c 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -316,7 +316,11 @@ struct SynthXilinxPass : public ScriptPass run("proc"); if (flatten || help_mode) run("flatten", "(with '-flatten')"); + if (active_design) + active_design->scratchpad_unset("tribuf.added_something"); run("tribuf -logic"); + if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something")) + log_error("Tristate buffers are unsupported without the '-iopad' option.\n"); run("deminout"); run("opt_expr"); run("opt_clean"); @@ -526,7 +530,7 @@ struct SynthXilinxPass : public ScriptPass if (check_label("map_cells")) { // Needs to be done before logic optimization, so that inverters (OE vs T) are handled. if (help_mode || !noiopad) - run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if not '-noiopad')"); + run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(skip if '-noiopad')"); std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; if (widemux > 0) techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); @@ -589,17 +593,15 @@ struct SynthXilinxPass : public ScriptPass if (!nosrl || help_mode) run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')"); std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v"; - if (help_mode) - techmap_args += stringf("[-map %s]", ff_map_file.c_str()); - else if (!abc9) + if (help_mode || !abc9) techmap_args += stringf(" -map %s", ff_map_file.c_str()); - run("techmap " + techmap_args, "(only if '-abc9')"); + run("techmap " + techmap_args); run("xilinx_dffopt"); } if (check_label("finalize")) { if (help_mode || !noclkbuf) - run("clkbufmap -buf BUFG O:I ", "(skip if '-noclkbuf')"); + run("clkbufmap -buf BUFG O:I", "(skip if '-noclkbuf')"); if (help_mode || ise) run("extractinv -inv INV O:I", "(only if '-ise')"); run("clean"); diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index 848f88d53..0d9c28fde 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -12,4 +12,7 @@ test_dsp48a_model_ref.v test_dsp48a1_model_ref.v test_dsp48a1_model_uut.v test_dsp48a1_model +test_dsp48_model_ref.v +test_dsp48_model_uut.v +test_dsp48_model *.vcd diff --git a/techlibs/xilinx/tests/test_dsp48_model.sh b/techlibs/xilinx/tests/test_dsp48_model.sh new file mode 100644 index 000000000..9a73f9b0c --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp48_model.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -ex +if [ -z $ISE_DIR ]; then + ISE_DIR=/opt/Xilinx/ISE/14.7 +fi +sed 's/DSP48 /DSP48_UUT /; /DSP48_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp48_model_uut.v +if [ ! -f "test_dsp48_model_ref.v" ]; then + cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48.v test_dsp48_model_ref.v +fi +for tb in mult_allreg mult_noreg mult_inreg +do + iverilog -s $tb -s glbl -o test_dsp48_model test_dsp48_model.v test_dsp48_model_uut.v test_dsp48_model_ref.v $ISE_DIR/ISE_DS/ISE/verilog/src/glbl.v + vvp -N ./test_dsp48_model +done diff --git a/techlibs/xilinx/tests/test_dsp48_model.v b/techlibs/xilinx/tests/test_dsp48_model.v new file mode 100644 index 000000000..d69c00e93 --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp48_model.v @@ -0,0 +1,287 @@ +`timescale 1ns / 1ps + +module testbench; + parameter integer AREG = 1; + parameter integer BREG = 1; + parameter integer CREG = 1; + parameter integer MREG = 1; + parameter integer PREG = 1; + parameter integer CARRYINREG = 1; + parameter integer CARRYINSELREG = 1; + parameter integer OPMODEREG = 1; + parameter integer SUBTRACTREG = 1; + parameter B_INPUT = "DIRECT"; + parameter LEGACY_MODE = "NONE"; + + reg CLK; + reg CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL; + reg RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL; + reg [17:0] A; + reg [17:0] B; + reg [47:0] C; + reg [17:0] BCIN; + reg [47:0] PCIN; + reg CARRYIN; + reg [6:0] OPMODE; + reg SUBTRACT; + reg [1:0] CARRYINSEL; + + output [47:0] P, REF_P; + output [17:0] BCOUT, REF_BCOUT; + output [47:0] PCOUT, REF_PCOUT; + + integer errcount = 0; + + reg ERROR_FLAG = 0; + + task clkcycle; + begin + #5; + CLK = ~CLK; + #10; + CLK = ~CLK; + #2; + ERROR_FLAG = 0; + if (REF_BCOUT !== BCOUT) begin + $display("ERROR at %1t: REF_BCOUT=%b UUT_BCOUT=%b DIFF=%b", $time, REF_BCOUT, BCOUT, REF_BCOUT ^ BCOUT); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + if (REF_P !== P) begin + $display("ERROR at %1t: REF_P=%b UUT_P=%b DIFF=%b", $time, REF_P, P, REF_P ^ P); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + if (REF_PCOUT !== PCOUT) begin + $display("ERROR at %1t: REF_PCOUT=%b UUT_PCOUT=%b DIFF=%b", $time, REF_PCOUT, PCOUT, REF_PCOUT ^ PCOUT); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + #3; + end + endtask + + reg config_valid = 0; + task drc; + begin + config_valid = 1; + + if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0; + if (OPMODE[1:0] == 2'b00 && CARRYINSEL == 2'b10) config_valid = 0; + if (OPMODE[1:0] == 2'b10 && CARRYINSEL == 2'b10) config_valid = 0; + if (OPMODE[1:0] == 2'b00 && CARRYINSEL == 2'b11) config_valid = 0; + if (OPMODE[1:0] == 2'b10 && CARRYINSEL == 2'b11) config_valid = 0; + if (OPMODE[3:2] == 2'b10) config_valid = 0; + if ((OPMODE[3:2] == 2'b01) ^ (OPMODE[1:0] == 2'b01) == 1'b1) config_valid = 0; + if ((OPMODE[6:4] == 3'b010 || OPMODE[6:4] == 3'b110) && PREG != 1) config_valid = 0; + if (OPMODE[6:4] == 3'b100) config_valid = 0; + if (OPMODE[6:4] == 3'b111) config_valid = 0; + if (OPMODE[6:4] == 3'b000 && CARRYINSEL == 2'b01) config_valid = 0; + if (OPMODE[6:4] == 3'b011 && CARRYINSEL == 2'b01) config_valid = 0; + + // Xilinx models consider these combinations invalid for an unknown reason. + if (CARRYINSEL == 2'b01 && OPMODE[3:2] == 2'b00) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0000011) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0000101) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0100011) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0111111) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b1100011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0000011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0000101) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0011111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0010011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0100011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0100101) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0101111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0110011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0111111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1010011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1011111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1100011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1100101) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1101111) config_valid = 0; + + if (CARRYINSEL == 2'b10 && OPMODE[3:0] == 4'b0101 && MREG == 1) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE[3:0] == 4'b0101 && MREG == 0) config_valid = 0; + end + endtask + + initial begin + $dumpfile("test_dsp48_model.vcd"); + $dumpvars(0, testbench); + + #2; + CLK = 1'b0; + {CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL} = 8'b11111111; + {A, B, C, PCIN, OPMODE, SUBTRACT, CARRYIN, CARRYINSEL} = 0; + {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = 7'b1111111; + repeat (10) begin + #10; + CLK = 1'b1; + #10; + CLK = 1'b0; + #10; + CLK = 1'b1; + #10; + CLK = 1'b0; + end + {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = 0; + + repeat (100000) begin + clkcycle; + config_valid = 0; + while (!config_valid) begin + A = $urandom; + B = $urandom; + C = {$urandom, $urandom}; + BCIN = $urandom; + PCIN = {$urandom, $urandom}; + + {CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL} = $urandom | $urandom | $urandom; + {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom; + {CARRYIN, CARRYINSEL, OPMODE, SUBTRACT} = $urandom; + + drc; + end + end + + if (errcount == 0) begin + $display("All tests passed."); + $finish; + end else begin + $display("Caught %1d errors.", errcount); + $stop; + end + end + + DSP48 #( + .AREG (AREG), + .BREG (BREG), + .CREG (CREG), + .MREG (MREG), + .PREG (PREG), + .CARRYINREG (CARRYINREG), + .CARRYINSELREG (CARRYINSELREG), + .OPMODEREG (OPMODEREG), + .SUBTRACTREG (SUBTRACTREG), + .B_INPUT (B_INPUT), + .LEGACY_MODE (LEGACY_MODE) + ) ref ( + .A (A), + .B (B), + .C (C), + .BCIN (BCIN), + .PCIN (PCIN), + .CARRYIN (CARRYIN), + .OPMODE (OPMODE), + .SUBTRACT (SUBTRACT), + .CARRYINSEL (CARRYINSEL), + .BCOUT (REF_BCOUT), + .P (REF_P), + .PCOUT (REF_PCOUT), + .CEA (CEA), + .CEB (CEB), + .CEC (CEC), + .CEM (CEM), + .CEP (CEP), + .CECARRYIN (CECARRYIN), + .CECINSUB (CECINSUB), + .CECTRL (CECTRL), + .CLK (CLK), + .RSTA (RSTA), + .RSTB (RSTB), + .RSTC (RSTC), + .RSTM (RSTM), + .RSTP (RSTP), + .RSTCARRYIN (RSTCARRYIN), + .RSTCTRL (RSTCTRL) + ); + + DSP48_UUT #( + .AREG (AREG), + .BREG (BREG), + .CREG (CREG), + .MREG (MREG), + .PREG (PREG), + .CARRYINREG (CARRYINREG), + .CARRYINSELREG (CARRYINSELREG), + .OPMODEREG (OPMODEREG), + .SUBTRACTREG (SUBTRACTREG), + .B_INPUT (B_INPUT), + .LEGACY_MODE (LEGACY_MODE) + ) uut ( + .A (A), + .B (B), + .C (C), + .BCIN (BCIN), + .PCIN (PCIN), + .CARRYIN (CARRYIN), + .OPMODE (OPMODE), + .SUBTRACT (SUBTRACT), + .CARRYINSEL (CARRYINSEL), + .BCOUT (BCOUT), + .P (P), + .PCOUT (PCOUT), + .CEA (CEA), + .CEB (CEB), + .CEC (CEC), + .CEM (CEM), + .CEP (CEP), + .CECARRYIN (CECARRYIN), + .CECINSUB (CECINSUB), + .CECTRL (CECTRL), + .CLK (CLK), + .RSTA (RSTA), + .RSTB (RSTB), + .RSTC (RSTC), + .RSTM (RSTM), + .RSTP (RSTP), + .RSTCARRYIN (RSTCARRYIN), + .RSTCTRL (RSTCTRL) + ); +endmodule + +module mult_noreg; + testbench #( + .AREG (0), + .BREG (0), + .CREG (0), + .MREG (0), + .PREG (0), + .CARRYINREG (0), + .CARRYINSELREG (0), + .OPMODEREG (0), + .SUBTRACTREG (0), + .B_INPUT ("DIRECT") + ) testbench (); +endmodule + +module mult_allreg; + testbench #( + .AREG (1), + .BREG (1), + .CREG (1), + .MREG (1), + .PREG (1), + .CARRYINREG (1), + .CARRYINSELREG (1), + .OPMODEREG (1), + .SUBTRACTREG (1), + .B_INPUT ("CASCADE") + ) testbench (); +endmodule + +module mult_inreg; + testbench #( + .AREG (1), + .BREG (1), + .CREG (1), + .MREG (0), + .PREG (0), + .CARRYINREG (1), + .CARRYINSELREG (0), + .OPMODEREG (0), + .SUBTRACTREG (0), + .B_INPUT ("DIRECT") + ) testbench (); +endmodule diff --git a/tests/arch/efinix/mux.ys b/tests/arch/efinix/mux.ys index b46f641e1..a5ab80d8b 100644 --- a/tests/arch/efinix/mux.ys +++ b/tests/arch/efinix/mux.ys @@ -36,6 +36,6 @@ proc equiv_opt -assert -map +/efinix/cells_sim.v synth_efinix # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux16 # Constrain all select calls below inside the top module -select -assert-count 12 t:EFX_LUT4 +select -assert-max 12 t:EFX_LUT4 select -assert-none t:EFX_LUT4 %% t:* %D diff --git a/tests/arch/ice40/bug1597.ys b/tests/arch/ice40/bug1597.ys new file mode 100644 index 000000000..b7983cfa4 --- /dev/null +++ b/tests/arch/ice40/bug1597.ys @@ -0,0 +1,72 @@ +read_verilog <<EOT +module top ( + input CLK, PIN_1, PIN_2, PIN_3, PIN_4, PIN_5, + PIN_6, PIN_7, PIN_8, PIN_9, PIN_10, PIN_11, PIN_12, PIN_13, PIN_25, + output USBPU, PIN_14, PIN_15, PIN_16, PIN_17, PIN_18, + PIN_19, PIN_20, PIN_21, PIN_22, PIN_23, PIN_24, +); + assign USBPU = 0; + + wire[5:0] parOut; + wire[5:0] chrg; + + assign PIN_14 = parOut[0]; + assign PIN_15 = parOut[1]; + assign PIN_16 = parOut[2]; + assign PIN_17 = parOut[3]; + assign PIN_18 = parOut[4]; + assign PIN_19 = parOut[5]; + assign chrg[0] = PIN_3; + assign chrg[1] = PIN_4; + assign chrg[2] = PIN_5; + assign chrg[3] = PIN_6; + assign chrg[4] = PIN_7; + assign chrg[5] = PIN_8; + + SSCounter6o sc6(PIN_1, CLK, PIN_2, PIN_9, chrg, parOut); + +endmodule + +module SSCounter6 (input wire rst, clk, adv, jmp, input wire [5:0] in, output reg[5:0] out); + always @(posedge clk, posedge rst) + if (rst) out <= 0; + else if (adv || jmp) out <= jmp ? in : out + 1; +endmodule + +// Optimized 6 bit counter, it should takes 7 cells. +/* b[5:1] /* b[0] +1010101010101010 in 1010101010101010 in +1100110011001100 jmp 1100110011001100 jmp +1111000011110000 loop 1111000011110000 loop +1111111100000000 carry 1111111100000000 - +---------------------- ---------------------- +1000101110111000 out 1000101110001011 out + 8 B B 8 8 B 8 B +*/ +module SSCounter6o (input wire rst, clk, adv, jmp, input wire [5:0] in, output wire[5:0] out); + wire[4:0] co; + wire[5:0] lo; + wire ien; + SB_LUT4 #(.LUT_INIT(16'hFFF0)) lien (ien, 0, 0, adv, jmp); + SB_CARRY c0 (co[0], jmp, out[0], 1), + c1 (co[1], jmp, out[1], co[0]), + c2 (co[2], jmp, out[2], co[1]), + c3 (co[3], jmp, out[3], co[2]), + c4 (co[4], jmp, out[4], co[3]); + SB_DFFER d0 (out[0], clk, ien, rst, lo[0]), + d1 (out[1], clk, ien, rst, lo[1]), + d2 (out[2], clk, ien, rst, lo[2]), + d3 (out[3], clk, ien, rst, lo[3]), + d4 (out[4], clk, ien, rst, lo[4]), + d5 (out[5], clk, ien, rst, lo[5]); + SB_LUT4 #(.LUT_INIT(16'h8B8B)) l0 (lo[0], in[0], jmp, out[0], 0); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l1 (lo[1], in[1], jmp, out[1], co[0]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l2 (lo[2], in[2], jmp, out[2], co[1]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l3 (lo[3], in[3], jmp, out[3], co[2]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l4 (lo[4], in[4], jmp, out[4], co[3]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l5 (lo[5], in[5], jmp, out[5], co[4]); +endmodule +EOT +hierarchy -top top +flatten +equiv_opt -multiclock -map +/ice40/cells_sim.v synth_ice40 diff --git a/tests/arch/ice40/ice40_opt.ys b/tests/arch/ice40/ice40_opt.ys index 5186d4800..71b68431e 100644 --- a/tests/arch/ice40/ice40_opt.ys +++ b/tests/arch/ice40/ice40_opt.ys @@ -1,24 +1,4 @@ read_verilog -icells -formal <<EOT -module \$__ICE40_CARRY_WRAPPER (output CO, O, input A, B, CI, I0, I3); - parameter LUT = 0; - SB_CARRY carry ( - .I0(A), - .I1(B), - .CI(CI), - .CO(CO) - ); - \$lut #( - .WIDTH(4), - .LUT(LUT) - ) lut ( - .A({I0,A,B,I3}), - .Y(O) - ); -endmodule -EOT -design -stash unmap - -read_verilog -icells -formal <<EOT module top(input CI, I0, output [1:0] CO, output O); wire A = 1'b0, B = 1'b0; \$__ICE40_CARRY_WRAPPER #( @@ -26,13 +6,14 @@ module top(input CI, I0, output [1:0] CO, output O); // A[1]: 1100 1100 1100 1100 // A[2]: 1111 0000 1111 0000 // A[3]: 1111 1111 0000 0000 - .LUT(~16'b 0110_1001_1001_0110) + .LUT(~16'b 0110_1001_1001_0110), + .I3_IS_CI(1'b1) ) u0 ( .A(A), .B(B), .CI(CI), .I0(I0), - .I3(CI), + .I3(1'bx), .CO(CO[0]), .O(O) ); @@ -40,7 +21,7 @@ module top(input CI, I0, output [1:0] CO, output O); endmodule EOT -equiv_opt -assert -map %unmap -map +/ice40/cells_sim.v ice40_opt +equiv_opt -assert -map +/ice40/abc9_model.v -map +/ice40/cells_sim.v ice40_opt design -load postopt select -assert-count 1 t:* select -assert-count 1 t:$lut @@ -105,3 +86,33 @@ select -assert-count 1 t:SB_LUT4 select -assert-count 1 t:SB_CARRY select -assert-count 1 t:SB_CARRY a:keep %i select -assert-count 1 t:SB_CARRY c:carry %i + + +design -reset +read_verilog -icells <<EOT +module top(input I3, I2, I1, I0, output O, O2); + SB_LUT4 #( + .LUT_INIT(8'b 1001_0110) + ) u0 ( + .I0(I0), + .I1(I1), + .I2(I2), + .I3(), + .O(O) + ); + wire CO; + \$__ICE40_CARRY_WRAPPER #( + .LUT(~8'b 1001_0110), + .I3_IS_CI(1'b0) + ) u1 ( + .A(1'b0), + .B(1'b0), + .CI(1'b0), + .I0(), + .I3(), + .CO(CO), + .O(O2) + ); +endmodule +EOT +ice40_opt diff --git a/tests/arch/xilinx/tribuf.sh b/tests/arch/xilinx/tribuf.sh new file mode 100644 index 000000000..636aed12a --- /dev/null +++ b/tests/arch/xilinx/tribuf.sh @@ -0,0 +1,5 @@ +! ../../../yosys ../common/tribuf.v -qp "synth_xilinx" +../../../yosys ../common/tribuf.v -qp "synth_xilinx -iopad; \ +select -assert-count 2 t:IBUF; \ +select -assert-count 1 t:INV; \ +select -assert-count 1 t:OBUFT" diff --git a/tests/sat/initval.ys b/tests/sat/initval.ys index 2079d2f34..1436724b0 100644 --- a/tests/sat/initval.ys +++ b/tests/sat/initval.ys @@ -2,3 +2,14 @@ read_verilog -sv initval.v proc;; sat -seq 10 -prove-asserts + +design -reset +read_verilog -icells <<EOT +module top(input clk, i, output [1:0] o); +(* init = 2'bx0 *) +wire [1:0] o; +assign o[1] = o[0]; +$_DFF_P_ dff (.C(clk), .D(i), .Q(o[0])); +endmodule +EOT +sat -seq 1 diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v index 8afd0ce96..e5837d480 100644 --- a/tests/simple_abc9/abc9.v +++ b/tests/simple_abc9/abc9.v @@ -213,7 +213,7 @@ module arbiter (clk, rst, request, acknowledge, grant, grant_valid, grant_encode input rst; endmodule -(* abc_box_id=1 *) +(* abc9_box_id=1, whitebox *) module MUXF8(input I0, I1, S, output O); endmodule @@ -291,3 +291,19 @@ module abc9_test035(input clk, d, output reg [1:0] q); always @(posedge clk) q[0] <= d; always @(negedge clk) q[1] <= q[0]; endmodule + +module abc9_test036(input A, B, S, output [1:0] O); + (* keep *) + MUXF8 m ( + .I0(I0), + .I1(I1), + .O(O[0]), + .S(S) + ); + MUXF8 m2 ( + .I0(I0), + .I1(I1), + .O(O[1]), + .S(S) + ); +endmodule diff --git a/tests/simple_abc9/run-test.sh b/tests/simple_abc9/run-test.sh index bc921daa9..32d7a80ca 100755 --- a/tests/simple_abc9/run-test.sh +++ b/tests/simple_abc9/run-test.sh @@ -28,4 +28,5 @@ exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v *.sv EXTRA_FLAGS="-n 300 -p abc9 -lut 4 -box ../abc.box; \ clean; \ check -assert; \ - select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'" + select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%; \ + setattr -mod -unset whitebox'" diff --git a/tests/techmap/abc9.ys b/tests/techmap/abc9.ys index 62b5dfef6..2140dde26 100644 --- a/tests/techmap/abc9.ys +++ b/tests/techmap/abc9.ys @@ -39,6 +39,35 @@ design -load gold scratchpad -copy abc9.script.flow3 abc9.script abc9 -lut 4 +design -reset +read_verilog <<EOT +module top(input a, b, output o); +(* keep *) wire w = a & b; +assign o = ~w; +endmodule +EOT + +simplemap +equiv_opt -assert abc9 -lut 4 +design -load postopt +select -assert-count 2 t:$lut + + +design -reset +read_verilog -icells <<EOT +module top(input a, b, output o); +wire w; +(* keep *) $_AND_ gate (.Y(w), .A(a), .B(b)); +assign o = ~w; +endmodule +EOT + +simplemap +equiv_opt -assert abc9 -lut 4 +design -load postopt +select -assert-count 1 t:$lut +select -assert-count 1 t:$_AND_ + design -reset read_verilog -icells <<EOT diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys index 81d0afd1b..0c7695089 100644 --- a/tests/various/abc9.ys +++ b/tests/various/abc9.ys @@ -14,6 +14,7 @@ design -import gate -as gate miter -equiv -flatten -make_assert -make_outputs gold gate miter sat -verify -prove-asserts -show-ports miter + design -load read hierarchy -top abc9_test028 proc @@ -23,6 +24,7 @@ select -assert-count 1 t:$lut r:LUT=2'b01 r:WIDTH=1 %i %i select -assert-count 1 t:unknown select -assert-none t:$lut t:unknown %% t: %D + design -load read hierarchy -top abc9_test032 proc @@ -38,3 +40,16 @@ design -import gate -as gate miter -equiv -flatten -make_assert -make_outputs gold gate miter sat -seq 10 -verify -prove-asserts -show-ports miter + + +design -reset +read_verilog -icells <<EOT +module abc9_test036(input clk, d, output q); +(* keep *) reg w; +$__ABC9_FF_ ff(.D(d), .Q(w)); +wire \ff.clock = clk; +wire \ff.init = 1'b0; +assign q = w; +endmodule +EOT +abc9 -lut 4 -dff diff --git a/tests/various/help.ys b/tests/various/help.ys new file mode 100644 index 000000000..9283ce8f1 --- /dev/null +++ b/tests/various/help.ys @@ -0,0 +1,2 @@ +help -all +help -celltypes |