diff options
136 files changed, 6074 insertions, 2318 deletions
@@ -55,10 +55,13 @@ Yosys 0.9 .. Yosys 0.9-dev - Added "check -mapped" - Added checking of SystemVerilog always block types (always_comb, always_latch and always_ff) + - Added support for SystemVerilog wildcard port connections (.*) - Added "xilinx_dffopt" pass - Added "scratchpad" pass - Added "abc9 -dff" - Added "synth_xilinx -dff" + - Improved support of $readmem[hb] Memory Content File inclusion + - Added "opt_lut_ins" pass Yosys 0.8 .. Yosys 0.9 ---------------------- @@ -728,6 +728,7 @@ test: $(TARGETS) $(EXTRA_TARGETS) +cd tests/arch/anlogic && bash run-test.sh $(SEEDOPT) +cd tests/arch/gowin && bash run-test.sh $(SEEDOPT) +cd tests/rpc && bash run-test.sh + +cd tests/memfile && bash run-test.sh @echo "" @echo " Passed \"make test\"." @echo "" @@ -69,7 +69,7 @@ prerequisites for building yosys: graphviz xdot pkg-config python3 libboost-system-dev \ libboost-python-dev libboost-filesystem-dev zlib1g-dev -Similarily, on Mac OS X Homebrew can be used to install dependencies: +Similarily, on Mac OS X Homebrew can be used to install dependencies (from within cloned yosys repository): $ brew tap Homebrew/bundle && brew bundle @@ -373,10 +373,15 @@ Verilog Attributes and non-standard features `abc9` to preserve the integrity of carry-chains. Specifying this attribute onto a bus port will affect only its most significant bit. -- The port attribute ``abc9_arrival`` specifies an integer (for output ports - only) to be used as the arrival time of this sequential port. It can be used, - for example, to specify the clk-to-Q delay of a flip-flop for consideration - during `abc9` techmapping. +- The output port attribute ``abc9_arrival`` specifies an integer, or a string + of space-separated integers to be used as the arrival time of this blackbox + port. It can be used, for example, to specify the clk-to-Q delay of a flip- + flop output for consideration during `abc9` techmapping. + +- The input port attribute ``abc9_required`` specifies an integer, or a string + of space-separated integers to be used as the required time of this blackbox + port. It can be used, for example, to specify the setup-time of a flip-flop + input for consideration during `abc9` techmapping. - The module attribute ``abc9_flop`` is a boolean marking the module as a flip-flop. This allows `abc9` to analyse its contents in order to perform @@ -387,6 +392,10 @@ Verilog Attributes and non-standard features according to the type of the always. These are checked for correctness in ``proc_dlatch``. +- The cell attribute ``wildcard_port_conns`` represents wildcard port + connections (SystemVerilog ``.*``). These are resolved to concrete + connections to matching wires in ``hierarchy``. + - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index a6c87159d..3cf36aca8 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,117 @@ 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,std::vector<int>>> arrivals_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 + // (all others are combinatorial) + 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 = arrivals_cache[cell->type]; + for (const auto &conn : cell->connections()) { + auto port_wire = inst_module->wire(conn.first); + if (!port_wire->port_output) + continue; + + auto r = cell_arrivals.insert(conn.first); + auto &arrivals = r.first->second; + if (r.second) { + auto it = port_wire->attributes.find("\\abc9_arrival"); + if (it == port_wire->attributes.end()) + continue; + if (it->second.flags == 0) + arrivals.emplace_back(it->second.as_int()); + else + for (const auto &tok : split_tokens(it->second.decode_string())) + arrivals.push_back(atoi(tok.c_str())); + } + + if (arrivals.empty()) + continue; + + if (GetSize(arrivals) > 1 && GetSize(arrivals) != GetSize(port_wire)) + log_error("%s.%s is %d bits wide but abc9_arrival = %s has %d value(s)!\n", log_id(cell->type), log_id(conn.first), + GetSize(port_wire), log_signal(it->second), GetSize(arrivals)); + + auto jt = arrivals.begin(); +#ifndef NDEBUG + if (ys_debug(1)) { + static std::set<std::pair<IdString,IdString>> seen; + if (seen.emplace(cell->type, conn.first).second) log("%s.%s abc9_arrival = %d\n", log_id(cell->type), log_id(conn.first), *jt); + } +#endif + for (auto bit : sigmap(conn.second)) { + arrival_times[bit] = *jt; + if (arrivals.size() > 1) + jt++; } - 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 +300,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 +315,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; - - bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); - - 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); - } + dict<IdString, std::vector<IdString>> box_ports; + for (auto cell : box_list) { + log_assert(cell); - 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); - } - } + RTLIL::Module* box_module = module->design->module(cell->type); + log_assert(box_module); + log_assert(box_module->attributes.count("\\abc9_box_id") || box_module->get_bool_attribute("\\abc9_flop")); - // 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; + 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; } - else { - rhs = RTLIL::SigSpec(State::S0, GetSize(w)); - cell->setPort(port_name, rhs); - } - - 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 +376,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 +456,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 +471,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 +591,45 @@ 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); + IdString derived_type = box_module->derive(box_module->design, cell->parameters); + box_module = box_module->design->module(derived_type); + log_assert(box_module); + + auto r = cell_cache.insert(derived_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++); } @@ -732,7 +650,7 @@ struct XAigerWriter log_assert(mergeability > 0); write_r_buffer(mergeability); - Const init = cell->attributes.at(ID(abc9_init)); + Const init = cell->attributes.at(ID(abc9_init), State::Sx); log_assert(GetSize(init) == 1); if (init == State::S1) write_s_buffer(1); @@ -759,82 +677,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 +770,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 6735d670f..199560ad0 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -246,19 +246,25 @@ struct EdifBackend : public Backend { else if (!ct.cell_input(cell_it.first, port_it.first)) dir = "OUTPUT"; } - if (port_it.second == 1) + int width = port_it.second; + int start = 0; + bool upto = false; + auto m = design->module(cell_it.first); + if (m) { + auto w = m->wire(port_it.first); + if (w) { + width = GetSize(w); + start = w->start_offset; + upto = w->upto; + } + } + if (width == 1) *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir); else { - int b[2] = {port_it.second-1, 0}; - auto m = design->module(cell_it.first); - if (m) { - auto w = m->wire(port_it.first); - if (w) { - b[w->upto ? 0 : 1] = w->start_offset; - b[w->upto ? 1 : 0] = w->start_offset+GetSize(w)-1; - } - } - *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), port_it.second, dir); + int b[2]; + b[upto ? 0 : 1] = start; + b[upto ? 1 : 0] = start+width-1; + *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), width, dir); } } *f << stringf(" )\n"); @@ -326,7 +332,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 +355,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 +368,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)); } } } @@ -390,18 +396,23 @@ struct EdifBackend : public Backend { if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1) 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))); else { int member_idx = GetSize(sig)-i-1; auto m = design->module(cell->type); + int width = sig.size(); if (m) { auto w = m->wire(p.first); - if (w) + if (w) { member_idx = GetSize(w)-i-1; + width = GetSize(w); + } + } + if (width == 1) + 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 { + 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))); } - net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", - EDIF_REF(p.first), member_idx, EDIF_REF(cell->name))); } } } @@ -410,11 +421,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(); } } @@ -431,7 +444,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"); @@ -446,6 +459,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/json/json.cc b/backends/json/json.cc index 107009ee4..6c924ff99 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -33,6 +33,7 @@ struct JsonWriter std::ostream &f; bool use_selection; bool aig_mode; + bool compat_int_mode; Design *design; Module *module; @@ -42,8 +43,9 @@ struct JsonWriter dict<SigBit, string> sigids; pool<Aig> aig_models; - JsonWriter(std::ostream &f, bool use_selection, bool aig_mode) : - f(f), use_selection(use_selection), aig_mode(aig_mode) { } + JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) : + f(f), use_selection(use_selection), aig_mode(aig_mode), + compat_int_mode(compat_int_mode) { } string get_string(string str) { @@ -102,8 +104,7 @@ struct JsonWriter if (state < 2) str += " "; f << get_string(str); - } else - if (GetSize(value) == 32 && value.is_fully_def()) { + } else if (compat_int_mode && GetSize(value) <= 32 && value.is_fully_def()) { if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) f << stringf("%d", value.as_int()); else @@ -294,6 +295,10 @@ struct JsonBackend : public Backend { log(" -aig\n"); log(" include AIG models for the different gate types\n"); log("\n"); + log(" -compat-int\n"); + log(" emit 32-bit or smaller fully-defined parameter values directly\n"); + log(" as JSON numbers (for compatibility with old parsers)\n"); + log("\n"); log("\n"); log("The general syntax of the JSON output created by this command is as follows:\n"); log("\n"); @@ -368,10 +373,9 @@ struct JsonBackend : public Backend { log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n"); log("\"z\" instead of a number.\n"); log("\n"); - log("Numeric 32-bit parameter and attribute values are written as decimal values.\n"); - log("Bit verctors of different sizes, or ones containing 'x' or 'z' bits, are written\n"); - log("as string holding the binary representation of the value. Strings are written\n"); - log("as strings, with an appended blank in cases of strings of the form /[01xz]* */.\n"); + log("Bit vectors (including integers) are written as string holding the binary"); + log("representation of the value. Strings are written as strings, with an appended"); + log("blank in cases of strings of the form /[01xz]* */.\n"); log("\n"); log("For example the following Verilog code:\n"); log("\n"); @@ -495,6 +499,7 @@ struct JsonBackend : public Backend { void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool aig_mode = false; + bool compat_int_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -503,13 +508,17 @@ struct JsonBackend : public Backend { aig_mode = true; continue; } + if (args[argidx] == "-compat-int") { + compat_int_mode = true; + continue; + } break; } extra_args(f, filename, args, argidx); log_header(design, "Executing JSON backend.\n"); - JsonWriter json_writer(*f, false, aig_mode); + JsonWriter json_writer(*f, false, aig_mode, compat_int_mode); json_writer.write_design(design); } } JsonBackend; @@ -530,6 +539,10 @@ struct JsonPass : public Pass { log(" -aig\n"); log(" also include AIG models for the different gate types\n"); log("\n"); + log(" -compat-int\n"); + log(" emit 32-bit or smaller fully-defined parameter values directly\n"); + log(" as JSON numbers (for compatibility with old parsers)\n"); + log("\n"); log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } @@ -537,6 +550,7 @@ struct JsonPass : public Pass { { std::string filename; bool aig_mode = false; + bool compat_int_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -549,6 +563,10 @@ struct JsonPass : public Pass { aig_mode = true; continue; } + if (args[argidx] == "-compat-int") { + compat_int_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -569,7 +587,7 @@ struct JsonPass : public Pass { f = &buf; } - JsonWriter json_writer(*f, true, aig_mode); + JsonWriter json_writer(*f, true, aig_mode, compat_int_mode); json_writer.write_design(design); if (!filename.empty()) { 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/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 54d0f6148..682c47a1f 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1066,6 +1066,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // initial begin // memid[0] = ... // end + dump_attributes(f, indent.c_str(), cell->attributes); f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset); if (use_init) { 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/frontends/ast/ast.h b/frontends/ast/ast.h index ffdcd9271..a50ae306d 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -248,6 +248,7 @@ namespace AST void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); AstNode *eval_const_function(AstNode *fcall); bool is_simple_const_expr(); + std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); // create a human-readable text representation of the AST (for debugging) void dumpAst(FILE *f, std::string indent) const; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 69056aeee..57107b76a 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -41,6 +41,103 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; +// Process a format string and arguments for $display, $write, $sprintf, etc + +std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) { + // Other arguments are placeholders. Process the string as we go through it + std::string sout; + for (size_t i = 0; i < sformat.length(); i++) + { + // format specifier + if (sformat[i] == '%') + { + // If there's no next character, that's a problem + if (i+1 >= sformat.length()) + log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str()); + + char cformat = sformat[++i]; + + // %% is special, does not need a matching argument + if (cformat == '%') + { + sout += '%'; + continue; + } + + // Simplify the argument + AstNode *node_arg = nullptr; + + // Everything from here on depends on the format specifier + switch (cformat) + { + case 's': + case 'S': + case 'd': + case 'D': + case 'x': + case 'X': + if (next_arg >= GetSize(children)) + log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n", + cformat, str.c_str()); + + node_arg = children[next_arg++]; + while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_arg->type != AST_CONSTANT) + log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); + break; + + case 'm': + case 'M': + break; + + default: + log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); + break; + } + + switch (cformat) + { + case 's': + case 'S': + sout += node_arg->bitsAsConst().decode_string(); + break; + + case 'd': + case 'D': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'x': + case 'X': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'm': + case 'M': + sout += log_id(current_module->name); + break; + + default: + log_abort(); + } + } + + // not a format specifier + else + sout += sformat[i]; + } + return sout; +} + + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -216,99 +313,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (node_string->type != AST_CONSTANT) log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str()); std::string sformat = node_string->bitsAsConst().decode_string(); - - // Other arguments are placeholders. Process the string as we go through it - std::string sout; - int next_arg = 1; - for (size_t i = 0; i < sformat.length(); i++) - { - // format specifier - if (sformat[i] == '%') - { - // If there's no next character, that's a problem - if (i+1 >= sformat.length()) - log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str()); - - char cformat = sformat[++i]; - - // %% is special, does not need a matching argument - if (cformat == '%') - { - sout += '%'; - continue; - } - - // Simplify the argument - AstNode *node_arg = nullptr; - - // Everything from here on depends on the format specifier - switch (cformat) - { - case 's': - case 'S': - case 'd': - case 'D': - case 'x': - case 'X': - if (next_arg >= GetSize(children)) - log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n", - cformat, str.c_str()); - - node_arg = children[next_arg++]; - while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_arg->type != AST_CONSTANT) - log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); - break; - - case 'm': - case 'M': - break; - - default: - log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); - break; - } - - switch (cformat) - { - case 's': - case 'S': - sout += node_arg->bitsAsConst().decode_string(); - break; - - case 'd': - case 'D': - { - char tmp[128]; - snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); - sout += tmp; - } - break; - - case 'x': - case 'X': - { - char tmp[128]; - snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); - sout += tmp; - } - break; - - case 'm': - case 'M': - sout += log_id(current_module->name); - break; - - default: - log_abort(); - } - } - - // not a format specifier - else - sout += sformat[i]; - } - + std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); // Finally, print the message (only include a \n for $display, not for $write) log("%s", sout.c_str()); if (str == "$display") @@ -2368,6 +2373,17 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (str == "\\$sformatf") { + AstNode *node_string = children[0]; + while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_string->type != AST_CONSTANT) + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); + std::string sformat = node_string->bitsAsConst().decode_string(); + std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); + newNode = AstNode::mkconst_str(sout); + goto apply_newNode; + } + if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) { AstNode *dpi_decl = current_scope[str]; @@ -3014,9 +3030,19 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m std::ifstream f; f.open(mem_filename.c_str()); - yosys_input_files.insert(mem_filename); - - if (f.fail()) + if (f.fail()) { +#ifdef _WIN32 + char slash = '\\'; +#else + char slash = '/'; +#endif + std::string path = filename.substr(0, filename.find_last_of(slash)+1); + f.open(path + mem_filename.c_str()); + yosys_input_files.insert(path + mem_filename); + } else { + yosys_input_files.insert(mem_filename); + } + if (f.fail() || GetSize(mem_filename) == 0) log_file_error(filename, linenum, "Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str()); log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 9274cf5ca..ae5815f8e 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -539,6 +539,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == OPER_REDUCE_NAND) { + Wire *tmp = module->addWire(NEW_ID); + cell = module->addReduceAnd(inst_name, IN, tmp, SIGNED); + module->addNot(NEW_ID, tmp, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); + return true; + } + if (inst->Type() == OPER_REDUCE_OR) { cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); @@ -1891,6 +1899,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); + for (auto nl : nl_todo) + nl->ChangePortBusStructures(1 /* hierarchical */); + VerificExtNets worker; for (auto nl : nl_todo) worker.run(nl); @@ -2408,7 +2419,7 @@ struct VerificPass : public Pass { else { if (argidx == GetSize(args)) - log_cmd_error("No top module specified.\n"); + cmd_error(args, argidx, "No top module specified.\n"); Array veri_modules, vhdl_units; for (; argidx < GetSize(args); argidx++) @@ -2470,6 +2481,9 @@ struct VerificPass : public Pass { worker.run(nl); } + for (auto nl : nl_todo) + nl->ChangePortBusStructures(1 /* hierarchical */); + if (!dumpfile.empty()) { VeriWrite veri_writer; veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign()); @@ -2495,7 +2509,7 @@ struct VerificPass : public Pass { goto check_error; } - log_cmd_error("Missing or unsupported mode parameter.\n"); + cmd_error(args, argidx, "Missing or unsupported mode parameter.\n"); check_error: if (!verific_error_msg.empty()) @@ -2568,14 +2582,14 @@ struct ReadPass : public Pass { static bool use_verific = verific_available; if (args.size() < 2 || args[1][0] != '-') - log_cmd_error("Missing mode parameter.\n"); + cmd_error(args, 1, "Missing mode parameter.\n"); if (args[1] == "-verific" || args[1] == "-noverific") { if (args.size() != 2) - log_cmd_error("Additional arguments to -verific/-noverific.\n"); + cmd_error(args, 1, "Additional arguments to -verific/-noverific.\n"); if (args[1] == "-verific") { if (!verific_available) - log_cmd_error("This version of Yosys is built without Verific support.\n"); + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); use_verific = true; } else { use_verific = false; @@ -2584,7 +2598,7 @@ struct ReadPass : public Pass { } if (args.size() < 3) - log_cmd_error("Missing file name parameter.\n"); + cmd_error(args, 3, "Missing file name parameter.\n"); if (args[1] == "-vlog95" || args[1] == "-vlog2k") { if (use_verific) { @@ -2616,7 +2630,7 @@ struct ReadPass : public Pass { args[0] = "verific"; Pass::call(design, args); } else { - log_cmd_error("This version of Yosys is built without Verific support.\n"); + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); } return; } @@ -2663,7 +2677,7 @@ struct ReadPass : public Pass { return; } - log_cmd_error("Missing or unsupported mode parameter.\n"); + cmd_error(args, 1, "Missing or unsupported mode parameter.\n"); } } ReadPass; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index ca23df3e8..9b43c250e 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -431,6 +431,8 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } +".*" { return TOK_WILDCARD_CONNECT; } + [-+]?[=*]> { if (!specify_mode) REJECT; frontend_verilog_yylval.string = new std::string(yytext); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index f25a8de28..9001aa162 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -152,7 +152,7 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP -%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR +%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH @@ -493,7 +493,7 @@ wire_type: astbuf3 = new AstNode(AST_WIRE); current_wire_rand = false; current_wire_const = false; - } wire_type_token_list delay { + } wire_type_token_list { $$ = astbuf3; }; @@ -1337,7 +1337,7 @@ wire_decl: } if (astbuf2 && astbuf2->children.size() != 2) frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); - } wire_name_list { + } delay wire_name_list { delete astbuf1; if (astbuf2 != NULL) delete astbuf2; @@ -1682,6 +1682,11 @@ cell_port: node->children.back()->str = *$3; delete $3; free_attr($1); + } | + attr TOK_WILDCARD_CONNECT { + if (!sv_mode) + frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode."); + astbuf2->attributes[ID(wildcard_port_conns)] = AstNode::mkconst_int(1, false); }; always_comb_or_latch: 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/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex index 3009bf2c0..be37d8d39 100644 --- a/manual/CHAPTER_Overview.tex +++ b/manual/CHAPTER_Overview.tex @@ -234,6 +234,8 @@ An RTLIL::Wire object has the following properties: \item The wire name \item A list of attributes \item A width (buses are just wires with a width > 1) +\item Bus direction (MSB to LSB or vice versa) +\item Lowest valid bit index (LSB or MSB depending on bus direction) \item If the wire is a port: port number and direction (input/output/inout) \end{itemize} @@ -246,6 +248,11 @@ This makes some aspects of RTLIL more complex but enables Yosys to be used for coarse grain synthesis where the cells of the target architecture operate on entire signal vectors instead of single bit wires. +In Verilog and VHDL, busses may have arbitrary bounds, and LSB can have either +the lowest or the highest bit index. In RTLIL, bit 0 always corresponds to LSB; +however, information from the HDL frontend is preserved so that the bus will be +correctly indexed in error messages, backend output, constraint files, etc. + An RTLIL::Cell object has the following properties: \begin{itemize} diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index c58c3f66a..fac5b48a4 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -721,6 +721,7 @@ class WClass: name = None namespace = None link_type = None + base_class = None id_ = None string_id = None hash_id = None @@ -732,6 +733,7 @@ class WClass: def __init__(self, name, link_type, id_, string_id = None, hash_id = None, needs_clone = False): self.name = name self.namespace = None + self.base_class = None self.link_type = link_type self.id_ = id_ self.string_id = string_id @@ -804,6 +806,8 @@ class WClass: for con in self.found_constrs: text += con.gen_decl() + if self.base_class is not None: + text += "\n\t\tvirtual ~" + self.name + "() { };" for var in self.found_vars: text += var.gen_decl() for fun in self.found_funs: @@ -908,15 +912,19 @@ class WClass: def gen_boost_py(self): body = self.gen_boost_py_body() + base_info = "" + if self.base_class is not None: + base_info = ", bases<" + (self.base_class.name) + ">" + if self.link_type == link_types.derive: - text = "\n\t\tclass_<" + self.name + ">(\"Cpp" + self.name + "\"" + text = "\n\t\tclass_<" + self.name + base_info + ">(\"Cpp" + self.name + "\"" text += body text += "\n\t\tclass_<" + self.name text += "Wrap, boost::noncopyable" text += ">(\"" + self.name + "\"" text += body else: - text = "\n\t\tclass_<" + self.name + ">(\"" + self.name + "\"" + text = "\n\t\tclass_<" + self.name + base_info + ">(\"" + self.name + "\"" text += body return text @@ -1935,6 +1943,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("{"))) @@ -1958,9 +1979,21 @@ def parse_header(source): for namespace in impl_namespaces: complete_namespace += "::" + namespace debug("\tFound " + struct_name + " in " + complete_namespace,2) + + base_class_name = None + if len(ugly_line.split(" : ")) > 1: # class is derived + deriv_str = ugly_line.split(" : ")[1] + if len(deriv_str.split("::")) > 1: # namespace of base class is given + base_class_name = deriv_str.split("::", 1)[1] + else: + base_class_name = deriv_str.split(" ")[0] + debug("\t " + struct_name + " is derived from " + base_class_name,2) + base_class = class_by_name(base_class_name) + class_ = (class_by_name(struct_name), ugly_line.count("{"))#calc_ident(line)) if struct_name in classnames: class_[0].namespace = complete_namespace + class_[0].base_class = base_class i += 1 continue @@ -2129,6 +2162,21 @@ def expand_functions(): new_funs.extend(expand_function(fun)) class_.found_funs = new_funs +def inherit_members(): + for source in sources: + for class_ in source.classes: + if class_.base_class: + base_funs = copy.deepcopy(class_.base_class.found_funs) + for fun in base_funs: + fun.member_of = class_ + fun.namespace = class_.namespace + base_vars = copy.deepcopy(class_.base_class.found_vars) + for var in base_vars: + var.member_of = class_ + var.namespace = class_.namespace + class_.found_funs.extend(base_funs) + class_.found_vars.extend(base_vars) + def clean_duplicates(): for source in sources: for class_ in source.classes: @@ -2165,6 +2213,7 @@ def gen_wrappers(filename, debug_level_ = 0): parse_header(source) expand_functions() + inherit_members() clean_duplicates() import shutil diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index a3e969ef1..e0d428811 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -668,6 +668,10 @@ struct ShowPass : public Pass { log(" -notitle\n"); log(" do not add the module name as graph title to the dot file\n"); log("\n"); + log(" -nobg\n"); + log(" don't run viewer in the background, IE wait for the viewer tool to\n"); + log(" exit before returning\n"); + log("\n"); log("When no <format> is specified, 'dot' is used. When no <format> and <viewer> is\n"); log("specified, 'xdot' is used to display the schematic (POSIX systems only).\n"); log("\n"); @@ -706,6 +710,7 @@ struct ShowPass : public Pass { bool flag_abbreviate = true; bool flag_notitle = false; bool custom_prefix = false; + std::string background = "&"; RTLIL::IdString colorattr; size_t argidx; @@ -787,6 +792,10 @@ struct ShowPass : public Pass { flag_notitle = true; continue; } + if (arg == "-nobg") { + background= ""; + continue; + } break; } extra_args(args, argidx, design); @@ -859,21 +868,19 @@ struct ShowPass : public Pass { // system()/cmd.exe does not understand single quotes nor // background tasks on Windows. So we have to pause yosys // until the viewer exits. - #define VIEW_CMD "%s \"%s\"" + std::string cmd = stringf("%s \"%s\"", viewer_exe.c_str(), out_file.c_str()); #else - #define VIEW_CMD "%s '%s' &" + std::string cmd = stringf("%s '%s' %s", viewer_exe.c_str(), out_file.c_str(), background.c_str()); #endif - std::string cmd = stringf(VIEW_CMD, viewer_exe.c_str(), out_file.c_str()); - #undef VIEW_CMD log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) log_cmd_error("Shell command failed!\n"); } else if (format.empty()) { #ifdef __APPLE__ - std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str()); + std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' %s", getuid(), dot_file.c_str(), dot_file.c_str(), background.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' %s", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), background.c_str()); #endif log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) diff --git a/passes/fsm/fsm_detect.cc b/passes/fsm/fsm_detect.cc index fb3896669..a1c8067b4 100644 --- a/passes/fsm/fsm_detect.cc +++ b/passes/fsm/fsm_detect.cc @@ -34,13 +34,20 @@ static SigSet<sig2driver_entry_t> sig2driver, sig2user; static std::set<RTLIL::Cell*> muxtree_cells; static SigPool sig_at_port; -static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor) +static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, pool<Cell*> &recursion_monitor, dict<RTLIL::SigSpec, bool> &mux_tree_cache) { + if (mux_tree_cache.find(sig) != mux_tree_cache.end()) + return mux_tree_cache.at(sig); + if (sig.is_fully_const() || old_sig == sig) { +ret_true: + mux_tree_cache[sig] = true; return true; } if (sig_at_port.check_any(assign_map(sig))) { +ret_false: + mux_tree_cache[sig] = false; return false; } @@ -49,13 +56,13 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo for (auto &cellport : cellport_list) { if ((cellport.first->type != "$mux" && cellport.first->type != "$pmux") || cellport.second != "\\Y") { - return false; + goto ret_false; } if (recursion_monitor.count(cellport.first)) { log_warning("logic loop in mux tree at signal %s in module %s.\n", log_signal(sig), RTLIL::id2cstr(module->name)); - return false; + goto ret_false; } recursion_monitor.insert(cellport.first); @@ -63,22 +70,22 @@ static bool check_state_mux_tree(RTLIL::SigSpec old_sig, RTLIL::SigSpec sig, poo RTLIL::SigSpec sig_a = assign_map(cellport.first->getPort("\\A")); RTLIL::SigSpec sig_b = assign_map(cellport.first->getPort("\\B")); - if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor)) { + if (!check_state_mux_tree(old_sig, sig_a, recursion_monitor, mux_tree_cache)) { recursion_monitor.erase(cellport.first); - return false; + goto ret_false; } for (int i = 0; i < sig_b.size(); i += sig_a.size()) - if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor)) { + if (!check_state_mux_tree(old_sig, sig_b.extract(i, sig_a.size()), recursion_monitor, mux_tree_cache)) { recursion_monitor.erase(cellport.first); - return false; + goto ret_false; } recursion_monitor.erase(cellport.first); muxtree_cells.insert(cellport.first); } - return true; + goto ret_true; } static bool check_state_users(RTLIL::SigSpec sig) @@ -143,11 +150,12 @@ static void detect_fsm(RTLIL::Wire *wire) pool<Cell*> recursion_monitor; RTLIL::SigSpec sig_q = assign_map(cellport.first->getPort("\\Q")); RTLIL::SigSpec sig_d = assign_map(cellport.first->getPort("\\D")); + dict<RTLIL::SigSpec, bool> mux_tree_cache; if (sig_q != assign_map(wire)) continue; - looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor); + looks_like_state_reg = check_state_mux_tree(sig_q, sig_d, recursion_monitor, mux_tree_cache); looks_like_good_state_reg = check_state_users(sig_q); if (!looks_like_state_reg) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index d8a628448..fa4a8ea29 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -548,6 +548,19 @@ RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod) return NULL; } +// Find a matching wire for an implicit port connection; traversing generate block scope +RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port) +{ + const std::string &cellname = cell->name.str(); + size_t idx = cellname.size(); + while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) { + Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1)); + if (found != nullptr) + return found; + } + return module->wire(port); +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } void help() YS_OVERRIDE @@ -970,15 +983,71 @@ struct HierarchyPass : public Pass { } } + // Determine default values + dict<IdString, dict<IdString, Const>> defaults_db; if (!nodefaults) { - dict<IdString, dict<IdString, Const>> defaults_db; - for (auto module : design->modules()) for (auto wire : module->wires()) if (wire->port_input && wire->attributes.count("\\defaultvalue")) defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue"); + } + // Process SV implicit wildcard port connections + std::set<Module*> blackbox_derivatives; + std::vector<Module*> design_modules = design->modules(); + for (auto module : design_modules) + { + for (auto cell : module->cells()) + { + if (!cell->get_bool_attribute(ID(wildcard_port_conns))) + continue; + Module *m = design->module(cell->type); + + if (m == nullptr) + log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n", + RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + + // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths + if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } + + auto old_connections = cell->connections(); + for (auto wire : m->wires()) { + // Find ports of the module that aren't explicitly connected + if (!wire->port_input && !wire->port_output) + continue; + if (old_connections.count(wire->name)) + continue; + // Make sure a wire of correct name exists in the parent + Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str()); + + // Missing wires are OK when a default value is set + if (!nodefaults && parent_wire == nullptr && defaults_db.count(cell->type) && defaults_db.at(cell->type).count(wire->name)) + continue; + + if (parent_wire == nullptr) + log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n", + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + if (parent_wire->width != wire->width) + log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n", + parent_wire->width, wire->width, + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + cell->setPort(wire->name, parent_wire); + } + cell->attributes.erase(ID(wildcard_port_conns)); + } + } + + if (!nodefaults) + { for (auto module : design->modules()) for (auto cell : module->cells()) { @@ -1000,9 +1069,6 @@ struct HierarchyPass : public Pass { } } - std::set<Module*> blackbox_derivatives; - std::vector<Module*> design_modules = design->modules(); - for (auto module : design_modules) { pool<Wire*> wand_wor_index; diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 002c1a6a1..3133927bb 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -15,6 +15,7 @@ OBJS += passes/opt/wreduce.o OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o +OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o endif diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc new file mode 100644 index 000000000..cf5248ced --- /dev/null +++ b/passes/opt/opt_lut_ins.cc @@ -0,0 +1,278 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptLutInsPass : public Pass { + OptLutInsPass() : Pass("opt_lut_ins", "discard unused LUT inputs") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_lut_ins [options] [selection]\n"); + log("\n"); + log("This pass removes unused inputs from LUT cells (that is, inputs that can not\n"); + log("influence the output signal given this LUT's value). While such LUTs cannot\n"); + log("be directly emitted by ABC, they can be a result of various post-ABC\n"); + log("transformations, such as mapping wide LUTs (not all sub-LUTs will use the\n"); + log("full set of inputs) or optimizations such as xilinx_dffopt.\n"); + log("\n"); + log(" -tech <technology>\n"); + log(" Instead of generic $lut cells, operate on LUT cells specific\n"); + log(" to the given technology. Valid values are: xilinx, ecp5, gowin.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_LUT_INS pass (discard unused LUT inputs).\n"); + string techname; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-tech" && argidx+1 < args.size()) { + techname = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (techname != "" && techname != "xilinx" && techname != "ecp5" && techname != "gowin") + log_cmd_error("Unsupported technology: '%s'\n", techname.c_str()); + + for (auto module : design->selected_modules()) + { + log("Optimizing LUTs in %s.\n", log_id(module)); + + std::vector<Cell *> remove_cells; + // Gather LUTs. + for (auto cell : module->selected_cells()) + { + if (cell->get_bool_attribute(ID::keep)) + continue; + Const lut; + std::vector<SigBit> inputs; + std::vector<SigBit> output; + bool ignore_const = false; + if (techname == "") { + if (cell->type != ID($lut)) + continue; + inputs = cell->getPort(ID::A).bits(); + output = cell->getPort(ID::Y); + lut = cell->getParam(ID(LUT)); + } else if (techname == "xilinx" || techname == "gowin") { + if (cell->type == ID(LUT1)) { + inputs = { + cell->getPort(ID(I0)), + }; + } else if (cell->type == ID(LUT2)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + }; + } else if (cell->type == ID(LUT3)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + }; + } else if (cell->type == ID(LUT4)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + }; + } else if (cell->type == ID(LUT5)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + cell->getPort(ID(I4)), + }; + } else if (cell->type == ID(LUT6)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + cell->getPort(ID(I4)), + cell->getPort(ID(I5)), + }; + } else { + // Not a LUT. + continue; + } + lut = cell->getParam(ID(INIT)); + if (techname == "xilinx") + output = cell->getPort(ID(O)); + else + output = cell->getPort(ID(F)); + } else if (techname == "ecp5") { + if (cell->type == ID(LUT4)) { + inputs = { + cell->getPort(ID::A), + cell->getPort(ID::B), + cell->getPort(ID(C)), + cell->getPort(ID(D)), + }; + lut = cell->getParam(ID(INIT)); + output = cell->getPort(ID(Z)); + ignore_const = true; + } else { + // Not a LUT. + continue; + } + } + std::vector<int> swizzle; + std::vector<SigBit> new_inputs; + bool doit = false; + for (int i = 0; i < GetSize(inputs); i++) { + SigBit input = inputs[i]; + if (!input.wire) { + if (input.data == State::S1) + swizzle.push_back(-2); + else + swizzle.push_back(-1); + // For ECP5, smaller LUTs are + // implemented as LUT4s with + // extra const inputs. Do not + // consider that to be a reason + // to redo a LUT. + if (!ignore_const) + doit = true; + } else { + bool redundant = true; + for (int j = 0; j < GetSize(lut); j++) { + if (lut[j] != lut[j ^ 1 << i]) + redundant = false; + } + if (redundant) { + swizzle.push_back(-1); + doit = true; + } else { + swizzle.push_back(GetSize(new_inputs)); + new_inputs.push_back(input); + } + } + } + if (!doit) + continue; + log(" Optimizing lut %s (%d -> %d)\n", log_id(cell), GetSize(inputs), GetSize(new_inputs)); + if (techname == "ecp5") { + // Pad the LUT to 4 inputs, adding consts from the front. + int extra = 4 - GetSize(new_inputs); + log_assert(extra >= 0); + if (extra) { + for (int i = 0; i < extra; i++) + new_inputs.insert(new_inputs.begin(), State::S0); + for (auto &swz : swizzle) + if (swz >= 0) + swz += extra; + } + } + Const new_lut(0, 1 << GetSize(new_inputs)); + for (int i = 0; i < GetSize(new_lut); i++) { + int lidx = 0; + for (int j = 0; j < GetSize(inputs); j++) { + int val; + if (swizzle[j] == -2) { + val = 1; + } else if (swizzle[j] == -1) { + val = 0; + } else { + val = (i >> swizzle[j]) & 1; + } + lidx |= val << j; + } + new_lut[i] = lut[lidx]; + } + // For ecp5, do not replace with a const driver — the nextpnr + // packer requires a complete set of LUTs for wide LUT muxes. + if (new_inputs.empty() && techname != "ecp5") { + // const driver. + remove_cells.push_back(cell); + module->connect(output, new_lut[0]); + } else { + if (techname == "") { + cell->setParam(ID(LUT), new_lut); + cell->setParam(ID(WIDTH), GetSize(new_inputs)); + cell->setPort(ID::A, new_inputs); + } else if (techname == "ecp5") { + log_assert(GetSize(new_inputs) == 4); + cell->setParam(ID(INIT), new_lut); + cell->setPort(ID::A, new_inputs[0]); + cell->setPort(ID::B, new_inputs[1]); + cell->setPort(ID(C), new_inputs[2]); + cell->setPort(ID(D), new_inputs[3]); + } else { + // xilinx, gowin + cell->setParam(ID(INIT), new_lut); + if (techname == "xilinx") + log_assert(GetSize(new_inputs) <= 6); + else + log_assert(GetSize(new_inputs) <= 4); + if (GetSize(new_inputs) == 1) + cell->type = ID(LUT1); + else if (GetSize(new_inputs) == 2) + cell->type = ID(LUT2); + else if (GetSize(new_inputs) == 3) + cell->type = ID(LUT3); + else if (GetSize(new_inputs) == 4) + cell->type = ID(LUT4); + else if (GetSize(new_inputs) == 5) + cell->type = ID(LUT5); + else if (GetSize(new_inputs) == 6) + cell->type = ID(LUT6); + else + log_assert(0); + cell->unsetPort(ID(I0)); + cell->unsetPort(ID(I1)); + cell->unsetPort(ID(I2)); + cell->unsetPort(ID(I3)); + cell->unsetPort(ID(I4)); + cell->unsetPort(ID(I5)); + cell->setPort(ID(I0), new_inputs[0]); + if (GetSize(new_inputs) >= 2) + cell->setPort(ID(I1), new_inputs[1]); + if (GetSize(new_inputs) >= 3) + cell->setPort(ID(I2), new_inputs[2]); + if (GetSize(new_inputs) >= 4) + cell->setPort(ID(I3), new_inputs[3]); + if (GetSize(new_inputs) >= 5) + cell->setPort(ID(I4), new_inputs[4]); + if (GetSize(new_inputs) >= 6) + cell->setPort(ID(I5), new_inputs[5]); + } + } + } + for (auto cell : remove_cells) + module->remove(cell); + } + } +} XilinxDffOptPass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index aaea6159e..8823a9061 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -222,7 +222,9 @@ struct OptMergeWorker return true; } - if (cell1->type.begins_with("$") && conn1.count(ID(Q)) != 0) { + if (conn1.count(ID(Q)) != 0 && (cell1->type.begins_with("$dff") || cell1->type.begins_with("$dlatch") || + cell1->type.begins_with("$_DFF") || cell1->type.begins_with("$_DLATCH") || cell1->type.begins_with("$_SR_") || + cell1->type.in("$adff", "$sr", "$ff", "$_FF_"))) { std::vector<RTLIL::SigBit> q1 = dff_init_map(cell1->getPort(ID(Q))).to_sigbit_vector(); std::vector<RTLIL::SigBit> q2 = dff_init_map(cell2->getPort(ID(Q))).to_sigbit_vector(); for (size_t i = 0; i < q1.size(); i++) @@ -323,6 +325,19 @@ struct OptMergeWorker log_signal(it.second), log_signal(other_sig)); module->connect(RTLIL::SigSig(it.second, other_sig)); assign_map.add(it.second, other_sig); + + if (it.first == ID(Q) && (cell->type.begins_with("$dff") || cell->type.begins_with("$dlatch") || + cell->type.begins_with("$_DFF") || cell->type.begins_with("$_DLATCH") || cell->type.begins_with("$_SR_") || + cell->type.in("$adff", "$sr", "$ff", "$_FF_"))) { + for (auto c : it.second.chunks()) { + auto jt = c.wire->attributes.find(ID(init)); + if (jt == c.wire->attributes.end()) + continue; + for (int i = c.offset; i < c.offset + c.width; i++) + jt->second[i] = State::Sx; + } + dff_init_map.add(it.second, Const(State::Sx, GetSize(it.second))); + } } } log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); diff --git a/passes/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_dsp.cc b/passes/pmgen/ice40_dsp.cc index f60e67158..c364cd91a 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -73,11 +73,11 @@ void create_ice40_dsp(ice40_dsp_pm &pm) // SB_MAC16 Input Interface SigSpec A = st.sigA; - A.extend_u0(16, st.mul->getParam(ID(A_SIGNED)).as_bool()); + A.extend_u0(16, st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool()); log_assert(GetSize(A) == 16); SigSpec B = st.sigB; - B.extend_u0(16, st.mul->getParam(ID(B_SIGNED)).as_bool()); + B.extend_u0(16, st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool()); log_assert(GetSize(B) == 16); SigSpec CD = st.sigCD; @@ -248,8 +248,8 @@ void create_ice40_dsp(ice40_dsp_pm &pm) cell->setParam(ID(BOTADDSUB_CARRYSELECT), Const(0, 2)); cell->setParam(ID(MODE_8x8), State::S0); - cell->setParam(ID(A_SIGNED), st.mul->getParam(ID(A_SIGNED)).as_bool()); - cell->setParam(ID(B_SIGNED), st.mul->getParam(ID(B_SIGNED)).as_bool()); + cell->setParam(ID(A_SIGNED), st.mul->parameters.at(ID(A_SIGNED), State::S0).as_bool()); + cell->setParam(ID(B_SIGNED), st.mul->parameters.at(ID(B_SIGNED), State::S0).as_bool()); if (st.ffO) { if (st.o_lo) diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 6b6d2b56f..9d649cb98 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -56,11 +56,16 @@ code sigA sigB sigH break; sigH.append(O[i]); } + // This sigM could have no users if downstream sinks (e.g. $add) is + // narrower than $mul result, for example + if (i == 0) + reject; + log_assert(nusers(O.extract_end(i)) <= 1); endcode code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol - if (mul->type != \SB_MAC16 || !param(mul, \A_REG).as_bool()) { + if (mul->type != \SB_MAC16 || !param(mul, \A_REG, State::S0).as_bool()) { argQ = sigA; subpattern(in_dffe); if (dff) { @@ -81,7 +86,7 @@ code argQ ffA ffAholdmux ffArstmux ffAholdpol ffArstpol sigA clock clock_pol endcode code argQ ffB ffBholdmux ffBrstmux ffBholdpol ffBrstpol sigB clock clock_pol - if (mul->type != \SB_MAC16 || !param(mul, \B_REG).as_bool()) { + if (mul->type != \SB_MAC16 || !param(mul, \B_REG, State::S0).as_bool()) { argQ = sigB; subpattern(in_dffe); if (dff) { @@ -104,7 +109,7 @@ endcode code argD ffFJKG sigH clock clock_pol if (nusers(sigH) == 2 && (mul->type != \SB_MAC16 || - (!param(mul, \TOP_8x8_MULT_REG).as_bool() && !param(mul, \BOT_8x8_MULT_REG).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1).as_bool()))) { + (!param(mul, \TOP_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \BOT_8x8_MULT_REG, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool() && !param(mul, \PIPELINE_16x16_MULT_REG1, State::S0).as_bool()))) { argD = sigH; subpattern(out_dffe); if (dff) { @@ -143,7 +148,7 @@ endcode code argD ffH sigH sigO clock clock_pol if (ffFJKG && nusers(sigH) == 2 && - (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2).as_bool())) { + (mul->type != \SB_MAC16 || !param(mul, \PIPELINE_16x16_MULT_REG2, State::S0).as_bool())) { argD = sigH; subpattern(out_dffe); if (dff) { @@ -174,7 +179,7 @@ reject_ffH: ; endcode match add - if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT).as_int() == 3) + if mul->type != \SB_MAC16 || (param(mul, \TOPOUTPUT_SELECT, State::S0).as_int() == 3 && param(mul, \BOTOUTPUT_SELECT, State::S0).as_int() == 3) select add->type.in($add) choice <IdString> AB {\A, \B} @@ -200,7 +205,7 @@ code sigCD sigO cd_signed if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) reject; // If accumulator, check adder width and signedness - if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(add, \A_SIGNED).as_bool())) + if (sigCD == sigH && (actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED, State::S0).as_bool() != param(add, \A_SIGNED).as_bool())) reject; sigO = port(add, \Y); @@ -275,7 +280,7 @@ endcode code argQ ffCD ffCDholdmux ffCDholdpol ffCDrstpol sigCD clock clock_pol if (!sigCD.empty() && sigCD != sigO && - (mul->type != \SB_MAC16 || (!param(mul, \C_REG).as_bool() && !param(mul, \D_REG).as_bool()))) { + (mul->type != \SB_MAC16 || (!param(mul, \C_REG, State::S0).as_bool() && !param(mul, \D_REG, State::S0).as_bool()))) { argQ = sigCD; subpattern(in_dffe); if (dff) { @@ -328,6 +333,8 @@ arg argD argQ clock clock_pol code dff = nullptr; + if (argQ.empty()) + reject; for (auto c : argQ.chunks()) { if (!c.wire) reject; 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/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc index 81c3c57c4..ae7967d7c 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/passes/pmgen/xilinx_dsp.cc @@ -767,6 +767,9 @@ struct XilinxDspPass : public Pass { log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n"); log("device.\n"); log("\n"); + log("This pass is a no-op if the scratchpad variable 'xilinx_dsp.multonly' is set\n"); + log("to 1.\n"); + log("\n"); log("\n"); log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n"); log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n"); @@ -805,6 +808,10 @@ struct XilinxDspPass : public Pass { family = "xcu"; for (auto module : design->selected_modules()) { + + if (design->scratchpad_get_bool("xilinx_dsp.multonly")) + continue; + // Experimental feature: pack $add/$sub cells with // (* use_dsp48="simd" *) into DSP48E1's using its // SIMD feature diff --git a/passes/pmgen/xilinx_dsp.pmg b/passes/pmgen/xilinx_dsp.pmg index 5d3b9c2eb..af47ab111 100644 --- a/passes/pmgen/xilinx_dsp.pmg +++ b/passes/pmgen/xilinx_dsp.pmg @@ -120,7 +120,7 @@ endcode // reset functionality, using a subpattern discussed above) // If matched, treat 'A' input as input of ADREG code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock - if (param(dsp, \ADREG).as_int() == 0) { + if (param(dsp, \ADREG, 1).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { @@ -176,7 +176,7 @@ code argQ ffAD ffADcemux ffADrstmux ffADcepol ffADrstpol sigA clock ffA2 ffA2cem // Only search for ffA2 if there was a pre-adder // (otherwise ffA2 would have been matched as ffAD) if (preAdd) { - if (param(dsp, \AREG).as_int() == 0) { + if (param(dsp, \AREG, 1).as_int() == 0) { argQ = sigA; subpattern(in_dffe); if (dff) { @@ -237,7 +237,7 @@ endcode // (5) Match 'B' input for B2REG // If B2REG, then match 'B' input for B1REG code argQ ffB2 ffB2cemux ffB2rstmux ffB2cepol ffBrstpol sigB clock ffB1 ffB1cemux ffB1rstmux ffB1cepol - if (param(dsp, \BREG).as_int() == 0) { + if (param(dsp, \BREG, 1).as_int() == 0) { argQ = sigB; subpattern(in_dffe); if (dff) { @@ -287,7 +287,7 @@ endcode // (6) Match 'D' input for DREG code argQ ffD ffDcemux ffDrstmux ffDcepol ffDrstpol sigD clock - if (param(dsp, \DREG).as_int() == 0) { + if (param(dsp, \DREG, 1).as_int() == 0) { argQ = sigD; subpattern(in_dffe); if (dff) { @@ -308,7 +308,7 @@ endcode // (7) Match 'P' output that exclusively drives an MREG code argD ffM ffMcemux ffMrstmux ffMcepol ffMrstpol sigM sigP clock - if (param(dsp, \MREG).as_int() == 0 && nusers(sigM) == 2) { + if (param(dsp, \MREG, 1).as_int() == 0 && nusers(sigM) == 2) { argD = sigM; subpattern(out_dffe); if (dff) { @@ -335,7 +335,7 @@ endcode // recognised in xilinx_dsp.cc). match postAdd // Ensure that Z mux is not already used - if port(dsp, \OPMODE, SigSpec()).extract(4,3).is_fully_zero() + if port(dsp, \OPMODE, SigSpec(0, 7)).extract(4,3).is_fully_zero() select postAdd->type.in($add) select GetSize(port(postAdd, \Y)) <= 48 @@ -363,7 +363,7 @@ endcode // (9) Match 'P' output that exclusively drives a PREG code argD ffP ffPcemux ffPrstmux ffPcepol ffPrstpol sigP clock - if (param(dsp, \PREG).as_int() == 0) { + if (param(dsp, \PREG, 1).as_int() == 0) { int users = 2; // If ffMcemux and no postAdd new-value net must have three users: ffMcemux, ffM and ffPcemux if (ffMcemux && !postAdd) users++; @@ -460,7 +460,7 @@ arg argD argQ clock code dff = nullptr; - if (GetSize(argQ) == 0) + if (argQ.empty()) reject; for (const auto &c : argQ.chunks()) { // Abandon matches when 'Q' is a constant diff --git a/passes/sat/Makefile.inc b/passes/sat/Makefile.inc index fc3ac879e..4bb4b0edc 100644 --- a/passes/sat/Makefile.inc +++ b/passes/sat/Makefile.inc @@ -12,4 +12,5 @@ OBJS += passes/sat/supercover.o OBJS += passes/sat/fmcombine.o OBJS += passes/sat/mutate.o OBJS += passes/sat/cutpoint.o +OBJS += passes/sat/fminit.o diff --git a/passes/sat/clk2fflogic.cc b/passes/sat/clk2fflogic.cc index 4bb4aa047..f9e7783a9 100644 --- a/passes/sat/clk2fflogic.cc +++ b/passes/sat/clk2fflogic.cc @@ -214,14 +214,38 @@ struct Clk2fflogicPass : public Pass { continue; } - if (cell->type.in("$dff", "$adff", "$dffsr")) + bool word_dff = cell->type.in("$dff", "$adff", "$dffsr"); + if (word_dff || cell->type.in(ID($_DFF_N_), ID($_DFF_P_), + ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_), + ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) { - bool clkpol = cell->parameters["\\CLK_POLARITY"].as_bool(); + bool clkpol; + SigSpec clk; + if (word_dff) { + clkpol = cell->parameters["\\CLK_POLARITY"].as_bool(); + clk = cell->getPort("\\CLK"); + } + else { + if (cell->type.in(ID($_DFF_P_), ID($_DFF_N_), + ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_))) + clkpol = cell->type[6] == 'P'; + else if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) + clkpol = cell->type[8] == 'P'; + else log_abort(); + clk = cell->getPort("\\C"); + } - SigSpec clk = cell->getPort("\\CLK"); Wire *past_clk = module->addWire(NEW_ID); past_clk->attributes["\\init"] = clkpol ? State::S1 : State::S0; - module->addFf(NEW_ID, clk, past_clk); + + if (word_dff) + module->addFf(NEW_ID, clk, past_clk); + else + module->addFfGate(NEW_ID, clk, past_clk); SigSpec sig_d = cell->getPort("\\D"); SigSpec sig_q = cell->getPort("\\Q"); @@ -244,8 +268,14 @@ struct Clk2fflogicPass : public Pass { Wire *past_d = module->addWire(NEW_ID, GetSize(sig_d)); Wire *past_q = module->addWire(NEW_ID, GetSize(sig_q)); - module->addFf(NEW_ID, sig_d, past_d); - module->addFf(NEW_ID, sig_q, past_q); + if (word_dff) { + module->addFf(NEW_ID, sig_d, past_d); + module->addFf(NEW_ID, sig_q, past_q); + } + else { + module->addFfGate(NEW_ID, sig_d, past_d); + module->addFfGate(NEW_ID, sig_q, past_q); + } if (cell->type == "$adff") { @@ -266,6 +296,26 @@ struct Clk2fflogicPass : public Pass { module->addMux(NEW_ID, rstval, qval, arst, sig_q); } else + if (cell->type.in(ID($_DFF_NN0_), ID($_DFF_NN1_), ID($_DFF_NP0_), ID($_DFF_NP1_), + ID($_DFF_PP0_), ID($_DFF_PP1_), ID($_DFF_PN0_), ID($_DFF_PN1_))) + { + SigSpec arst = cell->getPort("\\R"); + SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); + SigBit rstval = (cell->type[8] == '1'); + + Wire *past_arst = module->addWire(NEW_ID); + module->addFfGate(NEW_ID, arst, past_arst); + if (cell->type[7] == 'P') + arst = module->OrGate(NEW_ID, arst, past_arst); + else + arst = module->AndGate(NEW_ID, arst, past_arst); + + if (cell->type[7] == 'P') + module->addMuxGate(NEW_ID, qval, rstval, arst, sig_q); + else + module->addMuxGate(NEW_ID, rstval, qval, arst, sig_q); + } + else if (cell->type == "$dffsr") { SigSpec qval = module->Mux(NEW_ID, past_q, past_d, clock_edge); @@ -282,9 +332,30 @@ struct Clk2fflogicPass : public Pass { module->addAnd(NEW_ID, qval, clrval, sig_q); } else + if (cell->type.in(ID($_DFFSR_NNN_), ID($_DFFSR_NNP_), ID($_DFFSR_NPN_), ID($_DFFSR_NPP_), + ID($_DFFSR_PNN_), ID($_DFFSR_PNP_), ID($_DFFSR_PPN_), ID($_DFFSR_PPP_))) + { + SigSpec qval = module->MuxGate(NEW_ID, past_q, past_d, clock_edge); + SigSpec setval = cell->getPort("\\S"); + SigSpec clrval = cell->getPort("\\R"); + + if (cell->type[9] != 'P') + setval = module->Not(NEW_ID, setval); + + if (cell->type[10] == 'P') + clrval = module->Not(NEW_ID, clrval); + + qval = module->OrGate(NEW_ID, qval, setval); + module->addAndGate(NEW_ID, qval, clrval, sig_q); + } + else if (cell->type == "$dff") { module->addMux(NEW_ID, past_q, past_d, clock_edge, sig_q); } + else + { + module->addMuxGate(NEW_ID, past_q, past_d, clock_edge, sig_q); + } Const initval; bool assign_initval = false; diff --git a/passes/sat/fminit.cc b/passes/sat/fminit.cc new file mode 100644 index 000000000..f3f00b382 --- /dev/null +++ b/passes/sat/fminit.cc @@ -0,0 +1,197 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct FminitPass : public Pass { + FminitPass() : Pass("fminit", "set init values/sequences for formal") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" fminit [options] <selection>\n"); + log("\n"); + log("This pass creates init constraints (for example for reset sequences) in a formal\n"); + log("model.\n"); + log("\n"); + log(" -seq <signal> <sequence>\n"); + log(" Set sequence using comma-separated list of values, use 'z for\n"); + log(" unconstrained bits. The last value is used for the remainder of the\n"); + log(" trace.\n"); + log("\n"); + log(" -set <signal> <value>\n"); + log(" Add constant value constraint\n"); + log("\n"); + log(" -posedge <signal>\n"); + log(" -negedge <signal>\n"); + log(" Set clock for init sequences\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + vector<pair<string, vector<string>>> initdata; + vector<pair<string, string>> setdata; + string clocksignal; + bool clockedge; + + log_header(design, "Executing FMINIT pass.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-o" && argidx+1 < args.size()) { + // filename = args[++argidx]; + // continue; + // } + if (args[argidx] == "-seq" && argidx+2 < args.size()) { + string lhs = args[++argidx]; + string rhs = args[++argidx]; + initdata.push_back(make_pair(lhs, split_tokens(rhs, ","))); + continue; + } + if (args[argidx] == "-set" && argidx+2 < args.size()) { + string lhs = args[++argidx]; + string rhs = args[++argidx]; + setdata.push_back(make_pair(lhs, rhs)); + continue; + } + if (args[argidx] == "-posedge" && argidx+1 < args.size()) { + clocksignal = args[++argidx]; + clockedge = true; + continue; + } + if (args[argidx] == "-negedge" && argidx+1 < args.size()) { + clocksignal = args[++argidx]; + clockedge = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + Module *module = nullptr; + + for (auto mod : design->selected_modules()) { + if (module != nullptr) + log_error("'fminit' requires exactly one module to be selected.\n"); + module = mod; + } + + if (module == nullptr) + log_error("'fminit' requires exactly one module to be selected.\n"); + + SigSpec clksig; + if (!clocksignal.empty()) { + if (!SigSpec::parse(clksig, module, clocksignal)) + log_error("Error parsing expression '%s'.\n", clocksignal.c_str()); + } + + for (auto &it : setdata) + { + SigSpec lhs, rhs; + + if (!SigSpec::parse(lhs, module, it.first)) + log_error("Error parsing expression '%s'.\n", it.first.c_str()); + + if (!SigSpec::parse_rhs(lhs, rhs, module, it.second)) + log_error("Error parsing expression '%s'.\n", it.second.c_str()); + + SigSpec final_lhs, final_rhs; + + for (int i = 0; i < GetSize(rhs); i++) + if (rhs[i] != State::Sz) { + final_lhs.append(lhs[i]); + final_rhs.append(rhs[i]); + } + + if (!final_lhs.empty()) { + SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs); + module->addAssume(NEW_ID, eq, State::S1); + } + } + + vector<SigSpec> ctrlsig; + vector<SigSpec> ctrlsig_latched; + + for (auto &it : initdata) + { + SigSpec lhs, rhs; + + if (!SigSpec::parse(lhs, module, it.first)) + log_error("Error parsing expression '%s'.\n", it.first.c_str()); + + for (int i = 0; i < GetSize(it.second); i++) + { + if (i >= GetSize(ctrlsig)) + { + SigSpec insig = i > 0 ? ctrlsig.at(i-1) : State::S0; + + Wire *outwire = module->addWire(NEW_ID); + outwire->attributes[ID(init)] = i > 0 ? State::S0 : State::S1; + + if (clksig.empty()) + module->addFf(NEW_ID, insig, outwire); + else + module->addDff(NEW_ID, clksig, insig, outwire, clockedge); + + ctrlsig.push_back(outwire); + ctrlsig_latched.push_back(SigSpec()); + } + + if (i+1 == GetSize(it.second) && ctrlsig_latched[i].empty()) + { + Wire *ffwire = module->addWire(NEW_ID); + ffwire->attributes[ID(init)] = State::S0; + SigSpec outsig = module->Or(NEW_ID, ffwire, ctrlsig[i]); + + if (clksig.empty()) + module->addFf(NEW_ID, outsig, ffwire); + else + module->addDff(NEW_ID, clksig, outsig, ffwire, clockedge); + + ctrlsig_latched[i] = outsig; + } + + SigSpec ctrl = i+1 == GetSize(it.second) ? ctrlsig_latched[i] : ctrlsig[i]; + + SigSpec final_lhs, final_rhs; + + if (!SigSpec::parse_rhs(lhs, rhs, module, it.second[i])) + log_error("Error parsing expression '%s'.\n", it.second[i].c_str()); + + for (int i = 0; i < GetSize(rhs); i++) + if (rhs[i] != State::Sz) { + final_lhs.append(lhs[i]); + final_rhs.append(rhs[i]); + } + + if (!final_lhs.empty()) { + SigSpec eq = module->Eq(NEW_ID, final_lhs, final_rhs); + module->addAssume(NEW_ID, eq, ctrl); + } + } + } + } +} FminitPass; + +PRIVATE_NAMESPACE_END 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..c16db0d57 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -8,9 +8,12 @@ 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)"' +passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif endif diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 1f6cdaa22..5ae2fb22a 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,232 +169,175 @@ 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; + std::string box_file; -#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; + box_file.clear(); + } -#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_assert(!module->attributes.count(ID(abc9_box_id))); - - if (!design->selected_whole_module(module)) - log_error("Can't handle partially selected module %s!\n", log_id(module)); + log_pop(); + } - assign_map.set(module); + void script() YS_OVERRIDE + { + if (check_label("pre")) { + run("abc9_ops -check"); + run("scc -set_attr abc9_scc_id {}"); + if (help_mode) + run("abc9_ops -mark_scc -prep_delays -prep_xaiger [-dff]", "(option for -dff)"); + else + run("abc9_ops -mark_scc -prep_delays -prep_xaiger" + std::string(dff_mode ? " -dff" : ""), "(option for -dff)"); + run("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"); + } - typedef SigSpec clkdomain_t; - dict<clkdomain_t, int> clk_to_mergeability; + if (check_label("map")) { + if (help_mode) { + run("foreach module in selection"); + run(" abc9_ops -write_box [<value from -box>|(null)] <abc-temp-dir>/input.box"); + run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig"); + run(" abc9_exe [options] -cwd <abc-temp-dir> -box <abc-temp-dir>/input.box"); + run(" read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig"); + run(" abc9_ops -reintegrate"); + } + else { + auto selected_modules = active_design->selected_modules(); + active_design->selection_stack.emplace_back(false); - if (dff_mode) - for (auto cell : module->cells()) { - if (cell->type != "$__ABC9_FF_") + 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))); - 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); + log_push(); + active_design->selection().select(mod); - clkdomain_t key(abc9_clock); + if (!active_design->selected_whole_module(mod)) + log_error("Can't handle partially selected module %s!\n", log_id(mod)); - 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); + 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_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)); - 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 (box_file.empty()) + run(stringf("abc9_ops -write_box (null) %s/input.box", tempdir_name.c_str())); + else + run(stringf("abc9_ops -write_box %s %s/input.box", box_file.c_str(), tempdir_name.c_str())); + run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); + + int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs"); + + 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 -box %s/input.box", exe_cmd.str().c_str(), tempdir_name.c_str(), tempdir_name.c_str())); + run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod), tempdir_name.c_str(), tempdir_name.c_str())); + run("abc9_ops -reintegrate"); + } + else + log("Don't call ABC as there is nothing to map.\n"); + + 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..898285c69 --- /dev/null +++ b/passes/techmap/abc9_exe.cc @@ -0,0 +1,530 @@ +/* + * 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_EXE 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"); + } + } + + if (box_file.empty()) + log_cmd_error("abc9_exe '-box' option is mandatory.\n"); + + rewrite_filename(box_file); + if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') + 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..8f5718411 --- /dev/null +++ b/passes/techmap/abc9_ops.cc @@ -0,0 +1,1109 @@ +/* + * 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" + +#define ABC9_FLOPS_BASE_ID 8000 +#define ABC9_DELAY_BASE_ID 9000 + +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 check(RTLIL::Design *design) +{ + dict<IdString,IdString> box_lookup; + for (auto m : design->modules()) { + if (m->name.begins_with("$paramod")) + continue; + + auto flop = m->get_bool_attribute(ID(abc9_flop)); + auto it = m->attributes.find(ID(abc9_box_id)); + if (!flop) { + if (it == m->attributes.end()) + continue; + auto id = it->second.as_int(); + auto r = box_lookup.insert(std::make_pair(stringf("$__boxid%d", id), m->name)); + if (!r.second) + log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n", + log_id(m), id, log_id(r.first->second)); + } + + // Make carry in the last PI, and carry out the last PO + // since ABC requires it this way + IdString carry_in, carry_out; + for (const auto &port_name : m->ports) { + auto w = m->wire(port_name); + log_assert(w); + if (w->get_bool_attribute("\\abc9_carry")) { + if (w->port_input) { + if (carry_in != IdString()) + log_error("Module '%s' contains more than one (* abc9_carry *) input port.\n", log_id(m)); + carry_in = port_name; + } + if (w->port_output) { + if (carry_out != IdString()) + log_error("Module '%s' contains more than one (* abc9_carry *) output port.\n", log_id(m)); + carry_out = port_name; + } + } + + auto it = w->attributes.find("\\abc9_arrival"); + if (it != w->attributes.end()) { + int count = 0; + if (it->second.flags == 0) { + if (it->second.as_int() < 0) + log_error("%s.%s has negative arrival value %d!\n", log_id(m), log_id(port_name), + it->second.as_int()); + count++; + } + else + for (const auto &tok : split_tokens(it->second.decode_string())) { + if (tok.find_first_not_of("0123456789") != std::string::npos) + log_error("%s.%s has non-integer arrival value '%s'!\n", log_id(m), log_id(port_name), + tok.c_str()); + if (atoi(tok.c_str()) < 0) + log_error("%s.%s has negative arrival value %s!\n", log_id(m), log_id(port_name), + tok.c_str()); + count++; + } + if (count > 1 && count != GetSize(w)) + log_error("%s.%s is %d bits wide but abc9_arrival = %s has %d value(s)!\n", log_id(m), log_id(port_name), + GetSize(w), log_signal(it->second), count); + } + + it = w->attributes.find("\\abc9_required"); + if (it != w->attributes.end()) { + int count = 0; + if (it->second.flags == 0) { + if (it->second.as_int() < 0) + log_error("%s.%s has negative required value %d!\n", log_id(m), log_id(port_name), + it->second.as_int()); + count++; + } + else + for (const auto &tok : split_tokens(it->second.decode_string())) { + if (tok.find_first_not_of("0123456789") != std::string::npos) + log_error("%s.%s has non-integer required value '%s'!\n", log_id(m), log_id(port_name), + tok.c_str()); + if (atoi(tok.c_str()) < 0) + log_error("%s.%s has negative required value %s!\n", log_id(m), log_id(port_name), + tok.c_str()); + count++; + } + if (count > 1 && count != GetSize(w)) + log_error("%s.%s is %d bits wide but abc9_required = %s has %d value(s)!\n", log_id(m), log_id(port_name), + GetSize(w), log_signal(it->second), count); + } + } + + if (carry_in != IdString() && carry_out == IdString()) + log_error("Module '%s' contains an (* abc9_carry *) input port but no output port.\n", log_id(m)); + if (carry_in == IdString() && carry_out != IdString()) + log_error("Module '%s' contains an (* abc9_carry *) output port but no input port.\n", log_id(m)); + + if (flop) { + int num_outputs = 0; + for (auto port_name : m->ports) { + auto wire = m->wire(port_name); + if (wire->port_output) num_outputs++; + } + if (num_outputs != 1) + log_error("Module '%s' with (* abc9_flop *) has %d outputs (expect 1).\n", log_id(m), num_outputs); + } + } +} + +void mark_scc(RTLIL::Module *module) +{ + // For every unique SCC found, (arbitrarily) find the first + // 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 = cell->attributes.insert(ID(abc9_mergeability));; + log_assert(r2.second); + r2.first->second = r.first->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_flop = inst_module && inst_module->get_bool_attribute("\\abc9_flop"); + if (abc9_flop && !dff) + continue; + + if ((inst_module && inst_module->attributes.count("\\abc9_box_id")) || abc9_flop) { + auto r = box_ports.insert(cell->type); + if (r.second) { + // Make carry in the last PI, and carry out the last PO + // 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")) { + log_assert(w->port_input != w->port_output); + if (w->port_input) + carry_in = port_name; + else if (w->port_output) + carry_out = port_name; + } + else + r.first->second.push_back(port_name); + } + if (carry_in != IdString()) { + r.first->second.push_back(carry_in); + r.first->second.push_back(carry_out); + } + } + } + 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") && !box_module->get_bool_attribute("\\abc9_flop"))) + continue; + + cell->attributes["\\abc9_box_seq"] = box_count++; + + IdString derived_type = box_module->derive(design, cell->parameters); + box_module = design->module(derived_type); + + auto r = cell_cache.insert(derived_type); + auto &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_type); + + if (box_module->has_processes()) + Pass::call_on_module(design, box_module, "proc"); + + int box_inputs = 0; + for (auto port_name : box_ports.at(cell->type)) { + 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_type.c_str(), log_id(port_name)), GetSize(w)); + } + + // For flops only, create an extra 1-bit input that drives a new wire + // called "<cell>.abc9_ff.Q" that is used below + if (box_module->get_bool_attribute("\\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 prep_delays(RTLIL::Design *design) +{ + std::set<int> delays; + pool<Module*> flops; + std::vector<Cell*> cells; + dict<IdString,dict<IdString,std::vector<int>>> requireds_cache; + for (auto module : design->selected_modules()) { + if (module->processes.size() > 0) { + log("Skipping module %s as it contains processes.\n", log_id(module)); + continue; + } + + cells.clear(); + for (auto cell : module->cells()) { + if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_), ID($__ABC9_DELAY))) + continue; + + RTLIL::Module* inst_module = module->design->module(cell->type); + if (!inst_module) + continue; + if (!inst_module->get_blackbox_attribute()) + continue; + if (inst_module->get_bool_attribute(ID(abc9_flop))) { + IdString derived_type = inst_module->derive(design, cell->parameters); + inst_module = design->module(derived_type); + log_assert(inst_module); + flops.insert(inst_module); + continue; // because all flop required times + // will be captured in the flop box + } + if (inst_module->attributes.count(ID(abc9_box_id))) + continue; + cells.emplace_back(cell); + } + + delays.clear(); + for (auto cell : cells) { + RTLIL::Module* inst_module = module->design->module(cell->type); + log_assert(inst_module); + auto &cell_requireds = requireds_cache[cell->type]; + for (auto &conn : cell->connections_) { + auto port_wire = inst_module->wire(conn.first); + if (!port_wire->port_input) + continue; + + auto r = cell_requireds.insert(conn.first); + auto &requireds = r.first->second; + if (r.second) { + auto it = port_wire->attributes.find("\\abc9_required"); + if (it == port_wire->attributes.end()) + continue; + if (it->second.flags == 0) { + int delay = it->second.as_int(); + delays.insert(delay); + requireds.emplace_back(delay); + } + else + for (const auto &tok : split_tokens(it->second.decode_string())) { + int delay = atoi(tok.c_str()); + delays.insert(delay); + requireds.push_back(delay); + } + } + + if (requireds.empty()) + continue; + + SigSpec O = module->addWire(NEW_ID, GetSize(conn.second)); + auto it = requireds.begin(); + for (int i = 0; i < GetSize(conn.second); ++i) { +#ifndef NDEBUG + if (ys_debug(1)) { + static std::set<std::pair<IdString,IdString>> seen; + if (seen.emplace(cell->type, conn.first).second) log("%s.%s abc9_required = %d\n", log_id(cell->type), log_id(conn.first), requireds[i]); + } +#endif + auto box = module->addCell(NEW_ID, ID($__ABC9_DELAY)); + box->setPort(ID(I), conn.second[i]); + box->setPort(ID(O), O[i]); + box->setParam(ID(DELAY), *it); + if (requireds.size() > 1) + it++; + conn.second[i] = O[i]; + } + } + } + + std::stringstream ss; + bool first = true; + for (auto d : delays) { + if (first) + first = false; + else + ss << " "; + ss << d; + } + module->attributes[ID(abc9_delays)] = ss.str(); + } + + int flops_id = ABC9_FLOPS_BASE_ID; + std::stringstream ss; + for (auto flop_module : flops) { + int num_inputs = 0, num_outputs = 0; + for (auto port_name : flop_module->ports) { + auto wire = flop_module->wire(port_name); + if (wire->port_input) num_inputs++; + if (wire->port_output) num_outputs++; + } + log_assert(num_outputs == 1); + + auto r = flop_module->attributes.insert(ID(abc9_box_id)); + if (r.second) + r.first->second = flops_id++; + + ss << log_id(flop_module) << " " << r.first->second.as_int(); + ss << " 1 " << num_inputs+1 << " " << num_outputs << std::endl; + bool first = true; + for (auto port_name : flop_module->ports) { + auto wire = flop_module->wire(port_name); + if (!wire->port_input) + continue; + if (first) + first = false; + else + ss << " "; + ss << wire->attributes.at("\\abc9_required", 0).as_int(); + } + // Last input is 'abc9_ff.Q' + ss << " 0" << std::endl << std::endl; + } + design->scratchpad_set_string("abc9_ops.box.flops", ss.str()); +} + +void write_box(RTLIL::Module *module, const std::string &src, const std::string &dst) { + std::ofstream ofs(dst); + log_assert(ofs.is_open()); + + // Since ABC can only accept one box file, we have to copy + // over the existing box file + if (src != "(null)") { + std::ifstream ifs(src); + ofs << ifs.rdbuf() << std::endl; + ifs.close(); + } + + ofs << module->design->scratchpad_get_string("abc9_ops.box.flops"); + + auto it = module->attributes.find(ID(abc9_delays)); + if (it != module->attributes.end()) { + for (const auto &tok : split_tokens(it->second.decode_string())) { + int d = atoi(tok.c_str()); + ofs << "$__ABC9_DELAY@" << d << " " << ABC9_DELAY_BASE_ID + d << " 0 1 1" << std::endl; + ofs << d << std::endl; + } + module->attributes.erase(it); + } + + if (ofs.tellp() == 0) + ofs << "(dummy) 1 0 0 0"; + + ofs.close(); +} + +void reintegrate(RTLIL::Module *module) +{ + auto design = module->design; + 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) + continue; + + // Make carry in the last PI, and carry out the last PO + // since ABC requires it this way + IdString carry_in, carry_out; + for (const auto &port_name : m->ports) { + auto w = m->wire(port_name); + log_assert(w); + if (w->get_bool_attribute("\\abc9_carry")) { + log_assert(w->port_input != w->port_output); + if (w->port_input) + carry_in = port_name; + else if (w->port_output) + carry_out = port_name; + } + else + r.first->second.push_back(port_name); + } + + if (carry_in != IdString()) { + r.first->second.push_back(carry_in); + r.first->second.push_back(carry_out); + } + } + + 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_))) { + 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); + if (!existing_cell) + log_error("Cannot find existing box cell with name '%s' in original design.\n", log_id(mapped_cell)); + + if (existing_cell->type == ID($__ABC9_DELAY)) { + SigBit I = mapped_cell->getPort(ID(i)); + SigBit O = mapped_cell->getPort(ID(o)); + if (I.wire) + I.wire = module->wires_.at(remap_name(I.wire->name)); + log_assert(O.wire); + O.wire = module->wires_.at(remap_name(O.wire->name)); + module->connect(O, I); + continue; + } + +#ifndef NDEBUG + RTLIL::Module* box_module = design->module(existing_cell->type); + IdString derived_type = box_module->derive(design, existing_cell->parameters); + RTLIL::Module* derived_module = design->module(derived_type); + log_assert(derived_module); + log_assert(mapped_cell->type == stringf("$__boxid%d", derived_module->attributes.at("\\abc9_box_id").as_int())); +#endif + mapped_cell->type = existing_cell->type; + + RTLIL::Cell *cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type); + 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(derived_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(" -check\n"); + log(" check that the design is valid, e.g. (* abc9_box_id *) values are unique,\n"); + log(" (* abc9_carry *) is only given for one input/output port, etc.\n"); + log("\n"); + log(" -prep_delays\n"); + log(" insert `$__ABC9_DELAY' blackbox cells into the design to account for\n"); + log(" certain delays, e.g. (* abc9_required *) values.\n"); + log("\n"); + log(" -mark_scc\n"); + log(" for an arbitrarily chosen cell in each unique SCC of each selected module\n"); + log(" (tagged with an (* abc9_scc_id = <int> *) attribute), temporarily mark all\n"); + 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(" -write_box (<src>|(null)) <dst>\n"); + log(" copy the existing box file from <src> (skip if '(null)') and append any\n"); + log(" new box definitions.\n"); + log("\n"); + log(" -reintegrate\n"); + log(" for each selected module, re-intergrate the module '<module-name>$abc9'\n"); + log(" by first recovering ABC9 boxes, and then stitching in the remaining primary\n"); + 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 check_mode = false; + bool prep_delays_mode = false; + bool mark_scc_mode = false; + bool prep_dff_mode = false; + bool prep_xaiger_mode = false; + bool reintegrate_mode = false; + bool dff_mode = false; + std::string write_box_src, write_box_dst; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-check") { + check_mode = true; + continue; + } + if (arg == "-mark_scc") { + mark_scc_mode = true; + continue; + } + if (arg == "-prep_dff") { + prep_dff_mode = true; + continue; + } + if (arg == "-prep_xaiger") { + prep_xaiger_mode = true; + continue; + } + if (arg == "-prep_delays") { + prep_delays_mode = true; + continue; + } + if (arg == "-write_box" && argidx+2 < args.size()) { + write_box_src = args[++argidx]; + write_box_dst = args[++argidx]; + rewrite_filename(write_box_src); + rewrite_filename(write_box_dst); + continue; + } + if (arg == "-reintegrate") { + reintegrate_mode = true; + continue; + } + if (arg == "-dff") { + dff_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (!(check_mode || mark_scc_mode || prep_delays_mode || prep_xaiger_mode || prep_dff_mode || !write_box_src.empty() || reintegrate_mode)) + log_cmd_error("At least one of -check, -mark_scc, -prep_{delays,xaiger,dff}, -write_box, -reintegrate must be specified.\n"); + + if (dff_mode && !prep_xaiger_mode) + log_cmd_error("'-dff' option is only relevant for -prep_xaiger.\n"); + + if (check_mode) + check(design); + if (prep_delays_mode) + prep_delays(design); + + for (auto mod : design->selected_modules()) { + if (mod->get_bool_attribute("\\abc9_holes")) + continue; + + 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 (!write_box_src.empty()) + write_box(mod, write_box_src, write_box_dst); + if (mark_scc_mode) + mark_scc(mod); + if (prep_dff_mode) + prep_dff(mod); + if (prep_xaiger_mode) + prep_xaiger(mod, dff_mode); + if (reintegrate_mode) + reintegrate(mod); + } + } +} Abc9OpsPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index 531ac2b99..a6e4fac14 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -423,6 +423,15 @@ struct IopadmapPass : public Pass { } } + if (wire->port_output) { + auto jt = new_wire->attributes.find(ID(init)); + // For output ports, move \init attributes from old wire to new wire + if (jt != new_wire->attributes.end()) { + wire->attributes[ID(init)] = std::move(jt->second); + new_wire->attributes.erase(jt); + } + } + wire->port_id = 0; wire->port_input = false; wire->port_output = false; 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/achronix/Makefile.inc b/techlibs/achronix/Makefile.inc index 994cf0015..994cf0015 100755..100644 --- a/techlibs/achronix/Makefile.inc +++ b/techlibs/achronix/Makefile.inc diff --git a/techlibs/achronix/speedster22i/cells_arith.v b/techlibs/achronix/speedster22i/cells_arith.v index e2194cbd7..e2194cbd7 100755..100644 --- a/techlibs/achronix/speedster22i/cells_arith.v +++ b/techlibs/achronix/speedster22i/cells_arith.v diff --git a/techlibs/achronix/speedster22i/cells_map.v b/techlibs/achronix/speedster22i/cells_map.v index 9f647cbef..9f647cbef 100755..100644 --- a/techlibs/achronix/speedster22i/cells_map.v +++ b/techlibs/achronix/speedster22i/cells_map.v diff --git a/techlibs/achronix/speedster22i/cells_sim.v b/techlibs/achronix/speedster22i/cells_sim.v index a0c60b4be..a0c60b4be 100755..100644 --- a/techlibs/achronix/speedster22i/cells_sim.v +++ b/techlibs/achronix/speedster22i/cells_sim.v diff --git a/techlibs/achronix/synth_achronix.cc b/techlibs/achronix/synth_achronix.cc index 1dc6bdb2f..1dc6bdb2f 100755..100644 --- a/techlibs/achronix/synth_achronix.cc +++ b/techlibs/achronix/synth_achronix.cc diff --git a/techlibs/anlogic/synth_anlogic.cc b/techlibs/anlogic/synth_anlogic.cc index aaa6bda4a..96a231286 100644 --- a/techlibs/anlogic/synth_anlogic.cc +++ b/techlibs/anlogic/synth_anlogic.cc @@ -175,6 +175,7 @@ struct SynthAnlogicPass : public ScriptPass if (check_label("map_gates")) { run("techmap -map +/techmap.v -map +/anlogic/arith_map.v"); + run("opt -fast"); if (retime || help_mode) run("abc -dff -D 1", "(only if -retime)"); } diff --git a/techlibs/common/Makefile.inc b/techlibs/common/Makefile.inc index a42f63128..42f1068ad 100644 --- a/techlibs/common/Makefile.inc +++ b/techlibs/common/Makefile.inc @@ -29,4 +29,3 @@ $(eval $(call add_share_file,share,techlibs/common/gate2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2lut.v)) $(eval $(call add_share_file,share,techlibs/common/cells.lib)) $(eval $(call add_share_file,share,techlibs/common/mul2dsp.v)) -$(eval $(call add_share_file,share,techlibs/common/dummy.box)) diff --git a/techlibs/common/dummy.box b/techlibs/common/dummy.box deleted file mode 100644 index 0c18070a0..000000000 --- a/techlibs/common/dummy.box +++ /dev/null @@ -1 +0,0 @@ -(dummy) 1 0 0 0 diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index d7ec3947e..ecf4d5dc5 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -129,47 +129,82 @@ module _90_shift_shiftx (A, B, Y); input [B_WIDTH-1:0] B; output [Y_WIDTH-1:0] Y; - localparam BB_WIDTH = `MIN($clog2(`MAX(A_WIDTH, Y_WIDTH)) + (B_SIGNED ? 2 : 1), B_WIDTH); - localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH) + (B_SIGNED ? 2**(BB_WIDTH-1) : 0); - parameter _TECHMAP_CELLTYPE_ = ""; + parameter [B_WIDTH-1:0] _TECHMAP_CONSTMSK_B_ = 0; + parameter [B_WIDTH-1:0] _TECHMAP_CONSTVAL_B_ = 0; + localparam extbit = _TECHMAP_CELLTYPE_ == "$shift" ? 1'b0 : 1'bx; - wire [1023:0] _TECHMAP_DO_00_ = "proc;;"; - wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;"; + generate +`ifndef NO_LSB_FIRST_SHIFT_SHIFTX + // If $shift/$shiftx only shifts in units of Y_WIDTH + // (a common pattern created by pmux2shiftx) + // which is checked by ensuring that all that + // the appropriate LSBs of B are constant zero, + // then we can decompose LSB first instead of + // MSB first + localparam CLOG2_Y_WIDTH = $clog2(Y_WIDTH); + if (B_WIDTH > CLOG2_Y_WIDTH+1 && + _TECHMAP_CONSTMSK_B_[CLOG2_Y_WIDTH-1:0] == {CLOG2_Y_WIDTH{1'b1}} && + _TECHMAP_CONSTVAL_B_[CLOG2_Y_WIDTH-1:0] == {CLOG2_Y_WIDTH{1'b0}}) begin + // Halve the size of $shift/$shiftx by $mux-ing A according to + // the LSB of B, after discarding the zeroed bits + localparam Y_WIDTH2 = 2**CLOG2_Y_WIDTH; + localparam entries = (A_WIDTH+Y_WIDTH-1)/Y_WIDTH2; + localparam len = Y_WIDTH2 * ((entries+1)/2); + wire [len-1:0] AA; + wire [(A_WIDTH+Y_WIDTH2+Y_WIDTH-1)-1:0] Apad = {{(Y_WIDTH2+Y_WIDTH-1){extbit}}, A}; + genvar i; + for (i = 0; i < A_WIDTH; i=i+Y_WIDTH2*2) + assign AA[i/2 +: Y_WIDTH2] = B[CLOG2_Y_WIDTH] ? Apad[i+Y_WIDTH2 +: Y_WIDTH2] : Apad[i +: Y_WIDTH2]; + wire [B_WIDTH-2:0] BB = {B[B_WIDTH-1:CLOG2_Y_WIDTH+1], {CLOG2_Y_WIDTH{1'b0}}}; + if (_TECHMAP_CELLTYPE_ == "$shift") + $shift #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(len), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .Y(Y)); + else + $shiftx #(.A_SIGNED(A_SIGNED), .B_SIGNED(B_SIGNED), .A_WIDTH(len), .B_WIDTH(B_WIDTH-1), .Y_WIDTH(Y_WIDTH)) _TECHMAP_REPLACE_ (.A(AA), .B(BB), .Y(Y)); + end + else +`endif + begin + localparam BB_WIDTH = `MIN($clog2(`MAX(A_WIDTH, Y_WIDTH)) + (B_SIGNED ? 2 : 1), B_WIDTH); + localparam WIDTH = `MAX(A_WIDTH, Y_WIDTH) + (B_SIGNED ? 2**(BB_WIDTH-1) : 0); - integer i; - reg [WIDTH-1:0] buffer; - reg overflow; + wire [1023:0] _TECHMAP_DO_00_ = "proc;;"; + wire [1023:0] _TECHMAP_DO_01_ = "CONSTMAP; opt_muxtree; opt_expr -mux_undef -mux_bool -fine;;;"; - always @* begin - overflow = 0; - buffer = {WIDTH{extbit}}; - buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A; - - if (B_WIDTH > BB_WIDTH) begin - if (B_SIGNED) begin - for (i = BB_WIDTH; i < B_WIDTH; i = i+1) - if (B[i] != B[BB_WIDTH-1]) - overflow = 1; - end else - overflow = |B[B_WIDTH-1:BB_WIDTH]; - if (overflow) - buffer = {WIDTH{extbit}}; - end + integer i; + reg [WIDTH-1:0] buffer; + reg overflow; - for (i = BB_WIDTH-1; i >= 0; i = i-1) - if (B[i]) begin - if (B_SIGNED && i == BB_WIDTH-1) - buffer = {buffer, {2**i{extbit}}}; - else if (2**i < WIDTH) - buffer = {{2**i{extbit}}, buffer[WIDTH-1 : 2**i]}; - else - buffer = {WIDTH{extbit}}; + always @* begin + overflow = 0; + buffer = {WIDTH{extbit}}; + buffer[`MAX(A_WIDTH, Y_WIDTH)-1:0] = A; + + if (B_WIDTH > BB_WIDTH) begin + if (B_SIGNED) begin + for (i = BB_WIDTH; i < B_WIDTH; i = i+1) + if (B[i] != B[BB_WIDTH-1]) + overflow = 1; + end else + overflow = |B[B_WIDTH-1:BB_WIDTH]; + if (overflow) + buffer = {WIDTH{extbit}}; + end + + for (i = BB_WIDTH-1; i >= 0; i = i-1) + if (B[i]) begin + if (B_SIGNED && i == BB_WIDTH-1) + buffer = {buffer, {2**i{extbit}}}; + else if (2**i < WIDTH) + buffer = {{2**i{extbit}}, buffer[WIDTH-1 : 2**i]}; + else + buffer = {WIDTH{extbit}}; + end end - end - - assign Y = buffer; + assign Y = buffer; + end + endgenerate endmodule diff --git a/techlibs/coolrunner2/synth_coolrunner2.cc b/techlibs/coolrunner2/synth_coolrunner2.cc index 388e2b792..3bac8623d 100644 --- a/techlibs/coolrunner2/synth_coolrunner2.cc +++ b/techlibs/coolrunner2/synth_coolrunner2.cc @@ -144,8 +144,8 @@ struct SynthCoolrunner2Pass : public ScriptPass if (check_label("fine")) { run("opt -fast -full"); - run("techmap"); - run("techmap -map +/coolrunner2/cells_latch.v"); + run("techmap -map +/techmap.v -map +/coolrunner2/cells_latch.v"); + run("opt -fast"); run("dfflibmap -prepare -liberty +/coolrunner2/xc2_dff.lib"); } 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/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index 6583f43fd..d47b2bed4 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -289,6 +289,7 @@ struct SynthEcp5Pass : public ScriptPass run("techmap"); else run("techmap -map +/techmap.v -map +/ecp5/arith_map.v"); + run("opt -fast"); if (retime || help_mode) run("abc -dff -D 1", "(only if -retime)"); } @@ -343,6 +344,7 @@ struct SynthEcp5Pass : public ScriptPass else run("techmap -map +/ecp5/cells_map.v", "(with -D NO_LUT in vpr mode)"); + run("opt_lut_ins -tech ecp5"); run("clean"); } diff --git a/techlibs/efinix/synth_efinix.cc b/techlibs/efinix/synth_efinix.cc index 0efd91708..637d7c00d 100644 --- a/techlibs/efinix/synth_efinix.cc +++ b/techlibs/efinix/synth_efinix.cc @@ -175,6 +175,7 @@ struct SynthEfinixPass : public ScriptPass if (check_label("map_gates")) { run("techmap -map +/techmap.v -map +/efinix/arith_map.v"); + run("opt -fast"); if (retime || help_mode) run("abc -dff -D 1", "(only if -retime)"); } diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index c5b41b503..86ec9cdc2 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -191,7 +191,7 @@ struct SynthGowinPass : public ScriptPass if (!nobram && check_label("map_bram", "(skip if -nobram)")) { run("memory_bram -rules +/gowin/brams.txt"); - run("techmap -map +/gowin/brams_map.v -map +/gowin/cells_sim.v"); + run("techmap -map +/gowin/brams_map.v"); } if (!nolutram && check_label("map_lutram", "(skip if -nolutram)")) @@ -211,7 +211,7 @@ struct SynthGowinPass : public ScriptPass if (check_label("map_gates")) { run("techmap -map +/techmap.v -map +/gowin/arith_map.v"); - run("techmap -map +/techmap.v"); + run("opt -fast"); if (retime || help_mode) run("abc -dff -D 1", "(only if -retime)"); run("splitnets"); @@ -246,6 +246,7 @@ struct SynthGowinPass : public ScriptPass if (check_label("map_cells")) { run("techmap -map +/gowin/cells_map.v"); + run("opt_lut_ins -tech gowin"); run("setundef -undriven -params -zero"); run("hilomap -singleton -hicell VCC V -locell GND G"); if (!noiopads || help_mode) diff --git a/techlibs/greenpak4/synth_greenpak4.cc b/techlibs/greenpak4/synth_greenpak4.cc index e1fbe6b69..bfbb56d15 100644 --- a/techlibs/greenpak4/synth_greenpak4.cc +++ b/techlibs/greenpak4/synth_greenpak4.cc @@ -160,8 +160,7 @@ struct SynthGreenPAK4Pass : public ScriptPass run("opt -fast -mux_undef -undriven -fine"); run("memory_map"); run("opt -undriven -fine"); - run("techmap"); - run("techmap -map +/greenpak4/cells_latch.v"); + run("techmap -map +/techmap.v -map +/greenpak4/cells_latch.v"); run("dfflibmap -prepare -liberty +/greenpak4/gp_dff.lib"); run("opt -fast"); if (retime || help_mode) 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_ffinit.cc b/techlibs/ice40/ice40_ffinit.cc index 3089d8932..c098736e9 100644 --- a/techlibs/ice40/ice40_ffinit.cc +++ b/techlibs/ice40/ice40_ffinit.cc @@ -78,10 +78,12 @@ struct Ice40FfinitPass : public Pass { continue; if (initbits.count(bit)) { - if (initbits.at(bit) != val) - log_error("Conflicting init values for signal %s (%s = %s, %s = %s).\n", + if (initbits.at(bit) != val) { + log_warning("Conflicting init values for signal %s (%s = %s, %s = %s).\n", log_signal(bit), log_signal(SigBit(wire, i)), log_signal(val), log_signal(initbit_to_wire[bit]), log_signal(initbits.at(bit))); + initbits.at(bit) = State::Sx; + } continue; } @@ -114,6 +116,10 @@ struct Ice40FfinitPass : public Pass { continue; State val = initbits.at(bit_q); + + if (val == State::Sx) + continue; + handled_initbits.insert(bit_q); log("FF init value for cell %s (%s): %s = %c\n", log_id(cell), log_id(cell->type), 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/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 121bcff1f..fdb203dcb 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -273,7 +273,8 @@ struct SynthIce40Pass : public ScriptPass run("opt_expr"); run("opt_clean"); if (help_mode || dsp) { - run("memory_dff"); + run("memory_dff"); // ice40_dsp will merge registers, reserve memory port registers first + run("wreduce t:$mul"); run("techmap -map +/mul2dsp.v -map +/ice40/dsp_map.v -D DSP_A_MAXWIDTH=16 -D DSP_B_MAXWIDTH=16 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_Y_MINWIDTH=11 " "-D DSP_NAME=$__MUL16X16", "(if -dsp)"); @@ -315,6 +316,7 @@ struct SynthIce40Pass : public ScriptPass run("ice40_wrapcarry"); run("techmap -map +/techmap.v -map +/ice40/arith_map.v"); } + run("opt -fast"); if (retime || help_mode) run("abc -dff -D 1", "(only if -retime)"); run("ice40_opt"); 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/sf2/synth_sf2.cc b/techlibs/sf2/synth_sf2.cc index 543dfdb9e..5efa005c8 100644 --- a/techlibs/sf2/synth_sf2.cc +++ b/techlibs/sf2/synth_sf2.cc @@ -180,6 +180,7 @@ struct SynthSf2Pass : public ScriptPass run("memory_map"); run("opt -undriven -fine"); run("techmap -map +/techmap.v -map +/sf2/arith_map.v"); + run("opt -fast"); if (retime || help_mode) run("abc -dff -D 1", "(only if -retime)"); } diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc index 3f2fbcc85..d07bae12a 100644 --- a/techlibs/xilinx/Makefile.inc +++ b/techlibs/xilinx/Makefile.inc @@ -27,6 +27,10 @@ techlibs/xilinx/brams_init_8.vh: techlibs/xilinx/brams_init.mk $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_sim.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/cells_xtra.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc2v_brams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc2v_brams_map.v)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc3sa_brams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc3sda_brams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_brams_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_xcu_brams.txt)) @@ -34,7 +38,8 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc7_brams_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcu_brams_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcup_urams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xcup_urams_map.v)) -$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut4_lutrams.txt)) +$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut6_lutrams.txt)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lutrams_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v)) $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/xc6s_ff_map.v)) diff --git a/techlibs/xilinx/abc9_map.v b/techlibs/xilinx/abc9_map.v index 0652064cb..f2c401d66 100644 --- a/techlibs/xilinx/abc9_map.v +++ b/techlibs/xilinx/abc9_map.v @@ -68,13 +68,14 @@ // (c) a special abc9_ff.clock wire to capture its clock domain and polarity // (indicated to `abc9' so that it only performs sequential synthesis // (with reachability analysis) correctly on one domain at a time) -// (d) a special abc9_ff.init wire to encode the flop's initial state -// NOTE: in order to perform sequential synthesis, `abc9' also requires -// that the initial value of all flops be zero +// (d) an (* abc9_init *) attribute on the $__ABC9_FF_ cell capturing its +// initial state +// NOTE: in order to perform sequential synthesis, `abc9' requires that +// the initial value of all flops be zero // (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; @@ -103,14 +104,14 @@ module FDRE (output Q, input C, CE, D, R); ); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q(QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, IS_C_INVERTED}; - 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 @@ -130,15 +131,15 @@ module FDRE_1 (output Q, input C, CE, D, R); ); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q(QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, 1'b1 /* IS_C_INVERTED */}; - wire [0:0] abc9_ff.init = 1'b0; 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; @@ -166,14 +167,14 @@ module FDSE (output Q, input C, CE, D, S); .D(D), .Q($Q), .C(C), .CE(CE), .S(S) ); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q(QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, IS_C_INVERTED}; - 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 @@ -192,15 +193,15 @@ module FDSE_1 (output Q, input C, CE, D, S); .D(D), .Q($Q), .C(C), .CE(CE), .S(S) ); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q(QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, 1'b1 /* IS_C_INVERTED */}; - wire [0:0] abc9_ff.init = 1'b0; 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; @@ -215,11 +216,11 @@ module FDCE (output Q, input C, CE, D, CLR); .IS_PRE_INVERTED(IS_CLR_INVERTED) ) _TECHMAP_REPLACE_ ( .D(~D), .Q($Q), .C(C), .CE(CE), .PRE(CLR) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC1 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC1 below ); // Since this is an async flop, async behaviour is dealt with here $__ABC9_ASYNC1 abc_async (.A($QQ), .S(CLR ^ IS_CLR_INVERTED), .Y(QQ)); @@ -233,23 +234,23 @@ module FDCE (output Q, input C, CE, D, CLR); .IS_CLR_INVERTED(IS_CLR_INVERTED) ) _TECHMAP_REPLACE_ ( .D(D), .Q($Q), .C(C), .CE(CE), .CLR(CLR) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC0 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC0 below ); // Since this is an async flop, async behaviour is dealt with here $__ABC9_ASYNC0 abc_async (.A($QQ), .S(CLR ^ IS_CLR_INVERTED), .Y(QQ)); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q($QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, IS_C_INVERTED}; - 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 @@ -258,11 +259,11 @@ module FDCE_1 (output Q, input C, CE, D, CLR); .INIT(1'b0) ) _TECHMAP_REPLACE_ ( .D(~D), .Q($Q), .C(C), .CE(CE), .PRE(CLR) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC1 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC1 below ); $__ABC9_ASYNC1 abc_async (.A($QQ), .S(CLR), .Y(QQ)); end @@ -272,23 +273,23 @@ module FDCE_1 (output Q, input C, CE, D, CLR); .INIT(1'b0) ) _TECHMAP_REPLACE_ ( .D(D), .Q($Q), .C(C), .CE(CE), .CLR(CLR) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC0 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC0 below ); $__ABC9_ASYNC0 abc_async (.A($QQ), .S(CLR), .Y(QQ)); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q($QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, 1'b1 /* IS_C_INVERTED */}; - wire [0:0] abc9_ff.init = 1'b0; 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; @@ -303,11 +304,11 @@ module FDPE (output Q, input C, CE, D, PRE); .IS_CLR_INVERTED(IS_PRE_INVERTED), ) _TECHMAP_REPLACE_ ( .D(~D), .Q($Q), .C(C), .CE(CE), .CLR(PRE) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC0 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC0 below ); $__ABC9_ASYNC0 abc_async (.A($QQ), .S(PRE ^ IS_PRE_INVERTED), .Y(QQ)); end @@ -320,22 +321,22 @@ module FDPE (output Q, input C, CE, D, PRE); .IS_PRE_INVERTED(IS_PRE_INVERTED), ) _TECHMAP_REPLACE_ ( .D(D), .Q($Q), .C(C), .CE(CE), .PRE(PRE) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC1 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC1 below ); $__ABC9_ASYNC1 abc_async (.A($QQ), .S(PRE ^ IS_PRE_INVERTED), .Y(QQ)); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q($QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, IS_C_INVERTED}; - 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 @@ -344,11 +345,11 @@ module FDPE_1 (output Q, input C, CE, D, PRE); .INIT(1'b0) ) _TECHMAP_REPLACE_ ( .D(~D), .Q($Q), .C(C), .CE(CE), .CLR(PRE) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC0 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC0 below ); $__ABC9_ASYNC0 abc_async (.A($QQ), .S(PRE), .Y(QQ)); end @@ -358,19 +359,19 @@ module FDPE_1 (output Q, input C, CE, D, PRE); .INIT(1'b0) ) _TECHMAP_REPLACE_ ( .D(D), .Q($Q), .C(C), .CE(CE), .PRE(PRE) - // ^^^ Note that async - // control is not directly - // supported by abc9 but its - // behaviour is captured by - // $__ABC9_ASYNC1 below + // ^^^ Note that async + // control is not directly + // supported by abc9 but its + // behaviour is captured by + // $__ABC9_ASYNC1 below ); $__ABC9_ASYNC1 abc_async (.A($QQ), .S(PRE), .Y(QQ)); end endgenerate + (* abc9_init = 1'b0 *) $__ABC9_FF_ abc9_ff (.D($Q), .Q($QQ)); // Special signals wire [1:0] abc9_ff.clock = {C, 1'b1 /* IS_C_INVERTED */}; - wire [0:0] abc9_ff.init = 1'b0; wire [0:0] _TECHMAP_REPLACE_.abc9_ff.Q = $QQ; endmodule `endif diff --git a/techlibs/xilinx/abc9_model.v b/techlibs/xilinx/abc9_model.v index 204fa883f..782c53ab6 100644 --- a/techlibs/xilinx/abc9_model.v +++ b/techlibs/xilinx/abc9_model.v @@ -33,6 +33,11 @@ endmodule module \$__ABC9_FF_ (input D, output Q); endmodule +(* abc9_box_id = (9000+DELAY) *) +module \$__ABC9_DELAY (input I, output O); + parameter DELAY = 0; +endmodule + // Box to emulate async behaviour of FDC* (* abc9_box_id = 1000, lib_whitebox *) module \$__ABC9_ASYNC0 (input A, S, output Y); @@ -42,7 +47,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/abc9_xc7.box b/techlibs/xilinx/abc9_xc7.box index 13f4f0e61..48d492801 100644 --- a/techlibs/xilinx/abc9_xc7.box +++ b/techlibs/xilinx/abc9_xc7.box @@ -62,67 +62,6 @@ $__ABC9_ASYNC1 1001 1 2 1 #A S 0 764 # Y -# Flop boxes: -# * Max delays from https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf#L237-L251 -# https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf#L265-L277 -# * Exception: $abc9_currQ is a special input (located last) necessary for clock-enable functionality - -# Box 1100 : FDRE -# name ID w/b ins outs -FDRE 1100 1 5 1 -#C CE D R $abc9_currQ -#0 109 -46 404 0 -0 109 0 404 0 # Q (-46ps Tsu clamped to 0) - -# Box 1101 : FDRE_1 -# name ID w/b ins outs -FDRE_1 1101 1 5 1 -#C CE D R $abc9_currQ -#0 109 -46 404 0 -0 109 0 404 0 # Q (-46ps Tsu clamped to 0) - -# Box 1102 : FDSE -# name ID w/b ins outs -FDSE 1102 1 5 1 -#C CE D R $abc9_currQ -#0 109 -46 404 0 -0 109 0 404 0 # Q (-46ps Tsu clamped to 0) - -# Box 1103 : FDSE_1 -# name ID w/b ins outs -FDSE_1 1103 1 5 1 -#C CE D R $abc9_currQ -#0 109 -46 404 0 -0 109 0 404 0 # Q (-46ps Tsu clamped to 0) - -# Box 1104 : FDCE -# name ID w/b ins outs -FDCE 1104 1 5 1 -#C CE CLR D $abc9_currQ -#0 109 764 -46 0 -0 109 764 0 0 # Q (-46ps Tsu clamped to 0) - -# Box 1105 : FDCE_1 -# name ID w/b ins outs -FDCE_1 1105 1 5 1 -#C CE CLR D $abc9_currQ -#0 109 764 -46 0 -0 109 764 0 0 # Q (-46ps Tsu clamped to 0) - -# Box 1106 : FDPE -# name ID w/b ins outs -FDPE 1106 1 5 1 -#C CE D PRE $abc9_currQ -#0 109 -46 764 0 -0 109 0 764 0 # Q (-46ps Tsu clamped to 0) - -# Box 1107 : FDPE_1 -# name ID w/b ins outs -FDPE_1 1107 1 5 1 -#C CE D PRE $abc9_currQ -#0 109 -46 764 0 -0 109 0 764 0 # Q (-46ps Tsu clamped to 0) - # Box 2000 : $__ABC9_LUT6 # (private cell to emulate async behaviour of LUTRAMs) # SLICEM/A6LUT diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index 5c848d4e6..2b8b0dcc1 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -33,72 +33,13 @@ module _80_xilinx_lcu (P, G, CI, CO); genvar i; -`ifdef _CLB_CARRY - - localparam CARRY4_COUNT = (WIDTH + 3) / 4; - localparam MAX_WIDTH = CARRY4_COUNT * 4; - localparam PAD_WIDTH = MAX_WIDTH - WIDTH; - - wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, P & ~G}; - wire [MAX_WIDTH-1:0] C = CO; - - generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice - - // Partially occupied CARRY4 - if ((i+1)*4 > WIDTH) begin - - // First one - if (i == 0) begin - CARRY4 carry4_1st_part - ( - .CYINIT(CI), - .CI (1'd0), - .DI (G [(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]), - ); - // Another one - end else begin - CARRY4 carry4_part - ( - .CYINIT(1'd0), - .CI (C [i*4 - 1]), - .DI (G [(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]), - ); - end - - // Fully occupied CARRY4 - end else begin - - // First one - if (i == 0) begin - CARRY4 carry4_1st_full - ( - .CYINIT(CI), - .CI (1'd0), - .DI (G [((i+1)*4 - 1):i*4]), - .S (S [((i+1)*4 - 1):i*4]), - .CO (CO[((i+1)*4 - 1):i*4]), - ); - // Another one - end else begin - CARRY4 carry4_full - ( - .CYINIT(1'd0), - .CI (C [i*4 - 1]), - .DI (G [((i+1)*4 - 1):i*4]), - .S (S [((i+1)*4 - 1):i*4]), - .CO (CO[((i+1)*4 - 1):i*4]), - ); - end - - end - - end endgenerate +`ifdef _EXPLICIT_CARRY + localparam EXPLICIT_CARRY = 1'b1; +`else + localparam EXPLICIT_CARRY = 1'b0; +`endif -`elsif _EXPLICIT_CARRY +generate if (EXPLICIT_CARRY || `LUT_SIZE == 4) begin wire [WIDTH-1:0] C = {CO, CI}; wire [WIDTH-1:0] S = P & ~G; @@ -112,20 +53,39 @@ module _80_xilinx_lcu (P, G, CI, CO); ); end endgenerate -`else +end else begin - wire [WIDTH-1:0] C = {CO, CI}; - wire [WIDTH-1:0] S = P & ~G; + localparam CARRY4_COUNT = (WIDTH + 3) / 4; + localparam MAX_WIDTH = CARRY4_COUNT * 4; + localparam PAD_WIDTH = MAX_WIDTH - WIDTH; - generate for (i = 0; i < WIDTH; i = i + 1) begin:slice - MUXCY muxcy ( - .CI(C[i]), - .DI(G[i]), - .S(S[i]), - .O(CO[i]) - ); + wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, P & ~G}; + wire [MAX_WIDTH-1:0] GG = {{PAD_WIDTH{1'b0}}, G}; + wire [MAX_WIDTH-1:0] C; + assign CO = C; + + generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice + if (i == 0) begin + CARRY4 carry4 + ( + .CYINIT(CI), + .CI (1'd0), + .DI (GG[i*4 +: 4]), + .S (S [i*4 +: 4]), + .CO (C [i*4 +: 4]), + ); + end else begin + CARRY4 carry4 + ( + .CYINIT(1'd0), + .CI (C [i*4 - 1]), + .DI (GG[i*4 +: 4]), + .S (S [i*4 +: 4]), + .CO (C [i*4 +: 4]), + ); + end end endgenerate -`endif +end endgenerate endmodule @@ -161,82 +121,35 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); genvar i; -`ifdef _CLB_CARRY - - localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; - localparam MAX_WIDTH = CARRY4_COUNT * 4; - localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH; +`ifdef _EXPLICIT_CARRY + localparam EXPLICIT_CARRY = 1'b1; +`else + localparam EXPLICIT_CARRY = 1'b0; +`endif - wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB}; - wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA & BB}; +generate if (`LUT_SIZE == 4) begin - wire [MAX_WIDTH-1:0] C = CO; + wire [Y_WIDTH-1:0] C = {CO, CI}; + wire [Y_WIDTH-1:0] S = {AA ^ BB}; genvar i; - generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice - - // Partially occupied CARRY4 - if ((i+1)*4 > Y_WIDTH) begin - - // First one - if (i == 0) begin - CARRY4 carry4_1st_part - ( - .CYINIT(CI), - .CI (1'd0), - .DI (DI[(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .O (Y [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]) - ); - // Another one - end else begin - CARRY4 carry4_part - ( - .CYINIT(1'd0), - .CI (C [i*4 - 1]), - .DI (DI[(Y_WIDTH - 1):i*4]), - .S (S [(Y_WIDTH - 1):i*4]), - .O (Y [(Y_WIDTH - 1):i*4]), - .CO (CO[(Y_WIDTH - 1):i*4]) - ); - end - - // Fully occupied CARRY4 - end else begin - - // First one - if (i == 0) begin - CARRY4 carry4_1st_full - ( - .CYINIT(CI), - .CI (1'd0), - .DI (DI[((i+1)*4 - 1):i*4]), - .S (S [((i+1)*4 - 1):i*4]), - .O (Y [((i+1)*4 - 1):i*4]), - .CO (CO[((i+1)*4 - 1):i*4]) - ); - // Another one - end else begin - CARRY4 carry4_full - ( - .CYINIT(1'd0), - .CI (C [i*4 - 1]), - .DI (DI[((i+1)*4 - 1):i*4]), - .S (S [((i+1)*4 - 1):i*4]), - .O (Y [((i+1)*4 - 1):i*4]), - .CO (CO[((i+1)*4 - 1):i*4]) - ); - end - - end - + generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice + MUXCY muxcy ( + .CI(C[i]), + .DI(AA[i]), + .S(S[i]), + .O(CO[i]) + ); + XORCY xorcy ( + .CI(C[i]), + .LI(S[i]), + .O(Y[i]) + ); end endgenerate -`elsif _EXPLICIT_CARRY +end else if (EXPLICIT_CARRY) begin wire [Y_WIDTH-1:0] S = AA ^ BB; - wire [Y_WIDTH-1:0] DI = AA & BB; wire CINIT; // Carry chain. @@ -256,7 +169,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); generate for (i = 0; i < 1; i = i + 1) begin:slice CARRY0 #(.CYINIT_FABRIC(1)) carry( .CI_INIT(CI), - .DI(DI[0]), + .DI(AA[0]), .S(S[0]), .CO_CHAIN(CO_CHAIN[0]), .CO_FABRIC(CO[0]), @@ -268,7 +181,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); if(i % 4 == 0) begin CARRY0 carry ( .CI(C[i]), - .DI(DI[i]), + .DI(AA[i]), .S(S[i]), .CO_CHAIN(CO_CHAIN[i]), .CO_FABRIC(CO[i]), @@ -279,7 +192,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); begin CARRY carry ( .CI(C[i]), - .DI(DI[i]), + .DI(AA[i]), .S(S[i]), .CO_CHAIN(CO_CHAIN[i]), .CO_FABRIC(CO[i]), @@ -292,7 +205,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); if(i % 4 == 0) begin CARRY0 top_of_carry ( .CI(C[i]), - .DI(DI[i]), + .DI(AA[i]), .S(S[i]), .CO_CHAIN(CO_CHAIN[i]), .O(Y[i]) @@ -302,7 +215,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); begin CARRY top_of_carry ( .CI(C[i]), - .DI(DI[i]), + .DI(AA[i]), .S(S[i]), .CO_CHAIN(CO_CHAIN[i]), .O(Y[i]) @@ -331,28 +244,45 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO); end end endgenerate -`else +end else begin - wire [Y_WIDTH-1:0] S = AA ^ BB; - wire [Y_WIDTH-1:0] DI = AA & BB; + localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; + localparam MAX_WIDTH = CARRY4_COUNT * 4; + localparam PAD_WIDTH = MAX_WIDTH - Y_WIDTH; - wire [Y_WIDTH-1:0] C = {CO, CI}; + wire [MAX_WIDTH-1:0] S = {{PAD_WIDTH{1'b0}}, AA ^ BB}; + wire [MAX_WIDTH-1:0] DI = {{PAD_WIDTH{1'b0}}, AA}; - generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice - MUXCY muxcy ( - .CI(C[i]), - .DI(DI[i]), - .S(S[i]), - .O(CO[i]) - ); - XORCY xorcy ( - .CI(C[i]), - .LI(S[i]), - .O(Y[i]) - ); + wire [MAX_WIDTH-1:0] O; + wire [MAX_WIDTH-1:0] C; + assign Y = O, CO = C; + + genvar i; + generate for (i = 0; i < CARRY4_COUNT; i = i + 1) begin:slice + if (i == 0) begin + CARRY4 carry4 + ( + .CYINIT(CI), + .CI (1'd0), + .DI (DI[i*4 +: 4]), + .S (S [i*4 +: 4]), + .O (O [i*4 +: 4]), + .CO (C [i*4 +: 4]) + ); + end else begin + CARRY4 carry4 + ( + .CYINIT(1'd0), + .CI (C [i*4 - 1]), + .DI (DI[i*4 +: 4]), + .S (S [i*4 +: 4]), + .O (O [i*4 +: 4]), + .CO (C [i*4 +: 4]) + ); + end end endgenerate -`endif +end endgenerate assign X = S; endmodule diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index 22dca3c47..4692eba33 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -325,17 +325,20 @@ endmodule // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L238-L250 -(* abc9_box_id=1100, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDRE ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) (* invertible_pin = "IS_C_INVERTED" *) input C, + (* abc9_required=109 *) input CE, (* invertible_pin = "IS_D_INVERTED" *) + //(* abc9_required=-46 *) // Negative required times not currently supported input D, (* invertible_pin = "IS_R_INVERTED" *) + (* abc9_required=404 *) input R ); parameter [0:0] INIT = 1'b0; @@ -349,30 +352,38 @@ module FDRE ( endcase endgenerate endmodule -(* abc9_box_id=1101, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDRE_1 ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) input C, - input CE, D, R + (* abc9_required=109 *) + input CE, + //(* abc9_required=-46 *) // Negative required times not currently supported + input D, + (* abc9_required=404 *) + input R ); parameter [0:0] INIT = 1'b0; initial Q <= INIT; always @(negedge C) if (R) Q <= 1'b0; else if (CE) Q <= D; endmodule -(* abc9_box_id=1102, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDSE ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) (* invertible_pin = "IS_C_INVERTED" *) input C, + (* abc9_required=109 *) input CE, (* invertible_pin = "IS_D_INVERTED" *) + //(* abc9_required=-46 *) // Negative required times not currently supported input D, (* invertible_pin = "IS_S_INVERTED" *) + (* abc9_required=404 *) input S ); parameter [0:0] INIT = 1'b1; @@ -386,13 +397,18 @@ module FDSE ( endcase endgenerate endmodule -(* abc9_box_id=1103, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDSE_1 ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) input C, - input CE, D, S + (* abc9_required=109 *) + input CE, + //(* abc9_required=-46 *) // Negative required times not currently supported + input D, + (* abc9_required=404 *) + input S ); parameter [0:0] INIT = 1'b1; initial Q <= INIT; @@ -405,6 +421,7 @@ module FDRSE ( (* invertible_pin = "IS_C_INVERTED" *) input C, (* invertible_pin = "IS_CE_INVERTED" *) + (* abc9_required=109 *) input CE, (* invertible_pin = "IS_D_INVERTED" *) input D, @@ -434,17 +451,20 @@ module FDRSE ( Q <= d; endmodule -(* abc9_box_id=1104, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDCE ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) (* invertible_pin = "IS_C_INVERTED" *) input C, + (* abc9_required=109 *) input CE, (* invertible_pin = "IS_CLR_INVERTED" *) + (* abc9_required=764 *) input CLR, (* invertible_pin = "IS_D_INVERTED" *) + //(* abc9_required=-46 *) // Negative required times not currently supported input D ); parameter [0:0] INIT = 1'b0; @@ -460,30 +480,38 @@ module FDCE ( endcase endgenerate endmodule -(* abc9_box_id=1105, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDCE_1 ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) input C, - input CE, D, CLR + (* abc9_required=109 *) + input CE, + (* abc9_required=764 *) + input CLR, + //(* abc9_required=-46 *) // Negative required times not currently supported + input D ); parameter [0:0] INIT = 1'b0; initial Q <= INIT; always @(negedge C, posedge CLR) if (CLR) Q <= 1'b0; else if (CE) Q <= D; endmodule -(* abc9_box_id=1106, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDPE ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) (* invertible_pin = "IS_C_INVERTED" *) input C, + (* abc9_required=109 *) input CE, (* invertible_pin = "IS_D_INVERTED" *) + //(* abc9_required=-46 *) // Negative required times not currently supported input D, (* invertible_pin = "IS_PRE_INVERTED" *) + (* abc9_required=764 *) input PRE ); parameter [0:0] INIT = 1'b1; @@ -499,13 +527,18 @@ module FDPE ( endcase endgenerate endmodule -(* abc9_box_id=1107, lib_whitebox, abc9_flop *) +(* abc9_flop, lib_whitebox *) module FDPE_1 ( (* abc9_arrival=303 *) output reg Q, (* clkbuf_sink *) input C, - input CE, D, PRE + (* abc9_required=109 *) + input CE, + //(* abc9_required=-46 *) // Negative required times not currently supported + input D, + (* abc9_required=764 *) + input PRE ); parameter [0:0] INIT = 1'b1; initial Q <= INIT; @@ -1120,15 +1153,33 @@ module RAM16X1D_1 ( endmodule module RAM32X1D ( - // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L857 - (* abc9_arrival=1188 *) + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981 + (* abc9_arrival=1153 *) output DPO, SPO, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986 + (* abc9_required=453 *) input D, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) input WE, - input A0, A1, A2, A3, A4, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L800 + (* abc9_required=245 *) + input A0, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/clBLM_R.sdf#L798 + (* abc9_required=208 *) + input A1, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L796 + (* abc9_required=147 *) + input A2, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L794 + (* abc9_required=68 *) + input A3, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L792 + (* abc9_required=66 *) + input A4, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4 ); parameter INIT = 32'h0; @@ -1143,15 +1194,33 @@ module RAM32X1D ( endmodule module RAM32X1D_1 ( - // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L857 - (* abc9_arrival=1188 *) + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981 + (* abc9_arrival=1153 *) output DPO, SPO, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986 + (* abc9_required=453 *) input D, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) input WE, - input A0, A1, A2, A3, A4, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L800 + (* abc9_required=245 *) + input A0, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/clBLM_R.sdf#L798 + (* abc9_required=208 *) + input A1, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L796 + (* abc9_required=147 *) + input A2, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L794 + (* abc9_required=68 *) + input A3, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L792 + (* abc9_required=66 *) + input A4, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4 ); parameter INIT = 32'h0; @@ -1166,15 +1235,36 @@ module RAM32X1D_1 ( endmodule module RAM64X1D ( - // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889 + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981 (* abc9_arrival=1153 *) output DPO, SPO, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986 + (* abc9_required=453 *) input D, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) input WE, - input A0, A1, A2, A3, A4, A5, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L828 + (* abc9_required=362 *) + input A0, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L826 + (* abc9_required=245 *) + input A1, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L824 + (* abc9_required=208 *) + input A2, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L822 + (* abc9_required=147 *) + input A3, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L820 + (* abc9_required=68 *) + input A4, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818 + (* abc9_required=66 *) + input A5, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5 ); parameter INIT = 64'h0; @@ -1189,15 +1279,36 @@ module RAM64X1D ( endmodule module RAM64X1D_1 ( - // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889 + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981 (* abc9_arrival=1153 *) output DPO, SPO, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986 + (* abc9_required=453 *) input D, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) input WE, - input A0, A1, A2, A3, A4, A5, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L828 + (* abc9_required=362 *) + input A0, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L826 + (* abc9_required=245 *) + input A1, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L824 + (* abc9_required=208 *) + input A2, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L822 + (* abc9_required=147 *) + input A3, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L820 + (* abc9_required=68 *) + input A4, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818 + (* abc9_required=66 *) + input A5, input DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5 ); parameter INIT = 64'h0; @@ -1212,16 +1323,23 @@ module RAM64X1D_1 ( endmodule module RAM128X1D ( - // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889 - // plus 204ps to cross MUXF7 - (* abc9_arrival=1357 *) - output DPO, SPO, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L981 + // plus 208ps to cross MUXF7 + (* abc9_arrival=1359 *) + output DPO, SPO, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986 + (* abc9_required=453 *) input D, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) input WE, - input [6:0] A, DPRA + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818-830 + (* abc9_required="616 362 245 208 147 68 66" *) + input [6:0] A, + input [6:0] DPRA ); parameter INIT = 128'h0; parameter IS_WCLK_INVERTED = 1'b0; @@ -1253,24 +1371,44 @@ endmodule // Multi port. module RAM32M ( + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889 // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L857 - (* abc9_arrival=1188 *) + (* abc9_arrival="1153 1188" *) output [1:0] DOA, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957 // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L925 - (* abc9_arrival=1187 *) + (* abc9_arrival="1161 1187" *) output [1:0] DOB, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1025 // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L993 - (* abc9_arrival=1180 *) + (* abc9_arrival="1158 1180" *) output [1:0] DOC, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1093 // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1061 - (* abc9_arrival=1190 *) + (* abc9_arrival="1163 1190" *) output [1:0] DOD, - input [4:0] ADDRA, ADDRB, ADDRC, ADDRD, - input [1:0] DIA, DIB, DIC, DID, + input [4:0] ADDRA, ADDRB, ADDRC, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L792-L802 + (* abc9_required="245 208 147 68 66" *) + input [4:0] ADDRD, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986-L988 + (* abc9_required="453 384" *) + input [1:0] DIA, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1054-L1056 + (* abc9_required="461 354" *) + input [1:0] DIB, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1122-L1124 + (* abc9_required="457 375" *) + input [1:0] DIC, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1190-L1192 + (* abc9_required="310 334" *) + input [1:0] DID, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) - input WCLK, - input WE + input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) + input WE ); parameter [63:0] INIT_A = 64'h0000000000000000; parameter [63:0] INIT_B = 64'h0000000000000000; @@ -1367,22 +1505,38 @@ endmodule module RAM64M ( // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L889 (* abc9_arrival=1153 *) - output DOA, + output DOA, // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L957 (* abc9_arrival=1161 *) - output DOB, + output DOB, // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1025 (* abc9_arrival=1158 *) - output DOC, + output DOC, // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLM_R.sdf#L1093 (* abc9_arrival=1163 *) - output DOD, - input [5:0] ADDRA, ADDRB, ADDRC, ADDRD, - input DIA, DIB, DIC, DID, + output DOD, + input [5:0] ADDRA, ADDRB, ADDRC, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L818-L830 + (* abc9_required="362 245 208 147 68 66" *) + input [5:0] ADDRD, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L986-L988 + (* abc9_required=384 *) + input DIA, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1054-L1056 + (* abc9_required=354 *) + input DIB, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1122-L1124 + (* abc9_required=375 *) + input DIC, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L1190-L1192 + (* abc9_required=310 *) + input DID, (* clkbuf_sink *) (* invertible_pin = "IS_WCLK_INVERTED" *) - input WCLK, - input WE + input WCLK, + // Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/31f51ac5ec7448dd6f79a8267f147123e4413c21/artix7/timings/CLBLM_R.sdf#L834 + (* abc9_required=654 *) + input WE ); parameter [63:0] INIT_A = 64'h0000000000000000; parameter [63:0] INIT_B = 64'h0000000000000000; @@ -2155,7 +2309,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). @@ -2169,21 +2551,30 @@ module DSP48E1 ( output reg MULTSIGNOUT, output OVERFLOW, `ifdef YOSYS - (* abc9_arrival = \DSP48E1.P_arrival () *) + (* abc9_arrival = \P.abc9_arrival () *) `endif output reg signed [47:0] P, output reg PATTERNBDETECT, output reg PATTERNDETECT, `ifdef YOSYS - (* abc9_arrival = \DSP48E1.PCOUT_arrival () *) + (* abc9_arrival = \PCOUT.abc9_arrival () *) `endif output [47:0] PCOUT, output UNDERFLOW, +`ifdef YOSYS + (* abc9_required = \A.abc9_required () *) +`endif input signed [29:0] A, input [29:0] ACIN, input [3:0] ALUMODE, +`ifdef YOSYS + (* abc9_required = \B.abc9_required () *) +`endif input signed [17:0] B, input [17:0] BCIN, +`ifdef YOSYS + (* abc9_required = \C.abc9_required () *) +`endif input [47:0] C, input CARRYCASCIN, input CARRYIN, @@ -2202,10 +2593,16 @@ module DSP48E1 ( input CEM, input CEP, (* clkbuf_sink *) input CLK, +`ifdef YOSYS + (* abc9_required = \D.abc9_required () *) +`endif input [24:0] D, input [4:0] INMODE, input MULTSIGNIN, input [6:0] OPMODE, +`ifdef YOSYS + (* abc9_required = \PCIN.abc9_required () *) +`endif input [47:0] PCIN, input RSTA, input RSTALLCARRYIN, @@ -2250,69 +2647,133 @@ module DSP48E1 ( parameter [6:0] IS_OPMODE_INVERTED = 7'b0; `ifdef YOSYS - function integer \DSP48E1.P_arrival ; + function integer \A.abc9_required ; + begin + \A.abc9_required = 0; + if (AREG != 0) \A.abc9_required = 254; + else if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin + if (MREG != 0) \A.abc9_required = 1416; + else if (PREG != 0) \A.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 3030 : 2739) ; + end + else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin + // Worst-case from ADREG and MREG + if (MREG != 0) \A.abc9_required = 2400; + else if (ADREG != 0) \A.abc9_required = 1283; + else if (PREG != 0) \A.abc9_required = 3723; + else if (PREG != 0) \A.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 4014 : 3723) ; + end + else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin + if (PREG != 0) \A.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1730 : 1441) ; + end + end + endfunction + function integer \B.abc9_required ; + begin + \B.abc9_required = 0; + if (BREG != 0) \B.abc9_required = 324; + else if (MREG != 0) \B.abc9_required = 1285; + else if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin + if (PREG != 0) \B.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 2898 : 2608) ; + end + else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin + if (PREG != 0) \B.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 2898 : 2608) ; + end + else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin + if (PREG != 0) \B.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1718 : 1428) ; + end + end + endfunction + function integer \C.abc9_required ; + begin + \C.abc9_required = 0; + if (CREG != 0) \C.abc9_required = 168; + else if (PREG != 0) \C.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1534 : 1244) ; + end + endfunction + function integer \D.abc9_required ; + begin + \D.abc9_required = 0; + if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin + end + else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin + if (DREG != 0) \D.abc9_required = 248; + else if (ADREG != 0) \D.abc9_required = 1195; + else if (MREG != 0) \D.abc9_required = 2310; + else if (PREG != 0) \D.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 3925 : 3635) ; + end + else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin + end + end + endfunction + function integer \PCIN.abc9_required ; + begin + \PCIN.abc9_required = 0; + if (PREG != 0) \PCIN.abc9_required = (USE_PATTERN_DETECT != "NO_PATDET" ? 1315 : 1025) ; + end + endfunction + function integer \P.abc9_arrival ; begin - \DSP48E1.P_arrival = 0; + \P.abc9_arrival = 0; if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin - if (PREG != 0) \DSP48E1.P_arrival = 329; + if (PREG != 0) \P.abc9_arrival = 329; // Worst-case from CREG and MREG - else if (CREG != 0) \DSP48E1.P_arrival = 1687; - else if (MREG != 0) \DSP48E1.P_arrival = 1671; + else if (CREG != 0) \P.abc9_arrival = 1687; + else if (MREG != 0) \P.abc9_arrival = 1671; // Worst-case from AREG and BREG - else if (AREG != 0) \DSP48E1.P_arrival = 2952; - else if (BREG != 0) \DSP48E1.P_arrival = 2813; + else if (AREG != 0) \P.abc9_arrival = 2952; + else if (BREG != 0) \P.abc9_arrival = 2813; end else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin - if (PREG != 0) \DSP48E1.P_arrival = 329; + if (PREG != 0) \P.abc9_arrival = 329; // Worst-case from CREG and MREG - else if (CREG != 0) \DSP48E1.P_arrival = 1687; - else if (MREG != 0) \DSP48E1.P_arrival = 1671; + else if (CREG != 0) \P.abc9_arrival = 1687; + else if (MREG != 0) \P.abc9_arrival = 1671; // Worst-case from AREG, ADREG, BREG, DREG - else if (AREG != 0) \DSP48E1.P_arrival = 3935; - else if (DREG != 0) \DSP48E1.P_arrival = 3908; - else if (ADREG != 0) \DSP48E1.P_arrival = 2958; - else if (BREG != 0) \DSP48E1.P_arrival = 2813; + else if (AREG != 0) \P.abc9_arrival = 3935; + else if (DREG != 0) \P.abc9_arrival = 3908; + else if (ADREG != 0) \P.abc9_arrival = 2958; + else if (BREG != 0) \P.abc9_arrival = 2813; end else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin - if (PREG != 0) \DSP48E1.P_arrival = 329; + if (PREG != 0) \P.abc9_arrival = 329; // Worst-case from AREG, BREG, CREG - else if (CREG != 0) \DSP48E1.P_arrival = 1687; - else if (AREG != 0) \DSP48E1.P_arrival = 1632; - else if (BREG != 0) \DSP48E1.P_arrival = 1616; + else if (CREG != 0) \P.abc9_arrival = 1687; + else if (AREG != 0) \P.abc9_arrival = 1632; + else if (BREG != 0) \P.abc9_arrival = 1616; end //else // $error("Invalid DSP48E1 configuration"); end endfunction - function integer \DSP48E1.PCOUT_arrival ; + function integer \PCOUT.abc9_arrival ; begin - \DSP48E1.PCOUT_arrival = 0; + \PCOUT.abc9_arrival = 0; if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin - if (PREG != 0) \DSP48E1.PCOUT_arrival = 435; + if (PREG != 0) \PCOUT.abc9_arrival = 435; // Worst-case from CREG and MREG - else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835; - else if (MREG != 0) \DSP48E1.PCOUT_arrival = 1819; + else if (CREG != 0) \PCOUT.abc9_arrival = 1835; + else if (MREG != 0) \PCOUT.abc9_arrival = 1819; // Worst-case from AREG and BREG - else if (AREG != 0) \DSP48E1.PCOUT_arrival = 3098; - else if (BREG != 0) \DSP48E1.PCOUT_arrival = 2960; + else if (AREG != 0) \PCOUT.abc9_arrival = 3098; + else if (BREG != 0) \PCOUT.abc9_arrival = 2960; end else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin - if (PREG != 0) \DSP48E1.PCOUT_arrival = 435; + if (PREG != 0) \PCOUT.abc9_arrival = 435; // Worst-case from CREG and MREG - else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835; - else if (MREG != 0) \DSP48E1.PCOUT_arrival = 1819; + else if (CREG != 0) \PCOUT.abc9_arrival = 1835; + else if (MREG != 0) \PCOUT.abc9_arrival = 1819; // Worst-case from AREG, ADREG, BREG, DREG - else if (AREG != 0) \DSP48E1.PCOUT_arrival = 4083; - else if (DREG != 0) \DSP48E1.PCOUT_arrival = 4056; - else if (BREG != 0) \DSP48E1.PCOUT_arrival = 2960; - else if (ADREG != 0) \DSP48E1.PCOUT_arrival = 2859; + else if (AREG != 0) \PCOUT.abc9_arrival = 4083; + else if (DREG != 0) \PCOUT.abc9_arrival = 4056; + else if (BREG != 0) \PCOUT.abc9_arrival = 2960; + else if (ADREG != 0) \PCOUT.abc9_arrival = 2859; end else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin - if (PREG != 0) \DSP48E1.PCOUT_arrival = 435; + if (PREG != 0) \PCOUT.abc9_arrival = 435; // Worst-case from AREG, BREG, CREG - else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835; - else if (AREG != 0) \DSP48E1.PCOUT_arrival = 1780; - else if (BREG != 0) \DSP48E1.PCOUT_arrival = 1765; + else if (CREG != 0) \PCOUT.abc9_arrival = 1835; + else if (AREG != 0) \PCOUT.abc9_arrival = 1780; + else if (BREG != 0) \PCOUT.abc9_arrival = 1765; end //else // $error("Invalid DSP48E1 configuration"); diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py index d5c58c5d7..75646f594 100644 --- a/techlibs/xilinx/cells_xtra.py +++ b/techlibs/xilinx/cells_xtra.py @@ -180,18 +180,58 @@ CELLS = [ Cell('RAMB18E1', port_attrs={ 'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L143 'DOADO': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L163 'DOBDO': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L144 'DOPADOP': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L164 'DOPBDOP': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L13 + 'ADDRARDADDR': ['abc9_required=566'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L17 + 'ADDRBWRADDR': ['abc9_required=566'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L19 + 'WEA': ['abc9_required=532'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L21 + 'WEBWE': ['abc9_required=532'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L123 + 'DIADI': ['abc9_required=737'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L133 + 'DIBDI': ['abc9_required=737'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L125 + 'DIPADIP': ['abc9_required=737'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L135 + 'DIPBDIP': ['abc9_required=737'], }), Cell('RAMB36E1', port_attrs={ 'CLKARDCLK': ['clkbuf_sink'], 'CLKBWRCLK': ['clkbuf_sink'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L143 'DOADO': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L163 'DOBDO': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L144 'DOPADOP': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L164 'DOPBDOP': ['abc9_arrival=2454'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L13 + 'ADDRARDADDR': ['abc9_required=566'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L17 + 'ADDRBWRADDR': ['abc9_required=566'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L19 + 'WEA': ['abc9_required=532'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L21 + 'WEBWE': ['abc9_required=532'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L123 + 'DIADI': ['abc9_required=737'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L133 + 'DIBDI': ['abc9_required=737'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L125 + 'DIPADIP': ['abc9_required=737'], + # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/BRAM_L.sdf#L135 + 'DIPBDIP': ['abc9_required=737'], }), # Ultrascale. Cell('FIFO18E2', port_attrs={'RDCLK': ['clkbuf_sink'], 'WRCLK': ['clkbuf_sink']}), @@ -209,7 +249,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..e87f4ec76 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -4518,13 +4518,21 @@ module RAMB18E1 (...); input RSTREGARSTREG; (* invertible_pin = "IS_RSTREGB_INVERTED" *) input RSTREGB; + (* abc9_required=566 *) input [13:0] ADDRARDADDR; + (* abc9_required=566 *) input [13:0] ADDRBWRADDR; + (* abc9_required=737 *) input [15:0] DIADI; + (* abc9_required=737 *) input [15:0] DIBDI; + (* abc9_required=737 *) input [1:0] DIPADIP; + (* abc9_required=737 *) input [1:0] DIPBDIP; + (* abc9_required=532 *) input [1:0] WEA; + (* abc9_required=532 *) input [3:0] WEBWE; endmodule @@ -4742,13 +4750,21 @@ module RAMB36E1 (...); input REGCEB; input INJECTDBITERR; input INJECTSBITERR; + (* abc9_required=566 *) input [15:0] ADDRARDADDR; + (* abc9_required=566 *) input [15:0] ADDRBWRADDR; + (* abc9_required=737 *) input [31:0] DIADI; + (* abc9_required=737 *) input [31:0] DIBDI; + (* abc9_required=737 *) input [3:0] DIPADIP; + (* abc9_required=737 *) input [3:0] DIPBDIP; + (* abc9_required=532 *) input [3:0] WEA; + (* abc9_required=532 *) input [7:0] WEBWE; endmodule @@ -5476,49 +5492,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/lut4_lutrams.txt b/techlibs/xilinx/lut4_lutrams.txt new file mode 100644 index 000000000..2b344a9ee --- /dev/null +++ b/techlibs/xilinx/lut4_lutrams.txt @@ -0,0 +1,19 @@ +bram $__XILINX_RAM16X1D + init 1 + abits 4 + dbits 1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 0 1 + clkpol 0 2 +endbram + + +match $__XILINX_RAM16X1D + min bits 2 + min wports 1 + make_outreg +endmatch diff --git a/techlibs/xilinx/lutrams.txt b/techlibs/xilinx/lut6_lutrams.txt index 29f6b05cc..3b3cb81e1 100644 --- a/techlibs/xilinx/lutrams.txt +++ b/techlibs/xilinx/lut6_lutrams.txt @@ -1,17 +1,3 @@ - -bram $__XILINX_RAM16X1D - init 1 - abits 4 - dbits 1 - groups 2 - ports 1 1 - wrmode 0 1 - enable 0 1 - transp 0 0 - clocks 0 1 - clkpol 0 2 -endbram - bram $__XILINX_RAM32X1D init 1 abits 5 @@ -105,16 +91,6 @@ bram $__XILINX_RAM64X1Q endbram -# Disabled for now, pending support for LUT4 arches -# since on LUT6 arches this occupies same area as -# a RAM32X1D -#match $__XILINX_RAM16X1D -# min bits 2 -# min wports 1 -# make_outreg -# or_next_if_better -#endmatch - match $__XILINX_RAM32X1D min bits 3 min wports 1 @@ -153,7 +129,7 @@ endmatch match $__XILINX_RAM32X2Q min bits 5 - min rports 3 + min rports 2 min wports 1 make_outreg or_next_if_better @@ -161,7 +137,7 @@ endmatch match $__XILINX_RAM64X1Q min bits 5 - min rports 3 + min rports 2 min wports 1 make_outreg endmatch diff --git a/techlibs/xilinx/lut_map.v b/techlibs/xilinx/lut_map.v index 62d501632..ec2e3b234 100644 --- a/techlibs/xilinx/lut_map.v +++ b/techlibs/xilinx/lut_map.v @@ -29,90 +29,67 @@ 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 && WIDTH <= `LUT_WIDTH) begin + 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 == 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])); + if (WIDTH == 6 && WIDTH <= `LUT_WIDTH) begin + 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 == 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])); + if (WIDTH == 5 && WIDTH > `LUT_WIDTH) begin + wire f0, f1; + \$lut #(.LUT(LUT[15: 0]), .WIDTH(4)) lut0 (.A(A[3:0]), .Y(f0)); + \$lut #(.LUT(LUT[31:16]), .WIDTH(4)) lut1 (.A(A[3:0]), .Y(f1)); + MUXF5 mux5(.I0(f0), .I1(f1), .S(A[4]), .O(Y)); + end else + if (WIDTH == 6 && WIDTH > `LUT_WIDTH) begin + wire f0, f1; + \$lut #(.LUT(LUT[31: 0]), .WIDTH(5)) lut0 (.A(A[4:0]), .Y(f0)); + \$lut #(.LUT(LUT[63:32]), .WIDTH(5)) lut1 (.A(A[4:0]), .Y(f1)); + MUXF6 mux6(.I0(f0), .I1(f1), .S(A[5]), .O(Y)); 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])); + wire f0, f1; + \$lut #(.LUT(LUT[ 63: 0]), .WIDTH(6)) lut0 (.A(A[5:0]), .Y(f0)); + \$lut #(.LUT(LUT[127:64]), .WIDTH(6)) lut1 (.A(A[5:0]), .Y(f1)); + MUXF7 mux7(.I0(f0), .I1(f1), .S(A[6]), .O(Y)); 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])); + wire f0, f1; + \$lut #(.LUT(LUT[127: 0]), .WIDTH(7)) lut0 (.A(A[6:0]), .Y(f0)); + \$lut #(.LUT(LUT[255:128]), .WIDTH(7)) lut1 (.A(A[6:0]), .Y(f1)); + MUXF8 mux8(.I0(f0), .I1(f1), .S(A[7]), .O(Y)); + end else + if (WIDTH == 9) begin + wire f0, f1; + \$lut #(.LUT(LUT[255: 0]), .WIDTH(8)) lut0 (.A(A[7:0]), .Y(f0)); + \$lut #(.LUT(LUT[511:256]), .WIDTH(8)) lut1 (.A(A[7:0]), .Y(f1)); + MUXF9 mux9(.I0(f0), .I1(f1), .S(A[8]), .O(Y)); end else begin wire _TECHMAP_FAIL_ = 1; end diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 3dc05cd10..a6b422b83 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -49,10 +49,25 @@ struct SynthXilinxPass : public ScriptPass log(" -top <module>\n"); log(" use the specified module as top module\n"); log("\n"); - log(" -family {xcup|xcu|xc7|xc6v|xc5v|xc6s}\n"); + log(" -family <family>\n"); log(" run synthesis for the specified Xilinx architecture\n"); log(" generate the synthesis netlist for the specified family.\n"); - log(" default: xc7\n"); + log(" supported values:\n"); + log(" - xcup: Ultrascale Plus\n"); + log(" - xcu: Ultrascale\n"); + log(" - xc7: Series 7 (default)\n"); + log(" - xc6s: Spartan 6\n"); + log(" - xc6v: Virtex 6\n"); + log(" - xc5v: Virtex 5 (EXPERIMENTAL)\n"); + log(" - xc4v: Virtex 4 (EXPERIMENTAL)\n"); + log(" - xc3sda: Spartan 3A DSP (EXPERIMENTAL)\n"); + log(" - xc3sa: Spartan 3A (EXPERIMENTAL)\n"); + log(" - xc3se: Spartan 3E (EXPERIMENTAL)\n"); + log(" - xc3s: Spartan 3 (EXPERIMENTAL)\n"); + log(" - xc2vp: Virtex 2 Pro (EXPERIMENTAL)\n"); + log(" - xc2v: Virtex 2 (EXPERIMENTAL)\n"); + log(" - xcve: Virtex E, Spartan 2E (EXPERIMENTAL)\n"); + log(" - xcv: Virtex, Spartan 2 (EXPERIMENTAL)\n"); log("\n"); log(" -edif <file>\n"); log(" write the design to the specified edif file. writing of an output file\n"); @@ -82,10 +97,10 @@ struct SynthXilinxPass : public ScriptPass log(" do not use XORCY/MUXCY/CARRY4 cells in output netlist\n"); log("\n"); log(" -nowidelut\n"); - log(" do not use MUXF[78] resources to implement LUTs larger than LUT6s\n"); + log(" do not use MUXF[5-9] resources to implement LUTs larger than native for the target\n"); log("\n"); log(" -nodsp\n"); - log(" do not use DSP48E1s to implement multipliers and associated logic\n"); + log(" do not use DSP48*s to implement multipliers and associated logic\n"); log("\n"); log(" -noiopad\n"); log(" disable I/O buffer insertion (useful for hierarchical or \n"); @@ -131,6 +146,8 @@ struct SynthXilinxPass : public ScriptPass bool abc9, dff_mode; bool flatten_before_abc; int widemux; + int lut_size; + int widelut_size; void clear_flags() YS_OVERRIDE { @@ -156,6 +173,7 @@ struct SynthXilinxPass : public ScriptPass dff_mode = false; flatten_before_abc = false; widemux = 0; + lut_size = 6; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -270,9 +288,38 @@ struct SynthXilinxPass : public ScriptPass } extra_args(args, argidx, design); - if (family != "xcup" && family != "xcu" && family != "xc7" && family != "xc6v" && family != "xc5v" && family != "xc6s") + if (family == "xcup" || family == "xcu") { + lut_size = 6; + widelut_size = 9; + } else if (family == "xc7" || + family == "xc6v" || + family == "xc5v" || + family == "xc6s") { + lut_size = 6; + widelut_size = 8; + } else if (family == "xc4v" || + family == "xc3sda" || + family == "xc3sa" || + family == "xc3se" || + family == "xc3s" || + family == "xc2vp" || + family == "xc2v") { + lut_size = 4; + widelut_size = 8; + } else if (family == "xcve" || family == "xcv") { + lut_size = 4; + widelut_size = 6; + } else log_cmd_error("Invalid Xilinx -family setting: '%s'.\n", family.c_str()); + if (widemux != 0 && lut_size != 6) + log_cmd_error("-widemux is not currently supported for LUT4-based architectures.\n"); + + if (lut_size != 6) { + log_warning("Shift register inference not yet supported for family %s.\n", family.c_str()); + nosrl = true; + } + if (widemux != 0 && widemux < 2) log_cmd_error("-widemux value must be 0 or >= 2.\n"); @@ -292,6 +339,9 @@ struct SynthXilinxPass : public ScriptPass void script() YS_OVERRIDE { + std::string lut_size_s = std::to_string(lut_size); + if (help_mode) + lut_size_s = "[46]"; std::string ff_map_file; if (help_mode) ff_map_file = "+/xilinx/{family}_ff_map.v"; @@ -316,7 +366,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"); @@ -340,7 +394,7 @@ struct SynthXilinxPass : public ScriptPass run("clean", " (skip if '-nosrl' and '-widemux=0')"); } - run("techmap -map +/cmp2lut.v -D LUT_WIDTH=6"); + run("techmap -map +/cmp2lut.v -D LUT_WIDTH=" + lut_size_s); } if (check_label("map_dsp", "(skip if '-nodsp')")) { @@ -349,7 +403,7 @@ struct SynthXilinxPass : public ScriptPass // NB: Xilinx multipliers are signed only if (help_mode) run("techmap -map +/mul2dsp.v -map +/xilinx/{family}_dsp_map.v {options}"); - else if (family == "xc2v" || family == "xc3s" || family == "xc3se" || family == "xc3sa") + else if (family == "xc2v" || family == "xc2vp" || family == "xc3s" || family == "xc3se" || family == "xc3sa") run("techmap -map +/mul2dsp.v -map +/xilinx/xc3s_mult_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 " "-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers "-D DSP_Y_MINWIDTH=9 " // UG901 suggests small multiplies are those 4x4 and smaller @@ -434,7 +488,19 @@ struct SynthXilinxPass : public ScriptPass run("memory_bram -rules +/xilinx/{family}_brams.txt"); run("techmap -map +/xilinx/{family}_brams_map.v"); } else if (!nobram) { - if (family == "xc6s") { + if (family == "xc2v" || family == "xc2vp" || family == "xc3s" || family == "xc3se") { + run("memory_bram -rules +/xilinx/xc2v_brams.txt"); + run("techmap -map +/xilinx/xc2v_brams_map.v"); + } else if (family == "xc3sa") { + // Superset of Virtex 2 primitives — uses common map file. + run("memory_bram -rules +/xilinx/xc3sa_brams.txt"); + run("techmap -map +/xilinx/xc2v_brams_map.v"); + } else if (family == "xc3sda") { + // Supported block RAMs for Spartan 3A DSP are + // a subset of Spartan 6's ones. + run("memory_bram -rules +/xilinx/xc3sda_brams.txt"); + run("techmap -map +/xilinx/xc6s_brams_map.v"); + } else if (family == "xc6s") { run("memory_bram -rules +/xilinx/xc6s_brams.txt"); run("techmap -map +/xilinx/xc6s_brams_map.v"); } else if (family == "xc6v" || family == "xc7") { @@ -451,7 +517,7 @@ struct SynthXilinxPass : public ScriptPass if (check_label("map_lutram", "(skip if '-nolutram')")) { if (!nolutram || help_mode) { - run("memory_bram -rules +/xilinx/lutrams.txt"); + run("memory_bram -rules +/xilinx/lut" + lut_size_s + "_lutrams.txt"); run("techmap -map +/xilinx/lutrams_map.v"); } } @@ -477,9 +543,8 @@ struct SynthXilinxPass : public ScriptPass if (check_label("fine")) { run("dff2dffe -direct-match $_DFF_* -direct-match $__DFFS_*"); - if (help_mode) { - run("muxcover <internal options>, ('-widemux' only)"); - } + if (help_mode) + run("muxcover <internal options> ('-widemux' only)"); else if (widemux > 0) { constexpr int cost_mux2 = 100; std::string muxcover_args = stringf(" -nodecode -mux2=%d", cost_mux2); @@ -507,19 +572,15 @@ struct SynthXilinxPass : public ScriptPass if (!nosrl || help_mode) run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')"); - std::string techmap_args = " -map +/techmap.v"; + std::string techmap_args = " -map +/techmap.v -D LUT_SIZE=" + lut_size_s; if (help_mode) techmap_args += " [-map +/xilinx/mux_map.v]"; else if (widemux > 0) techmap_args += stringf(" -D MIN_MUX_INPUTS=%d -map +/xilinx/mux_map.v", widemux); - if (help_mode) - techmap_args += " [-map +/xilinx/arith_map.v]"; - else if (!nocarry) { + if (!nocarry) { techmap_args += " -map +/xilinx/arith_map.v"; if (vpr) techmap_args += " -D _EXPLICIT_CARRY"; - else - techmap_args += " -D _CLB_CARRY"; } run("techmap " + techmap_args); run("opt -fast"); @@ -528,7 +589,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); @@ -549,6 +610,8 @@ struct SynthXilinxPass : public ScriptPass if (help_mode) run("abc -luts 2:2,3,6:5[,10,20] [-dff] [-D 1]", "(option for 'nowidelut', '-dff', '-retime')"); else if (abc9) { + if (lut_size != 6) + log_error("'synth_xilinx -abc9' not currently supported for LUT4-based devices.\n"); if (family != "xc7") log_warning("'synth_xilinx -abc9' not currently supported for the '%s' family, " "will use timing for 'xc7' instead.\n", family.c_str()); @@ -574,10 +637,19 @@ struct SynthXilinxPass : public ScriptPass } else { std::string abc_opts; - if (nowidelut) - abc_opts += " -luts 2:2,3,6:5"; - else - abc_opts += " -luts 2:2,3,6:5,10,20"; + if (lut_size != 6) { + if (nowidelut) + abc_opts += " -lut " + lut_size_s; + else + abc_opts += " -lut " + lut_size_s + ":" + std::to_string(widelut_size); + } else { + if (nowidelut) + abc_opts += " -luts 2:2,3,6:5"; + else if (widelut_size == 8) + abc_opts += " -luts 2:2,3,6:5,10,20"; + else + abc_opts += " -luts 2:2,3,6:5,10,20,40"; + } if (dff_mode) abc_opts += " -dff"; if (retime) @@ -591,17 +663,22 @@ 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("xilinx_dffopt"); + techmap_args += " -D LUT_WIDTH=" + lut_size_s; + run("techmap " + techmap_args); + if (help_mode) + run("xilinx_dffopt [-lut4]"); + else if (lut_size == 4) + run("xilinx_dffopt -lut4"); + else + run("xilinx_dffopt"); + run("opt_lut_ins -tech xilinx"); } 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/techlibs/xilinx/xc2v_brams.txt b/techlibs/xilinx/xc2v_brams.txt new file mode 100644 index 000000000..ac8cfb552 --- /dev/null +++ b/techlibs/xilinx/xc2v_brams.txt @@ -0,0 +1,31 @@ +# Virtex 2, Virtex 2 Pro, Spartan 3, Spartan 3E block RAM rules. + +bram $__XILINX_RAMB16 + init 1 + abits 9 @a9d36 + dbits 36 @a9d36 + abits 10 @a10d18 + dbits 18 @a10d18 + abits 11 @a11d9 + dbits 9 @a11d9 + abits 12 @a12d4 + dbits 4 @a12d4 + abits 13 @a13d2 + dbits 2 @a13d2 + abits 14 @a14d1 + dbits 1 @a14d1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +match $__XILINX_RAMB16 + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp +endmatch diff --git a/techlibs/xilinx/xc2v_brams_map.v b/techlibs/xilinx/xc2v_brams_map.v new file mode 100644 index 000000000..dc698f956 --- /dev/null +++ b/techlibs/xilinx/xc2v_brams_map.v @@ -0,0 +1,266 @@ +// Virtex 2, Virtex 2 Pro, Spartan 3, Spartan 3E, Spartan 3A block RAM +// mapping (Spartan 3A is a superset of the other four). + +// ------------------------------------------------------------------------ + +module \$__XILINX_RAMB16 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 9; + parameter CFG_DBITS = 36; + parameter CFG_ENABLE_B = 1; + + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [18431:0] INIT = 18432'bx; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + output [CFG_DBITS-1:0] A1DATA; + input A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + input [CFG_DBITS-1:0] B1DATA; + input [CFG_ENABLE_B-1:0] B1EN; + + generate if (CFG_DBITS == 1) begin + wire DOB; + RAMB16_S1_S1 #( + `include "brams_init_16.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(1'd0), + .DOA(A1DATA), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(1'b0), + + .DIB(B1DATA), + .DOB(DOB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else if (CFG_DBITS == 2) begin + wire [1:0] DOB; + RAMB16_S2_S2 #( + `include "brams_init_16.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(2'd0), + .DOA(A1DATA), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(1'b0), + + .DIB(B1DATA), + .DOB(DOB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else if (CFG_DBITS == 4) begin + wire [3:0] DOB; + RAMB16_S4_S4 #( + `include "brams_init_16.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(4'd0), + .DOA(A1DATA), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(1'b0), + + .DIB(B1DATA), + .DOB(DOB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else if (CFG_DBITS == 9) begin + wire [7:0] DOB; + wire DOPB; + RAMB16_S9_S9 #( + `include "brams_init_18.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(8'd0), + .DIPA(1'd0), + .DOA(A1DATA[7:0]), + .DOPA(A1DATA[8]), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(1'b0), + + .DIB(B1DATA[7:0]), + .DIPB(B1DATA[8]), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else if (CFG_DBITS == 18) begin + wire [15:0] DOB; + wire [1:0] DOPB; + RAMB16_S18_S18 #( + `include "brams_init_18.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(16'd0), + .DIPA(2'd0), + .DOA({A1DATA[16:9], A1DATA[7:0]}), + .DOPA({A1DATA[17], A1DATA[8]}), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(1'b0), + + .DIB({B1DATA[16:9], B1DATA[7:0]}), + .DIPB({B1DATA[17], B1DATA[8]}), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else if (CFG_DBITS == 36) begin + wire [31:0] DOB; + wire [3:0] DOPB; + RAMB16_S36_S36 #( + `include "brams_init_18.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(32'd0), + .DIPA(4'd0), + .DOA({A1DATA[34:27], A1DATA[25:18], A1DATA[16:9], A1DATA[7:0]}), + .DOPA({A1DATA[35], A1DATA[26], A1DATA[17], A1DATA[8]}), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(1'b0), + + .DIB({B1DATA[34:27], B1DATA[25:18], B1DATA[16:9], B1DATA[7:0]}), + .DIPB({B1DATA[35], B1DATA[26], B1DATA[17], B1DATA[8]}), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else begin + $error("Strange block RAM data width."); + end endgenerate +endmodule + + +// Version with separate byte enables, only available on Spartan 3A. + +module \$__XILINX_RAMB16BWE (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); + parameter CFG_ABITS = 9; + parameter CFG_DBITS = 36; + parameter CFG_ENABLE_B = 4; + + parameter CLKPOL2 = 1; + parameter CLKPOL3 = 1; + parameter [18431:0] INIT = 18432'bx; + + input CLK2; + input CLK3; + + input [CFG_ABITS-1:0] A1ADDR; + output [CFG_DBITS-1:0] A1DATA; + input A1EN; + + input [CFG_ABITS-1:0] B1ADDR; + input [CFG_DBITS-1:0] B1DATA; + input [CFG_ENABLE_B-1:0] B1EN; + + generate if (CFG_DBITS == 18) begin + wire [15:0] DOB; + wire [1:0] DOPB; + RAMB16BWE_S18_S18 #( + `include "brams_init_18.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(16'd0), + .DIPA(2'd0), + .DOA({A1DATA[16:9], A1DATA[7:0]}), + .DOPA({A1DATA[17], A1DATA[8]}), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(2'b00), + + .DIB({B1DATA[16:9], B1DATA[7:0]}), + .DIPB({B1DATA[17], B1DATA[8]}), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else if (CFG_DBITS == 36) begin + wire [31:0] DOB; + wire [3:0] DOPB; + RAMB16BWE_S36_S36 #( + `include "brams_init_18.vh" + .WRITE_MODE_A("READ_FIRST"), + .WRITE_MODE_B("READ_FIRST"), + ) _TECHMAP_REPLACE_ ( + .DIA(32'd0), + .DIPA(4'd0), + .DOA({A1DATA[34:27], A1DATA[25:18], A1DATA[16:9], A1DATA[7:0]}), + .DOPA({A1DATA[35], A1DATA[26], A1DATA[17], A1DATA[8]}), + .ADDRA(A1ADDR), + .CLKA(CLK2 ^ !CLKPOL2), + .ENA(A1EN), + .SSRA(|0), + .WEA(4'b0000), + + .DIB({B1DATA[34:27], B1DATA[25:18], B1DATA[16:9], B1DATA[7:0]}), + .DIPB({B1DATA[35], B1DATA[26], B1DATA[17], B1DATA[8]}), + .DOB(DOB), + .DOPB(DOPB), + .ADDRB(B1ADDR), + .CLKB(CLK3 ^ !CLKPOL3), + .ENB(|1), + .SSRB(|0), + .WEB(B1EN) + ); + end else begin + $error("Strange block RAM data width."); + end endgenerate +endmodule diff --git a/techlibs/xilinx/xc3sa_brams.txt b/techlibs/xilinx/xc3sa_brams.txt new file mode 100644 index 000000000..22a62bd2c --- /dev/null +++ b/techlibs/xilinx/xc3sa_brams.txt @@ -0,0 +1,51 @@ +# Spartan 3A block RAM rules. + +bram $__XILINX_RAMB16 + init 1 + abits 11 @a11d9 + dbits 9 @a11d9 + abits 12 @a12d4 + dbits 4 @a12d4 + abits 13 @a13d2 + dbits 2 @a13d2 + abits 14 @a14d1 + dbits 1 @a14d1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +bram $__XILINX_RAMB16BWE + init 1 + abits 9 @a9d36 + dbits 36 @a9d36 + abits 10 @a10d18 + dbits 18 @a10d18 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 4 @a9d36 + enable 1 2 @a10d18 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +match $__XILINX_RAMB16 + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp + or_next_if_better +endmatch + +match $__XILINX_RAMB16BWE + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp +endmatch diff --git a/techlibs/xilinx/xc3sda_brams.txt b/techlibs/xilinx/xc3sda_brams.txt new file mode 100644 index 000000000..12c68ffd5 --- /dev/null +++ b/techlibs/xilinx/xc3sda_brams.txt @@ -0,0 +1,33 @@ +# Spartan 3A DSP block RAM rules. + +bram $__XILINX_RAMB16BWER_TDP + init 1 + abits 9 @a9d36 + dbits 36 @a9d36 + abits 10 @a10d18 + dbits 18 @a10d18 + abits 11 @a11d9 + dbits 9 @a11d9 + abits 12 @a12d4 + dbits 4 @a12d4 + abits 13 @a13d2 + dbits 2 @a13d2 + abits 14 @a14d1 + dbits 1 @a14d1 + groups 2 + ports 1 1 + wrmode 0 1 + enable 1 4 @a9d36 + enable 1 2 @a10d18 + enable 1 1 @a11d9 @a12d4 @a13d2 @a14d1 + transp 0 0 + clocks 2 3 + clkpol 2 3 +endbram + +match $__XILINX_RAMB16BWER_TDP + min bits 4096 + min efficiency 5 + shuffle_enable B + make_transp +endmatch diff --git a/techlibs/xilinx/xc6s_brams.txt b/techlibs/xilinx/xc6s_brams.txt index 17cd8e355..6457097db 100644 --- a/techlibs/xilinx/xc6s_brams.txt +++ b/techlibs/xilinx/xc6s_brams.txt @@ -1,3 +1,4 @@ +# Spartan 6 block RAM rules. bram $__XILINX_RAMB8BWER_SDP init 1 diff --git a/techlibs/xilinx/xc6s_brams_map.v b/techlibs/xilinx/xc6s_brams_map.v index 16fd15e74..9577eebe4 100644 --- a/techlibs/xilinx/xc6s_brams_map.v +++ b/techlibs/xilinx/xc6s_brams_map.v @@ -1,3 +1,6 @@ +// Spartan 3A DSP and Spartan 6 block RAM mapping (Spartan 6 is a superset of +// Spartan 3A DSP). + module \$__XILINX_RAMB8BWER_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; diff --git a/techlibs/xilinx/xc7_brams_map.v b/techlibs/xilinx/xc7_brams_map.v index 7ea49158d..2b6ad0da6 100644 --- a/techlibs/xilinx/xc7_brams_map.v +++ b/techlibs/xilinx/xc7_brams_map.v @@ -1,3 +1,5 @@ +// Virtex 6 and Series 7 block RAM mapping. + module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; diff --git a/techlibs/xilinx/xc7_xcu_brams.txt b/techlibs/xilinx/xc7_xcu_brams.txt index c63218ae1..650367abf 100644 --- a/techlibs/xilinx/xc7_xcu_brams.txt +++ b/techlibs/xilinx/xc7_xcu_brams.txt @@ -1,3 +1,5 @@ +# Virtex 6, Series 7, Ultrascale, Ultrascale Plus block RAM rules. + bram $__XILINX_RAMB36_SDP init 1 abits 9 diff --git a/techlibs/xilinx/xcu_brams_map.v b/techlibs/xilinx/xcu_brams_map.v index 6e7925b57..b6719b2dd 100644 --- a/techlibs/xilinx/xcu_brams_map.v +++ b/techlibs/xilinx/xcu_brams_map.v @@ -1,3 +1,5 @@ +// Ultrascale and Ultrascale Plus block RAM mapping. + module \$__XILINX_RAMB36_SDP (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B1EN); parameter CLKPOL2 = 1; parameter CLKPOL3 = 1; diff --git a/tests/aiger/.gitignore b/tests/aiger/.gitignore index 9a26bb8f4..b76bdb653 100644 --- a/tests/aiger/.gitignore +++ b/tests/aiger/.gitignore @@ -1 +1,3 @@ /*_ref.v +/*.aag.log +/*.aig.log diff --git a/tests/arch/ecp5/mux.ys b/tests/arch/ecp5/mux.ys index 22866832d..92463aa32 100644 --- a/tests/arch/ecp5/mux.ys +++ b/tests/arch/ecp5/mux.ys @@ -39,8 +39,8 @@ proc equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # 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:L6MUX21 -select -assert-count 34 t:LUT4 -select -assert-count 17 t:PFUMX +select -assert-count 8 t:L6MUX21 +select -assert-count 26 t:LUT4 +select -assert-count 12 t:PFUMX select -assert-none t:LUT4 t:L6MUX21 t:PFUMX %% t:* %D diff --git a/tests/arch/ecp5/opt_lut_ins.ys b/tests/arch/ecp5/opt_lut_ins.ys new file mode 100644 index 000000000..2bc546912 --- /dev/null +++ b/tests/arch/ecp5/opt_lut_ins.ys @@ -0,0 +1,32 @@ +read_ilang << EOF + +module \top + + wire input 1 \A + wire input 2 \B + wire input 3 \C + wire input 4 \D + + wire output 5 \Z + + cell \LUT4 $0 + parameter \INIT 16'1111110011000000 + connect \A \A + connect \B \B + connect \C \C + connect \D \D + connect \Z \Z + end +end + +EOF + +read_verilog -lib +/ecp5/cells_sim.v + +equiv_opt -assert -map +/ecp5/cells_sim.v opt_lut_ins -tech ecp5 + +design -load postopt + +select -assert-count 1 top/t:LUT4 +select -assert-count 0 top/w:A %co top/t:LUT4 %i +select -assert-count 1 top/w:B %co top/t:LUT4 %i diff --git a/tests/arch/efinix/mux.ys b/tests/arch/efinix/mux.ys index b46f641e1..67006b6f2 100644 --- a/tests/arch/efinix/mux.ys +++ b/tests/arch/efinix/mux.ys @@ -16,7 +16,7 @@ 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 mux4 # Constrain all select calls below inside the top module -select -assert-count 2 t:EFX_LUT4 +#select -assert-count 2 t:EFX_LUT4 select -assert-none t:EFX_LUT4 %% t:* %D @@ -26,7 +26,7 @@ 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 mux8 # Constrain all select calls below inside the top module -select -assert-count 5 t:EFX_LUT4 +#select -assert-count 5 t:EFX_LUT4 select -assert-none t:EFX_LUT4 %% t:* %D @@ -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/gowin/mux.ys b/tests/arch/gowin/mux.ys index afad29a89..33b092284 100644 --- a/tests/arch/gowin/mux.ys +++ b/tests/arch/gowin/mux.ys @@ -18,13 +18,13 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux4 # Constrain all select calls below inside the top module -select -assert-count 4 t:LUT4 +select -assert-count 4 t:LUT* select -assert-count 2 t:MUX2_LUT5 select -assert-count 1 t:MUX2_LUT6 select -assert-count 6 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D design -load read hierarchy -top mux8 @@ -35,7 +35,7 @@ cd mux8 # Constrain all select calls below inside the top module select -assert-count 11 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D design -load read hierarchy -top mux16 @@ -46,4 +46,4 @@ cd mux16 # Constrain all select calls below inside the top module select -assert-count 20 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% t:* %D +select -assert-none t:GND t:VCC t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% 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/bug1644.il.gz b/tests/arch/ice40/bug1644.il.gz Binary files differnew file mode 100644 index 000000000..363c510ef --- /dev/null +++ b/tests/arch/ice40/bug1644.il.gz diff --git a/tests/arch/ice40/bug1644.ys b/tests/arch/ice40/bug1644.ys new file mode 100644 index 000000000..5950f0e3c --- /dev/null +++ b/tests/arch/ice40/bug1644.ys @@ -0,0 +1,2 @@ +read_ilang bug1644.il.gz +synth_ice40 -top top -dsp -json adc_dac_pass_through.json -run :map_bram diff --git a/tests/arch/ice40/ice40_dsp.ys b/tests/arch/ice40/ice40_dsp.ys new file mode 100644 index 000000000..250273859 --- /dev/null +++ b/tests/arch/ice40/ice40_dsp.ys @@ -0,0 +1,11 @@ +read_verilog <<EOT +module top(input [15:0] a, b, output [31:0] o1, o2, o5); +SB_MAC16 m1 (.A(a), .B(16'd1234), .O(o1)); +assign o2 = a * 16'd0; +wire [31:0] o3, o4; +SB_MAC16 m2 (.A(a), .B(b), .O(o3)); +assign o4 = a * b; +SB_MAC16 m3 (.A(a), .B(b), .O(o5)); +endmodule +EOT +ice40_dsp 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/add_sub.ys b/tests/arch/xilinx/add_sub.ys index 70cfe81a3..6be9a73a3 100644 --- a/tests/arch/xilinx/add_sub.ys +++ b/tests/arch/xilinx/add_sub.ys @@ -1,11 +1,23 @@ read_verilog ../common/add_sub.v hierarchy -top top proc +design -save orig + equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -noiopad # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module stat -select -assert-count 16 t:LUT2 +select -assert-count 8 t:LUT2 select -assert-count 2 t:CARRY4 select -assert-none t:LUT2 t:CARRY4 %% t:* %D +design -load orig + +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3s -noiopad # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd top # Constrain all select calls below inside the top module +stat +select -assert-count 8 t:LUT2 +select -assert-count 6 t:MUXCY +select -assert-count 8 t:XORCY +select -assert-none t:LUT2 t:MUXCY t:XORCY %% t:* %D diff --git a/tests/various/bug1462.ys b/tests/arch/xilinx/bug1462.ys index 15cab5121..15cab5121 100644 --- a/tests/various/bug1462.ys +++ b/tests/arch/xilinx/bug1462.ys diff --git a/tests/arch/xilinx/dffs.ys b/tests/arch/xilinx/dffs.ys index dc764b033..deaf16bd6 100644 --- a/tests/arch/xilinx/dffs.ys +++ b/tests/arch/xilinx/dffs.ys @@ -8,7 +8,6 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p cd dff # Constrain all select calls below inside the top module select -assert-count 1 t:BUFG select -assert-count 1 t:FDRE - select -assert-none t:BUFG t:FDRE %% t:* %D @@ -20,6 +19,27 @@ design -load postopt # load the post-opt design (otherwise equiv_opt loads the p cd dffe # Constrain all select calls below inside the top module select -assert-count 1 t:BUFG select -assert-count 1 t:FDRE +select -assert-none t:BUFG t:FDRE %% t:* %D + + +design -load read +hierarchy -top dff +proc +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd dff # Constrain all select calls below inside the top module +select -assert-count 1 t:BUFG +select -assert-count 1 t:FDRE +select -assert-none t:BUFG t:FDRE %% t:* %D + +design -load read +hierarchy -top dffe +proc +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd dffe # Constrain all select calls below inside the top module +select -assert-count 1 t:BUFG +select -assert-count 1 t:FDRE select -assert-none t:BUFG t:FDRE %% t:* %D diff --git a/tests/arch/xilinx/fsm.ys b/tests/arch/xilinx/fsm.ys index a464fcfdb..fec4c6082 100644 --- a/tests/arch/xilinx/fsm.ys +++ b/tests/arch/xilinx/fsm.ys @@ -3,6 +3,8 @@ hierarchy -top fsm proc flatten +design -save orig + equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -noiopad miter -equiv -make_assert -flatten gold gate miter sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter @@ -17,3 +19,20 @@ select -assert-count 1 t:LUT2 select -assert-count 3 t:LUT5 select -assert-count 1 t:LUT6 select -assert-none t:BUFG t:FDRE t:FDSE t:LUT2 t:LUT5 t:LUT6 %% t:* %D + +design -load orig + +equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad +miter -equiv -make_assert -flatten gold gate miter +sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter + +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd fsm # Constrain all select calls below inside the top module +stat +select -assert-count 1 t:BUFG +select -assert-count 6 t:FDRE +select -assert-count 1 t:LUT1 +select -assert-count 3 t:LUT3 +select -assert-count 6 t:LUT4 +select -assert-count 6 t:MUXF5 +select -assert-none t:BUFG t:FDRE t:LUT1 t:LUT3 t:LUT4 t:MUXF5 %% t:* %D diff --git a/tests/arch/xilinx/lutram.ys b/tests/arch/xilinx/lutram.ys index 3f127a77e..cc7354501 100644 --- a/tests/arch/xilinx/lutram.ys +++ b/tests/arch/xilinx/lutram.ys @@ -135,3 +135,23 @@ select -assert-count 1 t:BUFG select -assert-count 6 t:FDRE select -assert-count 2 t:RAM64M select -assert-none t:BUFG t:FDRE t:RAM64M %% t:* %D + + +design -reset +read_verilog ../common/lutram.v +hierarchy -top lutram_1w1r -chparam A_WIDTH 4 +proc +memory -nomap +equiv_opt -run :prove -map +/xilinx/cells_sim.v synth_xilinx -family xc3s -noiopad +memory +opt -full + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -seq 3 -set-init-zero -show-inputs -show-outputs miter + +design -load postopt +cd lutram_1w1r +select -assert-count 1 t:BUFG +select -assert-count 8 t:FDRE +select -assert-count 8 t:RAM16X1D +select -assert-none t:BUFG t:FDRE t:RAM16X1D %% t:* %D diff --git a/tests/arch/xilinx/mux_lut4.ys b/tests/arch/xilinx/mux_lut4.ys new file mode 100644 index 000000000..3e3256993 --- /dev/null +++ b/tests/arch/xilinx/mux_lut4.ys @@ -0,0 +1,51 @@ +read_verilog ../common/mux.v +design -save read + +hierarchy -top mux2 +proc +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux2 # Constrain all select calls below inside the top module +select -assert-count 1 t:LUT3 + +select -assert-none t:LUT3 %% t:* %D + + +design -load read +hierarchy -top mux4 +proc +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux4 # Constrain all select calls below inside the top module +select -assert-count 4 t:LUT1 +select -assert-count 2 t:MUXF5 +select -assert-count 1 t:MUXF6 + +select -assert-none t:LUT1 t:MUXF5 t:MUXF6 %% t:* %D + + +design -load read +hierarchy -top mux8 +proc +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # equivalency check +design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) +cd mux8 # Constrain all select calls below inside the top module +select -assert-count 4 t:LUT1 +select -assert-count 3 t:LUT4 +select -assert-count 2 t:MUXF5 +select -assert-count 1 t:MUXF6 + +select -assert-none t:LUT1 t:LUT4 t:MUXF5 t:MUXF6 %% t:* %D + + +design -load read +hierarchy -top mux16 +proc +equiv_opt -assert -map +/xilinx/cells_sim.v synth_xilinx -family xc3se -noiopad # 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-max 32 t:LUT* +select -assert-max 8 t:MUXF6 +select -assert-max 4 t:MUXF7 + +select -assert-none t:LUT* t:MUXF5 t:MUXF6 t:MUXF7 %% t:* %D diff --git a/tests/arch/xilinx/opt_lut_ins.ys b/tests/arch/xilinx/opt_lut_ins.ys new file mode 100644 index 000000000..a01d02179 --- /dev/null +++ b/tests/arch/xilinx/opt_lut_ins.ys @@ -0,0 +1,25 @@ +read_ilang << EOF + +module \top + + wire width 4 input 1 \A + + wire output 2 \O + + cell \LUT4 $0 + parameter \INIT 16'1111110011000000 + connect \I0 \A [0] + connect \I1 \A [1] + connect \I2 \A [2] + connect \I3 \A [3] + connect \O \O + end +end + +EOF + +equiv_opt -assert -map +/xilinx/cells_sim.v opt_lut_ins -tech xilinx + +design -load postopt + +select -assert-count 1 t:LUT3 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/arch/xilinx/xilinx_dsp.ys b/tests/arch/xilinx/xilinx_dsp.ys new file mode 100644 index 000000000..3b9f52930 --- /dev/null +++ b/tests/arch/xilinx/xilinx_dsp.ys @@ -0,0 +1,11 @@ +read_verilog <<EOT +module top(input [24:0] a, input [17:0] b, output [42:0] o1, o2, o5); +DSP48E1 m1 (.A(a), .B(16'd1234), .P(o1)); +assign o2 = a * 16'd0; +wire [42:0] o3, o4; +DSP48E1 m2 (.A(a), .B(b), .P(o3)); +assign o4 = a * b; +DSP48E1 m3 (.A(a), .B(b), .P(o5)); +endmodule +EOT +xilinx_dsp diff --git a/tests/memfile/.gitignore b/tests/memfile/.gitignore new file mode 100644 index 000000000..61b0d4264 --- /dev/null +++ b/tests/memfile/.gitignore @@ -0,0 +1 @@ +temp* diff --git a/tests/memfile/content1.dat b/tests/memfile/content1.dat new file mode 100644 index 000000000..4d1c67c26 --- /dev/null +++ b/tests/memfile/content1.dat @@ -0,0 +1,64 @@ +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 +00001111000000001111111100000000 diff --git a/tests/memfile/memory.v b/tests/memfile/memory.v new file mode 100644 index 000000000..57106eae8 --- /dev/null +++ b/tests/memfile/memory.v @@ -0,0 +1,23 @@ +// A memory initialized with an external file + +module memory ( + input clk_i, + input we_i, + input [5:0] addr_i, + input [31:0] data_i, + output reg [31:0] data_o +); + +parameter MEMFILE = ""; + +reg [31:0] mem [0:63]; + +initial $readmemb(MEMFILE,mem); + +always @(posedge clk_i) begin + if (we_i) + mem[addr_i] <= data_i; + data_o <= mem[addr_i]; +end + +endmodule diff --git a/tests/memfile/run-test.sh b/tests/memfile/run-test.sh new file mode 100755 index 000000000..e43ddd093 --- /dev/null +++ b/tests/memfile/run-test.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e + +mkdir -p temp +cp content1.dat temp/content2.dat + +cd .. + +echo "Running from the parent directory with content1.dat" +../yosys -qp "read_verilog -defer memfile/memory.v; chparam -set MEMFILE \"content1.dat\" memory" +echo "Running from the parent directory with temp/content2.dat" +../yosys -qp "read_verilog -defer memfile/memory.v; chparam -set MEMFILE \"temp/content2.dat\" memory" +echo "Running from the parent directory with memfile/temp/content2.dat" +../yosys -qp "read_verilog -defer memfile/memory.v; chparam -set MEMFILE \"memfile/temp/content2.dat\" memory" + +cd memfile + +echo "Running from the same directory with content1.dat" +../../yosys -qp "read_verilog -defer memory.v; chparam -set MEMFILE \"content1.dat\" memory" +echo "Running from the same directory with temp/content2.dat" +../../yosys -qp "read_verilog -defer memory.v; chparam -set MEMFILE \"temp/content2.dat\" memory" + +cd temp + +echo "Running from a child directory with content1.dat" +../../../yosys -qp "read_verilog -defer ../memory.v; chparam -set MEMFILE \"content1.dat\" memory" +echo "Running from a child directory with temp/content2.dat" +../../../yosys -qp "read_verilog -defer ../memory.v; chparam -set MEMFILE \"temp/content2.dat\" memory" +echo "Running from a child directory with content2.dat" +../../../yosys -qp "read_verilog -defer ../memory.v; chparam -set MEMFILE \"temp/content2.dat\" memory" + +cd .. + +echo "Checking a failure when zero length filename is provided" +if ../../yosys -qp "read_verilog memory.v"; then + echo "The execution should fail but it didn't happen, which is WRONG." + exit 1 +else + echo "Execution failed, which is OK." +fi + +echo "Checking a failure when not existing filename is provided" +if ../../yosys -qp "read_verilog -defer memory.v; chparam -set MEMFILE \"content3.dat\" memory"; then + echo "The execution should fail but it didn't happen, which is WRONG." + exit 1 +else + echo "Execution failed, which is OK." +fi diff --git a/tests/opt/opt_lut_ins.ys b/tests/opt/opt_lut_ins.ys new file mode 100644 index 000000000..82460b164 --- /dev/null +++ b/tests/opt/opt_lut_ins.ys @@ -0,0 +1,23 @@ +read_ilang << EOF + +module \top + + wire width 4 input 1 \A + + wire output 2 \Y + + cell $lut \lut + parameter \LUT 16'1111110011000000 + parameter \WIDTH 4 + connect \A \A + connect \Y \Y + end +end + +EOF + +equiv_opt -assert opt_lut_ins + +design -load postopt + +select -assert-count 1 t:$lut r:WIDTH=3 %i diff --git a/tests/opt/opt_merge_init.ys b/tests/opt/opt_merge_init.ys new file mode 100644 index 000000000..a29c29df6 --- /dev/null +++ b/tests/opt/opt_merge_init.ys @@ -0,0 +1,49 @@ +read_verilog -icells <<EOT +module top(input clk, i, (* init = 1'b0 *) output o, p); + \$dff #( + .CLK_POLARITY(1'h1), + .WIDTH(32'd1) + ) ffo ( + .CLK(clk), + .D(i), + .Q(o) + ); + \$dff #( + .CLK_POLARITY(1'h1), + .WIDTH(32'd1) + ) ffp ( + .CLK(clk), + .D(i), + .Q(p) + ); +endmodule +EOT + +opt_merge +select -assert-count 1 a:init=1'0 + + +design -reset +read_verilog -icells <<EOT +module top(input clk, i, (* init = 2'b11 *) output [1:0] o); + \$dff #( + .CLK_POLARITY(1'h1), + .WIDTH(32'd1) + ) ff1 ( + .CLK(clk), + .D(i), + .Q(o[1]) + ); + \$dff #( + .CLK_POLARITY(1'h1), + .WIDTH(32'd1) + ) ff0 ( + .CLK(clk), + .D(i), + .Q(o[0]) + ); +endmodule +EOT + +opt_merge +select -assert-count 1 a:init=2'bx1 diff --git a/tests/rpc/frontend.py b/tests/rpc/frontend.py index eff41738a..eace07bf9 100644 --- a/tests/rpc/frontend.py +++ b/tests/rpc/frontend.py @@ -31,7 +31,7 @@ end import json import argparse -import sys, socket, os +import sys, socket, os, subprocess try: import msvcrt, win32pipe, win32file except ImportError: @@ -85,6 +85,7 @@ def main(): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(args.path) try: + ys_proc = subprocess.Popen(["../../yosys", "-ql", "unix.log", "-p", "connect_rpc -path {}; read_verilog design.v; hierarchy -top top; flatten; select -assert-count 1 t:$neg".format(args.path)]) sock.listen(1) conn, addr = sock.accept() file = conn.makefile("rw") @@ -93,7 +94,11 @@ def main(): if not input: break file.write(call(input) + "\n") file.flush() + ys_proc.wait(timeout=10) + if ys_proc.returncode: + raise subprocess.CalledProcessError(ys_proc.returncode, ys_proc.args) finally: + ys_proc.kill() sock.close() os.unlink(args.path) diff --git a/tests/rpc/run-test.sh b/tests/rpc/run-test.sh index 44ce7e674..eeb309347 100755 --- a/tests/rpc/run-test.sh +++ b/tests/rpc/run-test.sh @@ -4,3 +4,4 @@ for x in *.ys; do echo "Running $x.." ../../yosys -ql ${x%.ys}.log $x done +python3 frontend.py unix-socket frontend.sock diff --git a/tests/rpc/unix.ys b/tests/rpc/unix.ys deleted file mode 100644 index cc7ec14ab..000000000 --- a/tests/rpc/unix.ys +++ /dev/null @@ -1,6 +0,0 @@ -!python3 frontend.py unix-socket frontend.sock & sleep 0.1 -connect_rpc -path frontend.sock -read_verilog design.v -hierarchy -top top -flatten -select -assert-count 1 t:$neg diff --git a/tests/sat/clk2fflogic.ys b/tests/sat/clk2fflogic.ys new file mode 100644 index 000000000..6d6d9e490 --- /dev/null +++ b/tests/sat/clk2fflogic.ys @@ -0,0 +1,66 @@ +read_verilog -icells <<EOT +module top(input clk, d, s, r, output reg [17:0] q); +always @(posedge clk or posedge s) if ( s) q[ 0] <= 1'b1; else q[ 0] <= d; +always @(posedge clk or negedge s) if (!s) q[ 1] <= 1'b1; else q[ 1] <= d; +always @(posedge clk or posedge r) if ( r) q[ 2] <= 1'b0; else q[ 2] <= d; +always @(posedge clk or negedge r) if (!r) q[ 3] <= 1'b0; else q[ 3] <= d; +always @(negedge clk or posedge s) if ( s) q[ 4] <= 1'b1; else q[ 4] <= d; +always @(negedge clk or negedge s) if (!s) q[ 5] <= 1'b1; else q[ 5] <= d; +always @(negedge clk or posedge r) if ( r) q[ 6] <= 1'b0; else q[ 6] <= d; +always @(negedge clk or negedge r) if (!r) q[ 7] <= 1'b0; else q[ 7] <= d; + +// Seems like proc_dlatch always sets {SET,CLR}_POLARITY to true +always @(posedge clk or posedge s or posedge r) if ( r) q[ 8] <= 1'b0; else if ( s) q[ 8] <= 1'b1; else q[ 8] <= d; +//always @(posedge clk or posedge s or negedge r) if (!r) q[ 9] <= 1'b0; else if ( s) q[ 9] <= 1'b1; else q[ 9] <= d; +//always @(posedge clk or negedge s or posedge r) if ( r) q[10] <= 1'b0; else if (!s) q[10] <= 1'b1; else q[10] <= d; +//always @(posedge clk or negedge s or negedge r) if (!r) q[11] <= 1'b0; else if (!s) q[11] <= 1'b1; else q[11] <= d; +$dffsr #(.CLK_POLARITY(1'h1), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h1), .WIDTH(32'd1)) ppn (.CLK(clk), .CLR(r), .D(d), .Q(q[ 9]), .SET(s)); +$dffsr #(.CLK_POLARITY(1'h1), .CLR_POLARITY(1'h1), .SET_POLARITY(1'h0), .WIDTH(32'd1)) pnp (.CLK(clk), .CLR(r), .D(d), .Q(q[10]), .SET(s)); +$dffsr #(.CLK_POLARITY(1'h1), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h0), .WIDTH(32'd1)) pnn (.CLK(clk), .CLR(r), .D(d), .Q(q[11]), .SET(s)); + +always @(negedge clk or posedge s or posedge r) if ( r) q[12] <= 1'b0; else if ( s) q[12] <= 1'b1; else q[12] <= d; +//always @(negedge clk or posedge s or negedge r) if (!r) q[13] <= 1'b0; else if ( s) q[13] <= 1'b1; else q[13] <= d; +//always @(negedge clk or negedge s or posedge r) if ( r) q[14] <= 1'b0; else if (!s) q[14] <= 1'b1; else q[14] <= d; +//always @(negedge clk or negedge s or negedge r) if (!r) q[15] <= 1'b0; else if (!s) q[15] <= 1'b1; else q[15] <= d; +$dffsr #(.CLK_POLARITY(1'h0), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h1), .WIDTH(32'd1)) npn (.CLK(clk), .CLR(r), .D(d), .Q(q[13]), .SET(s)); +$dffsr #(.CLK_POLARITY(1'h0), .CLR_POLARITY(1'h1), .SET_POLARITY(1'h0), .WIDTH(32'd1)) nnp (.CLK(clk), .CLR(r), .D(d), .Q(q[14]), .SET(s)); +$dffsr #(.CLK_POLARITY(1'h0), .CLR_POLARITY(1'h0), .SET_POLARITY(1'h0), .WIDTH(32'd1)) nnn (.CLK(clk), .CLR(r), .D(d), .Q(q[15]), .SET(s)); + +always @(posedge clk) q[16] <= d; +always @(negedge clk) q[17] <= d; +endmodule +EOT +proc +select -assert-count 8 t:$adff +select -assert-count 8 t:$dffsr +select -assert-count 2 t:$dff +design -save gold + +simplemap +select -assert-count 1 t:$_DFF_NN0_ +select -assert-count 1 t:$_DFF_NN1_ +select -assert-count 1 t:$_DFF_NP0_ +select -assert-count 1 t:$_DFF_NP1_ +select -assert-count 1 t:$_DFF_PN0_ +select -assert-count 1 t:$_DFF_PN1_ +select -assert-count 1 t:$_DFF_PP0_ +select -assert-count 1 t:$_DFF_PP1_ +stat +select -assert-count 1 t:$_DFFSR_NNN_ +select -assert-count 1 t:$_DFFSR_NNP_ +select -assert-count 1 t:$_DFFSR_NPN_ +select -assert-count 1 t:$_DFFSR_NPP_ +select -assert-count 1 t:$_DFFSR_PNN_ +select -assert-count 1 t:$_DFFSR_PNP_ +select -assert-count 1 t:$_DFFSR_PPN_ +select -assert-count 1 t:$_DFFSR_PPP_ +select -assert-count 1 t:$_DFF_N_ +select -assert-count 1 t:$_DFF_P_ +design -stash gate + +design -import gold -as gold +design -import gate -as gate +clk2fflogic + +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports -set-init-undef -seq 10 miter 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/techmap/iopadmap.ys b/tests/techmap/iopadmap.ys index c058d1607..0bcc71cce 100644 --- a/tests/techmap/iopadmap.ys +++ b/tests/techmap/iopadmap.ys @@ -120,3 +120,40 @@ select -assert-count 1 g/t:iobuf select -assert-count 1 h/t:ibuf select -assert-count 1 h/t:iobuf select -assert-count 1 h/t:obuf + + +# Check that \init attributes get moved from output buffer +# to buffer input +design -reset +read_verilog << EOT +module obuf (input i, (* iopad_external_pin *) output o); endmodule +module obuft (input i, input oe, (* iopad_external_pin *) output o); endmodule +module iobuf (input i, input oe, output o, (* iopad_external_pin *) inout io); endmodule +module sub(input i, output o); endmodule + +module a(input i, (* init=1'b1 *) output o); +sub s(.i(i), .o(o)); +endmodule + +module b(input [1:0] i, oe, (* init=2'b1x *) output [1:0] o); +wire [1:0] w; +sub s1(.i(i[0]), .o(w[0])); +sub s2(.i(i[1]), .o(w[1])); +assign o = oe ? w : 2'bz; +endmodule + +module c(input i, oe, (* init=2'b00 *) inout io, output o1, o2); +assign io = oe ? i : 1'bz; +assign {o1,o2} = {io,io}; +endmodule +EOT +opt_clean +tribuf +simplemap +iopadmap -bits -outpad obuf i:o -toutpad obuft oe:i:o -tinoutpad iobuf oe:o:i:io +select -assert-count 1 a/c:s %co a/a:init=1'b1 %i +select -assert-count 1 a/a:init +select -assert-count 1 b/c:s* %co %a b/a:init=2'b1x %i +select -assert-count 1 b/a:init +select -assert-count 1 c/t:iobuf %co c/a:init=2'b00 %i +select -assert-count 1 c/a:init diff --git a/tests/techmap/run-test.sh b/tests/techmap/run-test.sh index 96489ff15..c16f204d9 100755 --- a/tests/techmap/run-test.sh +++ b/tests/techmap/run-test.sh @@ -6,7 +6,7 @@ for x in *.ys; do echo "all:: run-$x" echo "run-$x:" echo " @echo 'Running $x..'" - echo " @../../yosys -ql ${x%.ys}.log $x" + echo " @../../yosys -ql ${x%.ys}.log -e 'select out of bounds' $x" done for s in *.sh; do if [ "$s" != "run-test.sh" ]; then diff --git a/tests/techmap/shiftx2mux.ys b/tests/techmap/shiftx2mux.ys new file mode 100644 index 000000000..eb29680f6 --- /dev/null +++ b/tests/techmap/shiftx2mux.ys @@ -0,0 +1,121 @@ +read_verilog <<EOT +module sc1 (i1 , + i2 , + i3 , + i4 , + i5 , + i6 , + i7 , + i8 , + i9 , + i10, + i11, + i12, + i13, + i14, + i15, + binary_out, + encoder_in, + enable +); + +input [3:0] i1 ; +input [3:0] i2 ; +input [3:0] i3 ; +input [3:0] i4 ; +input [3:0] i5 ; +input [3:0] i6 ; +input [3:0] i7 ; +input [3:0] i8 ; +input [3:0] i9 ; +input [3:0] i10 ; +input [3:0] i11 ; +input [3:0] i12 ; +input [3:0] i13 ; +input [3:0] i14 ; +input [3:0] i15 ; + +output reg [3:0] binary_out ; + +input [3:0] encoder_in ; +input enable ; + + + +always @ (*) +begin + binary_out = 0; + if (enable) begin + case (encoder_in) + 4'h1 : binary_out = i1; + 4'h2 : binary_out = i2; + 4'h3 : binary_out = i3; + 4'h4 : binary_out = i4; + 4'h5 : binary_out = i5; + 4'h6 : binary_out = i6; + 4'h7 : binary_out = i7; + 4'h8 : binary_out = i8; + 4'h9 : binary_out = i9; + 4'ha : binary_out = i10; + 4'hb : binary_out = i11;/* + 4'hc : binary_out = i12; + 4'hd : binary_out = i13; + 4'he : binary_out = i14; + 4'hf : binary_out = i15;*/ + endcase + end +end +endmodule +EOT + +proc +pmux2shiftx +design -save gold + + +design -load gold +techmap -D NO_LSB_FIRST_SHIFT_SHIFTX +abc -lut 6 +select -assert-min 17 t:$lut + + +design -load gold +techmap +abc -lut 6 +select -assert-count 16 t:$lut + +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + + +design -load gold +techmap -D NO_LSB_FIRST_SHIFT_SHIFTX +abc9 -lut 6 +select -assert-min 17 t:$lut + + +design -load gold +techmap +abc9 -lut 6 +select -assert-count 16 t:$lut + +design -stash gate +design -import gold -as gold +design -import gate -as gate +miter -equiv -flatten -make_assert -make_outputs gold gate miter +sat -verify -prove-asserts -show-ports miter + + +design -reset +read_verilog <<EOT +module top(input [6:0] A, input [1:0] B, output [1:0] Y); +wire [7:0] AA = {1'bx, A}; +assign Y = AA[B*2 +: 2]; +endmodule +EOT +opt +wreduce +equiv_opt techmap 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/bug1614.ys b/tests/various/bug1614.ys new file mode 100644 index 000000000..6fbe84a4c --- /dev/null +++ b/tests/various/bug1614.ys @@ -0,0 +1,5 @@ +read_verilog <<EOT +module testcase; + wire [3:0] #1 a = 4'b0000; +endmodule +EOT 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 diff --git a/tests/various/sformatf.ys b/tests/various/sformatf.ys new file mode 100644 index 000000000..66d6b0dbe --- /dev/null +++ b/tests/various/sformatf.ys @@ -0,0 +1,12 @@ +read_verilog <<EOT + +module top; + localparam a = $sformatf("0x%x", 8'h5A); + localparam b = $sformatf("%d", 4'b011); + generate + if (a != "0x5a") $error("a incorrect!"); + if (b != "3") $error("b incorrect!"); + endgenerate +endmodule + +EOT diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh new file mode 100755 index 000000000..9a01447f7 --- /dev/null +++ b/tests/various/sv_implicit_ports.sh @@ -0,0 +1,124 @@ +#!/bin/bash + +trap 'echo "ERROR in sv_implicit_ports.sh" >&2; exit 1' ERR + +# Simple case +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + wire [7:0] b = 8'd42; + add add_i(.*); +endmodule +EOT + +# Generate block +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); +assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + generate + if (1) begin:ablock + wire [7:0] b = 8'd42; + add add_i(.*); + end + endgenerate +endmodule +EOT + +# Missing wire +((../../yosys -f "verilog -sv" -qp "hierarchy -top top" - || true) <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add add_i(.*); +endmodule +EOT +) 2>&1 | grep -F "ERROR: No matching wire for implicit port connection \`b' of cell top.add_i (add)." > /dev/null + +# Incorrectly sized wire +((../../yosys -f "verilog -sv" -qp "hierarchy -top top" - || true) <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + wire [6:0] b = 6'd42; + add add_i(.*); +endmodule +EOT +) 2>&1 | grep -F "ERROR: Width mismatch between wire (7 bits) and port (8 bits) for implicit port connection \`b' of cell top.add_i (add)." > /dev/null + +# Defaults +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a = 8'd00, input [7:0] b = 8'd01, output [7:0] q); +assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add add_i(.*); +endmodule +EOT + +# Parameterised module +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add #(parameter N=3) (input [N-1:0] a = 8'd00, input [N-1:0] b = 8'd01, output [N-1:0] q); +assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add #(.N(8)) add_i(.*); +endmodule +EOT + +# Parameterised blackbox module +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:add" - <<EOT +(* blackbox *) +module add #(parameter N=3) (input [N-1:0] a, b, output [N-1:0] q); +endmodule + +module top(input [7:0] a, b, output [7:0] q); + add #(.N(8)) add_i(.*); +endmodule +EOT + +# Parameterised blackbox module - incorrect width +((../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:add" - || true) <<EOT +(* blackbox *) +module add #(parameter N=3) (input [N-1:0] a, b, output [N-1:0] q); +endmodule + +module top(input [7:0] a, b, output [7:0] q); + add #(.N(6)) add_i(.*); +endmodule +EOT +) 2>&1 | grep -F "ERROR: Width mismatch between wire (8 bits) and port (6 bits) for implicit port connection \`q' of cell top.add_i (add)." > /dev/null + +# Mixed implicit and explicit 1 +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add add_i(.b(8'd42), .*); +endmodule +EOT + +# Mixed implicit and explicit 2 +(../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, input [9:0] b, output [7:0] q); + add add_i(.b, .*); +endmodule +EOT +) 2>&1 | grep -F "Warning: Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null |