Merge pull request #1181 from YosysHQ/xaig_dff

"abc9 -dff" option for sequential synthesis
This commit is contained in:
Eddie Hung 2020-01-06 16:50:07 -08:00 committed by GitHub
commit ce765aa4de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1531 additions and 1068 deletions

View File

@ -57,6 +57,8 @@ Yosys 0.9 .. Yosys 0.9-dev
always_latch and always_ff) always_latch and always_ff)
- Added "xilinx_dffopt" pass - Added "xilinx_dffopt" pass
- Added "scratchpad" pass - Added "scratchpad" pass
- Added "abc9 -dff"
- Added "synth_xilinx -dff"
Yosys 0.8 .. Yosys 0.9 Yosys 0.8 .. Yosys 0.9
---------------------- ----------------------

View File

@ -376,7 +376,11 @@ Verilog Attributes and non-standard features
- The port attribute ``abc9_arrival`` specifies an integer (for output ports - 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, 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 for example, to specify the clk-to-Q delay of a flip-flop for consideration
during techmapping. 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
sequential synthesis.
- The frontend sets attributes ``always_comb``, ``always_latch`` and - The frontend sets attributes ``always_comb``, ``always_latch`` and
``always_ff`` on processes derived from SystemVerilog style always blocks ``always_ff`` on processes derived from SystemVerilog style always blocks

View File

@ -787,6 +787,14 @@ struct AigerBackend : public Backend {
if (top_module == nullptr) if (top_module == nullptr)
log_error("Can't find top module in current design!\n"); log_error("Can't find top module in current design!\n");
if (!design->selected_whole_module(top_module))
log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
if (!top_module->processes.empty())
log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module));
if (!top_module->memories.empty())
log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module));
AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode); AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);

View File

@ -82,6 +82,7 @@ struct XAigerWriter
dict<SigBit, SigBit> not_map, alias_map; dict<SigBit, SigBit> not_map, alias_map;
dict<SigBit, pair<SigBit, SigBit>> and_map; dict<SigBit, pair<SigBit, SigBit>> and_map;
vector<SigBit> ci_bits, co_bits; vector<SigBit> ci_bits, co_bits;
dict<SigBit, Cell*> ff_bits;
dict<SigBit, float> arrival_times; dict<SigBit, float> arrival_times;
vector<pair<int, int>> aig_gates; vector<pair<int, int>> aig_gates;
@ -92,7 +93,7 @@ struct XAigerWriter
dict<SigBit, int> ordered_outputs; dict<SigBit, int> ordered_outputs;
vector<Cell*> box_list; vector<Cell*> box_list;
bool omode = false; dict<IdString, std::vector<IdString>> box_ports;
int mkgate(int a0, int a1) int mkgate(int a0, int a1)
{ {
@ -140,7 +141,6 @@ struct XAigerWriter
{ {
pool<SigBit> undriven_bits; pool<SigBit> undriven_bits;
pool<SigBit> unused_bits; pool<SigBit> unused_bits;
pool<SigBit> keep_bits;
// promote public wires // promote public wires
for (auto wire : module->wires()) for (auto wire : module->wires())
@ -152,46 +152,38 @@ struct XAigerWriter
if (wire->port_input) if (wire->port_input)
sigmap.add(wire); sigmap.add(wire);
// promote keep wires
for (auto wire : module->wires()) for (auto wire : module->wires())
{ if (wire->get_bool_attribute(ID::keep))
bool keep = wire->attributes.count("\\keep"); sigmap.add(wire);
for (auto wire : module->wires())
for (int i = 0; i < GetSize(wire); i++) for (int i = 0; i < GetSize(wire); i++)
{ {
SigBit wirebit(wire, i); SigBit wirebit(wire, i);
SigBit bit = sigmap(wirebit); SigBit bit = sigmap(wirebit);
if (bit.wire) { if (bit.wire == nullptr) {
undriven_bits.insert(bit); if (wire->port_output) {
unused_bits.insert(bit); aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
}
if (keep)
keep_bits.insert(wirebit);
if (wire->port_input || keep) {
if (bit != wirebit)
alias_map[bit] = wirebit;
input_bits.insert(wirebit);
}
if (wire->port_output || keep) {
if (bit != RTLIL::Sx) {
if (bit != wirebit)
alias_map[wirebit] = bit;
output_bits.insert(wirebit); output_bits.insert(wirebit);
} }
else continue;
log_debug("Skipping PO '%s' driven by 1'bx\n", log_signal(wirebit)); }
undriven_bits.insert(bit);
unused_bits.insert(bit);
if (wire->port_input)
input_bits.insert(bit);
if (wire->port_output) {
if (bit != wirebit)
alias_map[wirebit] = bit;
output_bits.insert(wirebit);
} }
} }
}
for (auto bit : input_bits)
undriven_bits.erase(sigmap(bit));
for (auto bit : output_bits)
if (!bit.wire->port_input)
unused_bits.erase(bit);
// TODO: Speed up toposort -- ultimately we care about // TODO: Speed up toposort -- ultimately we care about
// box ordering, but not individual AIG cells // box ordering, but not individual AIG cells
@ -207,11 +199,9 @@ struct XAigerWriter
unused_bits.erase(A); unused_bits.erase(A);
undriven_bits.erase(Y); undriven_bits.erase(Y);
not_map[Y] = A; not_map[Y] = A;
if (!holes_mode) { toposort.node(cell->name);
toposort.node(cell->name); bit_users[A].insert(cell->name);
bit_users[A].insert(cell->name); bit_drivers[Y].insert(cell->name);
bit_drivers[Y].insert(cell->name);
}
continue; continue;
} }
@ -224,89 +214,92 @@ struct XAigerWriter
unused_bits.erase(B); unused_bits.erase(B);
undriven_bits.erase(Y); undriven_bits.erase(Y);
and_map[Y] = make_pair(A, B); and_map[Y] = make_pair(A, B);
if (!holes_mode) { toposort.node(cell->name);
toposort.node(cell->name); bit_users[A].insert(cell->name);
bit_users[A].insert(cell->name); bit_users[B].insert(cell->name);
bit_users[B].insert(cell->name); bit_drivers[Y].insert(cell->name);
bit_drivers[Y].insert(cell->name);
}
continue; continue;
} }
log_assert(!holes_mode); 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;
}
RTLIL::Module* inst_module = module->design->module(cell->type); RTLIL::Module* inst_module = module->design->module(cell->type);
if (inst_module && inst_module->attributes.count("\\abc9_box_id")) { if (inst_module) {
abc9_box_seen = true; 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;
if (!holes_mode) { for (const auto &conn : cell->connections()) {
toposort.node(cell->name); auto port_wire = inst_module->wire(conn.first);
for (const auto &conn : cell->connections()) {
auto port_wire = inst_module->wire(conn.first); if (abc9_box) {
if (port_wire->port_input) { // Ignore inout for the sake of topographical ordering
// Ignore inout for the sake of topographical ordering if (port_wire->port_input && !port_wire->port_output)
if (port_wire->port_output) continue;
for (auto bit : sigmap(conn.second)) for (auto bit : sigmap(conn.second))
bit_users[bit].insert(cell->name); bit_users[bit].insert(cell->name);
}
if (port_wire->port_output) if (port_wire->port_output)
for (auto bit : sigmap(conn.second)) for (auto bit : sigmap(conn.second))
bit_drivers[bit].insert(cell->name); bit_drivers[bit].insert(cell->name);
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();
}
if (arrival)
for (auto bit : sigmap(conn.second))
arrival_times[bit] = arrival;
}
}
if (abc9_box) {
abc9_box_seen = true;
toposort.node(cell->name);
continue;
} }
} }
else {
bool cell_known = inst_module || cell->known();
for (const auto &c : cell->connections()) {
if (c.second.is_fully_const()) continue;
auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
if (!is_input && !is_output)
log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
if (is_input) { bool cell_known = inst_module || cell->known();
for (auto b : c.second) { for (const auto &c : cell->connections()) {
Wire *w = b.wire; if (c.second.is_fully_const()) continue;
if (!w) continue; auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
if (!w->port_output || !cell_known) { auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
SigBit I = sigmap(b); auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
if (I != b) if (!is_input && !is_output)
alias_map[b] = I; log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
output_bits.insert(b);
unused_bits.erase(b);
if (!cell_known) if (is_input)
keep_bits.insert(b); for (auto b : c.second) {
} Wire *w = b.wire;
if (!w) continue;
if (!w->port_output || !cell_known) {
SigBit I = sigmap(b);
if (I != b)
alias_map[b] = I;
output_bits.insert(b);
} }
} }
if (is_output) {
int arrival = 0;
if (port_wire) {
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();
}
}
for (auto b : c.second) {
Wire *w = b.wire;
if (!w) continue;
input_bits.insert(b);
SigBit O = sigmap(b);
if (O != b)
alias_map[O] = b;
undriven_bits.erase(O);
if (arrival)
arrival_times[b] = arrival;
}
}
}
} }
//log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
@ -346,12 +339,44 @@ struct XAigerWriter
bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */); 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);
}
if (carry_in != IdString() && carry_out == IdString())
log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module));
if (carry_in == IdString() && carry_out != IdString())
log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module));
if (carry_in != IdString()) {
r.first->second.push_back(carry_in);
r.first->second.push_back(carry_out);
}
}
// Fully pad all unused input connections of this box cell with S0 // Fully pad all unused input connections of this box cell with S0
// Fully pad all undriven output connections of this box cell with anonymous wires // Fully pad all undriven output connections of this box cell with anonymous wires
// NB: Assume box_module->ports are sorted alphabetically for (auto port_name : r.first->second) {
// (as RTLIL::Module::fixup_ports() would do) auto w = box_module->wire(port_name);
for (const auto &port_name : box_module->ports) {
RTLIL::Wire* w = box_module->wire(port_name);
log_assert(w); log_assert(w);
auto it = cell->connections_.find(port_name); auto it = cell->connections_.find(port_name);
if (w->port_input) { if (w->port_input) {
@ -366,7 +391,7 @@ struct XAigerWriter
cell->setPort(port_name, rhs); cell->setPort(port_name, rhs);
} }
for (auto b : rhs.bits()) { for (auto b : rhs) {
SigBit I = sigmap(b); SigBit I = sigmap(b);
if (b == RTLIL::Sx) if (b == RTLIL::Sx)
b = State::S0; b = State::S0;
@ -377,7 +402,7 @@ struct XAigerWriter
alias_map[b] = I; alias_map[b] = I;
} }
co_bits.emplace_back(b); co_bits.emplace_back(b);
unused_bits.erase(b); unused_bits.erase(I);
} }
} }
if (w->port_output) { if (w->port_output) {
@ -397,65 +422,53 @@ struct XAigerWriter
} }
for (const auto &b : rhs.bits()) { for (const auto &b : rhs.bits()) {
ci_bits.emplace_back(b);
SigBit O = sigmap(b); SigBit O = sigmap(b);
if (O != b) if (O != b)
alias_map[O] = b; alias_map[O] = b;
ci_bits.emplace_back(b);
undriven_bits.erase(O); undriven_bits.erase(O);
input_bits.erase(b);
} }
} }
} }
// 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);
}
}
box_list.emplace_back(cell); box_list.emplace_back(cell);
} }
// TODO: Free memory from toposort, bit_drivers, bit_users // TODO: Free memory from toposort, bit_drivers, bit_users
} }
for (auto bit : input_bits) { for (auto bit : input_bits)
if (!output_bits.count(bit)) undriven_bits.erase(bit);
continue; for (auto bit : output_bits)
RTLIL::Wire *wire = bit.wire; unused_bits.erase(sigmap(bit));
// If encountering an inout port, or a keep-ed wire, then create a new wire
// with $inout.out suffix, make it a PO driven by the existing inout, and
// inherit existing inout's drivers
if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
|| keep_bits.count(bit)) {
RTLIL::IdString wire_name = stringf("$%s$inout.out", wire->name.c_str());
RTLIL::Wire *new_wire = module->wire(wire_name);
if (!new_wire)
new_wire = module->addWire(wire_name, GetSize(wire));
SigBit new_bit(new_wire, bit.offset);
module->connect(new_bit, bit);
if (not_map.count(bit)) {
auto a = not_map.at(bit);
not_map[new_bit] = a;
}
else if (and_map.count(bit)) {
auto a = and_map.at(bit);
and_map[new_bit] = a;
}
else if (alias_map.count(bit)) {
auto a = alias_map.at(bit);
alias_map[new_bit] = a;
}
else
alias_map[new_bit] = bit;
output_bits.erase(bit);
output_bits.insert(new_bit);
}
}
for (auto bit : unused_bits) for (auto bit : unused_bits)
undriven_bits.erase(bit); undriven_bits.erase(bit);
if (!undriven_bits.empty() && !holes_mode) { // Make all undriven bits a primary input
undriven_bits.sort(); for (auto bit : undriven_bits) {
for (auto bit : undriven_bits) { input_bits.insert(bit);
log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); undriven_bits.erase(bit);
input_bits.insert(bit);
}
log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module));
} }
if (holes_mode) { if (holes_mode) {
@ -467,25 +480,27 @@ struct XAigerWriter
input_bits.sort(sort_by_port_id()); input_bits.sort(sort_by_port_id());
output_bits.sort(sort_by_port_id()); output_bits.sort(sort_by_port_id());
} }
else {
input_bits.sort();
output_bits.sort();
}
not_map.sort();
and_map.sort();
aig_map[State::S0] = 0; aig_map[State::S0] = 0;
aig_map[State::S1] = 1; aig_map[State::S1] = 1;
for (auto bit : input_bits) { for (const auto &bit : input_bits) {
aig_m++, aig_i++; aig_m++, aig_i++;
log_assert(!aig_map.count(bit)); log_assert(!aig_map.count(bit));
aig_map[bit] = 2*aig_m; aig_map[bit] = 2*aig_m;
} }
for (auto bit : ci_bits) { for (const auto &i : ff_bits) {
const Cell *cell = i.second;
const SigBit &q = sigmap(cell->getPort("\\Q"));
aig_m++, aig_i++; aig_m++, aig_i++;
log_assert(!aig_map.count(q));
aig_map[q] = 2*aig_m;
}
for (auto &bit : ci_bits) {
aig_m++, aig_i++;
log_assert(!aig_map.count(bit));
aig_map[bit] = 2*aig_m; aig_map[bit] = 2*aig_m;
} }
@ -494,15 +509,16 @@ struct XAigerWriter
aig_outputs.push_back(bit2aig(bit)); aig_outputs.push_back(bit2aig(bit));
} }
if (output_bits.empty()) { for (const auto &bit : output_bits) {
output_bits.insert(State::S0);
omode = true;
}
for (auto bit : output_bits) {
ordered_outputs[bit] = aig_o++; ordered_outputs[bit] = aig_o++;
aig_outputs.push_back(bit2aig(bit)); aig_outputs.push_back(bit2aig(bit));
} }
for (auto &i : ff_bits) {
const SigBit &d = i.first;
aig_o++;
aig_outputs.push_back(aig_map.at(d));
}
} }
void write_aiger(std::ostream &f, bool ascii_mode) void write_aiger(std::ostream &f, bool ascii_mode)
@ -564,7 +580,6 @@ struct XAigerWriter
f << "c"; f << "c";
log_assert(!output_bits.empty());
auto write_buffer = [](std::stringstream &buffer, int i32) { auto write_buffer = [](std::stringstream &buffer, int i32) {
int32_t i32_be = to_big_endian(i32); int32_t i32_be = to_big_endian(i32);
buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
@ -572,14 +587,14 @@ struct XAigerWriter
std::stringstream h_buffer; std::stringstream h_buffer;
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
write_h_buffer(1); write_h_buffer(1);
log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ci_bits)); log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits));
write_h_buffer(input_bits.size() + ci_bits.size()); write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(co_bits)); log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits));
write_h_buffer(output_bits.size() + GetSize(co_bits)); write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits));
log_debug("piNum = %d\n", GetSize(input_bits)); log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits));
write_h_buffer(input_bits.size()); write_h_buffer(input_bits.size() + ff_bits.size());
log_debug("poNum = %d\n", GetSize(output_bits)); log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits));
write_h_buffer(output_bits.size()); write_h_buffer(output_bits.size() + ff_bits.size());
log_debug("boxNum = %d\n", GetSize(box_list)); log_debug("boxNum = %d\n", GetSize(box_list));
write_h_buffer(box_list.size()); write_h_buffer(box_list.size());
@ -595,7 +610,7 @@ struct XAigerWriter
//for (auto bit : output_bits) //for (auto bit : output_bits)
// write_o_buffer(0); // write_o_buffer(0);
if (!box_list.empty()) { if (!box_list.empty() || !ff_bits.empty()) {
RTLIL::Module *holes_module = module->design->addModule("$__holes__"); RTLIL::Module *holes_module = module->design->addModule("$__holes__");
log_assert(holes_module); log_assert(holes_module);
@ -609,35 +624,23 @@ struct XAigerWriter
IdString derived_name = orig_box_module->derive(module->design, cell->parameters); IdString derived_name = orig_box_module->derive(module->design, cell->parameters);
RTLIL::Module* box_module = module->design->module(derived_name); RTLIL::Module* box_module = module->design->module(derived_name);
if (box_module->has_processes()) if (box_module->has_processes())
log_error("ABC9 box '%s' contains processes!\n", box_module->name.c_str()); Pass::call_on_module(module->design, box_module, "proc");
int box_inputs = 0, box_outputs = 0;
auto r = cell_cache.insert(std::make_pair(derived_name, nullptr)); auto r = cell_cache.insert(std::make_pair(derived_name, nullptr));
Cell *holes_cell = r.first->second; Cell *holes_cell = r.first->second;
if (r.second && box_module->get_bool_attribute("\\whitebox")) { if (r.second && box_module->get_bool_attribute("\\whitebox")) {
holes_cell = holes_module->addCell(cell->name, cell->type); holes_cell = holes_module->addCell(cell->name, cell->type);
holes_cell->parameters = cell->parameters; holes_cell->parameters = cell->parameters;
r.first->second = holes_cell; r.first->second = holes_cell;
// Since Module::derive() will create a new module, there
// is a chance that the ports will be alphabetically ordered
// again, which is a problem when carry-chains are involved.
// Inherit the port ordering from the original module here...
// (and set the port_id below, when iterating through those)
log_assert(GetSize(box_module->ports) == GetSize(orig_box_module->ports));
box_module->ports = orig_box_module->ports;
} }
// NB: Assume box_module->ports are sorted alphabetically int box_inputs = 0, box_outputs = 0;
// (as RTLIL::Module::fixup_ports() would do) for (auto port_name : box_ports.at(cell->type)) {
int box_port_id = 1;
for (const auto &port_name : box_module->ports) {
RTLIL::Wire *w = box_module->wire(port_name); RTLIL::Wire *w = box_module->wire(port_name);
log_assert(w); log_assert(w);
if (r.second)
w->port_id = box_port_id++;
RTLIL::Wire *holes_wire; RTLIL::Wire *holes_wire;
RTLIL::SigSpec port_sig; RTLIL::SigSpec port_sig;
if (w->port_input) if (w->port_input)
for (int i = 0; i < GetSize(w); i++) { for (int i = 0; i < GetSize(w); i++) {
box_inputs++; box_inputs++;
@ -655,9 +658,9 @@ struct XAigerWriter
box_outputs += GetSize(w); box_outputs += GetSize(w);
for (int i = 0; i < GetSize(w); i++) { for (int i = 0; i < GetSize(w); i++) {
if (GetSize(w) == 1) if (GetSize(w) == 1)
holes_wire = holes_module->addWire(stringf("%s.%s", cell->name.c_str(), log_id(w->name))); holes_wire = holes_module->addWire(stringf("$abc%s.%s", cell->name.c_str(), log_id(w->name)));
else else
holes_wire = holes_module->addWire(stringf("%s.%s[%d]", cell->name.c_str(), log_id(w->name), i)); holes_wire = holes_module->addWire(stringf("$abc%s.%s[%d]", cell->name.c_str(), log_id(w->name), i));
holes_wire->port_output = true; holes_wire->port_output = true;
holes_wire->port_id = port_id++; holes_wire->port_id = port_id++;
holes_module->ports.push_back(holes_wire->name); holes_module->ports.push_back(holes_wire->name);
@ -675,6 +678,23 @@ struct XAigerWriter
} }
} }
// 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")) {
log_assert(holes_cell);
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 *w = holes_module->addWire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
holes_module->connect(w, holes_wire);
}
write_h_buffer(box_inputs); write_h_buffer(box_inputs);
write_h_buffer(box_outputs); write_h_buffer(box_outputs);
write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int()); write_h_buffer(box_module->attributes.at("\\abc9_box_id").as_int());
@ -683,13 +703,48 @@ struct XAigerWriter
std::stringstream r_buffer; std::stringstream r_buffer;
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
write_r_buffer(0); log_debug("flopNum = %d\n", GetSize(ff_bits));
write_r_buffer(ff_bits.size());
std::stringstream s_buffer;
auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);
write_s_buffer(ff_bits.size());
for (const auto &i : ff_bits) {
const SigBit &d = i.first;
const Cell *cell = i.second;
int mergeability = cell->attributes.at(ID(abc9_mergeability)).as_int();
log_assert(mergeability > 0);
write_r_buffer(mergeability);
Const init = cell->attributes.at(ID(abc9_init));
log_assert(GetSize(init) == 1);
if (init == State::S1)
write_s_buffer(1);
else if (init == State::S0)
write_s_buffer(0);
else {
log_assert(init == State::Sx);
write_s_buffer(0);
}
write_i_buffer(arrival_times.at(d, 0));
//write_o_buffer(0);
}
f << "r"; f << "r";
std::string buffer_str = r_buffer.str(); std::string buffer_str = r_buffer.str();
int32_t buffer_size_be = to_big_endian(buffer_str.size()); 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(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
f.write(buffer_str.data(), buffer_str.size()); f.write(buffer_str.data(), buffer_str.size());
f << "s";
buffer_str = s_buffer.str();
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());
if (holes_module) { if (holes_module) {
log_push(); log_push();
@ -697,34 +752,64 @@ struct XAigerWriter
//holes_module->fixup_ports(); //holes_module->fixup_ports();
holes_module->check(); holes_module->check();
holes_module->design->selection_stack.emplace_back(false);
RTLIL::Selection& sel = holes_module->design->selection_stack.back();
sel.select(holes_module);
Pass::call(holes_module->design, "flatten -wb");
// Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger // Cannot techmap/aigmap/check all lib_whitebox-es outside of write_xaiger
// since boxes may contain parameters in which case `flatten` would have // since boxes may contain parameters in which case `flatten` would have
// created a new $paramod ... // created a new $paramod ...
Pass::call(holes_module->design, "techmap"); Pass::call_on_module(holes_module->design, holes_module, "flatten -wb; techmap; aigmap");
Pass::call(holes_module->design, "aigmap");
for (auto cell : holes_module->cells())
if (!cell->type.in("$_NOT_", "$_AND_"))
log_error("Whitebox contents cannot be represented as AIG. Please verify whiteboxes are synthesisable.\n");
holes_module->design->selection_stack.pop_back(); dict<SigSig, SigSig> 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 the combinatorial control logic that feeds the box
// (i.e. clock enable, synchronous reset, etc.)
replace.insert(std::make_pair(SigSig(port,Q), SigSig(port,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 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(conn);
if (it != replace.end())
conn = it->second;
}
// Move into a new (temporary) design so that "clean" will only // Move into a new (temporary) design so that "clean" will only
// operate (and run checks on) this one module // operate (and run checks on) this one module
RTLIL::Design *holes_design = new RTLIL::Design; RTLIL::Design *holes_design = new RTLIL::Design;
holes_module->design->modules_.erase(holes_module->name); module->design->modules_.erase(holes_module->name);
holes_design->add(holes_module); holes_design->add(holes_module);
Pass::call(holes_design, "clean -purge"); Pass::call(holes_design, "opt -purge");
std::stringstream a_buffer; std::stringstream a_buffer;
XAigerWriter writer(holes_module, true /* holes_mode */); XAigerWriter writer(holes_module, true /* holes_mode */);
writer.write_aiger(a_buffer, false /*ascii_mode*/); writer.write_aiger(a_buffer, false /*ascii_mode*/);
delete holes_design; delete holes_design;
f << "a"; f << "a";
@ -755,19 +840,20 @@ struct XAigerWriter
//f.write(buffer_str.data(), buffer_str.size()); //f.write(buffer_str.data(), buffer_str.size());
f << stringf("Generated by %s\n", yosys_version_str); f << stringf("Generated by %s\n", yosys_version_str);
module->design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
module->design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size());
module->design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size());
module->design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size());
} }
void write_map(std::ostream &f, bool verbose_map) void write_map(std::ostream &f)
{ {
dict<int, string> input_lines; dict<int, string> input_lines;
dict<int, string> output_lines; dict<int, string> output_lines;
dict<int, string> wire_lines;
for (auto wire : module->wires()) for (auto wire : module->wires())
{ {
//if (!verbose_map && wire->name[0] == '$')
// continue;
SigSpec sig = sigmap(wire); SigSpec sig = sigmap(wire);
for (int i = 0; i < GetSize(wire); i++) for (int i = 0; i < GetSize(wire); i++)
@ -781,17 +867,10 @@ struct XAigerWriter
if (output_bits.count(b)) { if (output_bits.count(b)) {
int o = ordered_outputs.at(b); int o = ordered_outputs.at(b);
output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), i, log_id(wire)); int init = 2;
output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init);
continue; continue;
} }
if (verbose_map) {
if (aig_map.count(sig[i]) == 0)
continue;
int a = aig_map.at(sig[i]);
wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire));
}
} }
} }
@ -805,15 +884,9 @@ struct XAigerWriter
f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
output_lines.sort(); output_lines.sort();
if (omode)
output_lines[State::S0] = "output 0 0 $__dummy__\n";
for (auto &it : output_lines) for (auto &it : output_lines)
f << it.second; f << it.second;
log_assert(output_lines.size() == output_bits.size()); log_assert(output_lines.size() == output_bits.size());
wire_lines.sort();
for (auto &it : wire_lines)
f << it.second;
} }
}; };
@ -825,8 +898,10 @@ struct XAigerBackend : public Backend {
log("\n"); log("\n");
log(" write_xaiger [options] [filename]\n"); log(" write_xaiger [options] [filename]\n");
log("\n"); log("\n");
log("Write the current design to an XAIGER file. The design must be flattened and\n"); log("Write the top module (according to the (* top *) attribute or if only one module\n");
log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\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("\n"); log("\n");
log(" -ascii\n"); log(" -ascii\n");
log(" write ASCII version of AIGER format\n"); log(" write ASCII version of AIGER format\n");
@ -834,14 +909,10 @@ struct XAigerBackend : public Backend {
log(" -map <filename>\n"); log(" -map <filename>\n");
log(" write an extra file with port and box symbols\n"); log(" write an extra file with port and box symbols\n");
log("\n"); log("\n");
log(" -vmap <filename>\n");
log(" like -map, but more verbose\n");
log("\n");
} }
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
bool ascii_mode = false; bool ascii_mode = false;
bool verbose_map = false;
std::string map_filename; std::string map_filename;
log_header(design, "Executing XAIGER backend.\n"); log_header(design, "Executing XAIGER backend.\n");
@ -857,11 +928,6 @@ struct XAigerBackend : public Backend {
map_filename = args[++argidx]; map_filename = args[++argidx];
continue; continue;
} }
if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) {
map_filename = args[++argidx];
verbose_map = true;
continue;
}
break; break;
} }
extra_args(f, filename, args, argidx, !ascii_mode); extra_args(f, filename, args, argidx, !ascii_mode);
@ -871,6 +937,14 @@ struct XAigerBackend : public Backend {
if (top_module == nullptr) if (top_module == nullptr)
log_error("Can't find top module in current design!\n"); log_error("Can't find top module in current design!\n");
if (!design->selected_whole_module(top_module))
log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
if (!top_module->processes.empty())
log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module));
if (!top_module->memories.empty())
log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module));
XAigerWriter writer(top_module); XAigerWriter writer(top_module);
writer.write_aiger(*f, ascii_mode); writer.write_aiger(*f, ascii_mode);
@ -879,7 +953,7 @@ struct XAigerBackend : public Backend {
mapf.open(map_filename.c_str(), std::ofstream::trunc); mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail()) if (mapf.fail())
log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
writer.write_map(mapf, verbose_map); writer.write_map(mapf);
} }
} }
} XAigerBackend; } XAigerBackend;

View File

@ -255,7 +255,7 @@ end_of_header:
else else
log_abort(); log_abort();
RTLIL::Wire* n0 = module->wire("\\__0__"); RTLIL::Wire* n0 = module->wire("$0");
if (n0) if (n0)
module->connect(n0, State::S0); module->connect(n0, State::S0);
@ -316,14 +316,14 @@ static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned litera
{ {
const unsigned variable = literal >> 1; const unsigned variable = literal >> 1;
const bool invert = literal & 1; const bool invert = literal & 1;
RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); RTLIL::IdString wire_name(stringf("$%d%s", variable, invert ? "b" : ""));
RTLIL::Wire *wire = module->wire(wire_name); RTLIL::Wire *wire = module->wire(wire_name);
if (wire) return wire; if (wire) return wire;
log_debug2("Creating %s\n", wire_name.c_str()); log_debug2("Creating %s\n", wire_name.c_str());
wire = module->addWire(wire_name); wire = module->addWire(wire_name);
wire->port_input = wire->port_output = false; wire->port_input = wire->port_output = false;
if (!invert) return wire; if (!invert) return wire;
RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable)); RTLIL::IdString wire_inv_name(stringf("$%d", variable));
RTLIL::Wire *wire_inv = module->wire(wire_inv_name); RTLIL::Wire *wire_inv = module->wire(wire_inv_name);
if (wire_inv) { if (wire_inv) {
if (module->cell(wire_inv_name)) return wire; if (module->cell(wire_inv_name)) return wire;
@ -335,12 +335,12 @@ static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned litera
} }
log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); log_debug2("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str());
module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire); module->addNotGate(stringf("$%d$not", variable), wire_inv, wire);
return wire; return wire;
} }
void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup) void AigerReader::parse_xaiger()
{ {
std::string header; std::string header;
f >> header; f >> header;
@ -372,108 +372,117 @@ void AigerReader::parse_xaiger(const dict<int,IdString> &box_lookup)
else else
log_abort(); log_abort();
RTLIL::Wire* n0 = module->wire("\\__0__"); RTLIL::Wire* n0 = module->wire("$0");
if (n0) if (n0)
module->connect(n0, State::S0); module->connect(n0, State::S0);
int c = f.get();
if (c != 'c')
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
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.) // Parse footer (symbol table, comments, etc.)
std::string s; std::string s;
bool comment_seen = false; for (int c = f.get(); c != EOF; c = f.get()) {
for (int c = f.peek(); c != EOF; c = f.peek()) { // XAIGER extensions
if (comment_seen || c == 'c') { if (c == 'm') {
if (!comment_seen) { uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
f.ignore(1); uint32_t lutNum = parse_xaiger_literal(f);
c = f.peek(); uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
comment_seen = true; log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize);
} ConstEvalAig ce(module);
if (c == '\n') for (unsigned i = 0; i < lutNum; ++i) {
break; uint32_t rootNodeID = parse_xaiger_literal(f);
f.ignore(1); uint32_t cutLeavesM = parse_xaiger_literal(f);
// XAIGER extensions log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
if (c == 'm') { RTLIL::Wire *output_sig = module->wire(stringf("$%d", rootNodeID));
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); uint32_t nodeID;
uint32_t lutNum = parse_xaiger_literal(f); RTLIL::SigSpec input_sig;
uint32_t lutSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); for (unsigned j = 0; j < cutLeavesM; ++j) {
log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); nodeID = parse_xaiger_literal(f);
ConstEvalAig ce(module); log_debug2("\t%u\n", nodeID);
for (unsigned i = 0; i < lutNum; ++i) { RTLIL::Wire *wire = module->wire(stringf("$%d", nodeID));
uint32_t rootNodeID = parse_xaiger_literal(f); log_assert(wire);
uint32_t cutLeavesM = parse_xaiger_literal(f); input_sig.append(wire);
log_debug2("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM);
RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID));
uint32_t nodeID;
RTLIL::SigSpec input_sig;
for (unsigned j = 0; j < cutLeavesM; ++j) {
nodeID = parse_xaiger_literal(f);
log_debug2("\t%u\n", nodeID);
RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID));
log_assert(wire);
input_sig.append(wire);
}
// 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) {
int gray = j ^ (j >> 1);
ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)});
RTLIL::SigBit o(output_sig);
bool success YS_ATTRIBUTE(unused) = ce.eval(o);
log_assert(success);
log_assert(o.wire == nullptr);
lut_mask[gray] = o.data;
}
RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID));
log_assert(output_cell);
module->remove(output_cell);
module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask));
} }
} // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
else if (c == 'r') { ce.clear();
uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
flopNum = parse_xaiger_literal(f); RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size());
log_debug("flopNum: %u\n", flopNum); for (int j = 0; j < (1 << cutLeavesM); ++j) {
log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); int gray = j ^ (j >> 1);
f.ignore(flopNum * sizeof(uint32_t)); ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)});
} RTLIL::SigBit o(output_sig);
else if (c == 'n') { bool success YS_ATTRIBUTE(unused) = ce.eval(o);
parse_xaiger_literal(f); log_assert(success);
f >> s; log_assert(o.wire == nullptr);
log_debug("n: '%s'\n", s.c_str()); lut_mask[gray] = o.data;
}
else if (c == 'h') {
f.ignore(sizeof(uint32_t));
uint32_t version YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_assert(version == 1);
uint32_t ciNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("ciNum = %u\n", ciNum);
uint32_t coNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("coNum = %u\n", coNum);
piNum = parse_xaiger_literal(f);
log_debug("piNum = %u\n", piNum);
uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("poNum = %u\n", poNum);
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 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));
boxes.emplace_back(cell);
} }
} RTLIL::Cell *output_cell = module->cell(stringf("$%d$and", rootNodeID));
else if (c == 'a' || c == 'i' || c == 'o') { log_assert(output_cell);
uint32_t dataSize = parse_xaiger_literal(f); module->remove(output_cell);
f.ignore(dataSize); module->addLut(stringf("$%d$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask));
}
else {
break;
} }
} }
else else if (c == 'r') {
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); uint32_t dataSize YS_ATTRIBUTE(unused) = 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));
}
else if (c == 'n') {
parse_xaiger_literal(f);
f >> s;
log_debug("n: '%s'\n", s.c_str());
}
else if (c == 'h') {
f.ignore(sizeof(uint32_t));
uint32_t version YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_assert(version == 1);
uint32_t ciNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("ciNum = %u\n", ciNum);
uint32_t coNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("coNum = %u\n", coNum);
piNum = parse_xaiger_literal(f);
log_debug("piNum = %u\n", piNum);
uint32_t poNum YS_ATTRIBUTE(unused) = parse_xaiger_literal(f);
log_debug("poNum = %u\n", poNum);
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 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));
boxes.emplace_back(cell);
}
}
else if (c == 'a' || c == 'i' || c == 'o' || c == 's') {
uint32_t dataSize = parse_xaiger_literal(f);
f.ignore(dataSize);
log_debug("ignoring '%c'\n", c);
}
else {
break;
}
} }
post_process(); post_process();
@ -550,7 +559,7 @@ void AigerReader::parse_aiger_ascii()
log_debug2("%d is an output\n", l1); log_debug2("%d is an output\n", l1);
const unsigned variable = l1 >> 1; const unsigned variable = l1 >> 1;
const bool invert = l1 & 1; const bool invert = l1 & 1;
RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? RTLIL::IdString wire_name(stringf("$%d%s", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix?
RTLIL::Wire *wire = module->wire(wire_name); RTLIL::Wire *wire = module->wire(wire_name);
if (!wire) if (!wire)
wire = createWireIfNotExists(module, l1); wire = createWireIfNotExists(module, l1);
@ -616,11 +625,12 @@ void AigerReader::parse_aiger_binary()
std::string line; std::string line;
// Parse inputs // Parse inputs
int digits = ceil(log10(I));
for (unsigned i = 1; i <= I; ++i) { for (unsigned i = 1; i <= I; ++i) {
log_debug2("%d is an input\n", i); log_debug2("%d is an input\n", i);
RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, i));
wire->port_input = true; wire->port_input = true;
log_assert(!wire->port_output); module->connect(createWireIfNotExists(module, i << 1), wire);
inputs.push_back(wire); inputs.push_back(wire);
} }
@ -670,23 +680,15 @@ void AigerReader::parse_aiger_binary()
} }
// Parse outputs // Parse outputs
digits = ceil(log10(O));
for (unsigned i = 0; i < O; ++i, ++line_count) { for (unsigned i = 0; i < O; ++i, ++line_count) {
if (!(f >> l1)) if (!(f >> l1))
log_error("Line %u cannot be interpreted as an output!\n", line_count); log_error("Line %u cannot be interpreted as an output!\n", line_count);
log_debug2("%d is an output\n", l1); log_debug2("%d is an output\n", l1);
const unsigned variable = l1 >> 1; RTLIL::Wire *wire = module->addWire(stringf("$o%0*d", digits, i));
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;
}
wire->port_output = true; wire->port_output = true;
module->connect(wire, createWireIfNotExists(module, l1));
outputs.push_back(wire); outputs.push_back(wire);
} }
std::getline(f, line); // Ignore up to start of next line std::getline(f, line); // Ignore up to start of next line
@ -733,56 +735,37 @@ void AigerReader::parse_aiger_binary()
void AigerReader::post_process() void AigerReader::post_process()
{ {
pool<IdString> seen_boxes; dict<IdString, std::vector<IdString>> box_ports;
unsigned ci_count = 0, co_count = 0; unsigned ci_count = 0, co_count = 0, flop_count = 0;
for (auto cell : boxes) { for (auto cell : boxes) {
RTLIL::Module* box_module = design->module(cell->type); RTLIL::Module* box_module = design->module(cell->type);
log_assert(box_module); log_assert(box_module);
if (seen_boxes.insert(cell->type).second) { auto r = box_ports.insert(cell->type);
auto it = box_module->attributes.find("\\abc9_carry"); if (r.second) {
if (it != box_module->attributes.end()) { // Make carry in the last PI, and carry out the last PO
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr; // since ABC requires it this way
auto carry_in_out = it->second.decode_string(); IdString carry_in, carry_out;
auto pos = carry_in_out.find(','); for (const auto &port_name : box_module->ports) {
if (pos == std::string::npos) auto w = box_module->wire(port_name);
log_error("'abc9_carry' attribute on module '%s' does not contain ','.\n", log_id(cell->type)); log_assert(w);
auto carry_in_name = RTLIL::escape_id(carry_in_out.substr(0, pos)); if (w->get_bool_attribute("\\abc9_carry")) {
carry_in = box_module->wire(carry_in_name); if (w->port_input)
if (!carry_in || !carry_in->port_input) carry_in = port_name;
log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an input port.\n", log_id(cell->type), carry_in_name.c_str()); if (w->port_output)
carry_out = port_name;
auto carry_out_name = RTLIL::escape_id(carry_in_out.substr(pos+1));
carry_out = box_module->wire(carry_out_name);
if (!carry_out || !carry_out->port_output)
log_error("'abc9_carry' on module '%s' contains '%s' which does not exist or is not an output port.\n", log_id(cell->type), carry_out_name.c_str());
auto &ports = box_module->ports;
for (auto jt = ports.begin(); jt != ports.end(); ) {
RTLIL::Wire* w = box_module->wire(*jt);
log_assert(w);
if (w == carry_in || w == carry_out) {
jt = ports.erase(jt);
continue;
}
if (w->port_id > carry_in->port_id)
--w->port_id;
if (w->port_id > carry_out->port_id)
--w->port_id;
log_assert(w->port_input || w->port_output);
log_assert(ports[w->port_id-1] == w->name);
++jt;
} }
ports.push_back(carry_in->name); else
carry_in->port_id = ports.size(); r.first->second.push_back(port_name);
ports.push_back(carry_out->name); }
carry_out->port_id = ports.size(); if (carry_in != IdString()) {
log_assert(carry_out != IdString());
r.first->second.push_back(carry_in);
r.first->second.push_back(carry_out);
} }
} }
// NB: Assume box_module->ports are sorted alphabetically for (auto port_name : box_ports.at(cell->type)) {
// (as RTLIL::Module::fixup_ports() would do)
for (auto port_name : box_module->ports) {
RTLIL::Wire* port = box_module->wire(port_name); RTLIL::Wire* port = box_module->wire(port_name);
log_assert(port); log_assert(port);
RTLIL::SigSpec rhs; RTLIL::SigSpec rhs;
@ -804,9 +787,32 @@ void AigerReader::post_process()
} }
rhs.append(wire); rhs.append(wire);
} }
cell->setPort(port_name, rhs); cell->setPort(port_name, rhs);
} }
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;
}
} }
dict<RTLIL::IdString, int> wideports_cache; dict<RTLIL::IdString, int> wideports_cache;
@ -868,16 +874,7 @@ void AigerReader::post_process()
// simply connect the latter to the former // simply connect the latter to the former
RTLIL::Wire* existing = module->wire(escaped_s); RTLIL::Wire* existing = module->wire(escaped_s);
if (!existing) { if (!existing) {
if (escaped_s.ends_with("$inout.out")) { module->rename(wire, escaped_s);
wire->port_output = false;
RTLIL::Wire *in_wire = module->wire(escaped_s.substr(1, escaped_s.size()-11));
log_assert(in_wire);
log_assert(in_wire->port_input && !in_wire->port_output);
in_wire->port_output = true;
module->connect(in_wire, wire);
}
else
module->rename(wire, escaped_s);
} }
else { else {
wire->port_output = false; wire->port_output = false;
@ -889,19 +886,9 @@ void AigerReader::post_process()
std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index); std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
RTLIL::Wire* existing = module->wire(indexed_name); RTLIL::Wire* existing = module->wire(indexed_name);
if (!existing) { if (!existing) {
if (escaped_s.ends_with("$inout.out")) { module->rename(wire, indexed_name);
wire->port_output = false; if (wideports)
RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(1, escaped_s.size()-11).c_str(), index)); wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
log_assert(in_wire);
log_assert(in_wire->port_input && !in_wire->port_output);
in_wire->port_output = true;
module->connect(in_wire, wire);
}
else {
module->rename(wire, indexed_name);
if (wideports)
wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
}
} }
else { else {
module->connect(wire, existing); module->connect(wire, existing);
@ -909,9 +896,13 @@ void AigerReader::post_process()
} }
} }
log_debug(" -> %s\n", log_id(wire)); log_debug(" -> %s\n", log_id(wire));
int init;
mf >> init;
if (init < 2)
wire->attributes["\\init"] = init;
} }
else if (type == "box") { else if (type == "box") {
RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable)); 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); module->rename(cell, escaped_s);
for (const auto &i : cell->connections()) { for (const auto &i : cell->connections()) {
@ -968,15 +959,10 @@ void AigerReader::post_process()
if (other_wire) { if (other_wire) {
other_wire->port_input = false; other_wire->port_input = false;
other_wire->port_output = false; other_wire->port_output = false;
} if (wire->port_input)
if (wire->port_input) {
if (other_wire)
module->connect(other_wire, SigSpec(wire, i)); module->connect(other_wire, SigSpec(wire, i));
} else
else { module->connect(SigSpec(wire, i), other_wire);
// Since we skip POs that are connected to Sx,
// re-connect them here
module->connect(SigSpec(wire, i), other_wire ? other_wire : SigSpec(RTLIL::Sx));
} }
} }
} }

View File

@ -47,7 +47,7 @@ struct AigerReader
AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports);
void parse_aiger(); void parse_aiger();
void parse_xaiger(const dict<int,IdString> &box_lookup); void parse_xaiger();
void parse_aiger_ascii(); void parse_aiger_ascii();
void parse_aiger_binary(); void parse_aiger_binary();
void post_process(); void post_process();

View File

@ -63,22 +63,16 @@ extern "C" int Abc_RealMain(int argc, char *argv[]);
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
bool markgroups;
int map_autoidx; int map_autoidx;
SigMap assign_map;
RTLIL::Module *module;
bool clk_polarity, en_polarity;
RTLIL::SigSpec clk_sig, en_sig;
inline std::string remap_name(RTLIL::IdString abc9_name) inline std::string remap_name(RTLIL::IdString abc9_name)
{ {
return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1); return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
} }
void handle_loops(RTLIL::Design *design) void handle_loops(RTLIL::Design *design, RTLIL::Module *module)
{ {
Pass::call(design, "scc -set_attr abc9_scc_id {}"); Pass::call(design, "scc -set_attr abc9_scc_id {} % w:*");
// For every unique SCC found, (arbitrarily) find the first // For every unique SCC found, (arbitrarily) find the first
// cell in the component, and select (and mark) all its output // cell in the component, and select (and mark) all its output
@ -253,49 +247,14 @@ struct abc9_output_filter
} }
}; };
void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file, 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 clk_str, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool /*keepff*/, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file, bool show_tempdir, std::string box_file, std::string lut_file,
std::string wire_delay, const dict<int,IdString> &box_lookup, bool nomfs std::string wire_delay, bool nomfs
) )
{ {
module = current_module;
map_autoidx = autoidx++; map_autoidx = autoidx++;
if (clk_str != "$")
{
clk_polarity = true;
clk_sig = RTLIL::SigSpec();
en_polarity = true;
en_sig = RTLIL::SigSpec();
}
if (!clk_str.empty() && clk_str != "$")
{
if (clk_str.find(',') != std::string::npos) {
int pos = clk_str.find(',');
std::string en_str = clk_str.substr(pos+1);
clk_str = clk_str.substr(0, pos);
if (en_str[0] == '!') {
en_polarity = false;
en_str = en_str.substr(1);
}
if (module->wires_.count(RTLIL::escape_id(en_str)) != 0)
en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0));
}
if (clk_str[0] == '!') {
clk_polarity = false;
clk_str = clk_str.substr(1);
}
if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0)
clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0));
}
if (dff_mode && clk_sig.empty())
log_cmd_error("Clock domain %s not found.\n", clk_str.c_str());
std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; std::string tempdir_name = "/tmp/yosys-abc-XXXXXX";
if (!cleanup) if (!cleanup)
tempdir_name[0] = tempdir_name[4] = '_'; tempdir_name[0] = tempdir_name[4] = '_';
@ -308,13 +267,13 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
if (!lut_costs.empty()) { if (!lut_costs.empty()) {
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
if (!box_file.empty()) if (!box_file.empty())
abc9_script += stringf("read_box -v %s; ", box_file.c_str()); abc9_script += stringf("read_box %s; ", box_file.c_str());
} }
else else
if (!lut_file.empty()) { if (!lut_file.empty()) {
abc9_script += stringf("read_lut %s; ", lut_file.c_str()); abc9_script += stringf("read_lut %s; ", lut_file.c_str());
if (!box_file.empty()) if (!box_file.empty())
abc9_script += stringf("read_box -v %s; ", box_file.c_str()); abc9_script += stringf("read_box %s; ", box_file.c_str());
} }
else else
log_abort(); log_abort();
@ -333,20 +292,10 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
} else } else
abc9_script += stringf("source %s", script_file.c_str()); abc9_script += stringf("source %s", script_file.c_str());
} else if (!lut_costs.empty() || !lut_file.empty()) { } else if (!lut_costs.empty() || !lut_file.empty()) {
//bool all_luts_cost_same = true;
//for (int this_cost : lut_costs)
// if (this_cost != lut_costs.front())
// all_luts_cost_same = false;
abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
//if (all_luts_cost_same && !fast_mode)
// abc9_script += "; lutpack {S}";
} else } else
log_abort(); log_abort();
//if (script_file.empty() && !delay_target.empty())
// for (size_t pos = abc9_script.find("dretime;"); pos != std::string::npos; pos = abc9_script.find("dretime;", pos+1))
// abc9_script = abc9_script.substr(0, pos) + "dretime; retime -o {D};" + abc9_script.substr(pos+8);
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos)) 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); abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
@ -360,7 +309,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos)) for (size_t pos = abc9_script.find("&mfs"); pos != std::string::npos; pos = abc9_script.find("&mfs", pos))
abc9_script = abc9_script.erase(pos, strlen("&mfs")); abc9_script = abc9_script.erase(pos, strlen("&mfs"));
abc9_script += stringf("; &write %s/output.aig", tempdir_name.c_str()); abc9_script += stringf("; &write -n %s/output.aig", tempdir_name.c_str());
abc9_script = add_echos_to_abc9_cmd(abc9_script); abc9_script = add_echos_to_abc9_cmd(abc9_script);
for (size_t i = 0; i+1 < abc9_script.size(); i++) for (size_t i = 0; i+1 < abc9_script.size(); i++)
@ -371,45 +320,22 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
fprintf(f, "%s\n", abc9_script.c_str()); fprintf(f, "%s\n", abc9_script.c_str());
fclose(f); fclose(f);
if (dff_mode || !clk_str.empty())
{
if (clk_sig.size() == 0)
log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching");
else {
log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig));
if (en_sig.size() != 0)
log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig));
log("\n");
}
}
bool count_output = false;
for (auto port_name : module->ports) {
RTLIL::Wire *port_wire = module->wire(port_name);
log_assert(port_wire);
if (port_wire->port_output) {
count_output = true;
break;
}
}
log_push(); log_push();
if (count_output) handle_loops(design, module);
{
design->selection_stack.emplace_back(false);
RTLIL::Selection& sel = design->selection_stack.back();
sel.select(module);
handle_loops(design); Pass::call(design, "aigmap -select");
Pass::call(design, "aigmap"); Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
//log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n", int count_outputs = design->scratchpad_get_int("write_xaiger.num_outputs");
// count_gates, GetSize(signal_list), count_input, count_output); 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"),
Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str())); 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::string buffer;
std::ifstream ifs; std::ifstream ifs;
#if 0 #if 0
@ -420,16 +346,14 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym"); buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym");
log_assert(!design->module(ID($__abc9__))); log_assert(!design->module(ID($__abc9__)));
{ {
AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, /*buffer.c_str()*/ "" /* map_filename */, true /* wideports */);
reader.parse_xaiger(); reader.parse_xaiger();
} }
ifs.close(); ifs.close();
Pass::call(design, stringf("write_verilog -noexpr -norename")); Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected"));
design->remove(design->module(ID($__abc9__))); design->remove(design->module(ID($__abc9__)));
#endif #endif
design->selection_stack.pop_back();
log_header(design, "Executing ABC9.\n"); log_header(design, "Executing ABC9.\n");
if (!lut_costs.empty()) { if (!lut_costs.empty()) {
@ -475,11 +399,11 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
log_assert(!design->module(ID($__abc9__))); log_assert(!design->module(ID($__abc9__)));
AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */); AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */);
reader.parse_xaiger(box_lookup); reader.parse_xaiger();
ifs.close(); ifs.close();
#if 0 #if 0
Pass::call(design, stringf("write_verilog -noexpr -norename")); Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected"));
#endif #endif
log_header(design, "Re-integrating ABC9 results.\n"); log_header(design, "Re-integrating ABC9 results.\n");
@ -487,43 +411,32 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
if (mapped_mod == NULL) if (mapped_mod == NULL)
log_error("ABC output file does not contain a module `$__abc9__'.\n"); log_error("ABC output file does not contain a module `$__abc9__'.\n");
pool<RTLIL::SigBit> output_bits; for (auto w : mapped_mod->wires())
for (auto &it : mapped_mod->wires_) { module->addWire(remap_name(w->name), GetSize(w));
RTLIL::Wire *w = it.second;
RTLIL::Wire *remap_wire = module->addWire(remap_name(w->name), GetSize(w));
if (markgroups) remap_wire->attributes[ID(abcgroup)] = map_autoidx;
if (w->port_output) {
RTLIL::Wire *wire = module->wire(w->name);
log_assert(wire);
for (int i = 0; i < GetSize(w); i++)
output_bits.insert({wire, i});
}
}
for (auto &it : module->connections_) {
auto &signal = it.first;
auto bits = signal.bits();
for (auto &b : bits)
if (output_bits.count(b))
b = module->addWire(NEW_ID);
signal = std::move(bits);
}
dict<IdString, bool> abc9_box; dict<IdString, bool> abc9_box;
vector<RTLIL::Cell*> boxes; vector<RTLIL::Cell*> boxes;
for (const auto &it : module->cells_) { for (auto it = module->cells_.begin(); it != module->cells_.end(); ) {
auto cell = it.second; auto cell = it->second;
if (cell->type.in(ID($_AND_), ID($_NOT_))) { if (cell->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_))) {
module->remove(cell); it = module->cells_.erase(it);
continue; continue;
} }
++it;
RTLIL::Module* box_module = design->module(cell->type);
auto jt = abc9_box.find(cell->type); auto jt = abc9_box.find(cell->type);
if (jt == abc9_box.end()) { if (jt == abc9_box.end())
RTLIL::Module* box_module = design->module(cell->type);
jt = abc9_box.insert(std::make_pair(cell->type, box_module && box_module->attributes.count(ID(abc9_box_id)))).first; 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);
} }
if (jt->second)
boxes.emplace_back(cell);
} }
dict<SigBit, pool<IdString>> bit_drivers, bit_users; dict<SigBit, pool<IdString>> bit_drivers, bit_users;
@ -532,19 +445,19 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks; dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks;
std::map<IdString, int> cell_stats; std::map<IdString, int> cell_stats;
for (auto c : mapped_mod->cells()) for (auto mapped_cell : mapped_mod->cells())
{ {
toposort.node(c->name); toposort.node(mapped_cell->name);
RTLIL::Cell *cell = nullptr; RTLIL::Cell *cell = nullptr;
if (c->type == ID($_NOT_)) { if (mapped_cell->type == ID($_NOT_)) {
RTLIL::SigBit a_bit = c->getPort(ID::A); RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
RTLIL::SigBit y_bit = c->getPort(ID::Y); RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
bit_users[a_bit].insert(c->name); bit_users[a_bit].insert(mapped_cell->name);
bit_drivers[y_bit].insert(c->name); bit_drivers[y_bit].insert(mapped_cell->name);
if (!a_bit.wire) { if (!a_bit.wire) {
c->setPort(ID::Y, module->addWire(NEW_ID)); mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name)); RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
log_assert(wire); log_assert(wire);
module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1); module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
@ -568,7 +481,7 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
if (!driver_lut) { if (!driver_lut) {
// If a driver couldn't be found (could be from PI or box CI) // If a driver couldn't be found (could be from PI or box CI)
// then implement using a LUT // then implement using a LUT
cell = module->addLut(remap_name(stringf("%s$lut", c->name.c_str())), cell = module->addLut(remap_name(stringf("%s$lut", 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(a_bit.wire->name)), a_bit.offset),
RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset), RTLIL::SigBit(module->wires_.at(remap_name(y_bit.wire->name)), y_bit.offset),
RTLIL::Const::from_string("01")); RTLIL::Const::from_string("01"));
@ -576,44 +489,46 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
cell_stats[ID($lut)]++; cell_stats[ID($lut)]++;
} }
else else
not2drivers[c] = driver_lut; not2drivers[mapped_cell] = driver_lut;
continue; continue;
} }
else else
log_abort(); log_abort();
if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
continue; continue;
} }
cell_stats[c->type]++; cell_stats[mapped_cell->type]++;
RTLIL::Cell *existing_cell = nullptr; RTLIL::Cell *existing_cell = nullptr;
if (c->type == ID($lut)) { if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
if (GetSize(c->getPort(ID::A)) == 1 && c->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) { if (mapped_cell->type == ID($lut) &&
SigSpec my_a = module->wires_.at(remap_name(c->getPort(ID::A).as_wire()->name)); GetSize(mapped_cell->getPort(ID::A)) == 1 &&
SigSpec my_y = module->wires_.at(remap_name(c->getPort(ID::Y).as_wire()->name)); 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); module->connect(my_y, my_a);
if (markgroups) c->attributes[ID(abcgroup)] = map_autoidx;
log_abort(); log_abort();
continue; continue;
} }
cell = module->addCell(remap_name(c->name), c->type); cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
} }
else { else {
existing_cell = module->cell(c->name); existing_cell = module->cell(mapped_cell->name);
log_assert(existing_cell); log_assert(existing_cell);
cell = module->addCell(remap_name(c->name), c->type); cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
} }
if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
if (existing_cell) { if (existing_cell) {
cell->parameters = existing_cell->parameters; cell->parameters = existing_cell->parameters;
cell->attributes = existing_cell->attributes; cell->attributes = existing_cell->attributes;
} }
else { else {
cell->parameters = c->parameters; cell->parameters = mapped_cell->parameters;
cell->attributes = c->attributes; cell->attributes = mapped_cell->attributes;
} }
for (auto &conn : c->connections()) {
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; RTLIL::SigSpec newsig;
for (auto c : conn.second.chunks()) { for (auto c : conn.second.chunks()) {
if (c.width == 0) if (c.width == 0)
@ -625,15 +540,17 @@ void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::stri
} }
cell->setPort(conn.first, newsig); cell->setPort(conn.first, newsig);
if (cell->input(conn.first)) { if (!abc9_flop) {
for (auto i : newsig) if (cell->input(conn.first)) {
bit2sinks[i].push_back(cell); for (auto i : newsig)
for (auto i : conn.second) bit2sinks[i].push_back(cell);
bit_users[i].insert(c->name); 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);
} }
if (cell->output(conn.first))
for (auto i : conn.second)
bit_drivers[i].insert(c->name);
} }
} }
@ -821,7 +738,7 @@ struct Abc9Pass : public Pass {
log(" abc9 [options] [selection]\n"); log(" abc9 [options] [selection]\n");
log("\n"); log("\n");
log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n");
log("library to a target architecture.\n"); log("library to a target architecture. Only fully-selected modules are supported.\n");
log("\n"); log("\n");
log(" -exe <command>\n"); log(" -exe <command>\n");
#ifdef ABCEXTERNAL #ifdef ABCEXTERNAL
@ -842,7 +759,7 @@ struct Abc9Pass : public Pass {
log(" if no -script parameter is given, the following scripts are used:\n"); log(" if no -script parameter is given, the following scripts are used:\n");
log("\n"); log("\n");
log(" for -lut/-luts (only one LUT size):\n"); log(" for -lut/-luts (only one LUT size):\n");
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str()); log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str());
log("\n"); log("\n");
log(" for -lut/-luts (different LUT sizes):\n"); log(" for -lut/-luts (different LUT sizes):\n");
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str()); log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str());
@ -858,8 +775,6 @@ struct Abc9Pass : public Pass {
log(" set delay target. the string {D} in the default scripts above is\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(" replaced by this option when used, and an empty string otherwise\n");
log(" (indicating best possible delay).\n"); log(" (indicating best possible delay).\n");
// log(" This also replaces 'dretime' with 'dretime; retime -o {D}' in the\n");
// log(" default scripts above.\n");
log("\n"); log("\n");
// log(" -S <num>\n"); // log(" -S <num>\n");
// log(" maximum number of LUT inputs shared.\n"); // log(" maximum number of LUT inputs shared.\n");
@ -881,19 +796,10 @@ struct Abc9Pass : public Pass {
log(" generate netlist using luts. Use the specified costs for luts with 1,\n"); log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
log(" 2, 3, .. inputs.\n"); log(" 2, 3, .. inputs.\n");
log("\n"); log("\n");
// log(" -dff\n"); log(" -dff\n");
// log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n"); log(" also pass $_ABC9_FF_ cells through to ABC. modules with many clock\n");
// log(" clock domains are automatically partitioned in clock domains and each\n"); log(" domains are marked as such and automatically partitioned by ABC.\n");
// log(" domain is passed through ABC independently.\n"); log("\n");
// log("\n");
// log(" -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n");
// log(" use only the specified clock domain. this is like -dff, but only FF\n");
// log(" cells that belong to the specified clock domain are used.\n");
// log("\n");
// log(" -keepff\n");
// log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n");
// log(" them, for example for equivalence checking.)\n");
// log("\n");
log(" -nocleanup\n"); log(" -nocleanup\n");
log(" when this option is used, the temporary files created by this pass\n"); log(" when this option is used, the temporary files created by this pass\n");
log(" are not removed. this is useful for debugging.\n"); log(" are not removed. this is useful for debugging.\n");
@ -902,11 +808,6 @@ struct Abc9Pass : public Pass {
log(" print the temp dir name in log. usually this is suppressed so that the\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(" command output is identical across runs.\n");
log("\n"); log("\n");
log(" -markgroups\n");
log(" set a 'abcgroup' attribute on all objects created by ABC. The value of\n");
log(" this attribute is a unique integer for each ABC process started. This\n");
log(" is useful for debugging the partitioning of clock domains.\n");
log("\n");
log(" -box <file>\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. Use with -lut.\n");
log("\n"); log("\n");
@ -914,8 +815,8 @@ struct Abc9Pass : public Pass {
log("internally. This is not going to \"run ABC on your design\". It will instead run\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("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("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("design as an XAIGER file with `write_xaiger' and then load that into ABC\n");
log("you want to use ABC to convert your design into another format.\n"); log("externally if you want to use ABC to convert your design into another format.\n");
log("\n"); log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
log("\n"); log("\n");
@ -925,8 +826,6 @@ struct Abc9Pass : public Pass {
log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\n"); log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\n");
log_push(); log_push();
assign_map.clear();
#ifdef ABCEXTERNAL #ifdef ABCEXTERNAL
std::string exe_file = ABCEXTERNAL; std::string exe_file = ABCEXTERNAL;
#else #else
@ -934,11 +833,10 @@ struct Abc9Pass : public Pass {
#endif #endif
std::string script_file, clk_str, box_file, lut_file; std::string script_file, clk_str, box_file, lut_file;
std::string delay_target, lutin_shared = "-S 1", wire_delay; std::string delay_target, lutin_shared = "-S 1", wire_delay;
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true; bool fast_mode = false, dff_mode = false, cleanup = true;
bool show_tempdir = false; bool show_tempdir = false;
bool nomfs = false; bool nomfs = false;
vector<int> lut_costs; vector<int> lut_costs;
markgroups = false;
#if 0 #if 0
cleanup = false; cleanup = false;
@ -962,9 +860,9 @@ struct Abc9Pass : public Pass {
lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg); lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg); luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg);
fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode); 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); cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup);
show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir); show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir);
markgroups = design->scratchpad_get_bool("abc9.markgroups", markgroups);
box_file = design->scratchpad_get_string("abc9.box", box_file); box_file = design->scratchpad_get_string("abc9.box", box_file);
if (design->scratchpad.count("abc9.W")) { if (design->scratchpad.count("abc9.W")) {
wire_delay = "-W " + design->scratchpad_get_string("abc9.W"); wire_delay = "-W " + design->scratchpad_get_string("abc9.W");
@ -1010,19 +908,10 @@ struct Abc9Pass : public Pass {
fast_mode = true; fast_mode = true;
continue; continue;
} }
//if (arg == "-dff") { if (arg == "-dff") {
// dff_mode = true; dff_mode = true;
// continue; continue;
//} }
//if (arg == "-clk" && argidx+1 < args.size()) {
// clk_str = args[++argidx];
// dff_mode = true;
// continue;
//}
//if (arg == "-keepff") {
// keepff = true;
// continue;
//}
if (arg == "-nocleanup") { if (arg == "-nocleanup") {
cleanup = false; cleanup = false;
continue; continue;
@ -1031,10 +920,6 @@ struct Abc9Pass : public Pass {
show_tempdir = true; show_tempdir = true;
continue; continue;
} }
if (arg == "-markgroups") {
markgroups = true;
continue;
}
if (arg == "-box" && argidx+1 < args.size()) { if (arg == "-box" && argidx+1 < args.size()) {
box_file = args[++argidx]; box_file = args[++argidx];
continue; continue;
@ -1105,235 +990,65 @@ struct Abc9Pass : public Pass {
if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+') if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+')
box_file = std::string(pwd) + "/" + box_file; box_file = std::string(pwd) + "/" + box_file;
dict<int,IdString> box_lookup; SigMap assign_map;
for (auto m : design->modules()) { CellTypes ct(design);
auto it = m->attributes.find(ID(abc9_box_id)); for (auto module : design->selected_modules())
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);
RTLIL::Wire *carry_in = nullptr, *carry_out = nullptr;
for (auto p : m->ports) {
auto w = m->wire(p);
log_assert(w);
if (w->attributes.count(ID(abc9_carry))) {
if (w->port_input) {
if (carry_in)
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
carry_in = w;
}
else if (w->port_output) {
if (carry_out)
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
carry_out = w;
}
}
}
if (carry_in || carry_out) {
if (carry_in && !carry_out)
log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m));
if (!carry_in && carry_out)
log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m));
// Make carry_in the last PI, and carry_out the last PO
// since ABC requires it this way
auto &ports = m->ports;
for (auto it = ports.begin(); it != ports.end(); ) {
RTLIL::Wire* w = m->wire(*it);
log_assert(w);
if (w == carry_in || w == carry_out) {
it = ports.erase(it);
continue;
}
if (w->port_id > carry_in->port_id)
--w->port_id;
if (w->port_id > carry_out->port_id)
--w->port_id;
log_assert(w->port_input || w->port_output);
log_assert(ports[w->port_id-1] == w->name);
++it;
}
ports.push_back(carry_in->name);
carry_in->port_id = ports.size();
ports.push_back(carry_out->name);
carry_out->port_id = ports.size();
}
}
for (auto mod : design->selected_modules())
{ {
if (mod->attributes.count(ID(abc9_box_id))) if (module->processes.size() > 0) {
continue; log("Skipping module %s as it contains processes.\n", log_id(module));
if (mod->processes.size() > 0) {
log("Skipping module %s as it contains processes.\n", log_id(mod));
continue; continue;
} }
log_assert(!module->attributes.count(ID(abc9_box_id)));
assign_map.set(mod); if (!design->selected_whole_module(module))
log_error("Can't handle partially selected module %s!\n", log_id(module));
if (!dff_mode || !clk_str.empty()) { assign_map.set(module);
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, dff_mode, clk_str, keepff,
delay_target, lutin_shared, fast_mode, show_tempdir,
box_file, lut_file, wire_delay, box_lookup, nomfs);
continue;
}
CellTypes ct(design); typedef SigSpec clkdomain_t;
dict<clkdomain_t, int> clk_to_mergeability;
std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); if (dff_mode)
std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); for (auto cell : module->cells()) {
if (cell->type != "$__ABC9_FF_")
continue;
std::set<RTLIL::Cell*> expand_queue, next_expand_queue; Wire *abc9_clock_wire = module->wire(stringf("%s.clock", cell->name.c_str()));
std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; if (abc9_clock_wire == NULL)
std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; 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);
typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; clkdomain_t key(abc9_clock);
std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells;
std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse;
std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; auto r = clk_to_mergeability.insert(std::make_pair(abc9_clock, clk_to_mergeability.size() + 1));
std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; auto r2 YS_ATTRIBUTE(unused) = cell->attributes.insert(std::make_pair(ID(abc9_mergeability), r.first->second));
log_assert(r2.second);
for (auto cell : all_cells) Wire *abc9_init_wire = module->wire(stringf("%s.init", cell->name.c_str()));
{ if (abc9_init_wire == NULL)
clkdomain_t key; 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);
for (auto &conn : cell->connections()) SigSpec abc9_init = assign_map(abc9_init_wire);
for (auto bit : conn.second) { if (!abc9_init.is_fully_const())
bit = assign_map(bit); log_error("'%s.init' is not a constant wire present in module '%s'.\n", cell->name.c_str(), log_id(module));
if (bit.wire != nullptr) { r2 = cell->attributes.insert(std::make_pair(ID(abc9_init), abc9_init.as_const()));
cell_to_bit[cell].insert(bit); log_assert(r2.second);
bit_to_cell[bit].insert(cell); }
if (ct.cell_input(cell->type, conn.first)) { else
cell_to_bit_up[cell].insert(bit); for (auto cell : module->cells()) {
bit_to_cell_down[bit].insert(cell); auto inst_module = design->module(cell->type);
} if (!inst_module || !inst_module->get_bool_attribute("\\abc9_flop"))
if (ct.cell_output(cell->type, conn.first)) { continue;
cell_to_bit_down[cell].insert(bit); cell->set_bool_attribute("\\abc9_keep");
bit_to_cell_up[bit].insert(cell);
}
}
} }
if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) design->selected_active_module = module->name.str();
{ abc9_module(design, module, script_file, exe_file, cleanup, lut_costs, dff_mode,
key = clkdomain_t(cell->type == ID($_DFF_P_), assign_map(cell->getPort(ID(C))), true, RTLIL::SigSpec()); delay_target, lutin_shared, fast_mode, show_tempdir,
} box_file, lut_file, wire_delay, nomfs);
else design->selected_active_module.clear();
if (cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_)))
{
bool this_clk_pol = cell->type.in(ID($_DFFE_PN_), ID($_DFFE_PP_));
bool this_en_pol = cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_));
key = clkdomain_t(this_clk_pol, assign_map(cell->getPort(ID(C))), this_en_pol, assign_map(cell->getPort(ID(E))));
}
else
continue;
unassigned_cells.erase(cell);
expand_queue.insert(cell);
expand_queue_up.insert(cell);
expand_queue_down.insert(cell);
assigned_cells[key].push_back(cell);
assigned_cells_reverse[cell] = key;
}
while (!expand_queue_up.empty() || !expand_queue_down.empty())
{
if (!expand_queue_up.empty())
{
RTLIL::Cell *cell = *expand_queue_up.begin();
clkdomain_t key = assigned_cells_reverse.at(cell);
expand_queue_up.erase(cell);
for (auto bit : cell_to_bit_up[cell])
for (auto c : bit_to_cell_up[bit])
if (unassigned_cells.count(c)) {
unassigned_cells.erase(c);
next_expand_queue_up.insert(c);
assigned_cells[key].push_back(c);
assigned_cells_reverse[c] = key;
expand_queue.insert(c);
}
}
if (!expand_queue_down.empty())
{
RTLIL::Cell *cell = *expand_queue_down.begin();
clkdomain_t key = assigned_cells_reverse.at(cell);
expand_queue_down.erase(cell);
for (auto bit : cell_to_bit_down[cell])
for (auto c : bit_to_cell_down[bit])
if (unassigned_cells.count(c)) {
unassigned_cells.erase(c);
next_expand_queue_up.insert(c);
assigned_cells[key].push_back(c);
assigned_cells_reverse[c] = key;
expand_queue.insert(c);
}
}
if (expand_queue_up.empty() && expand_queue_down.empty()) {
expand_queue_up.swap(next_expand_queue_up);
expand_queue_down.swap(next_expand_queue_down);
}
}
while (!expand_queue.empty())
{
RTLIL::Cell *cell = *expand_queue.begin();
clkdomain_t key = assigned_cells_reverse.at(cell);
expand_queue.erase(cell);
for (auto bit : cell_to_bit.at(cell)) {
for (auto c : bit_to_cell[bit])
if (unassigned_cells.count(c)) {
unassigned_cells.erase(c);
next_expand_queue.insert(c);
assigned_cells[key].push_back(c);
assigned_cells_reverse[c] = key;
}
bit_to_cell[bit].clear();
}
if (expand_queue.empty())
expand_queue.swap(next_expand_queue);
}
clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec());
for (auto cell : unassigned_cells) {
assigned_cells[key].push_back(cell);
assigned_cells_reverse[cell] = key;
}
log_header(design, "Summary of detected clock domains:\n");
for (auto &it : assigned_cells)
log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second),
std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)),
std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first)));
for (auto &it : assigned_cells) {
clk_polarity = std::get<0>(it.first);
clk_sig = assign_map(std::get<1>(it.first));
en_polarity = std::get<2>(it.first);
en_sig = assign_map(std::get<3>(it.first));
abc9_module(design, mod, script_file, exe_file, cleanup, lut_costs, !clk_sig.empty(), "$",
keepff, delay_target, lutin_shared, fast_mode, show_tempdir,
box_file, lut_file, wire_delay, box_lookup, nomfs);
assign_map.set(mod);
}
} }
assign_map.clear();
log_pop(); log_pop();
} }
} Abc9Pass; } Abc9Pass;

View File

@ -1,43 +1,36 @@
# NB: Inputs/Outputs must be ordered alphabetically # NB: Box inputs/outputs must each be in the same order
# (with exceptions for carry in/out) # as their corresponding module definition
# (with exceptions detailed below)
# Box 1 : CCU2C (2xCARRY + 2xLUT4) # Box 1 : CCU2C (2xCARRY + 2xLUT4)
# Outputs: S0, S1, COUT # (Exception: carry chain input/output must be the
# (NB: carry chain input/output must be last # last input and output and the entire bus has been
# input/output and bus has been moved # moved there overriding the otherwise
# there overriding the otherwise
# alphabetical ordering) # alphabetical ordering)
# name ID w/b ins outs # name ID w/b ins outs
CCU2C 1 1 9 3 CCU2C 1 1 9 3
#A0 B0 C0 D0 A1 B1 C1 D1 CIN
#A0 A1 B0 B1 C0 C1 D0 D1 CIN 379 379 275 141 - - - - 257 # S0
379 - 379 - 275 - 141 - 257 630 630 526 392 379 379 275 141 273 # S1
630 379 630 379 526 275 392 141 273 516 516 412 278 516 516 412 278 43 # COUT
516 516 516 516 412 412 278 278 43
# Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram) # Box 2 : TRELLIS_DPR16X4_COMB (16x4 dist ram)
# Outputs: DO0, DO1, DO2, DO3
# name ID w/b ins outs # name ID w/b ins outs
$__ABC9_DPR16X4_COMB 2 0 8 4 $__ABC9_DPR16X4_COMB 2 0 8 4
#$DO0 $DO1 $DO2 $DO3 RAD0 RAD1 RAD2 RAD3
#A0 A1 A2 A3 RAD0 RAD1 RAD2 RAD3 0 0 0 0 141 379 275 379 # DO0
0 0 0 0 141 379 275 379 0 0 0 0 141 379 275 379 # DO1
0 0 0 0 141 379 275 379 0 0 0 0 141 379 275 379 # DO2
0 0 0 0 141 379 275 379 0 0 0 0 141 379 275 379 # DO3
0 0 0 0 141 379 275 379
# Box 3 : PFUMX (MUX2) # Box 3 : PFUMX (MUX2)
# Outputs: Z
# name ID w/b ins outs # name ID w/b ins outs
PFUMX 3 1 3 1 PFUMX 3 1 3 1
#ALUT BLUT C0 #ALUT BLUT C0
98 98 151 98 98 151 # Z
# Box 4 : L6MUX21 (MUX2) # Box 4 : L6MUX21 (MUX2)
# Outputs: Z
# name ID w/b ins outs # name ID w/b ins outs
L6MUX21 4 1 3 1 L6MUX21 4 1 3 1
#D0 D1 SD #D0 D1 SD
140 141 148 140 141 148 # Z

View File

@ -1,24 +1,27 @@
// --------------------------------------- // ---------------------------------------
// Attach a (combinatorial) black-box onto the output
// of this LUTRAM primitive to capture its
// asynchronous read behaviour
module TRELLIS_DPR16X4 ( module TRELLIS_DPR16X4 (
input [3:0] DI, (* techmap_autopurge *) input [3:0] DI,
input [3:0] WAD, (* techmap_autopurge *) input [3:0] WAD,
input WRE, (* techmap_autopurge *) input WRE,
input WCK, (* techmap_autopurge *) input WCK,
input [3:0] RAD, (* techmap_autopurge *) input [3:0] RAD,
output [3:0] DO output [3:0] DO
); );
parameter WCKMUX = "WCK"; parameter WCKMUX = "WCK";
parameter WREMUX = "WRE"; parameter WREMUX = "WRE";
parameter [63:0] INITVAL = 64'h0000000000000000; parameter [63:0] INITVAL = 64'h0000000000000000;
wire [3:0] \$DO ; wire [3:0] $DO;
TRELLIS_DPR16X4 #( TRELLIS_DPR16X4 #(
.WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL) .WCKMUX(WCKMUX), .WREMUX(WREMUX), .INITVAL(INITVAL)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK), .DI(DI), .WAD(WAD), .WRE(WRE), .WCK(WCK),
.RAD(RAD), .DO(\$DO ) .RAD(RAD), .DO($DO)
); );
\$__ABC9_DPR16X4_COMB do (.A(\$DO ), .S(RAD), .Y(DO)); $__ABC9_DPR16X4_COMB do (.$DO($DO), .RAD(RAD), .DO(DO));
endmodule endmodule

View File

@ -1,5 +1,5 @@
// --------------------------------------- // ---------------------------------------
(* abc9_box_id=2 *) (* abc9_box_id=2 *)
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y); module \$__ABC9_DPR16X4_COMB (input [3:0] $DO, RAD, output [3:0] DO);
endmodule endmodule

View File

@ -1,5 +1,5 @@
// --------------------------------------- // ---------------------------------------
module \$__ABC9_DPR16X4_COMB (input [3:0] A, S, output [3:0] Y); module \$__ABC9_DPR16X4_COMB (input [3:0] $DO, RAD, output [3:0] DO);
assign Y = A; assign DO = $DO;
endmodule endmodule

View File

@ -1,13 +1,17 @@
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt # From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
# NB: Inputs/Outputs must be ordered alphabetically # NB: Box inputs/outputs must each be in the same order
# (with exceptions for carry in/out) # as their corresponding module definition
# (with exceptions detailed below)
# Inputs: A B I0 I3 CI # Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
# Outputs: O CO # SB_LUT4+SB_CARRY)
# (NB: carry chain input/output must be last # (Exception: carry chain input/output must be the
# input/output and have been moved there # last input and output and the entire bus has been
# overriding the alphabetical ordering) # moved there overriding the otherwise
$__ICE40_CARRY_WRAPPER 1 1 5 2 # alphabetical ordering)
400 379 449 316 316 # name ID w/b ins outs
259 231 - - 126 $__ICE40_CARRY_WRAPPER 1 1 5 2
#A B I0 I3 CI
400 379 449 316 316 # O
259 231 - - 126 # CO

View File

@ -1,13 +1,17 @@
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt # From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt
# NB: Inputs/Outputs must be ordered alphabetically # NB: Box inputs/outputs must each be in the same order
# (with exceptions for carry in/out) # as their corresponding module definition
# (with exceptions detailed below)
# Inputs: A B I0 I3 CI # Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
# Outputs: O CO # SB_LUT4+SB_CARRY)
# (NB: carry chain input/output must be last # (Exception: carry chain input/output must be the
# input/output and have been moved there # last input and output and the entire bus has been
# overriding the alphabetical ordering) # moved there overriding the otherwise
$__ICE40_CARRY_WRAPPER 1 1 5 2 # alphabetical ordering)
589 558 661 465 465 # name ID w/b ins outs
675 609 - - 186 $__ICE40_CARRY_WRAPPER 1 1 5 2
#A B I0 I3 CI
589 558 661 465 465 # O
675 609 - - 186 # CO

View File

@ -1,13 +1,18 @@
# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt # From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
# NB: Inputs/Outputs must be ordered alphabetically # NB: Box inputs/outputs must each be in the same order
# (with exceptions for carry in/out) # as their corresponding module definition
# (with exceptions detailed below)
# Inputs: A B I0 I3 CI # Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve
# Outputs: O CO # SB_LUT4+SB_CARRY)
# (NB: carry chain input/output must be last # Outputs: O, CO
# input/output and have been moved there # (Exception: carry chain input/output must be the
# overriding the alphabetical ordering) # last input and output and the entire bus has been
$__ICE40_CARRY_WRAPPER 1 1 5 2 # moved there overriding the otherwise
1231 1205 1285 874 874 # alphabetical ordering)
675 609 - - 278 # name ID w/b ins outs
$__ICE40_CARRY_WRAPPER 1 1 5 2
#A B I0 I3 CI
1231 1205 1285 874 874 # O
675 609 - - 278 # CO

View File

@ -18,8 +18,366 @@
* *
*/ */
// ============================================================================ // The following techmapping rules are intended to be run (with -max_iter 1)
// before invoking the `abc9` pass in order to transform the design into
// a format that it understands.
`ifdef DFF_MODE
// For example, (complex) flip-flops are expected to be described as an
// combinatorial box (containing all control logic such as clock enable
// or synchronous resets) followed by a basic D-Q flop.
// Yosys will automatically analyse the simulation model (described in
// cells_sim.v) and detach any $_DFF_P_ or $_DFF_N_ cells present in
// order to extract the combinatorial control logic left behind.
// Specifically, a simulation model similar to the one below:
//
// ++===================================++
// || Sim model ||
// || /\/\/\/\ ||
// D -->>-----< > +------+ ||
// R -->>-----< Comb. > |$_DFF_| ||
// CE -->>-----< logic >-----| [NP]_|---+---->>-- Q
// || +--< > +------+ | ||
// || | \/\/\/\/ | ||
// || | | ||
// || +----------------------------+ ||
// || ||
// ++===================================++
//
// is transformed into:
//
// ++==================++
// || Comb box ||
// || ||
// || /\/\/\/\ ||
// D -->>-----< > ||
// R -->>-----< Comb. > || +-----------+
// CE -->>-----< logic >--->>-- $Q --|$__ABC9_FF_|--+-->> Q
// abc9_ff.Q +-->>-----< > || +-----------+ |
// | || \/\/\/\/ || |
// | || || |
// | ++==================++ |
// | |
// +-----------------------------------------------+
//
// The purpose of the following FD* rules are to wrap the flop with:
// (a) a special $__ABC9_FF_ in front of the FD*'s output, indicating to abc9
// the connectivity of its basic D-Q flop
// (b) an optional $__ABC9_ASYNC_ cell in front of $__ABC_FF_'s output to
// capture asynchronous behaviour
// (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
// (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);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
parameter [0:0] IS_R_INVERTED = 1'b0;
wire QQ, $Q;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDSE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.IS_S_INVERTED(IS_R_INVERTED)
) _TECHMAP_REPLACE_ (
.D(~D), .Q($Q), .C(C), .CE(CE), .S(R)
);
end
else begin
assign Q = QQ;
FDRE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.IS_R_INVERTED(IS_R_INVERTED)
) _TECHMAP_REPLACE_ (
.D(D), .Q($Q), .C(C), .CE(CE), .R(R)
);
end
endgenerate
$__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);
parameter [0:0] INIT = 1'b0;
wire QQ, $Q;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDSE_1 #(
.INIT(1'b0)
) _TECHMAP_REPLACE_ (
.D(~D), .Q($Q), .C(C), .CE(CE), .S(R)
);
end
else begin
assign Q = QQ;
FDRE_1 #(
.INIT(1'b0)
) _TECHMAP_REPLACE_ (
.D(D), .Q($Q), .C(C), .CE(CE), .R(R)
);
end
endgenerate
$__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);
parameter [0:0] INIT = 1'b1;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
parameter [0:0] IS_S_INVERTED = 1'b0;
wire QQ, $Q;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDRE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.IS_R_INVERTED(IS_S_INVERTED)
) _TECHMAP_REPLACE_ (
.D(~D), .Q($Q), .C(C), .CE(CE), .R(S)
);
end
else begin
assign Q = QQ;
FDSE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.IS_S_INVERTED(IS_S_INVERTED)
) _TECHMAP_REPLACE_ (
.D(D), .Q($Q), .C(C), .CE(CE), .S(S)
);
end endgenerate
$__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);
parameter [0:0] INIT = 1'b1;
wire QQ, $Q;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDRE_1 #(
.INIT(1'b0)
) _TECHMAP_REPLACE_ (
.D(~D), .Q($Q), .C(C), .CE(CE), .R(S)
);
end
else begin
assign Q = QQ;
FDSE_1 #(
.INIT(1'b0)
) _TECHMAP_REPLACE_ (
.D(D), .Q($Q), .C(C), .CE(CE), .S(S)
);
end endgenerate
$__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);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
parameter [0:0] IS_CLR_INVERTED = 1'b0;
wire QQ, $Q, $QQ;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDPE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.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
);
// 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));
end
else begin
assign Q = QQ;
FDCE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.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
);
// 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_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);
parameter [0:0] INIT = 1'b0;
wire QQ, $Q, $QQ;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDPE_1 #(
.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
);
$__ABC9_ASYNC1 abc_async (.A($QQ), .S(CLR), .Y(QQ));
end
else begin
assign Q = QQ;
FDCE_1 #(
.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
);
$__ABC9_ASYNC0 abc_async (.A($QQ), .S(CLR), .Y(QQ));
end endgenerate
$__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);
parameter [0:0] INIT = 1'b1;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
parameter [0:0] IS_PRE_INVERTED = 1'b0;
wire QQ, $Q, $QQ;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDCE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.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
);
$__ABC9_ASYNC0 abc_async (.A($QQ), .S(PRE ^ IS_PRE_INVERTED), .Y(QQ));
end
else begin
assign Q = QQ;
FDPE #(
.INIT(1'b0),
.IS_C_INVERTED(IS_C_INVERTED),
.IS_D_INVERTED(IS_D_INVERTED),
.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
);
$__ABC9_ASYNC1 abc_async (.A($QQ), .S(PRE ^ IS_PRE_INVERTED), .Y(QQ));
end endgenerate
$__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);
parameter [0:0] INIT = 1'b1;
wire QQ, $Q, $QQ;
generate if (INIT == 1'b1) begin
assign Q = ~QQ;
FDCE_1 #(
.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
);
$__ABC9_ASYNC0 abc_async (.A($QQ), .S(PRE), .Y(QQ));
end
else begin
assign Q = QQ;
FDPE_1 #(
.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
);
$__ABC9_ASYNC1 abc_async (.A($QQ), .S(PRE), .Y(QQ));
end endgenerate
$__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
// Attach a (combinatorial) black-box onto the output
// of thes LUTRAM primitives to capture their
// asynchronous read behaviour
module RAM32X1D ( module RAM32X1D (
output DPO, SPO, output DPO, SPO,
(* techmap_autopurge *) input D, (* techmap_autopurge *) input D,
@ -30,17 +388,17 @@ module RAM32X1D (
); );
parameter INIT = 32'h0; parameter INIT = 32'h0;
parameter IS_WCLK_INVERTED = 1'b0; parameter IS_WCLK_INVERTED = 1'b0;
wire \$DPO , \$SPO ; wire $DPO, $SPO;
RAM32X1D #( RAM32X1D #(
.INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED) .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.DPO(\$DPO ), .SPO(\$SPO ), .DPO($DPO), .SPO($SPO),
.D(D), .WCLK(WCLK), .WE(WE), .D(D), .WCLK(WCLK), .WE(WE),
.A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), .A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4),
.DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4) .DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4)
); );
\$__ABC9_LUT6 spo (.A(\$SPO ), .S({1'b1, A4, A3, A2, A1, A0}), .Y(SPO)); $__ABC9_LUT6 spo (.A($SPO), .S({1'b1, A4, A3, A2, A1, A0}), .Y(SPO));
\$__ABC9_LUT6 dpo (.A(\$DPO ), .S({1'b1, DPRA4, DPRA3, DPRA2, DPRA1, DPRA0}), .Y(DPO)); $__ABC9_LUT6 dpo (.A($DPO), .S({1'b1, DPRA4, DPRA3, DPRA2, DPRA1, DPRA0}), .Y(DPO));
endmodule endmodule
module RAM64X1D ( module RAM64X1D (
@ -53,17 +411,17 @@ module RAM64X1D (
); );
parameter INIT = 64'h0; parameter INIT = 64'h0;
parameter IS_WCLK_INVERTED = 1'b0; parameter IS_WCLK_INVERTED = 1'b0;
wire \$DPO , \$SPO ; wire $DPO, $SPO;
RAM64X1D #( RAM64X1D #(
.INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED) .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.DPO(\$DPO ), .SPO(\$SPO ), .DPO($DPO), .SPO($SPO),
.D(D), .WCLK(WCLK), .WE(WE), .D(D), .WCLK(WCLK), .WE(WE),
.A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), .A5(A5), .A0(A0), .A1(A1), .A2(A2), .A3(A3), .A4(A4), .A5(A5),
.DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4), .DPRA5(DPRA5) .DPRA0(DPRA0), .DPRA1(DPRA1), .DPRA2(DPRA2), .DPRA3(DPRA3), .DPRA4(DPRA4), .DPRA5(DPRA5)
); );
\$__ABC9_LUT6 spo (.A(\$SPO ), .S({A5, A4, A3, A2, A1, A0}), .Y(SPO)); $__ABC9_LUT6 spo (.A($SPO), .S({A5, A4, A3, A2, A1, A0}), .Y(SPO));
\$__ABC9_LUT6 dpo (.A(\$DPO ), .S({DPRA5, DPRA4, DPRA3, DPRA2, DPRA1, DPRA0}), .Y(DPO)); $__ABC9_LUT6 dpo (.A($DPO), .S({DPRA5, DPRA4, DPRA3, DPRA2, DPRA1, DPRA0}), .Y(DPO));
endmodule endmodule
module RAM128X1D ( module RAM128X1D (
@ -75,17 +433,17 @@ module RAM128X1D (
); );
parameter INIT = 128'h0; parameter INIT = 128'h0;
parameter IS_WCLK_INVERTED = 1'b0; parameter IS_WCLK_INVERTED = 1'b0;
wire \$DPO , \$SPO ; wire $DPO, $SPO;
RAM128X1D #( RAM128X1D #(
.INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED) .INIT(INIT), .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.DPO(\$DPO ), .SPO(\$SPO ), .DPO($DPO), .SPO($SPO),
.D(D), .WCLK(WCLK), .WE(WE), .D(D), .WCLK(WCLK), .WE(WE),
.A(A), .A(A),
.DPRA(DPRA) .DPRA(DPRA)
); );
\$__ABC9_LUT7 spo (.A(\$SPO ), .S(A), .Y(SPO)); $__ABC9_LUT7 spo (.A($SPO), .S(A), .Y(SPO));
\$__ABC9_LUT7 dpo (.A(\$DPO ), .S(DPRA), .Y(DPO)); $__ABC9_LUT7 dpo (.A($DPO), .S(DPRA), .Y(DPO));
endmodule endmodule
module RAM32M ( module RAM32M (
@ -109,24 +467,24 @@ module RAM32M (
parameter [63:0] INIT_C = 64'h0000000000000000; parameter [63:0] INIT_C = 64'h0000000000000000;
parameter [63:0] INIT_D = 64'h0000000000000000; parameter [63:0] INIT_D = 64'h0000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0; parameter [0:0] IS_WCLK_INVERTED = 1'b0;
wire [1:0] \$DOA , \$DOB , \$DOC , \$DOD ; wire [1:0] $DOA, $DOB, $DOC, $DOD;
RAM32M #( RAM32M #(
.INIT_A(INIT_A), .INIT_B(INIT_B), .INIT_C(INIT_C), .INIT_D(INIT_D), .INIT_A(INIT_A), .INIT_B(INIT_B), .INIT_C(INIT_C), .INIT_D(INIT_D),
.IS_WCLK_INVERTED(IS_WCLK_INVERTED) .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.DOA(\$DOA ), .DOB(\$DOB ), .DOC(\$DOC ), .DOD(\$DOD ), .DOA($DOA), .DOB($DOB), .DOC($DOC), .DOD($DOD),
.WCLK(WCLK), .WE(WE), .WCLK(WCLK), .WE(WE),
.ADDRA(ADDRA), .ADDRB(ADDRB), .ADDRC(ADDRC), .ADDRD(ADDRD), .ADDRA(ADDRA), .ADDRB(ADDRB), .ADDRC(ADDRC), .ADDRD(ADDRD),
.DIA(DIA), .DIB(DIB), .DIC(DIC), .DID(DID) .DIA(DIA), .DIB(DIB), .DIC(DIC), .DID(DID)
); );
\$__ABC9_LUT6 doa0 (.A(\$DOA [0]), .S({1'b1, ADDRA}), .Y(DOA[0])); $__ABC9_LUT6 doa0 (.A($DOA[0]), .S({1'b1, ADDRA}), .Y(DOA[0]));
\$__ABC9_LUT6 doa1 (.A(\$DOA [1]), .S({1'b1, ADDRA}), .Y(DOA[1])); $__ABC9_LUT6 doa1 (.A($DOA[1]), .S({1'b1, ADDRA}), .Y(DOA[1]));
\$__ABC9_LUT6 dob0 (.A(\$DOB [0]), .S({1'b1, ADDRB}), .Y(DOB[0])); $__ABC9_LUT6 dob0 (.A($DOB[0]), .S({1'b1, ADDRB}), .Y(DOB[0]));
\$__ABC9_LUT6 dob1 (.A(\$DOB [1]), .S({1'b1, ADDRB}), .Y(DOB[1])); $__ABC9_LUT6 dob1 (.A($DOB[1]), .S({1'b1, ADDRB}), .Y(DOB[1]));
\$__ABC9_LUT6 doc0 (.A(\$DOC [0]), .S({1'b1, ADDRC}), .Y(DOC[0])); $__ABC9_LUT6 doc0 (.A($DOC[0]), .S({1'b1, ADDRC}), .Y(DOC[0]));
\$__ABC9_LUT6 doc1 (.A(\$DOC [1]), .S({1'b1, ADDRC}), .Y(DOC[1])); $__ABC9_LUT6 doc1 (.A($DOC[1]), .S({1'b1, ADDRC}), .Y(DOC[1]));
\$__ABC9_LUT6 dod0 (.A(\$DOD [0]), .S({1'b1, ADDRD}), .Y(DOD[0])); $__ABC9_LUT6 dod0 (.A($DOD[0]), .S({1'b1, ADDRD}), .Y(DOD[0]));
\$__ABC9_LUT6 dod1 (.A(\$DOD [1]), .S({1'b1, ADDRD}), .Y(DOD[1])); $__ABC9_LUT6 dod1 (.A($DOD[1]), .S({1'b1, ADDRD}), .Y(DOD[1]));
endmodule endmodule
module RAM64M ( module RAM64M (
@ -150,20 +508,20 @@ module RAM64M (
parameter [63:0] INIT_C = 64'h0000000000000000; parameter [63:0] INIT_C = 64'h0000000000000000;
parameter [63:0] INIT_D = 64'h0000000000000000; parameter [63:0] INIT_D = 64'h0000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0; parameter [0:0] IS_WCLK_INVERTED = 1'b0;
wire \$DOA , \$DOB , \$DOC , \$DOD ; wire $DOA, $DOB, $DOC, $DOD;
RAM64M #( RAM64M #(
.INIT_A(INIT_A), .INIT_B(INIT_B), .INIT_C(INIT_C), .INIT_D(INIT_D), .INIT_A(INIT_A), .INIT_B(INIT_B), .INIT_C(INIT_C), .INIT_D(INIT_D),
.IS_WCLK_INVERTED(IS_WCLK_INVERTED) .IS_WCLK_INVERTED(IS_WCLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.DOA(\$DOA ), .DOB(\$DOB ), .DOC(\$DOC ), .DOD(\$DOD ), .DOA($DOA), .DOB($DOB), .DOC($DOC), .DOD($DOD),
.WCLK(WCLK), .WE(WE), .WCLK(WCLK), .WE(WE),
.ADDRA(ADDRA), .ADDRB(ADDRB), .ADDRC(ADDRC), .ADDRD(ADDRD), .ADDRA(ADDRA), .ADDRB(ADDRB), .ADDRC(ADDRC), .ADDRD(ADDRD),
.DIA(DIA), .DIB(DIB), .DIC(DIC), .DID(DID) .DIA(DIA), .DIB(DIB), .DIC(DIC), .DID(DID)
); );
\$__ABC9_LUT6 doa (.A(\$DOA ), .S(ADDRA), .Y(DOA)); $__ABC9_LUT6 doa (.A($DOA), .S(ADDRA), .Y(DOA));
\$__ABC9_LUT6 dob (.A(\$DOB ), .S(ADDRB), .Y(DOB)); $__ABC9_LUT6 dob (.A($DOB), .S(ADDRB), .Y(DOB));
\$__ABC9_LUT6 doc (.A(\$DOC ), .S(ADDRC), .Y(DOC)); $__ABC9_LUT6 doc (.A($DOC), .S(ADDRC), .Y(DOC));
\$__ABC9_LUT6 dod (.A(\$DOD ), .S(ADDRD), .Y(DOD)); $__ABC9_LUT6 dod (.A($DOD), .S(ADDRD), .Y(DOD));
endmodule endmodule
module SRL16E ( module SRL16E (
@ -172,14 +530,14 @@ module SRL16E (
); );
parameter [15:0] INIT = 16'h0000; parameter [15:0] INIT = 16'h0000;
parameter [0:0] IS_CLK_INVERTED = 1'b0; parameter [0:0] IS_CLK_INVERTED = 1'b0;
wire \$Q ; wire $Q;
SRL16E #( SRL16E #(
.INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED) .INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.Q(\$Q ), .Q($Q),
.A0(A0), .A1(A1), .A2(A2), .A3(A3), .CE(CE), .CLK(CLK), .D(D) .A0(A0), .A1(A1), .A2(A2), .A3(A3), .CE(CE), .CLK(CLK), .D(D)
); );
\$__ABC9_LUT6 q (.A(\$Q ), .S({1'b1, A3, A2, A1, A0, 1'b1}), .Y(Q)); $__ABC9_LUT6 q (.A($Q), .S({1'b1, A3, A2, A1, A0, 1'b1}), .Y(Q));
endmodule endmodule
module SRLC32E ( module SRLC32E (
@ -190,14 +548,14 @@ module SRLC32E (
); );
parameter [31:0] INIT = 32'h00000000; parameter [31:0] INIT = 32'h00000000;
parameter [0:0] IS_CLK_INVERTED = 1'b0; parameter [0:0] IS_CLK_INVERTED = 1'b0;
wire \$Q ; wire $Q;
SRLC32E #( SRLC32E #(
.INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED) .INIT(INIT), .IS_CLK_INVERTED(IS_CLK_INVERTED)
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.Q(\$Q ), .Q31(Q31), .Q($Q), .Q31(Q31),
.A(A), .CE(CE), .CLK(CLK), .D(D) .A(A), .CE(CE), .CLK(CLK), .D(D)
); );
\$__ABC9_LUT6 q (.A(\$Q ), .S({1'b1, A}), .Y(Q)); $__ABC9_LUT6 q (.A($Q), .S({1'b1, A}), .Y(Q));
endmodule endmodule
module DSP48E1 ( module DSP48E1 (

View File

@ -30,7 +30,22 @@ module \$__XILINX_MUXF78 (output O, input I0, I1, I2, I3, S0, S1);
: (S0 ? I1 : I0); : (S0 ? I1 : I0);
endmodule endmodule
// Box to emulate comb/seq behaviour of RAMD{32,64} and SRL{16,32} module \$__ABC9_FF_ (input D, output Q);
endmodule
// Box to emulate async behaviour of FDC*
(* abc9_box_id = 1000, lib_whitebox *)
module \$__ABC9_ASYNC0 (input A, S, output Y);
assign Y = S ? 1'b0 : A;
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;
endmodule
// Box to emulate comb/seq behaviour of RAM{32,64} and SRL{16,32}
// Necessary since RAMD* and SRL* have both combinatorial (i.e. // Necessary since RAMD* and SRL* have both combinatorial (i.e.
// same-cycle read operation) and sequential (write operation // same-cycle read operation) and sequential (write operation
// is only committed on the next clock edge). // is only committed on the next clock edge).
@ -39,7 +54,7 @@ endmodule
(* abc9_box_id=2000 *) (* abc9_box_id=2000 *)
module \$__ABC9_LUT6 (input A, input [5:0] S, output Y); module \$__ABC9_LUT6 (input A, input [5:0] S, output Y);
endmodule endmodule
// Box to emulate comb/seq behaviour of RAMD128 // Box to emulate comb/seq behaviour of RAM128
(* abc9_box_id=2001 *) (* abc9_box_id=2001 *)
module \$__ABC9_LUT7 (input A, input [6:0] S, output Y); module \$__ABC9_LUT7 (input A, input [6:0] S, output Y);
endmodule endmodule

View File

@ -20,6 +20,15 @@
// ============================================================================ // ============================================================================
(* techmap_celltype = "$__ABC9_ASYNC0 $__ABC9_ASYNC1" *)
module $__ABC9_ASYNC01(input A, S, output Y);
assign Y = A;
endmodule
module $__ABC9_FF_(input D, output Q);
assign Q = D;
endmodule
module $__ABC9_LUT6(input A, input [5:0] S, output Y); module $__ABC9_LUT6(input A, input [5:0] S, output Y);
assign Y = A; assign Y = A;
endmodule endmodule

View File

@ -1,64 +1,142 @@
# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf # Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf
# https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf # https://github.com/SymbiFlow/prjxray-db/blob/23c8b0851f979f0799318eaca90174413a46b257/artix7/timings/slicel.sdf
# NB: Inputs/Outputs must be ordered alphabetically # NB: Box inputs/outputs must each be in the same order
# (with exceptions for carry in/out) # as their corresponding module definition
# (with exceptions detailed below)
# Average across F7[AB]MUX # Box 1 : MUXF7
# Inputs: I0 I1 S0 # Max delays from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L451-L453
# Outputs: O # name ID w/b ins outs
MUXF7 1 1 3 1 MUXF7 1 1 3 1
204 208 286 #I0 I1 S0
204 208 286 # O
# Inputs: I0 I1 S0 # Box 2 : MUXF8
# Outputs: O # Max delays from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L462-L464
MUXF8 2 1 3 1 # name ID w/b ins outs
104 94 273 MUXF8 2 1 3 1
#I0 I1 S0
104 94 273 # O
# Box containing MUXF7.[AB] + MUXF8, # Box 3 : $__MUXF78
# Necessary to make these an atomic unit so that # (private cell used to preserve 2xMUXF7 + 1xMUXF8
# ABC cannot optimise just one of the MUXF7 away # an atomic unit so that ABC cannot optimise just
# and expect to save on its delay # one of the MUXF7 away and expect to save on its
# Inputs: I0 I1 I2 I3 S0 S1 # delay, since MUXF8 is only reachable through an
# Outputs: O # MUXF7)
$__MUXF78 3 1 6 1 # name ID w/b ins outs
294 297 311 317 390 273 $__MUXF78 3 1 6 1
#I0 I1 I2 I3 S0 S1
294 297 311 317 390 273 # O
# CARRY4 + CARRY4_[ABCD]X # Box 4 : CARRY4 + CARRY4_[ABCD]X
# Inputs: CYINIT DI0 DI1 DI2 DI3 S0 S1 S2 S3 CI # (Exception: carry chain input/output must be the
# Outputs: O0 O1 O2 O3 CO0 CO1 CO2 CO3 # last input and output and the entire bus has been
# (NB: carry chain input/output must be last
# input/output and the entire bus has been
# moved there overriding the otherwise # moved there overriding the otherwise
# alphabetical ordering) # alphabetical ordering)
CARRY4 4 1 10 8 # Max delays from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L11-L46
482 - - - - 223 - - - 222 # name ID w/b ins outs
598 407 - - - 400 205 - - 334 CARRY4 4 1 10 8
584 556 537 - - 523 558 226 - 239 #CYINIT DI0 DI1 DI2 DI3 S0 S1 S2 S3 CI
642 615 596 438 - 582 618 330 227 313 482 - - - - 223 - - - 222 # O0
536 379 - - - 340 - - - 271 598 407 - - - 400 205 - - 334 # O1
494 465 445 - - 433 469 - - 157 584 556 537 - - 523 558 226 - 239 # O2
592 540 520 356 - 512 548 292 - 228 642 615 596 438 - 582 618 330 227 313 # O3
580 526 507 398 385 508 528 378 380 114 536 379 - - - 340 - - - 271 # CO0
494 465 445 - - 433 469 - - 157 # CO1
592 540 520 356 - 512 548 292 - 228 # CO2
580 526 507 398 385 508 528 378 380 114 # CO3
# Box 1000 : $__ABC9_ASYNC0
# (private cell to emulate async behaviour of FDC*)
# name ID w/b ins outs
$__ABC9_ASYNC0 1000 1 2 1
#A S
0 764 # Y
# Box 1001 : $__ABC9_ASYNC1
# (private cell to emulate async behaviour of FDP*)
# name ID w/b ins outs
$__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 # SLICEM/A6LUT
# Box to emulate comb/seq behaviour of RAMD{32,64} and SRL{16,32} # name ID w/b ins outs
# Necessary since RAMD* and SRL* have both combinatorial (i.e. $__ABC9_LUT6 2000 0 7 1
# same-cycle read operation) and sequential (write operation #A S0 S1 S2 S3 S4 S5
# is only committed on the next clock edge). 0 642 631 472 407 238 127 # Y
# To model the combinatorial path, such cells have to be split
# into comb and seq parts, with this box modelling only the former.
# Inputs: A S0 S1 S2 S3 S4 S5
# Outputs: Y
$__ABC9_LUT6 2000 0 7 1
0 642 631 472 407 238 127
# SLICEM/A6LUT + F7BMUX # Box 2001 : $__ABC9_LUT6
# Box to emulate comb/seq behaviour of RAMD128 # (private cell to emulate async behaviour of LUITRAMs)
# Inputs: A S0 S1 S2 S3 S4 S5 S6 # name ID w/b ins outs
# Outputs: DPO SPO
$__ABC9_LUT7 2001 0 8 1 $__ABC9_LUT7 2001 0 8 1
0 1047 1036 877 812 643 532 478 #A S0 S1 S2 S3 S4 S5 S6
0 1047 1036 877 812 643 532 478 # Y
# Boxes used to represent the comb behaviour of various modes # Boxes used to represent the comb behaviour of various modes
# of DSP48E1 # of DSP48E1

View File

@ -325,6 +325,7 @@ endmodule
// Max delay from: https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf#L238-L250 // 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 *)
module FDRE ( module FDRE (
(* abc9_arrival=303 *) (* abc9_arrival=303 *)
output reg Q, output reg Q,
@ -348,6 +349,20 @@ module FDRE (
endcase endgenerate endcase endgenerate
endmodule endmodule
(* abc9_box_id=1101, lib_whitebox, abc9_flop *)
module FDRE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, 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 *)
module FDSE ( module FDSE (
(* abc9_arrival=303 *) (* abc9_arrival=303 *)
output reg Q, output reg Q,
@ -371,6 +386,19 @@ module FDSE (
endcase endgenerate endcase endgenerate
endmodule endmodule
(* abc9_box_id=1103, lib_whitebox, abc9_flop *)
module FDSE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, S
);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C) if (S) Q <= 1'b1; else if (CE) Q <= D;
endmodule
module FDRSE ( module FDRSE (
output reg Q, output reg Q,
(* clkbuf_sink *) (* clkbuf_sink *)
@ -406,6 +434,7 @@ module FDRSE (
Q <= d; Q <= d;
endmodule endmodule
(* abc9_box_id=1104, lib_whitebox, abc9_flop *)
module FDCE ( module FDCE (
(* abc9_arrival=303 *) (* abc9_arrival=303 *)
output reg Q, output reg Q,
@ -413,10 +442,10 @@ module FDCE (
(* invertible_pin = "IS_C_INVERTED" *) (* invertible_pin = "IS_C_INVERTED" *)
input C, input C,
input CE, input CE,
(* invertible_pin = "IS_D_INVERTED" *)
input D,
(* invertible_pin = "IS_CLR_INVERTED" *) (* invertible_pin = "IS_CLR_INVERTED" *)
input CLR input CLR,
(* invertible_pin = "IS_D_INVERTED" *)
input D
); );
parameter [0:0] INIT = 1'b0; parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0; parameter [0:0] IS_C_INVERTED = 1'b0;
@ -431,6 +460,20 @@ module FDCE (
endcase endgenerate endcase endgenerate
endmodule endmodule
(* abc9_box_id=1105, lib_whitebox, abc9_flop *)
module FDCE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, CLR
);
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 *)
module FDPE ( module FDPE (
(* abc9_arrival=303 *) (* abc9_arrival=303 *)
output reg Q, output reg Q,
@ -456,6 +499,19 @@ module FDPE (
endcase endgenerate endcase endgenerate
endmodule endmodule
(* abc9_box_id=1107, lib_whitebox, abc9_flop *)
module FDPE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, PRE
);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
endmodule
module FDCPE ( module FDCPE (
output wire Q, output wire Q,
(* clkbuf_sink *) (* clkbuf_sink *)
@ -501,54 +557,6 @@ module FDCPE (
assign Q = qs ? qp : qc; assign Q = qs ? qp : qc;
endmodule endmodule
module FDRE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, R
);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C) if (R) Q <= 1'b0; else if(CE) Q <= D;
endmodule
module FDSE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, S
);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C) if (S) Q <= 1'b1; else if(CE) Q <= D;
endmodule
module FDCE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, CLR
);
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
module FDPE_1 (
(* abc9_arrival=303 *)
output reg Q,
(* clkbuf_sink *)
input C,
input CE, D, PRE
);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
endmodule
module LDCE ( module LDCE (
output reg Q, output reg Q,
(* invertible_pin = "IS_CLR_INVERTED" *) (* invertible_pin = "IS_CLR_INVERTED" *)

View File

@ -107,8 +107,12 @@ struct SynthXilinxPass : public ScriptPass
log(" -flatten\n"); log(" -flatten\n");
log(" flatten design before synthesis\n"); log(" flatten design before synthesis\n");
log("\n"); log("\n");
log(" -dff\n");
log(" run 'abc'/'abc9' with -dff option\n");
log("\n");
log(" -retime\n"); log(" -retime\n");
log(" run 'abc' with '-dff -D 1' options\n"); log(" run 'abc' with '-D 1' option to enable flip-flop retiming.\n");
log(" implies -dff.\n");
log("\n"); log("\n");
log(" -abc9\n"); log(" -abc9\n");
log(" use new ABC9 flow (EXPERIMENTAL)\n"); log(" use new ABC9 flow (EXPERIMENTAL)\n");
@ -120,7 +124,8 @@ struct SynthXilinxPass : public ScriptPass
} }
std::string top_opt, edif_file, blif_file, family; std::string top_opt, edif_file, blif_file, family;
bool flatten, retime, vpr, ise, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram, abc9; bool flatten, retime, vpr, ise, noiopad, noclkbuf, nobram, nolutram, nosrl, nocarry, nowidelut, nodsp, uram;
bool abc9, dff_mode;
bool flatten_before_abc; bool flatten_before_abc;
int widemux; int widemux;
@ -145,6 +150,7 @@ struct SynthXilinxPass : public ScriptPass
nodsp = false; nodsp = false;
uram = false; uram = false;
abc9 = false; abc9 = false;
dff_mode = false;
flatten_before_abc = false; flatten_before_abc = false;
widemux = 0; widemux = 0;
} }
@ -190,6 +196,7 @@ struct SynthXilinxPass : public ScriptPass
continue; continue;
} }
if (args[argidx] == "-retime") { if (args[argidx] == "-retime") {
dff_mode = true;
retime = true; retime = true;
continue; continue;
} }
@ -252,6 +259,10 @@ struct SynthXilinxPass : public ScriptPass
uram = true; uram = true;
continue; continue;
} }
if (args[argidx] == "-dff") {
dff_mode = true;
continue;
}
break; break;
} }
extra_args(args, argidx, design); extra_args(args, argidx, design);
@ -287,10 +298,11 @@ struct SynthXilinxPass : public ScriptPass
ff_map_file = "+/xilinx/xc7_ff_map.v"; ff_map_file = "+/xilinx/xc7_ff_map.v";
if (check_label("begin")) { if (check_label("begin")) {
std::string read_args;
if (vpr) if (vpr)
run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v"); read_args += " -D_EXPLICIT_CARRY";
else read_args += " -lib +/xilinx/cells_sim.v";
run("read_verilog -lib +/xilinx/cells_sim.v"); run("read_verilog" + read_args);
run("read_verilog -lib +/xilinx/cells_xtra.v"); run("read_verilog -lib +/xilinx/cells_xtra.v");
@ -532,12 +544,15 @@ struct SynthXilinxPass : public ScriptPass
if (flatten_before_abc) if (flatten_before_abc)
run("flatten"); run("flatten");
if (help_mode) if (help_mode)
run("abc -luts 2:2,3,6:5[,10,20] [-dff]", "(option for 'nowidelut'; option for '-retime')"); run("abc -luts 2:2,3,6:5[,10,20] [-dff] [-D 1]", "(option for 'nowidelut', '-dff', '-retime')");
else if (abc9) { else if (abc9) {
if (family != "xc7") if (family != "xc7")
log_warning("'synth_xilinx -abc9' not currently supported for the '%s' family, " log_warning("'synth_xilinx -abc9' not currently supported for the '%s' family, "
"will use timing for 'xc7' instead.\n", family.c_str()); "will use timing for 'xc7' instead.\n", family.c_str());
run("techmap -map +/xilinx/abc9_map.v -max_iter 1"); std::string techmap_args = "-map +/xilinx/abc9_map.v -max_iter 1";
if (dff_mode)
techmap_args += " -D DFF_MODE";
run("techmap " + techmap_args);
run("read_verilog -icells -lib +/xilinx/abc9_model.v"); run("read_verilog -icells -lib +/xilinx/abc9_model.v");
std::string abc9_opts = " -box +/xilinx/abc9_xc7.box"; std::string abc9_opts = " -box +/xilinx/abc9_xc7.box";
abc9_opts += stringf(" -W %d", XC7_WIRE_DELAY); abc9_opts += stringf(" -W %d", XC7_WIRE_DELAY);
@ -546,13 +561,22 @@ struct SynthXilinxPass : public ScriptPass
abc9_opts += " -lut +/xilinx/abc9_xc7_nowide.lut"; abc9_opts += " -lut +/xilinx/abc9_xc7_nowide.lut";
else else
abc9_opts += " -lut +/xilinx/abc9_xc7.lut"; abc9_opts += " -lut +/xilinx/abc9_xc7.lut";
if (dff_mode)
abc9_opts += " -dff";
run("abc9" + abc9_opts); run("abc9" + abc9_opts);
run("techmap -map +/xilinx/abc9_unmap.v");
} }
else { else {
std::string abc_opts;
if (nowidelut) if (nowidelut)
run("abc -luts 2:2,3,6:5" + string(retime ? " -dff -D 1" : "")); abc_opts += " -luts 2:2,3,6:5";
else else
run("abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff -D 1" : "")); abc_opts += " -luts 2:2,3,6:5,10,20";
if (dff_mode)
abc_opts += " -dff";
if (retime)
abc_opts += " -D 1";
run("abc" + abc_opts);
} }
run("clean"); run("clean");
@ -562,14 +586,11 @@ struct SynthXilinxPass : public ScriptPass
run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')"); run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')");
std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v"; std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v";
if (help_mode) if (help_mode)
techmap_args += " [-map " + ff_map_file + "]"; techmap_args += stringf("[-map %s]", ff_map_file.c_str());
else if (abc9) else if (!abc9)
techmap_args += " -map +/xilinx/abc9_unmap.v"; techmap_args += stringf(" -map %s", ff_map_file.c_str());
else run("techmap " + techmap_args, "(only if '-abc9')");
techmap_args += " -map " + ff_map_file;
run("techmap " + techmap_args);
run("xilinx_dffopt"); run("xilinx_dffopt");
run("clean");
} }
if (check_label("finalize")) { if (check_label("finalize")) {
@ -577,6 +598,7 @@ struct SynthXilinxPass : public ScriptPass
run("clkbufmap -buf BUFG O:I ", "(skip if '-noclkbuf')"); run("clkbufmap -buf BUFG O:I ", "(skip if '-noclkbuf')");
if (help_mode || ise) if (help_mode || ise)
run("extractinv -inv INV O:I", "(only if '-ise')"); run("extractinv -inv INV O:I", "(only if '-ise')");
run("clean");
} }
if (check_label("check")) { if (check_label("check")) {

View File

@ -0,0 +1,32 @@
read_verilog <<EOT
module top(input C, D, output [7:0] Q);
FDRE fd1(.C(C), .CE(1'b1), .D(D), .R(1'b1), .Q(Q[0]));
FDSE fd2(.C(C), .CE(1'b1), .D(D), .S(1'b1), .Q(Q[1]));
FDCE fd3(.C(C), .CE(1'b1), .D(D), .CLR(1'b1), .Q(Q[2]));
FDPE fd4(.C(C), .CE(1'b1), .D(D), .PRE(1'b1), .Q(Q[3]));
FDRE_1 fd5(.C(C), .CE(1'b1), .D(D), .R(1'b1), .Q(Q[4]));
FDSE_1 fd6(.C(C), .CE(1'b1), .D(D), .S(1'b1), .Q(Q[5]));
FDCE_1 fd7(.C(C), .CE(1'b1), .D(D), .CLR(1'b1), .Q(Q[6]));
FDPE_1 fd8(.C(C), .CE(1'b1), .D(D), .PRE(1'b1), .Q(Q[7]));
endmodule
EOT
equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf
design -load postopt
select -assert-none t:FD*
design -reset
read_verilog <<EOT
module top(input C, D, output [7:0] Q);
FDRE fd1(.C(C), .CE(1'b0), .D(D), .R(1'b0), .Q(Q[0]));
FDSE fd2(.C(C), .CE(1'b0), .D(D), .S(1'b0), .Q(Q[1]));
FDCE fd3(.C(C), .CE(1'b0), .D(D), .CLR(1'b0), .Q(Q[2]));
FDPE fd4(.C(C), .CE(1'b0), .D(D), .PRE(1'b0), .Q(Q[3]));
FDRE_1 fd5(.C(C), .CE(1'b0), .D(D), .R(1'b0), .Q(Q[4]));
FDSE_1 fd6(.C(C), .CE(1'b0), .D(D), .S(1'b0), .Q(Q[5]));
FDCE_1 fd7(.C(C), .CE(1'b0), .D(D), .CLR(1'b0), .Q(Q[6]));
FDPE_1 fd8(.C(C), .CE(1'b0), .D(D), .PRE(1'b0), .Q(Q[7]));
endmodule
EOT
equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf
design -load postopt
select -assert-none t:FD*

View File

@ -0,0 +1,91 @@
read_verilog <<EOT
module top(input C, CE, D, R, output [1:0] Q);
FDRE #(.INIT(1'b1)) ff1 (.C(C), .CE(CE), .D(D), .R(R), .Q(Q[0]));
FDRE_1 #(.INIT(1'b1)) ff2 (.C(C), .CE(CE), .D(D), .R(R), .Q(Q[1]));
endmodule
EOT
design -save gold
techmap -map +/xilinx/abc9_map.v -max_iter 1 -D DFF_MODE
techmap -map +/xilinx/abc9_unmap.v
select -assert-count 1 t:FDSE
select -assert-count 1 t:FDSE_1
techmap -autoproc -map +/xilinx/cells_sim.v
design -stash gate
design -import gold -as gold
design -import gate -as gate
techmap -autoproc -map +/xilinx/cells_sim.v
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -seq 2 -verify -prove-asserts -show-ports miter
design -reset
read_verilog <<EOT
module top(input C, CE, D, S, output [1:0] Q);
FDSE #(.INIT(1'b1)) ff1 (.C(C), .CE(CE), .D(D), .S(S), .Q(Q[0]));
FDSE_1 #(.INIT(1'b1)) ff2 (.C(C), .CE(CE), .D(D), .S(S), .Q(Q[1]));
endmodule
EOT
design -save gold
techmap -map +/xilinx/abc9_map.v -max_iter 1 -D DFF_MODE
techmap -map +/xilinx/abc9_unmap.v
select -assert-count 1 t:FDRE
select -assert-count 1 t:FDRE_1
techmap -autoproc -map +/xilinx/cells_sim.v
design -stash gate
design -import gold -as gold
design -import gate -as gate
techmap -autoproc -map +/xilinx/cells_sim.v
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -seq 2 -set-init-zero -verify -prove-asserts -show-ports miter
design -reset
read_verilog <<EOT
module top(input C, CE, D, PRE, output [1:0] Q);
FDPE #(.INIT(1'b1)) ff1 (.C(C), .CE(CE), .D(D), .PRE(PRE), .Q(Q[0]));
FDPE_1 #(.INIT(1'b1)) ff2 (.C(C), .CE(CE), .D(D), .PRE(PRE), .Q(Q[1]));
endmodule
EOT
design -save gold
techmap -map +/xilinx/abc9_map.v -max_iter 1 -D DFF_MODE
techmap -map +/xilinx/abc9_unmap.v
select -assert-count 1 t:FDCE
select -assert-count 1 t:FDCE_1
techmap -autoproc -map +/xilinx/cells_sim.v
design -stash gate
design -import gold -as gold
design -import gate -as gate
techmap -autoproc -map +/xilinx/cells_sim.v
clk2fflogic
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -seq 2 -set-init-zero -verify -prove-asserts -show-ports miter
design -reset
read_verilog <<EOT
module top(input C, CE, D, CLR, output [1:0] Q);
FDCE #(.INIT(1'b1)) ff1 (.C(C), .CE(CE), .D(D), .CLR(CLR), .Q(Q[0]));
FDCE_1 #(.INIT(1'b1)) ff2 (.C(C), .CE(CE), .D(D), .CLR(CLR), .Q(Q[1]));
endmodule
EOT
design -save gold
techmap -map +/xilinx/abc9_map.v -max_iter 1 -D DFF_MODE
techmap -map +/xilinx/abc9_unmap.v
select -assert-count 1 t:FDPE
techmap -autoproc -map +/xilinx/cells_sim.v
design -stash gate
design -import gold -as gold
design -import gate -as gate
techmap -autoproc -map +/xilinx/cells_sim.v
clk2fflogic
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -seq 2 -set-init-zero -verify -prove-asserts -show-ports miter

View File

@ -264,3 +264,30 @@ always @*
if (en) if (en)
q <= d; q <= d;
endmodule endmodule
module abc9_test031(input clk1, clk2, d, output reg q1, q2);
always @(posedge clk1) q1 <= d;
always @(negedge clk2) q2 <= q1;
endmodule
module abc9_test032(input clk, d, r, output reg q);
always @(posedge clk or posedge r)
if (r) q <= 1'b0;
else q <= d;
endmodule
module abc9_test033(input clk, d, r, output reg q);
always @(negedge clk or posedge r)
if (r) q <= 1'b1;
else q <= d;
endmodule
module abc9_test034(input clk, d, output reg q1, q2);
always @(posedge clk) q1 <= d;
always @(posedge clk) q2 <= q1;
endmodule
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

View File

@ -20,10 +20,12 @@ fi
cp ../simple/*.v . cp ../simple/*.v .
cp ../simple/*.sv . cp ../simple/*.sv .
DOLLAR='?' DOLLAR='?'
exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-n 300 -p '\ exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v *.sv EXTRA_FLAGS="-n 300 -p '\
hierarchy; \ hierarchy; \
synth -run coarse; \ synth -run coarse; \
opt -full; \ opt -full; \
techmap; abc9 -lut 4 -box ../abc.box; \ techmap; \
abc9 -lut 4 -box ../abc.box; \
clean; \
check -assert; \ check -assert; \
select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'" select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'"

View File

@ -9,3 +9,10 @@ wire w;
unknown u(~i, w); unknown u(~i, w);
unknown2 u2(w, o); unknown2 u2(w, o);
endmodule endmodule
module abc9_test032(input clk, d, r, output reg q);
initial q = 1'b0;
always @(negedge clk or negedge r)
if (!r) q <= 1'b0;
else q <= d;
endmodule

View File

@ -22,3 +22,19 @@ abc9 -lut 4
select -assert-count 1 t:$lut r:LUT=2'b01 r:WIDTH=1 %i %i select -assert-count 1 t:$lut r:LUT=2'b01 r:WIDTH=1 %i %i
select -assert-count 1 t:unknown select -assert-count 1 t:unknown
select -assert-none t:$lut t:unknown %% t: %D select -assert-none t:$lut t:unknown %% t: %D
design -load read
hierarchy -top abc9_test032
proc
clk2fflogic
design -save gold
abc9 -lut 4
check
design -stash gate
design -import gold -as gold
design -import gate -as gate
miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -seq 10 -verify -prove-asserts -show-ports miter