Merge remote-tracking branch 'origin/eddie/abc9_refactor' into eddie/abc9_required

This commit is contained in:
Eddie Hung 2020-01-08 18:30:20 -08:00
commit 57f6826e29
21 changed files with 1981 additions and 3015 deletions

View File

@ -128,7 +128,7 @@ bumpversion:
# is just a symlink to your actual ABC working directory, as 'make mrproper' # is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally # will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC.. # delete your work on ABC..
ABCREV = 623b5e8 ABCREV = 144c5be
ABCPULL = 1 ABCPULL = 1
ABCURL ?= https://github.com/berkeley-abc/abc ABCURL ?= https://github.com/berkeley-abc/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1 ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1

View File

@ -376,10 +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 - The module attribute ``abc9_flop`` is a boolean marking the module as a
whitebox that describes the synchronous behaviour of a flip-flop. 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

@ -137,7 +137,7 @@ struct XAigerWriter
return a; return a;
} }
XAigerWriter(Module *module) : module(module), sigmap(module) XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module)
{ {
pool<SigBit> undriven_bits; pool<SigBit> undriven_bits;
pool<SigBit> unused_bits; pool<SigBit> unused_bits;
@ -157,12 +157,8 @@ struct XAigerWriter
if (wire->get_bool_attribute(ID::keep)) if (wire->get_bool_attribute(ID::keep))
sigmap.add(wire); sigmap.add(wire);
// First, collect all the ports in port_id order
// since module->wires() could be sorted for (auto wire : module->wires())
// alphabetically
for (auto port : module->ports) {
auto wire = module->wire(port);
log_assert(wire);
for (int i = 0; i < GetSize(wire); i++) for (int i = 0; i < GetSize(wire); i++)
{ {
SigBit wirebit(wire, i); SigBit wirebit(wire, i);
@ -176,6 +172,9 @@ struct XAigerWriter
continue; continue;
} }
undriven_bits.insert(bit);
unused_bits.insert(bit);
if (wire->port_input) if (wire->port_input)
input_bits.insert(bit); input_bits.insert(bit);
@ -185,19 +184,6 @@ struct XAigerWriter
output_bits.insert(wirebit); output_bits.insert(wirebit);
} }
} }
}
for (auto wire : module->wires())
for (int i = 0; i < GetSize(wire); i++)
{
SigBit wirebit(wire, i);
SigBit bit = sigmap(wirebit);
if (bit.wire) {
undriven_bits.insert(bit);
unused_bits.insert(bit);
}
}
for (auto cell : module->cells()) { for (auto cell : module->cells()) {
if (cell->type == "$_NOT_") if (cell->type == "$_NOT_")
@ -332,14 +318,13 @@ struct XAigerWriter
} }
} }
// Fully pad all unused input connections of this box cell with S0
// Fully pad all undriven output connections of this box cell with anonymous wires
for (auto port_name : r.first->second) { for (auto port_name : r.first->second) {
auto w = box_module->wire(port_name); auto w = box_module->wire(port_name);
log_assert(w); log_assert(w);
auto rhs = cell->getPort(port_name);
SigSpec rhs = cell->connections_.at(port_name, SigSpec()); if (w->port_input)
if (w->port_input) {
// Add padding to fill entire port
rhs.append(SigSpec(State::Sx, GetSize(w)-GetSize(rhs)));
for (auto b : rhs) { for (auto b : rhs) {
SigBit I = sigmap(b); SigBit I = sigmap(b);
if (b == RTLIL::Sx) if (b == RTLIL::Sx)
@ -353,11 +338,8 @@ struct XAigerWriter
co_bits.emplace_back(b); co_bits.emplace_back(b);
unused_bits.erase(I); unused_bits.erase(I);
} }
} if (w->port_output)
if (w->port_output) { for (const auto &b : rhs.bits()) {
// Add padding to fill entire port
rhs.append(SigSpec(State::Sx, GetSize(w)-GetSize(rhs)));
for (const auto &b : rhs) {
SigBit O = sigmap(b); SigBit O = sigmap(b);
if (O != b) if (O != b)
alias_map[O] = b; alias_map[O] = b;
@ -365,7 +347,6 @@ struct XAigerWriter
undriven_bits.erase(O); undriven_bits.erase(O);
} }
} }
}
// Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box // 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")) { if (box_module->get_bool_attribute("\\abc9_flop")) {
@ -402,12 +383,20 @@ struct XAigerWriter
undriven_bits.erase(bit); undriven_bits.erase(bit);
} }
if (holes_mode) {
struct sort_by_port_id {
bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const {
return a.wire->port_id < b.wire->port_id;
}
};
input_bits.sort(sort_by_port_id());
output_bits.sort(sort_by_port_id());
}
aig_map[State::S0] = 0; aig_map[State::S0] = 0;
aig_map[State::S1] = 1; aig_map[State::S1] = 1;
// pool<> iterates in LIFO order... for (const auto &bit : input_bits) {
for (int i = input_bits.size()-1; i >= 0; i--) {
const auto &bit = *input_bits.element(i);
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;
@ -423,21 +412,16 @@ struct XAigerWriter
for (auto &bit : ci_bits) { for (auto &bit : ci_bits) {
aig_m++, aig_i++; aig_m++, aig_i++;
// State::Sx if padding
if (bit != State::Sx) {
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 : co_bits) { for (auto bit : co_bits) {
ordered_outputs[bit] = aig_o++; ordered_outputs[bit] = aig_o++;
aig_outputs.push_back(bit2aig(bit)); aig_outputs.push_back(bit2aig(bit));
} }
// pool<> iterates in LIFO order... for (const auto &bit : output_bits) {
for (int i = output_bits.size()-1; i >= 0; i--) {
const auto &bit = *output_bits.element(i);
ordered_outputs[bit] = aig_o++; ordered_outputs[bit] = aig_o++;
aig_outputs.push_back(bit2aig(bit)); aig_outputs.push_back(bit2aig(bit));
} }
@ -618,7 +602,7 @@ struct XAigerWriter
if (holes_module) { if (holes_module) {
std::stringstream a_buffer; std::stringstream a_buffer;
XAigerWriter writer(holes_module); XAigerWriter writer(holes_module, true /* holes_mode */);
writer.write_aiger(a_buffer, false /*ascii_mode*/); writer.write_aiger(a_buffer, false /*ascii_mode*/);
f << "a"; f << "a";
@ -654,17 +638,13 @@ struct XAigerWriter
module->design->scratchpad_set_int("write_xaiger.num_outputs", output_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++)
@ -682,14 +662,6 @@ struct XAigerWriter
output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init); 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));
}
} }
} }
@ -706,10 +678,6 @@ struct XAigerWriter
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;
} }
}; };
@ -721,8 +689,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");
@ -730,14 +700,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");
@ -753,11 +719,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);
@ -767,6 +728,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);
@ -775,7 +744,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

@ -271,14 +271,24 @@ end_of_header:
if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size())) if ((c == 'i' && l1 > inputs.size()) || (c == 'l' && l1 > latches.size()) || (c == 'o' && l1 > outputs.size()))
log_error("Line %u has invalid symbol position!\n", line_count); log_error("Line %u has invalid symbol position!\n", line_count);
RTLIL::IdString escaped_s = stringf("\\%s", s.c_str());
RTLIL::Wire* wire; RTLIL::Wire* wire;
if (c == 'i') wire = inputs[l1]; if (c == 'i') wire = inputs[l1];
else if (c == 'l') wire = latches[l1]; else if (c == 'l') wire = latches[l1];
else if (c == 'o') wire = outputs[l1]; else if (c == 'o') {
wire = module->wire(escaped_s);
if (wire) {
// Could have been renamed by a latch
module->swap_names(wire, outputs[l1]);
module->connect(outputs[l1], wire);
goto next;
}
wire = outputs[l1];
}
else if (c == 'b') wire = bad_properties[l1]; else if (c == 'b') wire = bad_properties[l1];
else log_abort(); else log_abort();
module->rename(wire, stringf("\\%s", s.c_str())); module->rename(wire, escaped_s);
} }
else if (c == 'j' || c == 'f') { else if (c == 'j' || c == 'f') {
// TODO // TODO
@ -293,6 +303,7 @@ end_of_header:
} }
else else
log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
next:
std::getline(f, line); // Ignore up to start of next line std::getline(f, line); // Ignore up to start of next line
} }
@ -382,21 +393,6 @@ void AigerReader::parse_xaiger()
if (f.peek() == '\n') if (f.peek() == '\n')
f.get(); 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;
for (int c = f.get(); c != EOF; c = f.get()) { for (int c = f.get(); c != EOF; c = f.get()) {
@ -467,11 +463,14 @@ void AigerReader::parse_xaiger()
uint32_t boxNum = parse_xaiger_literal(f); uint32_t boxNum = parse_xaiger_literal(f);
log_debug("boxNum = %u\n", boxNum); log_debug("boxNum = %u\n", boxNum);
for (unsigned i = 0; i < boxNum; i++) { for (unsigned i = 0; i < boxNum; i++) {
f.ignore(2*sizeof(uint32_t)); uint32_t boxInputs = parse_xaiger_literal(f);
uint32_t boxOutputs = parse_xaiger_literal(f);
uint32_t boxUniqueId = parse_xaiger_literal(f); uint32_t boxUniqueId = parse_xaiger_literal(f);
log_assert(boxUniqueId > 0); log_assert(boxUniqueId > 0);
uint32_t oldBoxNum = parse_xaiger_literal(f); uint32_t oldBoxNum = parse_xaiger_literal(f);
RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), box_lookup.at(boxUniqueId)); RTLIL::Cell* cell = module->addCell(stringf("$box%u", oldBoxNum), stringf("$__boxid%u", boxUniqueId));
cell->setPort("\\i", SigSpec(State::S0, boxInputs));
cell->setPort("\\o", SigSpec(State::S0, boxOutputs));
boxes.emplace_back(cell); boxes.emplace_back(cell);
} }
} }
@ -496,13 +495,15 @@ void AigerReader::parse_aiger_ascii()
unsigned l1, l2, l3; unsigned l1, l2, l3;
// Parse inputs // Parse inputs
int digits = ceil(log10(I));
for (unsigned i = 1; i <= I; ++i, ++line_count) { for (unsigned i = 1; i <= I; ++i, ++line_count) {
if (!(f >> l1)) if (!(f >> l1))
log_error("Line %u cannot be interpreted as an input!\n", line_count); log_error("Line %u cannot be interpreted as an input!\n", line_count);
log_debug2("%d is an input\n", l1); log_debug2("%d is an input\n", l1);
log_assert(!(l1 & 1)); // Inputs can't be inverted log_assert(!(l1 & 1)); // Inputs can't be inverted
RTLIL::Wire *wire = createWireIfNotExists(module, l1); RTLIL::Wire *wire = module->addWire(stringf("$i%0*d", digits, l1 >> 1));
wire->port_input = true; wire->port_input = true;
module->connect(createWireIfNotExists(module, l1), wire);
inputs.push_back(wire); inputs.push_back(wire);
} }
@ -516,12 +517,14 @@ void AigerReader::parse_aiger_ascii()
clk_wire->port_input = true; clk_wire->port_input = true;
clk_wire->port_output = false; clk_wire->port_output = false;
} }
digits = ceil(log10(L));
for (unsigned i = 0; i < L; ++i, ++line_count) { for (unsigned i = 0; i < L; ++i, ++line_count) {
if (!(f >> l1 >> l2)) if (!(f >> l1 >> l2))
log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_error("Line %u cannot be interpreted as a latch!\n", line_count);
log_debug2("%d %d is a latch\n", l1, l2); log_debug2("%d %d is a latch\n", l1, l2);
log_assert(!(l1 & 1)); log_assert(!(l1 & 1));
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1));
module->connect(createWireIfNotExists(module, l1), q_wire);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
if (clk_wire) if (clk_wire)
@ -552,25 +555,18 @@ void AigerReader::parse_aiger_ascii()
} }
// 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
// Parse bad properties // Parse bad properties
for (unsigned i = 0; i < B; ++i, ++line_count) { for (unsigned i = 0; i < B; ++i, ++line_count) {
@ -582,6 +578,8 @@ void AigerReader::parse_aiger_ascii()
wire->port_output = true; wire->port_output = true;
bad_properties.push_back(wire); bad_properties.push_back(wire);
} }
//if (B > 0)
// std::getline(f, line); // Ignore up to start of next line
// TODO: Parse invariant constraints // TODO: Parse invariant constraints
for (unsigned i = 0; i < C; ++i, ++line_count) for (unsigned i = 0; i < C; ++i, ++line_count)
@ -644,12 +642,14 @@ void AigerReader::parse_aiger_binary()
clk_wire->port_input = true; clk_wire->port_input = true;
clk_wire->port_output = false; clk_wire->port_output = false;
} }
digits = ceil(log10(L));
l1 = (I+1) * 2; l1 = (I+1) * 2;
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
if (!(f >> l2)) if (!(f >> l2))
log_error("Line %u cannot be interpreted as a latch!\n", line_count); log_error("Line %u cannot be interpreted as a latch!\n", line_count);
log_debug("%d %d is a latch\n", l1, l2); log_debug("%d %d is a latch\n", l1, l2);
RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); RTLIL::Wire *q_wire = module->addWire(stringf("$l%0*d", digits, l1 >> 1));
module->connect(createWireIfNotExists(module, l1), q_wire);
RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); RTLIL::Wire *d_wire = createWireIfNotExists(module, l2);
if (clk_wire) if (clk_wire)
@ -735,74 +735,39 @@ void AigerReader::parse_aiger_binary()
void AigerReader::post_process() void AigerReader::post_process()
{ {
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); for (auto &bit : cell->connections_.at("\\i")) {
log_assert(box_module); log_assert(bit == State::S0);
auto r = box_ports.insert(cell->type);
if (r.second) {
// Make carry in the last PI, and carry out the last PO
// since ABC requires it this way
IdString carry_in, carry_out;
for (const auto &port_name : box_module->ports) {
auto w = box_module->wire(port_name);
log_assert(w);
if (w->get_bool_attribute("\\abc9_carry")) {
if (w->port_input)
carry_in = port_name;
if (w->port_output)
carry_out = port_name;
}
else
r.first->second.push_back(port_name);
}
if (carry_in != IdString()) {
log_assert(carry_out != IdString());
r.first->second.push_back(carry_in);
r.first->second.push_back(carry_out);
}
}
for (auto port_name : box_ports.at(cell->type)) {
RTLIL::Wire* port = box_module->wire(port_name);
log_assert(port);
RTLIL::SigSpec rhs;
for (int i = 0; i < GetSize(port); i++) {
RTLIL::Wire* wire = nullptr;
if (port->port_input) {
log_assert(co_count < outputs.size()); log_assert(co_count < outputs.size());
wire = outputs[co_count++]; bit = outputs[co_count++];
log_assert(wire); log_assert(bit.wire && GetSize(bit.wire) == 1);
log_assert(wire->port_output); log_assert(bit.wire->port_output);
wire->port_output = false; bit.wire->port_output = false;
} }
if (port->port_output) { for (auto &bit : cell->connections_.at("\\o")) {
log_assert(bit == State::S0);
log_assert((piNum + ci_count) < inputs.size()); log_assert((piNum + ci_count) < inputs.size());
wire = inputs[piNum + ci_count++]; bit = inputs[piNum + ci_count++];
log_assert(wire); log_assert(bit.wire && GetSize(bit.wire) == 1);
log_assert(wire->port_input); log_assert(bit.wire->port_input);
wire->port_input = false; bit.wire->port_input = false;
} }
rhs.append(wire);
}
cell->setPort(port_name, rhs);
} }
if (box_module->attributes.count("\\abc9_flop")) { for (uint32_t i = 0; i < flopNum; i++) {
log_assert(co_count < outputs.size()); log_assert(co_count < outputs.size());
Wire *wire = outputs[co_count++]; Wire *wire = outputs[co_count++];
log_assert(wire); log_assert(wire);
log_assert(wire->port_output); log_assert(wire->port_output);
wire->port_output = false; wire->port_output = false;
RTLIL::Wire *d = outputs[outputs.size() - flopNum + flop_count]; RTLIL::Wire *d = outputs[outputs.size() - flopNum + i];
log_assert(d); log_assert(d);
log_assert(d->port_output); log_assert(d->port_output);
d->port_output = false; d->port_output = false;
RTLIL::Wire *q = inputs[piNum - flopNum + flop_count]; RTLIL::Wire *q = inputs[piNum - flopNum + i];
log_assert(q); log_assert(q);
log_assert(q->port_input); log_assert(q->port_input);
q->port_input = false; q->port_input = false;
@ -810,9 +775,6 @@ void AigerReader::post_process()
auto ff = module->addCell(NEW_ID, "$__ABC9_FF_"); auto ff = module->addCell(NEW_ID, "$__ABC9_FF_");
ff->setPort("\\D", d); ff->setPort("\\D", d);
ff->setPort("\\Q", q); ff->setPort("\\Q", q);
flop_count++;
continue;
}
} }
dict<RTLIL::IdString, int> wideports_cache; dict<RTLIL::IdString, int> wideports_cache;
@ -862,10 +824,6 @@ void AigerReader::post_process()
RTLIL::Wire* wire = outputs[variable + co_count]; RTLIL::Wire* wire = outputs[variable + co_count];
log_assert(wire); log_assert(wire);
log_assert(wire->port_output); log_assert(wire->port_output);
if (escaped_s == "$__dummy__") {
wire->port_output = false;
continue;
}
log_debug("Renaming output %s", log_id(wire)); log_debug("Renaming output %s", log_id(wire));
if (index == 0) { if (index == 0) {
@ -903,26 +861,8 @@ void AigerReader::post_process()
} }
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()) {
RTLIL::IdString port_name = i.first;
RTLIL::SigSpec rhs = i.second;
int index = 0;
for (auto bit : rhs.bits()) {
RTLIL::Wire* wire = bit.wire;
RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name)));
if (index == 0)
module->rename(wire, escaped_s);
else if (index > 0) {
module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index));
if (wideports)
wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
}
index++;
}
}
}
} }
else else
log_error("Symbol type '%s' not recognised.\n", type.c_str()); log_error("Symbol type '%s' not recognised.\n", type.c_str());
@ -1000,28 +940,31 @@ struct AigerFrontend : public Frontend {
log("Load module from an AIGER file into the current design.\n"); log("Load module from an AIGER file into the current design.\n");
log("\n"); log("\n");
log(" -module_name <module_name>\n"); log(" -module_name <module_name>\n");
log(" Name of module to be created (default: <filename>)\n"); log(" name of module to be created (default: <filename>)\n");
log("\n"); log("\n");
log(" -clk_name <wire_name>\n"); log(" -clk_name <wire_name>\n");
log(" If specified, AIGER latches to be transformed into $_DFF_P_ cells\n"); log(" if specified, AIGER latches to be transformed into $_DFF_P_ cells\n");
log(" clocked by wire of this name. Otherwise, $_FF_ cells will be used.\n"); log(" clocked by wire of this name. otherwise, $_FF_ cells will be used\n");
log("\n"); log("\n");
log(" -map <filename>\n"); log(" -map <filename>\n");
log(" read file with port and latch symbols\n"); log(" read file with port and latch symbols\n");
log("\n"); log("\n");
log(" -wideports\n"); log(" -wideports\n");
log(" Merge ports that match the pattern 'name[int]' into a single\n"); log(" merge ports that match the pattern 'name[int]' into a single\n");
log(" multi-bit port 'name'.\n"); log(" multi-bit port 'name'\n");
log("\n");
log(" -xaiger\n");
log(" read XAIGER extensions\n");
log("\n"); log("\n");
} }
void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{ {
log_header(design, "Executing AIGER frontend.\n"); log_header(design, "Executing AIGER frontend.\n");
RTLIL::IdString clk_name = "\\clk"; RTLIL::IdString clk_name;
RTLIL::IdString module_name; RTLIL::IdString module_name;
std::string map_filename; std::string map_filename;
bool wideports = false; bool wideports = false, xaiger = false;
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) { for (argidx = 1; argidx < args.size(); argidx++) {
@ -1042,6 +985,10 @@ struct AigerFrontend : public Frontend {
wideports = true; wideports = true;
continue; continue;
} }
if (arg == "-xaiger") {
xaiger = true;
continue;
}
break; break;
} }
extra_args(f, filename, args, argidx, true); extra_args(f, filename, args, argidx, true);
@ -1061,6 +1008,9 @@ struct AigerFrontend : public Frontend {
} }
AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports);
if (xaiger)
reader.parse_xaiger();
else
reader.parse_aiger(); reader.parse_aiger();
} }
} AigerFrontend; } AigerFrontend;

View File

@ -301,8 +301,7 @@ struct SccPass : public Pass {
RTLIL::Selection newSelection(false); RTLIL::Selection newSelection(false);
int scc_counter = 0; int scc_counter = 0;
for (auto mod : design->modules()) for (auto mod : design->selected_modules())
if (!mod->get_blackbox_attribute() && design->selected(mod))
{ {
SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth); SccWorker worker(design, mod, nofeedbackMode, allCellTypes, maxDepth);

View File

@ -8,7 +8,7 @@ OBJS += passes/techmap/libparse.o
ifeq ($(ENABLE_ABC),1) ifeq ($(ENABLE_ABC),1)
OBJS += passes/techmap/abc.o OBJS += passes/techmap/abc.o
OBJS += passes/techmap/abc9.o OBJS += passes/techmap/abc9.o
OBJS += passes/techmap/abc9_map.o OBJS += passes/techmap/abc9_exe.o
OBJS += passes/techmap/abc9_ops.o OBJS += passes/techmap/abc9_ops.o
ifneq ($(ABCEXTERNAL),) ifneq ($(ABCEXTERNAL),)
passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"'

View File

@ -1514,7 +1514,47 @@ struct AbcPass : public Pass {
#endif #endif
#endif #endif
size_t argidx; // get arguments from scratchpad first, then override by command arguments
std::string lut_arg, luts_arg, g_arg;
exe_file = design->scratchpad_get_string("abc.exe", exe_file /* inherit default value if not set */);
script_file = design->scratchpad_get_string("abc.script", script_file);
liberty_file = design->scratchpad_get_string("abc.liberty", liberty_file);
constr_file = design->scratchpad_get_string("abc.constr", constr_file);
if (design->scratchpad.count("abc.D")) {
delay_target = "-D " + design->scratchpad_get_string("abc.D");
}
if (design->scratchpad.count("abc.I")) {
sop_inputs = "-I " + design->scratchpad_get_string("abc.I");
}
if (design->scratchpad.count("abc.P")) {
sop_products = "-P " + design->scratchpad_get_string("abc.P");
}
if (design->scratchpad.count("abc.S")) {
lutin_shared = "-S " + design->scratchpad_get_string("abc.S");
}
lut_arg = design->scratchpad_get_string("abc.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc.luts", luts_arg);
sop_mode = design->scratchpad_get_bool("abc.sop", sop_mode);
map_mux4 = design->scratchpad_get_bool("abc.mux4", map_mux4);
map_mux8 = design->scratchpad_get_bool("abc.mux8", map_mux8);
map_mux16 = design->scratchpad_get_bool("abc.mux16", map_mux16);
abc_dress = design->scratchpad_get_bool("abc.dress", abc_dress);
g_arg = design->scratchpad_get_string("abc.g", g_arg);
fast_mode = design->scratchpad_get_bool("abc.fast", fast_mode);
dff_mode = design->scratchpad_get_bool("abc.dff", dff_mode);
if (design->scratchpad.count("abc.clk")) {
clk_str = design->scratchpad_get_string("abc.clk");
dff_mode = true;
}
keepff = design->scratchpad_get_bool("abc.keepff", keepff);
cleanup = !design->scratchpad_get_bool("abc.nocleanup", !cleanup);
keepff = design->scratchpad_get_bool("abc.keepff", keepff);
show_tempdir = design->scratchpad_get_bool("abc.showtmp", show_tempdir);
markgroups = design->scratchpad_get_bool("abc.markgroups", markgroups);
size_t argidx, g_argidx;
bool g_arg_from_cmd = false;
char pwd [PATH_MAX]; char pwd [PATH_MAX];
if (!getcwd(pwd, sizeof(pwd))) { if (!getcwd(pwd, sizeof(pwd))) {
log_cmd_error("getcwd failed: %s\n", strerror(errno)); log_cmd_error("getcwd failed: %s\n", strerror(errno));
@ -1528,23 +1568,14 @@ struct AbcPass : public Pass {
} }
if (arg == "-script" && argidx+1 < args.size()) { if (arg == "-script" && argidx+1 < args.size()) {
script_file = args[++argidx]; script_file = args[++argidx];
rewrite_filename(script_file);
if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
script_file = std::string(pwd) + "/" + script_file;
continue; continue;
} }
if (arg == "-liberty" && argidx+1 < args.size()) { if (arg == "-liberty" && argidx+1 < args.size()) {
liberty_file = args[++argidx]; liberty_file = args[++argidx];
rewrite_filename(liberty_file);
if (!liberty_file.empty() && !is_absolute_path(liberty_file))
liberty_file = std::string(pwd) + "/" + liberty_file;
continue; continue;
} }
if (arg == "-constr" && argidx+1 < args.size()) { if (arg == "-constr" && argidx+1 < args.size()) {
rewrite_filename(constr_file);
constr_file = args[++argidx]; constr_file = args[++argidx];
if (!constr_file.empty() && !is_absolute_path(constr_file))
constr_file = std::string(pwd) + "/" + constr_file;
continue; continue;
} }
if (arg == "-D" && argidx+1 < args.size()) { if (arg == "-D" && argidx+1 < args.size()) {
@ -1564,37 +1595,11 @@ struct AbcPass : public Pass {
continue; continue;
} }
if (arg == "-lut" && argidx+1 < args.size()) { if (arg == "-lut" && argidx+1 < args.size()) {
string arg = args[++argidx]; lut_arg = args[++argidx];
size_t pos = arg.find_first_of(':');
int lut_mode = 0, lut_mode2 = 0;
if (pos != string::npos) {
lut_mode = atoi(arg.substr(0, pos).c_str());
lut_mode2 = atoi(arg.substr(pos+1).c_str());
} else {
lut_mode = atoi(arg.c_str());
lut_mode2 = lut_mode;
}
lut_costs.clear();
for (int i = 0; i < lut_mode; i++)
lut_costs.push_back(1);
for (int i = lut_mode; i < lut_mode2; i++)
lut_costs.push_back(2 << (i - lut_mode));
continue; continue;
} }
if (arg == "-luts" && argidx+1 < args.size()) { if (arg == "-luts" && argidx+1 < args.size()) {
lut_costs.clear(); luts_arg = args[++argidx];
for (auto &tok : split_tokens(args[++argidx], ",")) {
auto parts = split_tokens(tok, ":");
if (GetSize(parts) == 0 && !lut_costs.empty())
lut_costs.push_back(lut_costs.back());
else if (GetSize(parts) == 1)
lut_costs.push_back(atoi(parts.at(0).c_str()));
else if (GetSize(parts) == 2)
while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
lut_costs.push_back(atoi(parts.at(1).c_str()));
else
log_cmd_error("Invalid -luts syntax.\n");
}
continue; continue;
} }
if (arg == "-sop") { if (arg == "-sop") {
@ -1618,7 +1623,93 @@ struct AbcPass : public Pass {
continue; continue;
} }
if (arg == "-g" && argidx+1 < args.size()) { if (arg == "-g" && argidx+1 < args.size()) {
for (auto g : split_tokens(args[++argidx], ",")) { if (g_arg_from_cmd)
log_cmd_error("Can only use -g once. Please combine.");
g_arg = args[++argidx];
g_argidx = argidx;
g_arg_from_cmd = true;
continue;
}
if (arg == "-fast") {
fast_mode = true;
continue;
}
if (arg == "-dff") {
dff_mode = true;
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") {
cleanup = false;
continue;
}
if (arg == "-showtmp") {
show_tempdir = true;
continue;
}
if (arg == "-markgroups") {
markgroups = true;
continue;
}
break;
}
extra_args(args, argidx, design);
rewrite_filename(script_file);
if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
script_file = std::string(pwd) + "/" + script_file;
rewrite_filename(liberty_file);
if (!liberty_file.empty() && !is_absolute_path(liberty_file))
liberty_file = std::string(pwd) + "/" + liberty_file;
rewrite_filename(constr_file);
if (!constr_file.empty() && !is_absolute_path(constr_file))
constr_file = std::string(pwd) + "/" + constr_file;
// handle -lut argument
if (!lut_arg.empty()) {
size_t pos = lut_arg.find_first_of(':');
int lut_mode = 0, lut_mode2 = 0;
if (pos != string::npos) {
lut_mode = atoi(lut_arg.substr(0, pos).c_str());
lut_mode2 = atoi(lut_arg.substr(pos+1).c_str());
} else {
lut_mode = atoi(lut_arg.c_str());
lut_mode2 = lut_mode;
}
lut_costs.clear();
for (int i = 0; i < lut_mode; i++)
lut_costs.push_back(1);
for (int i = lut_mode; i < lut_mode2; i++)
lut_costs.push_back(2 << (i - lut_mode));
}
//handle -luts argument
if (!luts_arg.empty()){
lut_costs.clear();
for (auto &tok : split_tokens(luts_arg, ",")) {
auto parts = split_tokens(tok, ":");
if (GetSize(parts) == 0 && !lut_costs.empty())
lut_costs.push_back(lut_costs.back());
else if (GetSize(parts) == 1)
lut_costs.push_back(atoi(parts.at(0).c_str()));
else if (GetSize(parts) == 2)
while (GetSize(lut_costs) < std::atoi(parts.at(0).c_str()))
lut_costs.push_back(atoi(parts.at(1).c_str()));
else
log_cmd_error("Invalid -luts syntax.\n");
}
}
// handle -g argument
if (!g_arg.empty()){
for (auto g : split_tokens(g_arg, ",")) {
vector<string> gate_list; vector<string> gate_list;
bool remove_gates = false; bool remove_gates = false;
if (GetSize(g) > 0 && g[0] == '-') { if (GetSize(g) > 0 && g[0] == '-') {
@ -1724,7 +1815,10 @@ struct AbcPass : public Pass {
gate_list.push_back("MUX"); gate_list.push_back("MUX");
gate_list.push_back("NMUX"); gate_list.push_back("NMUX");
} }
cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); if (g_arg_from_cmd)
cmd_error(args, g_argidx, stringf("Unsupported gate type: %s", g.c_str()));
else
log_cmd_error("Unsupported gate type: %s", g.c_str());
ok_gate: ok_gate:
gate_list.push_back(g); gate_list.push_back(g);
ok_alias: ok_alias:
@ -1735,40 +1829,7 @@ struct AbcPass : public Pass {
enabled_gates.insert(gate); enabled_gates.insert(gate);
} }
} }
continue;
} }
if (arg == "-fast") {
fast_mode = true;
continue;
}
if (arg == "-dff") {
dff_mode = true;
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") {
cleanup = false;
continue;
}
if (arg == "-showtmp") {
show_tempdir = true;
continue;
}
if (arg == "-markgroups") {
markgroups = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (!lut_costs.empty() && !liberty_file.empty()) if (!lut_costs.empty() && !liberty_file.empty())
log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n"); log_cmd_error("Got -lut and -liberty! These two options are exclusive.\n");

View File

@ -40,7 +40,7 @@ struct Abc9Pass : public ScriptPass
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
@ -113,11 +113,6 @@ struct Abc9Pass : public ScriptPass
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");
@ -134,13 +129,13 @@ struct Abc9Pass : public ScriptPass
log("\n"); log("\n");
} }
std::stringstream map_cmd; std::stringstream exe_cmd;
bool dff_mode, cleanup; bool dff_mode, cleanup;
void clear_flags() YS_OVERRIDE void clear_flags() YS_OVERRIDE
{ {
map_cmd.str(""); exe_cmd.str("");
map_cmd << "abc9_map"; exe_cmd << "abc9_exe";
dff_mode = false; dff_mode = false;
cleanup = true; cleanup = true;
} }
@ -150,6 +145,10 @@ struct Abc9Pass : public ScriptPass
std::string run_from, run_to; std::string run_from, run_to;
clear_flags(); clear_flags();
// get arguments from scratchpad first, then override by command arguments
dff_mode = design->scratchpad_get_bool("abc9.dff", dff_mode);
cleanup = !design->scratchpad_get_bool("abc9.nocleanup", !cleanup);
size_t argidx; size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) { for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx]; std::string arg = args[argidx];
@ -157,13 +156,13 @@ struct Abc9Pass : public ScriptPass
/* arg == "-S" || */ arg == "-lut" || arg == "-luts" || /* arg == "-S" || */ arg == "-lut" || arg == "-luts" ||
arg == "-box" || arg == "-W") && arg == "-box" || arg == "-W") &&
argidx+1 < args.size()) { argidx+1 < args.size()) {
map_cmd << " " << arg << " " << args[++argidx]; exe_cmd << " " << arg << " " << args[++argidx];
continue; continue;
} }
if (arg == "-fast" || /* arg == "-dff" || */ if (arg == "-fast" || /* arg == "-dff" || */
/* arg == "-nocleanup" || */ arg == "-showtmp" || arg == "-markgroups" || /* arg == "-nocleanup" || */ arg == "-showtmp" ||
arg == "-nomfs") { arg == "-nomfs") {
map_cmd << " " << arg; exe_cmd << " " << arg;
continue; continue;
} }
if (arg == "-dff") { if (arg == "-dff") {
@ -185,6 +184,7 @@ struct Abc9Pass : public ScriptPass
void script() YS_OVERRIDE void script() YS_OVERRIDE
{ {
if (check_label("pre")) {
run("scc -set_attr abc9_scc_id {}"); run("scc -set_attr abc9_scc_id {}");
if (help_mode) if (help_mode)
run("abc9_ops -break_scc -prep_holes [-dff]", "(option for -dff)"); run("abc9_ops -break_scc -prep_holes [-dff]", "(option for -dff)");
@ -194,37 +194,60 @@ struct Abc9Pass : public ScriptPass
run("flatten -wb @abc9_holes"); run("flatten -wb @abc9_holes");
run("techmap @abc9_holes"); run("techmap @abc9_holes");
run("aigmap"); run("aigmap");
if (dff_mode) if (dff_mode || help_mode)
run("abc9_ops -prep_dff"); run("abc9_ops -prep_dff", "(only if -dff)");
run("opt -purge @abc9_holes"); run("opt -purge @abc9_holes");
run("wbflip @abc9_holes"); run("wbflip @abc9_holes");
}
if (check_label("map")) {
if (help_mode) {
run("foreach module in selection");
run(" write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig");
run(" abc9_exe [options] -cwd <abc-temp-dir>");
run(" read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig");
run(" abc9_ops -reintegrate");
}
else {
auto selected_modules = active_design->selected_modules(); auto selected_modules = active_design->selected_modules();
active_design->selection_stack.emplace_back(false); active_design->selection_stack.emplace_back(false);
for (auto mod : selected_modules) { for (auto mod : selected_modules) {
if (mod->get_blackbox_attribute())
continue;
if (mod->processes.size() > 0) { if (mod->processes.size() > 0) {
log("Skipping module %s as it contains processes.\n", log_id(mod)); log("Skipping module %s as it contains processes.\n", log_id(mod));
continue; continue;
} }
log_assert(!mod->attributes.count(ID(abc9_box_id)));
active_design->selection().select(mod); active_design->selection().select(mod);
if (!active_design->selected_whole_module(mod))
log_error("Can't handle partially selected module %s!\n", log_id(mod));
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] = '_';
tempdir_name = make_temp_dir(tempdir_name); tempdir_name = make_temp_dir(tempdir_name);
run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()), run(stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
"write_xaiger -map <abc-temp-dir>/input.sym <abc-temp-dir>/input.xaig");
run(stringf("%s -tempdir %s", map_cmd.str().c_str(), tempdir_name.c_str()),
"abc9_map [options] -tempdir <abc-temp-dir>");
if (cleanup) int num_outputs = active_design->scratchpad_get_int("write_xaiger.num_outputs");
{ log("Extracted %d AND gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
active_design->scratchpad_get_int("write_xaiger.num_ands"),
active_design->scratchpad_get_int("write_xaiger.num_wires"),
active_design->scratchpad_get_int("write_xaiger.num_inputs"),
num_outputs);
if (num_outputs) {
run(stringf("%s -cwd %s", exe_cmd.str().c_str(), tempdir_name.c_str()),
"abc9_exe [options] -cwd <abc-temp-dir>");
run(stringf("read_aiger -xaiger -wideports -module_name %s$abc9 -map %s/input.sym %s/output.aig", log_id(mod->name), tempdir_name.c_str(), tempdir_name.c_str()),
"read_aiger -xaiger -wideports -module_name <module-name>$abc9 -map <abc-temp-dir>/input.sym <abc-temp-dir>/output.aig");
run("abc9_ops -reintegrate");
}
else
log("Don't call ABC as there is nothing to map.\n");
if (cleanup) {
log("Removing temp directory.\n"); log("Removing temp directory.\n");
remove_directory(tempdir_name); remove_directory(tempdir_name);
} }
@ -233,7 +256,10 @@ struct Abc9Pass : public ScriptPass
} }
active_design->selection_stack.pop_back(); active_design->selection_stack.pop_back();
}
}
if (check_label("post"))
run("abc9_ops -unbreak_scc"); run("abc9_ops -unbreak_scc");
} }
} Abc9Pass; } Abc9Pass;

544
passes/techmap/abc9_exe.cc Normal file
View File

@ -0,0 +1,544 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
// [[CITE]] ABC
// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
// http://www.eecs.berkeley.edu/~alanmi/abc/
#if 0
// Based on &flow3 - better QoR but more experimental
#define ABC_COMMAND_LUT "&st; &ps -l; &sweep -v; &scorr; " \
"&st; &if {W}; &save; &st; &syn2; &if {W} -v; &save; &load; "\
"&st; &if -g -K 6; &dch -f; &if {W} -v; &save; &load; "\
"&st; &if -g -K 6; &synch2; &if {W} -v; &save; &load; "\
"&mfs; &ps -l"
#else
#define ABC_COMMAND_LUT "&st; &scorr; &sweep; &dc2; &st; &dch -f; &ps; &if {W} {D} -v; &mfs; &ps -l"
#endif
#define ABC_FAST_COMMAND_LUT "&st; &if {W} {D}"
#include "kernel/register.h"
#include "kernel/log.h"
#ifndef _WIN32
# include <unistd.h>
# include <dirent.h>
#endif
#ifdef YOSYS_LINK_ABC
extern "C" int Abc_RealMain(int argc, char *argv[]);
#endif
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
std::string add_echos_to_abc9_cmd(std::string str)
{
std::string new_str, token;
for (size_t i = 0; i < str.size(); i++) {
token += str[i];
if (str[i] == ';') {
while (i+1 < str.size() && str[i+1] == ' ')
i++;
new_str += "echo + " + token + " " + token + " ";
token.clear();
}
}
if (!token.empty()) {
if (!new_str.empty())
new_str += "echo + " + token + "; ";
new_str += token;
}
return new_str;
}
std::string fold_abc9_cmd(std::string str)
{
std::string token, new_str = " ";
int char_counter = 10;
for (size_t i = 0; i <= str.size(); i++) {
if (i < str.size())
token += str[i];
if (i == str.size() || str[i] == ';') {
if (char_counter + token.size() > 75)
new_str += "\n ", char_counter = 14;
new_str += token, char_counter += token.size();
token.clear();
}
}
return new_str;
}
std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
{
if (show_tempdir)
return text;
while (1) {
size_t pos = text.find(tempdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
}
std::string selfdir_name = proc_self_dirname();
if (selfdir_name != "/") {
while (1) {
size_t pos = text.find(selfdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
}
}
return text;
}
struct abc9_output_filter
{
bool got_cr;
int escape_seq_state;
std::string linebuf;
std::string tempdir_name;
bool show_tempdir;
abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
{
got_cr = false;
escape_seq_state = 0;
}
void next_char(char ch)
{
if (escape_seq_state == 0 && ch == '\033') {
escape_seq_state = 1;
return;
}
if (escape_seq_state == 1) {
escape_seq_state = ch == '[' ? 2 : 0;
return;
}
if (escape_seq_state == 2) {
if ((ch < '0' || '9' < ch) && ch != ';')
escape_seq_state = 0;
return;
}
escape_seq_state = 0;
if (ch == '\r') {
got_cr = true;
return;
}
if (ch == '\n') {
log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str());
got_cr = false, linebuf.clear();
return;
}
if (got_cr)
got_cr = false, linebuf.clear();
linebuf += ch;
}
void next_line(const std::string &line)
{
//int pi, po;
//if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) {
// log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n",
// pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???",
// po, po_map.count(po) ? po_map.at(po).c_str() : "???");
// return;
//}
for (char ch : line)
next_char(ch);
}
};
void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file,
vector<int> lut_costs, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
std::string wire_delay, bool nomfs, std::string tempdir_name
)
{
//FIXME:
//log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n",
// module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
std::string abc9_script;
if (!lut_costs.empty()) {
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
if (!box_file.empty())
abc9_script += stringf("read_box %s; ", box_file.c_str());
}
else
if (!lut_file.empty()) {
abc9_script += stringf("read_lut %s; ", lut_file.c_str());
if (!box_file.empty())
abc9_script += stringf("read_box %s; ", box_file.c_str());
}
else
log_abort();
abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
if (!script_file.empty()) {
if (script_file[0] == '+') {
for (size_t i = 1; i < script_file.size(); i++)
if (script_file[i] == '\'')
abc9_script += "'\\''";
else if (script_file[i] == ',')
abc9_script += " ";
else
abc9_script += script_file[i];
} else
abc9_script += stringf("source %s", script_file.c_str());
} else if (!lut_costs.empty() || !lut_file.empty()) {
abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
} else
log_abort();
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
// abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
if (nomfs)
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 += stringf("; &write -n %s/output.aig", tempdir_name.c_str());
abc9_script = add_echos_to_abc9_cmd(abc9_script);
for (size_t i = 0; i+1 < abc9_script.size(); i++)
if (abc9_script[i] == ';' && abc9_script[i+1] == ' ')
abc9_script[i+1] = '\n';
FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
fprintf(f, "%s\n", abc9_script.c_str());
fclose(f);
std::string buffer;
log_header(design, "Executing ABC9.\n");
if (!lut_costs.empty()) {
buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
if (f == NULL)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
for (int i = 0; i < GetSize(lut_costs); i++)
fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
fclose(f);
}
buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
#ifndef YOSYS_LINK_ABC
abc9_output_filter filt(tempdir_name, show_tempdir);
int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1));
#else
// These needs to be mutable, supposedly due to getopt
char *abc9_argv[5];
string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
abc9_argv[0] = strdup(exe_file.c_str());
abc9_argv[1] = strdup("-s");
abc9_argv[2] = strdup("-f");
abc9_argv[3] = strdup(tmp_script_name.c_str());
abc9_argv[4] = 0;
int ret = Abc_RealMain(4, abc9_argv);
free(abc9_argv[0]);
free(abc9_argv[1]);
free(abc9_argv[2]);
free(abc9_argv[3]);
#endif
if (ret != 0)
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
}
struct Abc9ExePass : public Pass {
Abc9ExePass() : Pass("abc9_exe", "use ABC9 for technology mapping") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" abc9_exe [options] [selection]\n");
log("\n");
log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n");
log("library to a target architecture.\n");
log("\n");
log(" -exe <command>\n");
#ifdef ABCEXTERNAL
log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
#else
log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
#endif
log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
log("\n");
log(" -script <file>\n");
log(" use the specified ABC script file instead of the default script.\n");
log("\n");
log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
log(" string is interpreted as the command string to be passed to ABC. The\n");
log(" leading plus sign is removed and all commas (,) in the string are\n");
log(" replaced with blanks before the string is passed to ABC.\n");
log("\n");
log(" if no -script parameter is given, the following scripts are used:\n");
log("\n");
log(" for -lut/-luts (only one LUT size):\n");
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str());
log("\n");
log(" for -lut/-luts (different LUT sizes):\n");
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str());
log("\n");
log(" -fast\n");
log(" use different default scripts that are slightly faster (at the cost\n");
log(" of output quality):\n");
log("\n");
log(" for -lut/-luts:\n");
log("%s\n", fold_abc9_cmd(ABC_FAST_COMMAND_LUT).c_str());
log("\n");
log(" -D <picoseconds>\n");
log(" set delay target. the string {D} in the default scripts above is\n");
log(" replaced by this option when used, and an empty string otherwise\n");
log(" (indicating best possible delay).\n");
log("\n");
// log(" -S <num>\n");
// log(" maximum number of LUT inputs shared.\n");
// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
// log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
log(" -lut <w1>:<w2>\n");
log(" generate netlist using luts of (max) the specified width <w2>. All\n");
log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
log(" the area cost doubles with each additional input bit. the delay cost\n");
log(" is still constant for all lut widths.\n");
log("\n");
log(" -lut <file>\n");
log(" pass this file with lut library to ABC.\n");
log("\n");
log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
log(" 2, 3, .. inputs.\n");
log("\n");
log(" -showtmp\n");
log(" print the temp dir name in log. usually this is suppressed so that the\n");
log(" command output is identical across runs.\n");
log("\n");
log(" -box <file>\n");
log(" pass this file with box library to ABC. Use with -lut.\n");
log("\n");
log(" -cwd <dir>\n");
log(" use this as the current working directory, inside which the 'input.xaig'\n");
log(" file is expected. temporary files will be created in this directory, and\n");
log(" the mapped result will be written to 'output.aig'.\n");
log("\n");
log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
log("ABC on logic snippets extracted from your design. You will not get any useful\n");
log("output when passing an ABC script that writes a file. Instead write your full\n");
log("design as BLIF file with write_blif and then load that into ABC externally if\n");
log("you want to use ABC to convert your design into another format.\n");
log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n");
#ifdef ABCEXTERNAL
std::string exe_file = ABCEXTERNAL;
#else
std::string exe_file = proc_self_dirname() + "yosys-abc";
#endif
std::string script_file, clk_str, box_file, lut_file;
std::string delay_target, lutin_shared = "-S 1", wire_delay;
std::string tempdir_name;
bool fast_mode = false;
bool show_tempdir = false;
bool nomfs = false;
vector<int> lut_costs;
#if 0
cleanup = false;
show_tempdir = true;
#endif
#ifdef _WIN32
#ifndef ABCEXTERNAL
if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
exe_file = proc_self_dirname() + "..\\yosys-abc";
#endif
#endif
std::string lut_arg, luts_arg;
exe_file = design->scratchpad_get_string("abc9.exe", exe_file /* inherit default value if not set */);
script_file = design->scratchpad_get_string("abc9.script", script_file);
if (design->scratchpad.count("abc9.D")) {
delay_target = "-D " + design->scratchpad_get_string("abc9.D");
}
lut_arg = design->scratchpad_get_string("abc9.lut", lut_arg);
luts_arg = design->scratchpad_get_string("abc9.luts", luts_arg);
fast_mode = design->scratchpad_get_bool("abc9.fast", fast_mode);
show_tempdir = design->scratchpad_get_bool("abc9.showtmp", show_tempdir);
box_file = design->scratchpad_get_string("abc9.box", box_file);
if (design->scratchpad.count("abc9.W")) {
wire_delay = "-W " + design->scratchpad_get_string("abc9.W");
}
nomfs = design->scratchpad_get_bool("abc9.nomfs", nomfs);
size_t argidx;
char pwd [PATH_MAX];
if (!getcwd(pwd, sizeof(pwd))) {
log_cmd_error("getcwd failed: %s\n", strerror(errno));
log_abort();
}
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
if (arg == "-exe" && argidx+1 < args.size()) {
exe_file = args[++argidx];
continue;
}
if (arg == "-script" && argidx+1 < args.size()) {
script_file = args[++argidx];
continue;
}
if (arg == "-D" && argidx+1 < args.size()) {
delay_target = "-D " + args[++argidx];
continue;
}
//if (arg == "-S" && argidx+1 < args.size()) {
// lutin_shared = "-S " + args[++argidx];
// continue;
//}
if (arg == "-lut" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
}
if (arg == "-luts" && argidx+1 < args.size()) {
lut_arg = args[++argidx];
continue;
}
if (arg == "-fast") {
fast_mode = true;
continue;
}
if (arg == "-showtmp") {
show_tempdir = true;
continue;
}
if (arg == "-box" && argidx+1 < args.size()) {
box_file = args[++argidx];
continue;
}
if (arg == "-W" && argidx+1 < args.size()) {
wire_delay = "-W " + args[++argidx];
continue;
}
if (arg == "-nomfs") {
nomfs = true;
continue;
}
if (arg == "-cwd" && argidx+1 < args.size()) {
tempdir_name = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, design);
rewrite_filename(script_file);
if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
script_file = std::string(pwd) + "/" + script_file;
// handle -lut / -luts args
if (!lut_arg.empty()) {
string arg = lut_arg;
if (arg.find_first_not_of("0123456789:") == std::string::npos) {
size_t pos = arg.find_first_of(':');
int lut_mode = 0, lut_mode2 = 0;
if (pos != string::npos) {
lut_mode = atoi(arg.substr(0, pos).c_str());
lut_mode2 = atoi(arg.substr(pos+1).c_str());
} else {
lut_mode = atoi(arg.c_str());
lut_mode2 = lut_mode;
}
lut_costs.clear();
for (int i = 0; i < lut_mode; i++)
lut_costs.push_back(1);
for (int i = lut_mode; i < lut_mode2; i++)
lut_costs.push_back(2 << (i - lut_mode));
}
else {
lut_file = arg;
rewrite_filename(lut_file);
if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+')
lut_file = std::string(pwd) + "/" + lut_file;
}
}
if (!luts_arg.empty()) {
lut_costs.clear();
for (auto &tok : split_tokens(luts_arg, ",")) {
auto parts = split_tokens(tok, ":");
if (GetSize(parts) == 0 && !lut_costs.empty())
lut_costs.push_back(lut_costs.back());
else if (GetSize(parts) == 1)
lut_costs.push_back(atoi(parts.at(0).c_str()));
else if (GetSize(parts) == 2)
while (GetSize(lut_costs) < atoi(parts.at(0).c_str()))
lut_costs.push_back(atoi(parts.at(1).c_str()));
else
log_cmd_error("Invalid -luts syntax.\n");
}
}
// ABC expects a box file for XAIG
if (box_file.empty())
box_file = "+/dummy.box";
rewrite_filename(box_file);
if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+')
box_file = std::string(pwd) + "/" + box_file;
if (tempdir_name.empty())
log_cmd_error("abc9_exe '-cwd' option is mandatory.\n");
abc9_module(design, script_file, exe_file, lut_costs,
delay_target, lutin_shared, fast_mode, show_tempdir,
box_file, lut_file, wire_delay, nomfs, tempdir_name);
}
} Abc9ExePass;
PRIVATE_NAMESPACE_END

View File

@ -1,881 +0,0 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
* 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
// [[CITE]] ABC
// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
// http://www.eecs.berkeley.edu/~alanmi/abc/
#if 0
// Based on &flow3 - better QoR but more experimental
#define ABC_COMMAND_LUT "&st; &ps -l; &sweep -v; &scorr; " \
"&st; &if {W}; &save; &st; &syn2; &if {W} -v; &save; &load; "\
"&st; &if -g -K 6; &dch -f; &if {W} -v; &save; &load; "\
"&st; &if -g -K 6; &synch2; &if {W} -v; &save; &load; "\
"&mfs; &ps -l"
#else
#define ABC_COMMAND_LUT "&st; &scorr; &sweep; &dc2; &st; &dch -f; &ps; &if {W} {D} -v; &mfs; &ps -l"
#endif
#define ABC_FAST_COMMAND_LUT "&st; &if {W} {D}"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/cost.h"
#include "kernel/log.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cerrno>
#include <sstream>
#include <climits>
#ifndef _WIN32
# include <unistd.h>
# include <dirent.h>
#endif
#include "frontends/aiger/aigerparse.h"
#include "kernel/utils.h"
#ifdef YOSYS_LINK_ABC
extern "C" int Abc_RealMain(int argc, char *argv[]);
#endif
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
bool markgroups;
int map_autoidx;
inline std::string remap_name(RTLIL::IdString abc9_name)
{
return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
}
std::string add_echos_to_abc9_cmd(std::string str)
{
std::string new_str, token;
for (size_t i = 0; i < str.size(); i++) {
token += str[i];
if (str[i] == ';') {
while (i+1 < str.size() && str[i+1] == ' ')
i++;
new_str += "echo + " + token + " " + token + " ";
token.clear();
}
}
if (!token.empty()) {
if (!new_str.empty())
new_str += "echo + " + token + "; ";
new_str += token;
}
return new_str;
}
std::string fold_abc9_cmd(std::string str)
{
std::string token, new_str = " ";
int char_counter = 10;
for (size_t i = 0; i <= str.size(); i++) {
if (i < str.size())
token += str[i];
if (i == str.size() || str[i] == ';') {
if (char_counter + token.size() > 75)
new_str += "\n ", char_counter = 14;
new_str += token, char_counter += token.size();
token.clear();
}
}
return new_str;
}
std::string replace_tempdir(std::string text, std::string tempdir_name, bool show_tempdir)
{
if (show_tempdir)
return text;
while (1) {
size_t pos = text.find(tempdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<abc-temp-dir>" + text.substr(pos + GetSize(tempdir_name));
}
std::string selfdir_name = proc_self_dirname();
if (selfdir_name != "/") {
while (1) {
size_t pos = text.find(selfdir_name);
if (pos == std::string::npos)
break;
text = text.substr(0, pos) + "<yosys-exe-dir>/" + text.substr(pos + GetSize(selfdir_name));
}
}
return text;
}
struct abc9_output_filter
{
bool got_cr;
int escape_seq_state;
std::string linebuf;
std::string tempdir_name;
bool show_tempdir;
abc9_output_filter(std::string tempdir_name, bool show_tempdir) : tempdir_name(tempdir_name), show_tempdir(show_tempdir)
{
got_cr = false;
escape_seq_state = 0;
}
void next_char(char ch)
{
if (escape_seq_state == 0 && ch == '\033') {
escape_seq_state = 1;
return;
}
if (escape_seq_state == 1) {
escape_seq_state = ch == '[' ? 2 : 0;
return;
}
if (escape_seq_state == 2) {
if ((ch < '0' || '9' < ch) && ch != ';')
escape_seq_state = 0;
return;
}
escape_seq_state = 0;
if (ch == '\r') {
got_cr = true;
return;
}
if (ch == '\n') {
log("ABC: %s\n", replace_tempdir(linebuf, tempdir_name, show_tempdir).c_str());
got_cr = false, linebuf.clear();
return;
}
if (got_cr)
got_cr = false, linebuf.clear();
linebuf += ch;
}
void next_line(const std::string &line)
{
//int pi, po;
//if (sscanf(line.c_str(), "Start-point = pi%d. End-point = po%d.", &pi, &po) == 2) {
// log("ABC: Start-point = pi%d (%s). End-point = po%d (%s).\n",
// pi, pi_map.count(pi) ? pi_map.at(pi).c_str() : "???",
// po, po_map.count(po) ? po_map.at(po).c_str() : "???");
// return;
//}
for (char ch : line)
next_char(ch);
}
};
void abc9_module(RTLIL::Design *design, RTLIL::Module *module, std::string script_file, std::string exe_file,
vector<int> lut_costs, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode,
bool show_tempdir, std::string box_file, std::string lut_file,
std::string wire_delay, bool nomfs, std::string tempdir_name
)
{
map_autoidx = autoidx++;
//FIXME:
//log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n",
// module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
std::string abc9_script;
if (!lut_costs.empty()) {
abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
if (!box_file.empty())
abc9_script += stringf("read_box %s; ", box_file.c_str());
}
else
if (!lut_file.empty()) {
abc9_script += stringf("read_lut %s; ", lut_file.c_str());
if (!box_file.empty())
abc9_script += stringf("read_box %s; ", box_file.c_str());
}
else
log_abort();
abc9_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
if (!script_file.empty()) {
if (script_file[0] == '+') {
for (size_t i = 1; i < script_file.size(); i++)
if (script_file[i] == '\'')
abc9_script += "'\\''";
else if (script_file[i] == ',')
abc9_script += " ";
else
abc9_script += script_file[i];
} else
abc9_script += stringf("source %s", script_file.c_str());
} else if (!lut_costs.empty() || !lut_file.empty()) {
abc9_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
} else
log_abort();
for (size_t pos = abc9_script.find("{D}"); pos != std::string::npos; pos = abc9_script.find("{D}", pos))
abc9_script = abc9_script.substr(0, pos) + delay_target + abc9_script.substr(pos+3);
//for (size_t pos = abc9_script.find("{S}"); pos != std::string::npos; pos = abc9_script.find("{S}", pos))
// abc9_script = abc9_script.substr(0, pos) + lutin_shared + abc9_script.substr(pos+3);
for (size_t pos = abc9_script.find("{W}"); pos != std::string::npos; pos = abc9_script.find("{W}", pos))
abc9_script = abc9_script.substr(0, pos) + wire_delay + abc9_script.substr(pos+3);
if (nomfs)
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 += stringf("; &write -n %s/output.aig", tempdir_name.c_str());
abc9_script = add_echos_to_abc9_cmd(abc9_script);
for (size_t i = 0; i+1 < abc9_script.size(); i++)
if (abc9_script[i] == ';' && abc9_script[i+1] == ' ')
abc9_script[i+1] = '\n';
FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt");
fprintf(f, "%s\n", abc9_script.c_str());
fclose(f);
int count_outputs = design->scratchpad_get_int("write_xaiger.num_outputs");
log("Extracted %d AND gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
design->scratchpad_get_int("write_xaiger.num_ands"),
design->scratchpad_get_int("write_xaiger.num_wires"),
design->scratchpad_get_int("write_xaiger.num_inputs"),
count_outputs);
if (count_outputs > 0) {
std::string buffer;
std::ifstream ifs;
#if 0
buffer = stringf("%s/%s", tempdir_name.c_str(), "input.xaig");
ifs.open(buffer);
if (ifs.fail())
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym");
log_assert(!design->module(ID($__abc9__)));
{
AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */);
reader.parse_xaiger();
}
ifs.close();
Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected"));
design->remove(design->module(ID($__abc9__)));
#endif
log_header(design, "Executing ABC9.\n");
if (!lut_costs.empty()) {
buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
if (f == NULL)
log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
for (int i = 0; i < GetSize(lut_costs); i++)
fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
fclose(f);
}
buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
#ifndef YOSYS_LINK_ABC
abc9_output_filter filt(tempdir_name, show_tempdir);
int ret = run_command(buffer, std::bind(&abc9_output_filter::next_line, filt, std::placeholders::_1));
#else
// These needs to be mutable, supposedly due to getopt
char *abc9_argv[5];
string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str());
abc9_argv[0] = strdup(exe_file.c_str());
abc9_argv[1] = strdup("-s");
abc9_argv[2] = strdup("-f");
abc9_argv[3] = strdup(tmp_script_name.c_str());
abc9_argv[4] = 0;
int ret = Abc_RealMain(4, abc9_argv);
free(abc9_argv[0]);
free(abc9_argv[1]);
free(abc9_argv[2]);
free(abc9_argv[3]);
#endif
if (ret != 0)
log_error("ABC: execution of command \"%s\" failed: return code %d.\n", buffer.c_str(), ret);
buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig");
ifs.open(buffer, std::ifstream::binary);
if (ifs.fail())
log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym");
log_assert(!design->module(ID($__abc9__)));
AigerReader reader(design, ifs, ID($__abc9__), "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */);
reader.parse_xaiger();
ifs.close();
#if 0
Pass::call_on_module(design, design->module(ID($__abc9__)), stringf("write_verilog -noexpr -norename -selected"));
#endif
log_header(design, "Re-integrating ABC9 results.\n");
RTLIL::Module *mapped_mod = design->module(ID($__abc9__));
if (mapped_mod == NULL)
log_error("ABC output file does not contain a module `$__abc9__'.\n");
for (auto &it : mapped_mod->wires_) {
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;
}
for (auto it = module->cells_.begin(); it != module->cells_.end(); )
if (it->second->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_)))
it = module->cells_.erase(it);
else
++it;
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers;
dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks;
std::map<IdString, int> cell_stats;
for (auto mapped_cell : mapped_mod->cells())
{
toposort.node(mapped_cell->name);
RTLIL::Cell *cell = nullptr;
if (mapped_cell->type == ID($_NOT_)) {
RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
bit_users[a_bit].insert(mapped_cell->name);
bit_drivers[y_bit].insert(mapped_cell->name);
if (!a_bit.wire) {
mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
log_assert(wire);
module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
}
else if (!lut_costs.empty() || !lut_file.empty()) {
RTLIL::Cell* driver_lut = nullptr;
// ABC can return NOT gates that drive POs
if (!a_bit.wire->port_input) {
// If it's not a NOT gate that that comes from a PI directly,
// find the driver LUT and clone that to guarantee that we won't
// increase the max logic depth
// (TODO: Optimise by not cloning unless will increase depth)
RTLIL::IdString driver_name;
if (GetSize(a_bit.wire) == 1)
driver_name = stringf("%s$lut", a_bit.wire->name.c_str());
else
driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset);
driver_lut = mapped_mod->cell(driver_name);
}
if (!driver_lut) {
// If a driver couldn't be found (could be from PI or box CI)
// then implement using a LUT
cell = module->addLut(remap_name(stringf("%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(y_bit.wire->name)), y_bit.offset),
RTLIL::Const::from_string("01"));
bit2sinks[cell->getPort(ID::A)].push_back(cell);
cell_stats[ID($lut)]++;
}
else
not2drivers[mapped_cell] = driver_lut;
continue;
}
else
log_abort();
if (cell && markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
continue;
}
cell_stats[mapped_cell->type]++;
RTLIL::Cell *existing_cell = nullptr;
if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
if (mapped_cell->type == ID($lut) &&
GetSize(mapped_cell->getPort(ID::A)) == 1 &&
mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) {
SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name));
SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name));
module->connect(my_y, my_a);
if (markgroups) mapped_cell->attributes[ID(abcgroup)] = map_autoidx;
log_abort();
continue;
}
cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
}
else {
existing_cell = module->cell(mapped_cell->name);
log_assert(existing_cell);
cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
}
if (markgroups) cell->attributes[ID(abcgroup)] = map_autoidx;
if (existing_cell) {
cell->parameters = existing_cell->parameters;
cell->attributes = existing_cell->attributes;
if (cell->attributes.erase("\\abc9_box_seq")) {
module->swap_names(cell, existing_cell);
module->remove(existing_cell);
}
}
else {
cell->parameters = mapped_cell->parameters;
cell->attributes = mapped_cell->attributes;
}
RTLIL::Module* box_module = design->module(mapped_cell->type);
auto abc9_flop = box_module && box_module->attributes.count("\\abc9_flop");
for (auto &conn : mapped_cell->connections()) {
RTLIL::SigSpec newsig;
for (auto c : conn.second.chunks()) {
if (c.width == 0)
continue;
//log_assert(c.width == 1);
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));
newsig.append(c);
}
cell->setPort(conn.first, newsig);
if (!abc9_flop) {
if (cell->input(conn.first)) {
for (auto i : newsig)
bit2sinks[i].push_back(cell);
for (auto i : conn.second)
bit_users[i].insert(mapped_cell->name);
}
if (cell->output(conn.first))
for (auto i : conn.second)
bit_drivers[i].insert(mapped_cell->name);
}
}
}
// Copy connections (and rename) from mapped_mod to module
for (auto conn : mapped_mod->connections()) {
if (!conn.first.is_fully_const()) {
auto chunks = conn.first.chunks();
for (auto &c : chunks)
c.wire = module->wires_.at(remap_name(c.wire->name));
conn.first = std::move(chunks);
}
if (!conn.second.is_fully_const()) {
auto chunks = conn.second.chunks();
for (auto &c : chunks)
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));
conn.second = std::move(chunks);
}
module->connect(conn);
}
for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
int in_wires = 0, out_wires = 0;
// Stitch in mapped_mod's inputs/outputs into module
for (auto port : mapped_mod->ports) {
RTLIL::Wire *w = mapped_mod->wire(port);
RTLIL::Wire *wire = module->wire(port);
log_assert(wire);
RTLIL::Wire *remap_wire = module->wire(remap_name(port));
RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
log_assert(GetSize(signal) >= GetSize(remap_wire));
RTLIL::SigSig conn;
if (w->port_output) {
conn.first = signal;
conn.second = remap_wire;
out_wires++;
module->connect(conn);
}
else if (w->port_input) {
conn.first = remap_wire;
conn.second = signal;
in_wires++;
module->connect(conn);
}
}
for (auto &it : bit_users)
if (bit_drivers.count(it.first))
for (auto driver_cell : bit_drivers.at(it.first))
for (auto user_cell : it.second)
toposort.edge(driver_cell, user_cell);
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
log_assert(no_loops);
for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
RTLIL::Cell *not_cell = mapped_mod->cell(*ii);
log_assert(not_cell);
if (not_cell->type != ID($_NOT_))
continue;
auto it = not2drivers.find(not_cell);
if (it == not2drivers.end())
continue;
RTLIL::Cell *driver_lut = it->second;
RTLIL::SigBit a_bit = not_cell->getPort(ID::A);
RTLIL::SigBit y_bit = not_cell->getPort(ID::Y);
RTLIL::Const driver_mask;
a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name));
y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name));
auto jt = bit2sinks.find(a_bit);
if (jt == bit2sinks.end())
goto clone_lut;
for (auto sink_cell : jt->second)
if (sink_cell->type != ID($lut))
goto clone_lut;
// Push downstream LUTs past inverter
for (auto sink_cell : jt->second) {
SigSpec A = sink_cell->getPort(ID::A);
RTLIL::Const mask = sink_cell->getParam(ID(LUT));
int index = 0;
for (; index < GetSize(A); index++)
if (A[index] == a_bit)
break;
log_assert(index < GetSize(A));
int i = 0;
while (i < GetSize(mask)) {
for (int j = 0; j < (1 << index); j++)
std::swap(mask[i+j], mask[i+j+(1 << index)]);
i += 1 << (index+1);
}
A[index] = y_bit;
sink_cell->setPort(ID::A, A);
sink_cell->setParam(ID(LUT), mask);
}
// Since we have rewritten all sinks (which we know
// to be only LUTs) to be after the inverter, we can
// go ahead and clone the LUT with the expectation
// that the original driving LUT will become dangling
// and get cleaned away
clone_lut:
driver_mask = driver_lut->getParam(ID(LUT));
for (auto &b : driver_mask.bits) {
if (b == RTLIL::State::S0) b = RTLIL::State::S1;
else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
}
auto cell = module->addLut(NEW_ID,
driver_lut->getPort(ID::A),
y_bit,
driver_mask);
for (auto &bit : cell->connections_.at(ID::A)) {
bit.wire = module->wires_.at(remap_name(bit.wire->name));
bit2sinks[bit].push_back(cell);
}
}
//log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
log("ABC RESULTS: input signals: %8d\n", in_wires);
log("ABC RESULTS: output signals: %8d\n", out_wires);
design->remove(mapped_mod);
}
//else
//{
// log("Don't call ABC as there is nothing to map.\n");
//}
}
struct Abc9MapPass : public Pass {
Abc9MapPass() : Pass("abc9_map", "use ABC9 for technology mapping") { }
void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" abc9_map [options] [selection]\n");
log("\n");
log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n");
log("library to a target architecture.\n");
log("\n");
log(" -exe <command>\n");
#ifdef ABCEXTERNAL
log(" use the specified command instead of \"" ABCEXTERNAL "\" to execute ABC.\n");
#else
log(" use the specified command instead of \"<yosys-bindir>/yosys-abc\" to execute ABC.\n");
#endif
log(" This can e.g. be used to call a specific version of ABC or a wrapper.\n");
log("\n");
log(" -script <file>\n");
log(" use the specified ABC script file instead of the default script.\n");
log("\n");
log(" if <file> starts with a plus sign (+), then the rest of the filename\n");
log(" string is interpreted as the command string to be passed to ABC. The\n");
log(" leading plus sign is removed and all commas (,) in the string are\n");
log(" replaced with blanks before the string is passed to ABC.\n");
log("\n");
log(" if no -script parameter is given, the following scripts are used:\n");
log("\n");
log(" for -lut/-luts (only one LUT size):\n");
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT /*"; lutpack {S}"*/).c_str());
log("\n");
log(" for -lut/-luts (different LUT sizes):\n");
log("%s\n", fold_abc9_cmd(ABC_COMMAND_LUT).c_str());
log("\n");
log(" -fast\n");
log(" use different default scripts that are slightly faster (at the cost\n");
log(" of output quality):\n");
log("\n");
log(" for -lut/-luts:\n");
log("%s\n", fold_abc9_cmd(ABC_FAST_COMMAND_LUT).c_str());
log("\n");
log(" -D <picoseconds>\n");
log(" set delay target. the string {D} in the default scripts above is\n");
log(" replaced by this option when used, and an empty string otherwise\n");
log(" (indicating best possible delay).\n");
// log(" This also replaces 'dretime' with 'dretime; retime -o {D}' in the\n");
// log(" default scripts above.\n");
log("\n");
// log(" -S <num>\n");
// log(" maximum number of LUT inputs shared.\n");
// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
// log("\n");
log(" -lut <width>\n");
log(" generate netlist using luts of (max) the specified width.\n");
log("\n");
log(" -lut <w1>:<w2>\n");
log(" generate netlist using luts of (max) the specified width <w2>. All\n");
log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
log(" the area cost doubles with each additional input bit. the delay cost\n");
log(" is still constant for all lut widths.\n");
log("\n");
log(" -lut <file>\n");
log(" pass this file with lut library to ABC.\n");
log("\n");
log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
log(" 2, 3, .. inputs.\n");
log("\n");
// log(" -dff\n");
// log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n");
// log(" clock domains are automatically partitioned in clock domains and each\n");
// log(" domain is passed through ABC independently.\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(" -showtmp\n");
log(" print the temp dir name in log. usually this is suppressed so that the\n");
log(" command output is identical across runs.\n");
log("\n");
log(" -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(" pass this file with box library to ABC. Use with -lut.\n");
log("\n");
log(" -tempdir <dir>\n");
log(" use this as the temp dir.\n");
log("\n");
log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
log("ABC on logic snippets extracted from your design. You will not get any useful\n");
log("output when passing an ABC script that writes a file. Instead write your full\n");
log("design as BLIF file with write_blif and then load that into ABC externally if\n");
log("you want to use ABC to convert your design into another format.\n");
log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing ABC9_MAP pass (technology mapping using ABC9).\n");
#ifdef ABCEXTERNAL
std::string exe_file = ABCEXTERNAL;
#else
std::string exe_file = proc_self_dirname() + "yosys-abc";
#endif
std::string script_file, clk_str, box_file, lut_file;
std::string delay_target, lutin_shared = "-S 1", wire_delay;
std::string tempdir_name;
bool fast_mode = false;
bool show_tempdir = false;
bool nomfs = false;
vector<int> lut_costs;
markgroups = false;
#if 0
cleanup = false;
show_tempdir = true;
#endif
#ifdef _WIN32
#ifndef ABCEXTERNAL
if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe"))
exe_file = proc_self_dirname() + "..\\yosys-abc";
#endif
#endif
size_t argidx;
char pwd [PATH_MAX];
if (!getcwd(pwd, sizeof(pwd))) {
log_cmd_error("getcwd failed: %s\n", strerror(errno));
log_abort();
}
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
if (arg == "-exe" && argidx+1 < args.size()) {
exe_file = args[++argidx];
continue;
}
if (arg == "-script" && argidx+1 < args.size()) {
script_file = args[++argidx];
rewrite_filename(script_file);
if (!script_file.empty() && !is_absolute_path(script_file) && script_file[0] != '+')
script_file = std::string(pwd) + "/" + script_file;
continue;
}
if (arg == "-D" && argidx+1 < args.size()) {
delay_target = "-D " + args[++argidx];
continue;
}
//if (arg == "-S" && argidx+1 < args.size()) {
// lutin_shared = "-S " + args[++argidx];
// continue;
//}
if (arg == "-lut" && argidx+1 < args.size()) {
string arg = args[++argidx];
if (arg.find_first_not_of("0123456789:") == std::string::npos) {
size_t pos = arg.find_first_of(':');
int lut_mode = 0, lut_mode2 = 0;
if (pos != string::npos) {
lut_mode = atoi(arg.substr(0, pos).c_str());
lut_mode2 = atoi(arg.substr(pos+1).c_str());
} else {
lut_mode = atoi(arg.c_str());
lut_mode2 = lut_mode;
}
lut_costs.clear();
for (int i = 0; i < lut_mode; i++)
lut_costs.push_back(1);
for (int i = lut_mode; i < lut_mode2; i++)
lut_costs.push_back(2 << (i - lut_mode));
}
else {
lut_file = arg;
rewrite_filename(lut_file);
if (!lut_file.empty() && !is_absolute_path(lut_file) && lut_file[0] != '+')
lut_file = std::string(pwd) + "/" + lut_file;
}
continue;
}
if (arg == "-luts" && argidx+1 < args.size()) {
lut_costs.clear();
for (auto &tok : split_tokens(args[++argidx], ",")) {
auto parts = split_tokens(tok, ":");
if (GetSize(parts) == 0 && !lut_costs.empty())
lut_costs.push_back(lut_costs.back());
else if (GetSize(parts) == 1)
lut_costs.push_back(atoi(parts.at(0).c_str()));
else if (GetSize(parts) == 2)
while (GetSize(lut_costs) < atoi(parts.at(0).c_str()))
lut_costs.push_back(atoi(parts.at(1).c_str()));
else
log_cmd_error("Invalid -luts syntax.\n");
}
continue;
}
if (arg == "-fast") {
fast_mode = true;
continue;
}
if (arg == "-showtmp") {
show_tempdir = true;
continue;
}
if (arg == "-markgroups") {
markgroups = true;
continue;
}
if (arg == "-box" && argidx+1 < args.size()) {
box_file = args[++argidx];
continue;
}
if (arg == "-W" && argidx+1 < args.size()) {
wire_delay = "-W " + args[++argidx];
continue;
}
if (arg == "-nomfs") {
nomfs = true;
continue;
}
if (arg == "-tempdir" && argidx+1 < args.size()) {
tempdir_name = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, design);
// ABC expects a box file for XAIG
if (box_file.empty())
box_file = "+/dummy.box";
rewrite_filename(box_file);
if (!box_file.empty() && !is_absolute_path(box_file) && box_file[0] != '+')
box_file = std::string(pwd) + "/" + box_file;
if (tempdir_name.empty())
log_cmd_error("abc9_map '-tempdir' option is mandatory.\n");
for (auto mod : design->selected_modules())
{
if (mod->processes.size() > 0) {
log("Skipping module %s as it contains processes.\n", log_id(mod));
continue;
}
abc9_module(design, mod, script_file, exe_file, lut_costs,
delay_target, lutin_shared, fast_mode, show_tempdir,
box_file, lut_file, wire_delay, nomfs, tempdir_name);
}
}
} Abc9MapPass;
PRIVATE_NAMESPACE_END

View File

@ -26,6 +26,13 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN
int map_autoidx;
inline std::string remap_name(RTLIL::IdString abc9_name)
{
return stringf("$abc$%d$%s", map_autoidx, abc9_name.c_str()+1);
}
void break_scc(RTLIL::Module *module) void break_scc(RTLIL::Module *module)
{ {
// For every unique SCC found, (arbitrarily) find the first // For every unique SCC found, (arbitrarily) find the first
@ -254,6 +261,45 @@ void prep_holes(RTLIL::Module *module, bool dff)
RTLIL::Module* box_module = design->module(cell->type); RTLIL::Module* box_module = design->module(cell->type);
if (!box_module || !box_module->attributes.count("\\abc9_box_id")) if (!box_module || !box_module->attributes.count("\\abc9_box_id"))
continue; continue;
bool blackbox = box_module->get_blackbox_attribute(true /* ignore_wb */);
// Fully pad all unused input connections of this box cell with S0
// Fully pad all undriven output connections of this box cell with anonymous wires
for (const auto &port_name : box_module->ports) {
RTLIL::Wire* w = box_module->wire(port_name);
log_assert(w);
auto it = cell->connections_.find(port_name);
if (w->port_input) {
RTLIL::SigSpec rhs;
if (it != cell->connections_.end()) {
if (GetSize(it->second) < GetSize(w))
it->second.append(RTLIL::SigSpec(State::S0, GetSize(w)-GetSize(it->second)));
rhs = it->second;
}
else {
rhs = RTLIL::SigSpec(State::S0, GetSize(w));
cell->setPort(port_name, rhs);
}
}
if (w->port_output) {
RTLIL::SigSpec rhs;
auto it = cell->connections_.find(w->name);
if (it != cell->connections_.end()) {
if (GetSize(it->second) < GetSize(w))
it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second)));
rhs = it->second;
}
else {
Wire *wire = module->addWire(NEW_ID, GetSize(w));
if (blackbox)
wire->set_bool_attribute(ID(abc9_padding));
rhs = wire;
cell->setPort(port_name, rhs);
}
}
}
cell->attributes["\\abc9_box_seq"] = box_list.size(); cell->attributes["\\abc9_box_seq"] = box_list.size();
box_list.emplace_back(cell); box_list.emplace_back(cell);
} }
@ -377,6 +423,383 @@ void prep_holes(RTLIL::Module *module, bool dff)
} }
} }
void reintegrate(RTLIL::Module *module)
{
auto design = module->design;
log_assert(design);
map_autoidx = autoidx++;
RTLIL::Module *mapped_mod = design->module(stringf("%s$abc9", module->name.c_str()));
if (mapped_mod == NULL)
log_error("ABC output file does not contain a module `%s$abc'.\n", log_id(module));
for (auto w : mapped_mod->wires())
module->addWire(remap_name(w->name), GetSize(w));
dict<IdString,IdString> box_lookup;
dict<IdString,std::vector<IdString>> box_ports;
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(stringf("$__boxid%d", id), m->name));
if (!r.second)
log_error("Module '%s' has the same abc9_box_id = %d value as '%s'.\n",
log_id(m), id, log_id(r.first->second));
log_assert(r.second);
auto r2 = box_ports.insert(m->name);
if (r2.second) {
// Make carry in the last PI, and carry out the last PO
// since ABC requires it this way
IdString carry_in, carry_out;
for (const auto &port_name : m->ports) {
auto w = m->wire(port_name);
log_assert(w);
if (w->get_bool_attribute("\\abc9_carry")) {
if (w->port_input) {
if (carry_in != IdString())
log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(m));
carry_in = port_name;
}
if (w->port_output) {
if (carry_out != IdString())
log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(m));
carry_out = port_name;
}
}
else
r2.first->second.push_back(port_name);
}
if (carry_in != IdString() && carry_out == IdString())
log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(m));
if (carry_in == IdString() && carry_out != IdString())
log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(m));
if (carry_in != IdString()) {
r2.first->second.push_back(carry_in);
r2.first->second.push_back(carry_out);
}
}
}
for (auto it = module->cells_.begin(); it != module->cells_.end(); )
if (it->second->type.in(ID($_AND_), ID($_NOT_), ID($__ABC9_FF_)))
it = module->cells_.erase(it);
else
++it;
dict<SigBit, pool<IdString>> bit_drivers, bit_users;
TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
dict<RTLIL::Cell*,RTLIL::Cell*> not2drivers;
dict<SigBit, std::vector<RTLIL::Cell*>> bit2sinks;
std::map<IdString, int> cell_stats;
for (auto mapped_cell : mapped_mod->cells())
{
toposort.node(mapped_cell->name);
RTLIL::Cell *cell = nullptr;
if (mapped_cell->type == ID($_NOT_)) {
RTLIL::SigBit a_bit = mapped_cell->getPort(ID::A);
RTLIL::SigBit y_bit = mapped_cell->getPort(ID::Y);
bit_users[a_bit].insert(mapped_cell->name);
bit_drivers[y_bit].insert(mapped_cell->name);
if (!a_bit.wire) {
mapped_cell->setPort(ID::Y, module->addWire(NEW_ID));
RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
log_assert(wire);
module->connect(RTLIL::SigBit(wire, y_bit.offset), State::S1);
}
else {
RTLIL::Cell* driver_lut = nullptr;
// ABC can return NOT gates that drive POs
if (!a_bit.wire->port_input) {
// If it's not a NOT gate that that comes from a PI directly,
// find the driver LUT and clone that to guarantee that we won't
// increase the max logic depth
// (TODO: Optimise by not cloning unless will increase depth)
RTLIL::IdString driver_name;
if (GetSize(a_bit.wire) == 1)
driver_name = stringf("%s$lut", a_bit.wire->name.c_str());
else
driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset);
driver_lut = mapped_mod->cell(driver_name);
}
if (!driver_lut) {
// If a driver couldn't be found (could be from PI or box CI)
// then implement using a LUT
cell = module->addLut(remap_name(stringf("%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(y_bit.wire->name)), y_bit.offset),
RTLIL::Const::from_string("01"));
bit2sinks[cell->getPort(ID::A)].push_back(cell);
cell_stats[ID($lut)]++;
}
else
not2drivers[mapped_cell] = driver_lut;
}
continue;
}
cell_stats[mapped_cell->type]++;
RTLIL::Cell *existing_cell = nullptr;
if (mapped_cell->type.in(ID($lut), ID($__ABC9_FF_))) {
if (mapped_cell->type == ID($lut) &&
GetSize(mapped_cell->getPort(ID::A)) == 1 &&
mapped_cell->getParam(ID(LUT)) == RTLIL::Const::from_string("01")) {
SigSpec my_a = module->wires_.at(remap_name(mapped_cell->getPort(ID::A).as_wire()->name));
SigSpec my_y = module->wires_.at(remap_name(mapped_cell->getPort(ID::Y).as_wire()->name));
module->connect(my_y, my_a);
log_abort();
continue;
}
cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
}
else {
existing_cell = module->cell(mapped_cell->name);
log_assert(existing_cell);
if (mapped_cell->type.begins_with("$__boxid")) {
auto type = box_lookup.at(mapped_cell->type, IdString());
if (type == IdString())
log_error("No module with abc9_box_id = %s found.\n", mapped_cell->type.c_str() + strlen("$__boxid"));
mapped_cell->type = type;
}
cell = module->addCell(remap_name(mapped_cell->name), mapped_cell->type);
}
if (existing_cell) {
auto it = mapped_cell->connections_.find("\\i");
log_assert(it != mapped_cell->connections_.end());
SigSpec inputs = std::move(it->second);
mapped_cell->connections_.erase(it);
it = mapped_cell->connections_.find("\\o");
log_assert(it != mapped_cell->connections_.end());
SigSpec outputs = std::move(it->second);
mapped_cell->connections_.erase(it);
RTLIL::Module* box_module = design->module(mapped_cell->type);
auto abc9_flop = box_module->attributes.count("\\abc9_flop");
if (!abc9_flop) {
for (const auto &i : inputs)
bit_users[i].insert(mapped_cell->name);
for (const auto &i : outputs)
bit_drivers[i].insert(mapped_cell->name);
}
int input_count = 0, output_count = 0;
for (const auto &port_name : box_ports.at(cell->type)) {
RTLIL::Wire *w = box_module->wire(port_name);
log_assert(w);
SigSpec sig;
if (w->port_input) {
sig = inputs.extract(input_count, GetSize(w));
input_count += GetSize(w);
}
if (w->port_output) {
sig = outputs.extract(output_count, GetSize(w));
output_count += GetSize(w);
}
SigSpec newsig;
for (auto c : sig.chunks()) {
if (c.width == 0)
continue;
//log_assert(c.width == 1);
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));
newsig.append(c);
}
auto it = existing_cell->connections_.find(port_name);
if (it == existing_cell->connections_.end())
continue;
if (GetSize(newsig) > GetSize(it->second))
newsig = newsig.extract(0, GetSize(it->second));
else
log_assert(GetSize(newsig) == GetSize(it->second));
cell->setPort(port_name, newsig);
if (w->port_input && !abc9_flop)
for (const auto &i : newsig)
bit2sinks[i].push_back(cell);
}
}
else {
for (auto &mapped_conn : mapped_cell->connections()) {
RTLIL::SigSpec newsig;
for (auto c : mapped_conn.second.chunks()) {
if (c.width == 0)
continue;
//log_assert(c.width == 1);
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));
newsig.append(c);
}
cell->setPort(mapped_conn.first, newsig);
if (cell->input(mapped_conn.first)) {
for (auto i : newsig)
bit2sinks[i].push_back(cell);
for (auto i : mapped_conn.second)
bit_users[i].insert(mapped_cell->name);
}
if (cell->output(mapped_conn.first))
for (auto i : mapped_conn.second)
bit_drivers[i].insert(mapped_cell->name);
}
}
if (existing_cell) {
cell->parameters = existing_cell->parameters;
cell->attributes = existing_cell->attributes;
if (cell->attributes.erase("\\abc9_box_seq")) {
module->swap_names(cell, existing_cell);
module->remove(existing_cell);
}
}
else {
cell->parameters = mapped_cell->parameters;
cell->attributes = mapped_cell->attributes;
}
}
// Copy connections (and rename) from mapped_mod to module
for (auto conn : mapped_mod->connections()) {
if (!conn.first.is_fully_const()) {
auto chunks = conn.first.chunks();
for (auto &c : chunks)
c.wire = module->wires_.at(remap_name(c.wire->name));
conn.first = std::move(chunks);
}
if (!conn.second.is_fully_const()) {
auto chunks = conn.second.chunks();
for (auto &c : chunks)
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));
conn.second = std::move(chunks);
}
module->connect(conn);
}
for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
int in_wires = 0, out_wires = 0;
// Stitch in mapped_mod's inputs/outputs into module
for (auto port : mapped_mod->ports) {
RTLIL::Wire *w = mapped_mod->wire(port);
RTLIL::Wire *wire = module->wire(port);
log_assert(wire);
RTLIL::Wire *remap_wire = module->wire(remap_name(port));
RTLIL::SigSpec signal(wire, 0, GetSize(remap_wire));
log_assert(GetSize(signal) >= GetSize(remap_wire));
RTLIL::SigSig conn;
if (w->port_output) {
conn.first = signal;
conn.second = remap_wire;
out_wires++;
module->connect(conn);
}
else if (w->port_input) {
conn.first = remap_wire;
conn.second = signal;
in_wires++;
module->connect(conn);
}
}
for (auto &it : bit_users)
if (bit_drivers.count(it.first))
for (auto driver_cell : bit_drivers.at(it.first))
for (auto user_cell : it.second)
toposort.edge(driver_cell, user_cell);
bool no_loops YS_ATTRIBUTE(unused) = toposort.sort();
log_assert(no_loops);
for (auto ii = toposort.sorted.rbegin(); ii != toposort.sorted.rend(); ii++) {
RTLIL::Cell *not_cell = mapped_mod->cell(*ii);
log_assert(not_cell);
if (not_cell->type != ID($_NOT_))
continue;
auto it = not2drivers.find(not_cell);
if (it == not2drivers.end())
continue;
RTLIL::Cell *driver_lut = it->second;
RTLIL::SigBit a_bit = not_cell->getPort(ID::A);
RTLIL::SigBit y_bit = not_cell->getPort(ID::Y);
RTLIL::Const driver_mask;
a_bit.wire = module->wires_.at(remap_name(a_bit.wire->name));
y_bit.wire = module->wires_.at(remap_name(y_bit.wire->name));
auto jt = bit2sinks.find(a_bit);
if (jt == bit2sinks.end())
goto clone_lut;
for (auto sink_cell : jt->second)
if (sink_cell->type != ID($lut))
goto clone_lut;
// Push downstream LUTs past inverter
for (auto sink_cell : jt->second) {
SigSpec A = sink_cell->getPort(ID::A);
RTLIL::Const mask = sink_cell->getParam(ID(LUT));
int index = 0;
for (; index < GetSize(A); index++)
if (A[index] == a_bit)
break;
log_assert(index < GetSize(A));
int i = 0;
while (i < GetSize(mask)) {
for (int j = 0; j < (1 << index); j++)
std::swap(mask[i+j], mask[i+j+(1 << index)]);
i += 1 << (index+1);
}
A[index] = y_bit;
sink_cell->setPort(ID::A, A);
sink_cell->setParam(ID(LUT), mask);
}
// Since we have rewritten all sinks (which we know
// to be only LUTs) to be after the inverter, we can
// go ahead and clone the LUT with the expectation
// that the original driving LUT will become dangling
// and get cleaned away
clone_lut:
driver_mask = driver_lut->getParam(ID(LUT));
for (auto &b : driver_mask.bits) {
if (b == RTLIL::State::S0) b = RTLIL::State::S1;
else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
}
auto cell = module->addLut(NEW_ID,
driver_lut->getPort(ID::A),
y_bit,
driver_mask);
for (auto &bit : cell->connections_.at(ID::A)) {
bit.wire = module->wires_.at(remap_name(bit.wire->name));
bit2sinks[bit].push_back(cell);
}
}
//log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
log("ABC RESULTS: input signals: %8d\n", in_wires);
log("ABC RESULTS: output signals: %8d\n", out_wires);
design->remove(mapped_mod);
}
struct Abc9OpsPass : public Pass { struct Abc9OpsPass : public Pass {
Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { } Abc9OpsPass() : Pass("abc9_ops", "helper functions for ABC9") { }
void help() YS_OVERRIDE void help() YS_OVERRIDE
@ -394,6 +817,7 @@ struct Abc9OpsPass : public Pass {
bool unbreak_scc_mode = false; bool unbreak_scc_mode = false;
bool prep_dff_mode = false; bool prep_dff_mode = false;
bool prep_holes_mode = false; bool prep_holes_mode = false;
bool reintegrate_mode = false;
bool dff_mode = false; bool dff_mode = false;
size_t argidx; size_t argidx;
@ -415,6 +839,10 @@ struct Abc9OpsPass : public Pass {
prep_holes_mode = true; prep_holes_mode = true;
continue; continue;
} }
if (arg == "-reintegrate") {
reintegrate_mode = true;
continue;
}
if (arg == "-dff") { if (arg == "-dff") {
dff_mode = true; dff_mode = true;
continue; continue;
@ -424,8 +852,6 @@ struct Abc9OpsPass : public Pass {
extra_args(args, argidx, design); extra_args(args, argidx, design);
for (auto mod : design->selected_modules()) { for (auto mod : design->selected_modules()) {
if (mod->get_blackbox_attribute())
continue;
if (mod->get_bool_attribute("\\abc9_holes")) if (mod->get_bool_attribute("\\abc9_holes"))
continue; continue;
@ -442,6 +868,8 @@ struct Abc9OpsPass : public Pass {
prep_dff(mod); prep_dff(mod);
if (prep_holes_mode) if (prep_holes_mode)
prep_holes(mod, dff_mode); prep_holes(mod, dff_mode);
if (reintegrate_mode)
reintegrate(mod);
} }
} }
} Abc9OpsPass; } Abc9OpsPass;

View File

@ -128,6 +128,8 @@ static void run_ice40_opts(Module *module)
new_attr.insert(std::make_pair(a.first, a.second)); new_attr.insert(std::make_pair(a.first, a.second));
else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID(module_not_derived))) else if (a.first.in(ID(SB_LUT4.name), ID::keep, ID(module_not_derived)))
continue; continue;
else if (a.first.begins_with("\\SB_CARRY.\\"))
continue;
else else
log_abort(); log_abort();
cell->attributes = std::move(new_attr); cell->attributes = std::move(new_attr);

View File

@ -640,11 +640,9 @@ module DSP48E1 (
parameter [4:0] IS_INMODE_INVERTED = 5'b0; parameter [4:0] IS_INMODE_INVERTED = 5'b0;
parameter [6:0] IS_OPMODE_INVERTED = 7'b0; parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
parameter _TECHMAP_CELLTYPE_ = ""; wire [47:0] $P, $PCOUT;
localparam techmap_guard = (_TECHMAP_CELLTYPE_ != "");
`define DSP48E1_INST(__CELL__) """ DSP48E1 #(
__CELL__ #(
.ACASCREG(ACASCREG), .ACASCREG(ACASCREG),
.ADREG(ADREG), .ADREG(ADREG),
.ALUMODEREG(ALUMODEREG), .ALUMODEREG(ALUMODEREG),
@ -682,17 +680,17 @@ __CELL__ #(
.CARRYOUT(CARRYOUT), .CARRYOUT(CARRYOUT),
.MULTSIGNOUT(MULTSIGNOUT), .MULTSIGNOUT(MULTSIGNOUT),
.OVERFLOW(OVERFLOW), .OVERFLOW(OVERFLOW),
.P(oP), .P($P),
.PATTERNBDETECT(PATTERNBDETECT), .PATTERNBDETECT(PATTERNBDETECT),
.PATTERNDETECT(PATTERNDETECT), .PATTERNDETECT(PATTERNDETECT),
.PCOUT(oPCOUT), .PCOUT($PCOUT),
.UNDERFLOW(UNDERFLOW), .UNDERFLOW(UNDERFLOW),
.A(iA), .A(A),
.ACIN(ACIN), .ACIN(ACIN),
.ALUMODE(ALUMODE), .ALUMODE(ALUMODE),
.B(iB), .B(B),
.BCIN(BCIN), .BCIN(BCIN),
.C(iC), .C(C),
.CARRYCASCIN(CARRYCASCIN), .CARRYCASCIN(CARRYCASCIN),
.CARRYIN(CARRYIN), .CARRYIN(CARRYIN),
.CARRYINSEL(CARRYINSEL), .CARRYINSEL(CARRYINSEL),
@ -710,7 +708,7 @@ __CELL__ #(
.CEM(CEM), .CEM(CEM),
.CEP(CEP), .CEP(CEP),
.CLK(CLK), .CLK(CLK),
.D(iD), .D(D),
.INMODE(INMODE), .INMODE(INMODE),
.MULTSIGNIN(MULTSIGNIN), .MULTSIGNIN(MULTSIGNIN),
.OPMODE(OPMODE), .OPMODE(OPMODE),
@ -726,158 +724,35 @@ __CELL__ #(
.RSTM(RSTM), .RSTM(RSTM),
.RSTP(RSTP) .RSTP(RSTP)
); );
"""
wire [29:0] iA;
wire [17:0] iB;
wire [47:0] iC;
wire [24:0] iD;
wire pA, pB, pC, pD, pAD, pM, pP;
wire [47:0] oP, mP;
wire [47:0] oPCOUT, mPCOUT;
generate generate
if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin wire [29:0] $A;
// Disconnect the A-input if MREG is enabled, since wire [17:0] $B;
// combinatorial path is broken wire [47:0] $C;
if (AREG == 0 && MREG == 0 && PREG == 0) wire [24:0] $D;
assign iA = A, pA = 1'bx;
else
$__ABC9_REG #(.WIDTH(30)) rA (.I(A), .O(iA), .Q(pA));
if (BREG == 0 && MREG == 0 && PREG == 0)
assign iB = B, pB = 1'bx;
else
$__ABC9_REG #(.WIDTH(18)) rB (.I(B), .O(iB), .Q(pB));
if (CREG == 0 && PREG == 0)
assign iC = C, pC = 1'bx;
else
$__ABC9_REG #(.WIDTH(48)) rC (.I(C), .O(iC), .Q(pC));
if (DREG == 0)
assign iD = D;
else if (techmap_guard)
$error("Invalid DSP48E1 configuration: DREG enabled but USE_DPORT == \"FALSE\"");
assign pD = 1'bx;
if (ADREG == 1 && techmap_guard)
$error("Invalid DSP48E1 configuration: ADREG enabled but USE_DPORT == \"FALSE\"");
assign pAD = 1'bx;
if (PREG == 0) begin if (PREG == 0) begin
if (MREG == 1) if (MREG == 0 && AREG == 0) assign $A = A;
$__ABC9_REG rM (.Q(pM)); else assign $A = 30'bx;
else if (MREG == 0 && BREG == 0) assign $B = B;
assign pM = 1'bx; else assign $B = 18'bx;
assign pP = 1'bx; if (MREG == 0 && DREG == 0) assign $D = D;
end else begin else assign $D = 25'bx;
assign pM = 1'bx;
$__ABC9_REG rP (.Q(pP)); if (CREG == 0) assign $C = C;
else assign $C = 48'bx;
end
else begin
assign $A = 30'bx, $B = 18'bx, $C = 48'bx, $D = 25'bx;
end end
if (MREG == 0 && PREG == 0) if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE")
assign mP = oP, mPCOUT = oPCOUT; $__ABC9_DSP48E1_MULT dsp_comb(.$A($A), .$B($B), .$C($C), .$D($D), .$P($P), .$PCIN(PCIN), .$PCOUT($PCOUT), .P(P), .PCOUT(PCOUT));
else else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE")
assign mP = 1'bx, mPCOUT = 1'bx; $__ABC9_DSP48E1_MULT_DPORT dsp_comb(.$A($A), .$B($B), .$C($C), .$D($D), .$P($P), .$PCIN(PCIN), .$PCOUT($PCOUT), .P(P), .PCOUT(PCOUT));
$__ABC9_DSP48E1_MULT_P_MUX muxP ( else if (USE_MULT == "NONE" && USE_DPORT == "FALSE")
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oP), .Mq(pM), .P(mP), .Pq(pP), .O(P) $__ABC9_DSP48E1 dsp_comb(.$A($A), .$B($B), .$C($C), .$D($D), .$P($P), .$PCIN(PCIN), .$PCOUT($PCOUT), .P(P), .PCOUT(PCOUT));
);
$__ABC9_DSP48E1_MULT_PCOUT_MUX muxPCOUT (
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oPCOUT), .Mq(pM), .P(mPCOUT), .Pq(pP), .O(PCOUT)
);
`DSP48E1_INST($__ABC9_DSP48E1_MULT )
end
else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
// Disconnect the A-input if MREG is enabled, since
// combinatorial path is broken
if (AREG == 0 && ADREG == 0 && MREG == 0 && PREG == 0)
assign iA = A, pA = 1'bx;
else
$__ABC9_REG #(.WIDTH(30)) rA (.I(A), .O(iA), .Q(pA));
if (BREG == 0 && MREG == 0 && PREG == 0)
assign iB = B, pB = 1'bx;
else
$__ABC9_REG #(.WIDTH(18)) rB (.I(B), .O(iB), .Q(pB));
if (CREG == 0 && PREG == 0)
assign iC = C, pC = 1'bx;
else
$__ABC9_REG #(.WIDTH(48)) rC (.I(C), .O(iC), .Q(pC));
if (DREG == 0 && ADREG == 0)
assign iD = D, pD = 1'bx;
else
$__ABC9_REG #(.WIDTH(25)) rD (.I(D), .O(iD), .Q(pD));
if (PREG == 0) begin
if (MREG == 1) begin
assign pAD = 1'bx;
$__ABC9_REG rM (.Q(pM));
end else begin
if (ADREG == 1)
$__ABC9_REG rAD (.Q(pAD));
else
assign pAD = 1'bx;
assign pM = 1'bx;
end
assign pP = 1'bx;
end else begin
assign pAD = 1'bx, pM = 1'bx;
$__ABC9_REG rP (.Q(pP));
end
if (MREG == 0 && PREG == 0)
assign mP = oP, mPCOUT = oPCOUT;
else
assign mP = 1'bx, mPCOUT = 1'bx;
$__ABC9_DSP48E1_MULT_DPORT_P_MUX muxP (
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oP), .Mq(pM), .P(mP), .Pq(pP), .O(P)
);
$__ABC9_DSP48E1_MULT_DPORT_PCOUT_MUX muxPCOUT (
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oPCOUT), .Mq(pM), .P(mPCOUT), .Pq(pP), .O(PCOUT)
);
`DSP48E1_INST($__ABC9_DSP48E1_MULT_DPORT )
end
else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
// Disconnect the A-input if MREG is enabled, since
// combinatorial path is broken
if (AREG == 0 && PREG == 0)
assign iA = A, pA = 1'bx;
else
$__ABC9_REG #(.WIDTH(30)) rA (.I(A), .O(iA), .Q(pA));
if (BREG == 0 && PREG == 0)
assign iB = B, pB = 1'bx;
else
$__ABC9_REG #(.WIDTH(18)) rB (.I(B), .O(iB), .Q(pB));
if (CREG == 0 && PREG == 0)
assign iC = C, pC = 1'bx;
else
$__ABC9_REG #(.WIDTH(48)) rC (.I(C), .O(iC), .Q(pC));
if (DREG == 1 && techmap_guard)
$error("Invalid DSP48E1 configuration: DREG enabled but USE_DPORT == \"FALSE\"");
assign pD = 1'bx;
if (ADREG == 1 && techmap_guard)
$error("Invalid DSP48E1 configuration: ADREG enabled but USE_DPORT == \"FALSE\"");
assign pAD = 1'bx;
if (MREG == 1 && techmap_guard)
$error("Invalid DSP48E1 configuration: MREG enabled but USE_MULT == \"NONE\"");
assign pM = 1'bx;
if (PREG == 1)
$__ABC9_REG rP (.Q(pP));
else
assign pP = 1'bx;
if (MREG == 0 && PREG == 0)
assign mP = oP, mPCOUT = oPCOUT;
else
assign mP = 1'bx, mPCOUT = 1'bx;
$__ABC9_DSP48E1_P_MUX muxP (
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oP), .Mq(pM), .P(mP), .Pq(pP), .O(P)
);
$__ABC9_DSP48E1_PCOUT_MUX muxPCOUT (
.Aq(pA), .Bq(pB), .Cq(pC), .Dq(pD), .ADq(pAD), .I(oPCOUT), .Mq(pM), .P(mPCOUT), .Pq(pP), .O(PCOUT)
);
`DSP48E1_INST($__ABC9_DSP48E1 )
end
else else
$error("Invalid DSP48E1 configuration"); $error("Invalid DSP48E1 configuration");
endgenerate endgenerate
`undef DSP48E1_INST
endmodule endmodule

View File

@ -59,147 +59,22 @@ endmodule
module \$__ABC9_LUT7 (input A, input [6:0] S, output Y); module \$__ABC9_LUT7 (input A, input [6:0] S, output Y);
endmodule endmodule
// Boxes used to represent the comb behaviour of various modes
// Modules used to model the comb/seq behaviour of DSP48E1 // of DSP48E1
// With abc9_map.v responsible for splicing the below modules
// between the combinatorial DSP48E1 box (e.g. disconnecting
// A when AREG, MREG or PREG is enabled and splicing in the
// "$__ABC9_DSP48E1_REG" blackbox as "REG" in the diagram below)
// this acts to first disables the combinatorial path (as there
// is no connectivity through REG), and secondly, since this is
// blackbox a new PI will be introduced with an arrival time of
// zero.
// Note: Since these "$__ABC9_DSP48E1_REG" modules are of a
// sequential nature, they are not passed as a box to ABC and
// (desirably) represented as PO/PIs.
//
// At the DSP output, we place a blackbox mux ("M" in the diagram
// below) to capture the fact that the critical-path could come
// from any one of its inputs.
// In contrast to "REG", the "$__ABC9_DSP48E1_*_MUX" modules are
// combinatorial blackboxes that do get passed to ABC.
// The propagation delay through this box (specified in the box
// file) captures the arrival time of the register (i.e.
// propagation from AREG to P after clock edge), or zero delay
// for the combinatorial path from the DSP.
//
// Doing so should means that ABC is able to analyse the
// worst-case delay through to P, regardless of if it was
// through any combinatorial paths (e.g. B, below) or an
// internal register (A2REG).
// However, the true value of being as complete as this is
// questionable since if AREG=1 and BREG=0 (as below)
// then the worse-case path would very likely be through B
// and very unlikely to be through AREG.Q...?
//
// In graphical form:
//
// +-----+
// +------>> REG >>----+
// | +-----+ |
// | |
// | +---------+ | __
// A >>-+X X-| | +--| \
// | DSP48E1 |P | M |--->> P
// | AREG=1 |-------|__/
// B >>------| |
// +---------+
//
`define ABC9_DSP48E1_MUX(__NAME__) """
module __NAME__ (input Aq, ADq, Bq, Cq, Dq, input [47:0] I, input Mq, input [47:0] P, input Pq, output [47:0] O);
endmodule
"""
(* abc9_box_id=2100 *) `ABC9_DSP48E1_MUX(\$__ABC9_DSP48E1_MULT_P_MUX )
(* abc9_box_id=2101 *) `ABC9_DSP48E1_MUX(\$__ABC9_DSP48E1_MULT_PCOUT_MUX )
(* abc9_box_id=2102 *) `ABC9_DSP48E1_MUX(\$__ABC9_DSP48E1_MULT_DPORT_P_MUX )
(* abc9_box_id=2103 *) `ABC9_DSP48E1_MUX(\$__ABC9_DSP48E1_MULT_DPORT_PCOUT_MUX )
(* abc9_box_id=2104 *) `ABC9_DSP48E1_MUX(\$__ABC9_DSP48E1_P_MUX )
(* abc9_box_id=2105 *) `ABC9_DSP48E1_MUX(\$__ABC9_DSP48E1_PCOUT_MUX )
`define ABC9_DSP48E1(__NAME__) """ `define ABC9_DSP48E1(__NAME__) """
module __NAME__ ( module __NAME__ (
output [29:0] ACOUT, input [29:0] $A,
output [17:0] BCOUT, input [17:0] $B,
output reg CARRYCASCOUT, input [47:0] $C,
output reg [3:0] CARRYOUT, input [24:0] $D,
output reg MULTSIGNOUT, input [47:0] $P,
output OVERFLOW, input [47:0] $PCIN,
output reg signed [47:0] P, input [47:0] $PCOUT,
output PATTERNBDETECT, output [47:0] P,
output PATTERNDETECT, output [47:0] PCOUT);
output [47:0] PCOUT,
output UNDERFLOW,
input signed [29:0] A,
input [29:0] ACIN,
input [3:0] ALUMODE,
input signed [17:0] B,
input [17:0] BCIN,
input [47:0] C,
input CARRYCASCIN,
input CARRYIN,
input [2:0] CARRYINSEL,
input CEA1,
input CEA2,
input CEAD,
input CEALUMODE,
input CEB1,
input CEB2,
input CEC,
input CECARRYIN,
input CECTRL,
input CED,
input CEINMODE,
input CEM,
input CEP,
input CLK,
input [24:0] D,
input [4:0] INMODE,
input MULTSIGNIN,
input [6:0] OPMODE,
input [47:0] PCIN,
input RSTA,
input RSTALLCARRYIN,
input RSTALUMODE,
input RSTB,
input RSTC,
input RSTCTRL,
input RSTD,
input RSTINMODE,
input RSTM,
input RSTP
);
parameter integer ACASCREG = 1;
parameter integer ADREG = 1;
parameter integer ALUMODEREG = 1;
parameter integer AREG = 1;
parameter AUTORESET_PATDET = "NO_RESET";
parameter A_INPUT = "DIRECT";
parameter integer BCASCREG = 1;
parameter integer BREG = 1;
parameter B_INPUT = "DIRECT";
parameter integer CARRYINREG = 1;
parameter integer CARRYINSELREG = 1;
parameter integer CREG = 1;
parameter integer DREG = 1;
parameter integer INMODEREG = 1;
parameter integer MREG = 1;
parameter integer OPMODEREG = 1;
parameter integer PREG = 1;
parameter SEL_MASK = "MASK";
parameter SEL_PATTERN = "PATTERN";
parameter USE_DPORT = "FALSE";
parameter USE_MULT = "MULTIPLY";
parameter USE_PATTERN_DETECT = "NO_PATDET";
parameter USE_SIMD = "ONE48";
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
parameter [47:0] PATTERN = 48'h000000000000;
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
parameter [0:0] IS_CLK_INVERTED = 1'b0;
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
endmodule endmodule
""" """
(* abc9_box_id=3000 *) `ABC9_DSP48E1(\$__ABC9_DSP48E1_MULT ) (* abc9_box_id=3000 *) `ABC9_DSP48E1($__ABC9_DSP48E1_MULT)
(* abc9_box_id=3001 *) `ABC9_DSP48E1(\$__ABC9_DSP48E1_MULT_DPORT ) (* abc9_box_id=3001 *) `ABC9_DSP48E1($__ABC9_DSP48E1_MULT_DPORT)
(* abc9_box_id=3002 *) `ABC9_DSP48E1(\$__ABC9_DSP48E1 ) (* abc9_box_id=3002 *) `ABC9_DSP48E1($__ABC9_DSP48E1)
`undef ABC9_DSP48E1

View File

@ -21,200 +21,32 @@
// ============================================================================ // ============================================================================
(* techmap_celltype = "$__ABC9_ASYNC0 $__ABC9_ASYNC1" *) (* techmap_celltype = "$__ABC9_ASYNC0 $__ABC9_ASYNC1" *)
module \$__ABC9_ASYNC01 (input A, S, output Y); module $__ABC9_ASYNC01(input A, S, output Y);
assign Y = A; assign Y = A;
endmodule endmodule
module \$__ABC9_FF_ (input D, output Q); module $__ABC9_FF_(input D, output Q);
assign Q = D; assign Q = D;
endmodule 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
module \$__ABC9_LUT7 (input A, input [6:0] S, output Y); module $__ABC9_LUT7(input A, input [6:0] S, output Y);
assign Y = A; assign Y = A;
endmodule endmodule
module \$__ABC9_REG (input [WIDTH-1:0] I, output [WIDTH-1:0] O, output Q);
parameter WIDTH = 1;
assign O = I;
endmodule
(* techmap_celltype = "$__ABC9_DSP48E1_MULT_P_MUX $__ABC9_DSP48E1_MULT_PCOUT_MUX $__ABC9_DSP48E1_MULT_DPORT_P_MUX $__ABC9_DSP48E1_MULT_DPORT_PCOUT_MUX $__ABC9_DSP48E1_P_MUX $__ABC9_DSP48E1_PCOUT_MUX" *)
module \$__ABC9_DSP48E1_MUX (
input Aq, Bq, Cq, Dq, ADq,
input [47:0] I,
input Mq,
input [47:0] P,
input Pq,
output [47:0] O
);
assign O = I;
endmodule
(* techmap_celltype = "$__ABC9_DSP48E1_MULT $__ABC9_DSP48E1_MULT_DPORT $__ABC9_DSP48E1" *) (* techmap_celltype = "$__ABC9_DSP48E1_MULT $__ABC9_DSP48E1_MULT_DPORT $__ABC9_DSP48E1" *)
module \$__ABC9_DSP48E1 ( module $ABC9_DSP48E1(
(* techmap_autopurge *) output [29:0] ACOUT, input [29:0] $A,
(* techmap_autopurge *) output [17:0] BCOUT, input [17:0] $B,
(* techmap_autopurge *) output reg CARRYCASCOUT, input [47:0] $C,
(* techmap_autopurge *) output reg [3:0] CARRYOUT, input [24:0] $D,
(* techmap_autopurge *) output reg MULTSIGNOUT, input [47:0] $P,
(* techmap_autopurge *) output OVERFLOW, input [47:0] $PCIN,
(* techmap_autopurge *) output reg signed [47:0] P, input [47:0] $PCOUT,
(* techmap_autopurge *) output PATTERNBDETECT, output [47:0] P,
(* techmap_autopurge *) output PATTERNDETECT, output [47:0] PCOUT
(* techmap_autopurge *) output [47:0] PCOUT,
(* techmap_autopurge *) output UNDERFLOW,
(* techmap_autopurge *) input signed [29:0] A,
(* techmap_autopurge *) input [29:0] ACIN,
(* techmap_autopurge *) input [3:0] ALUMODE,
(* techmap_autopurge *) input signed [17:0] B,
(* techmap_autopurge *) input [17:0] BCIN,
(* techmap_autopurge *) input [47:0] C,
(* techmap_autopurge *) input CARRYCASCIN,
(* techmap_autopurge *) input CARRYIN,
(* techmap_autopurge *) input [2:0] CARRYINSEL,
(* techmap_autopurge *) input CEA1,
(* techmap_autopurge *) input CEA2,
(* techmap_autopurge *) input CEAD,
(* techmap_autopurge *) input CEALUMODE,
(* techmap_autopurge *) input CEB1,
(* techmap_autopurge *) input CEB2,
(* techmap_autopurge *) input CEC,
(* techmap_autopurge *) input CECARRYIN,
(* techmap_autopurge *) input CECTRL,
(* techmap_autopurge *) input CED,
(* techmap_autopurge *) input CEINMODE,
(* techmap_autopurge *) input CEM,
(* techmap_autopurge *) input CEP,
(* techmap_autopurge *) input CLK,
(* techmap_autopurge *) input [24:0] D,
(* techmap_autopurge *) input [4:0] INMODE,
(* techmap_autopurge *) input MULTSIGNIN,
(* techmap_autopurge *) input [6:0] OPMODE,
(* techmap_autopurge *) input [47:0] PCIN,
(* techmap_autopurge *) input RSTA,
(* techmap_autopurge *) input RSTALLCARRYIN,
(* techmap_autopurge *) input RSTALUMODE,
(* techmap_autopurge *) input RSTB,
(* techmap_autopurge *) input RSTC,
(* techmap_autopurge *) input RSTCTRL,
(* techmap_autopurge *) input RSTD,
(* techmap_autopurge *) input RSTINMODE,
(* techmap_autopurge *) input RSTM,
(* techmap_autopurge *) input RSTP
);
parameter integer ACASCREG = 1;
parameter integer ADREG = 1;
parameter integer ALUMODEREG = 1;
parameter integer AREG = 1;
parameter AUTORESET_PATDET = "NO_RESET";
parameter A_INPUT = "DIRECT";
parameter integer BCASCREG = 1;
parameter integer BREG = 1;
parameter B_INPUT = "DIRECT";
parameter integer CARRYINREG = 1;
parameter integer CARRYINSELREG = 1;
parameter integer CREG = 1;
parameter integer DREG = 1;
parameter integer INMODEREG = 1;
parameter integer MREG = 1;
parameter integer OPMODEREG = 1;
parameter integer PREG = 1;
parameter SEL_MASK = "MASK";
parameter SEL_PATTERN = "PATTERN";
parameter USE_DPORT = "FALSE";
parameter USE_MULT = "MULTIPLY";
parameter USE_PATTERN_DETECT = "NO_PATDET";
parameter USE_SIMD = "ONE48";
parameter [47:0] MASK = 48'h3FFFFFFFFFFF;
parameter [47:0] PATTERN = 48'h000000000000;
parameter [3:0] IS_ALUMODE_INVERTED = 4'b0;
parameter [0:0] IS_CARRYIN_INVERTED = 1'b0;
parameter [0:0] IS_CLK_INVERTED = 1'b0;
parameter [4:0] IS_INMODE_INVERTED = 5'b0;
parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
DSP48E1 #(
.ACASCREG(ACASCREG),
.ADREG(ADREG),
.ALUMODEREG(ALUMODEREG),
.AREG(AREG),
.AUTORESET_PATDET(AUTORESET_PATDET),
.A_INPUT(A_INPUT),
.BCASCREG(BCASCREG),
.BREG(BREG),
.B_INPUT(B_INPUT),
.CARRYINREG(CARRYINREG),
.CARRYINSELREG(CARRYINSELREG),
.CREG(CREG),
.DREG(DREG),
.INMODEREG(INMODEREG),
.MREG(MREG),
.OPMODEREG(OPMODEREG),
.PREG(PREG),
.SEL_MASK(SEL_MASK),
.SEL_PATTERN(SEL_PATTERN),
.USE_DPORT(USE_DPORT),
.USE_MULT(USE_MULT),
.USE_PATTERN_DETECT(USE_PATTERN_DETECT),
.USE_SIMD(USE_SIMD),
.MASK(MASK),
.PATTERN(PATTERN),
.IS_ALUMODE_INVERTED(IS_ALUMODE_INVERTED),
.IS_CARRYIN_INVERTED(IS_CARRYIN_INVERTED),
.IS_CLK_INVERTED(IS_CLK_INVERTED),
.IS_INMODE_INVERTED(IS_INMODE_INVERTED),
.IS_OPMODE_INVERTED(IS_OPMODE_INVERTED)
) _TECHMAP_REPLACE_ (
.ACOUT(ACOUT),
.BCOUT(BCOUT),
.CARRYCASCOUT(CARRYCASCOUT),
.CARRYOUT(CARRYOUT),
.MULTSIGNOUT(MULTSIGNOUT),
.OVERFLOW(OVERFLOW),
.P(P),
.PATTERNBDETECT(PATTERNBDETECT),
.PATTERNDETECT(PATTERNDETECT),
.PCOUT(PCOUT),
.UNDERFLOW(UNDERFLOW),
.A(A),
.ACIN(ACIN),
.ALUMODE(ALUMODE),
.B(B),
.BCIN(BCIN),
.C(C),
.CARRYCASCIN(CARRYCASCIN),
.CARRYIN(CARRYIN),
.CARRYINSEL(CARRYINSEL),
.CEA1(CEA1),
.CEA2(CEA2),
.CEAD(CEAD),
.CEALUMODE(CEALUMODE),
.CEB1(CEB1),
.CEB2(CEB2),
.CEC(CEC),
.CECARRYIN(CECARRYIN),
.CECTRL(CECTRL),
.CED(CED),
.CEINMODE(CEINMODE),
.CEM(CEM),
.CEP(CEP),
.CLK(CLK),
.D(D),
.INMODE(INMODE),
.MULTSIGNIN(MULTSIGNIN),
.OPMODE(OPMODE),
.PCIN(PCIN),
.RSTA(RSTA),
.RSTALLCARRYIN(RSTALLCARRYIN),
.RSTALUMODE(RSTALUMODE),
.RSTB(RSTB),
.RSTC(RSTC),
.RSTCTRL(RSTCTRL),
.RSTD(RSTD),
.RSTINMODE(RSTINMODE),
.RSTM(RSTM),
.RSTP(RSTP)
); );
assign P = $P, PCOUT = $PCOUT;
endmodule endmodule

File diff suppressed because it is too large Load Diff

View File

@ -2289,9 +2289,15 @@ module DSP48E1 (
output reg [3:0] CARRYOUT, output reg [3:0] CARRYOUT,
output reg MULTSIGNOUT, output reg MULTSIGNOUT,
output OVERFLOW, output OVERFLOW,
`ifdef YOSYS
(* abc9_arrival = \DSP48E1.P_arrival () *)
`endif
output reg signed [47:0] P, output reg signed [47:0] P,
output reg PATTERNBDETECT, output reg PATTERNBDETECT,
output reg PATTERNDETECT, output reg PATTERNDETECT,
`ifdef YOSYS
(* abc9_arrival = \DSP48E1.PCOUT_arrival () *)
`endif
output [47:0] PCOUT, output [47:0] PCOUT,
output UNDERFLOW, output UNDERFLOW,
input signed [29:0] A, input signed [29:0] A,
@ -2364,6 +2370,77 @@ module DSP48E1 (
parameter [4:0] IS_INMODE_INVERTED = 5'b0; parameter [4:0] IS_INMODE_INVERTED = 5'b0;
parameter [6:0] IS_OPMODE_INVERTED = 7'b0; parameter [6:0] IS_OPMODE_INVERTED = 7'b0;
`ifdef YOSYS
function integer \DSP48E1.P_arrival ;
begin
\DSP48E1.P_arrival = 0;
if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
if (PREG != 0) \DSP48E1.P_arrival = 329;
// Worst-case from CREG and MREG
else if (CREG != 0) \DSP48E1.P_arrival = 1687;
else if (MREG != 0) \DSP48E1.P_arrival = 1671;
// Worst-case from AREG and BREG
else if (AREG != 0) \DSP48E1.P_arrival = 2952;
else if (BREG != 0) \DSP48E1.P_arrival = 2813;
end
else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
if (PREG != 0) \DSP48E1.P_arrival = 329;
// Worst-case from CREG and MREG
else if (CREG != 0) \DSP48E1.P_arrival = 1687;
else if (MREG != 0) \DSP48E1.P_arrival = 1671;
// Worst-case from AREG, ADREG, BREG, DREG
else if (AREG != 0) \DSP48E1.P_arrival = 3935;
else if (DREG != 0) \DSP48E1.P_arrival = 3908;
else if (ADREG != 0) \DSP48E1.P_arrival = 2958;
else if (BREG != 0) \DSP48E1.P_arrival = 2813;
end
else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
if (PREG != 0) \DSP48E1.P_arrival = 329;
// Worst-case from AREG, BREG, CREG
else if (CREG != 0) \DSP48E1.P_arrival = 1687;
else if (AREG != 0) \DSP48E1.P_arrival = 1632;
else if (BREG != 0) \DSP48E1.P_arrival = 1616;
end
//else
// $error("Invalid DSP48E1 configuration");
end
endfunction
function integer \DSP48E1.PCOUT_arrival ;
begin
\DSP48E1.PCOUT_arrival = 0;
if (USE_MULT == "MULTIPLY" && USE_DPORT == "FALSE") begin
if (PREG != 0) \DSP48E1.PCOUT_arrival = 435;
// Worst-case from CREG and MREG
else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835;
else if (MREG != 0) \DSP48E1.PCOUT_arrival = 1819;
// Worst-case from AREG and BREG
else if (AREG != 0) \DSP48E1.PCOUT_arrival = 3098;
else if (BREG != 0) \DSP48E1.PCOUT_arrival = 2960;
end
else if (USE_MULT == "MULTIPLY" && USE_DPORT == "TRUE") begin
if (PREG != 0) \DSP48E1.PCOUT_arrival = 435;
// Worst-case from CREG and MREG
else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835;
else if (MREG != 0) \DSP48E1.PCOUT_arrival = 1819;
// Worst-case from AREG, ADREG, BREG, DREG
else if (AREG != 0) \DSP48E1.PCOUT_arrival = 4083;
else if (DREG != 0) \DSP48E1.PCOUT_arrival = 4056;
else if (BREG != 0) \DSP48E1.PCOUT_arrival = 2960;
else if (ADREG != 0) \DSP48E1.PCOUT_arrival = 2859;
end
else if (USE_MULT == "NONE" && USE_DPORT == "FALSE") begin
if (PREG != 0) \DSP48E1.PCOUT_arrival = 435;
// Worst-case from AREG, BREG, CREG
else if (CREG != 0) \DSP48E1.PCOUT_arrival = 1835;
else if (AREG != 0) \DSP48E1.PCOUT_arrival = 1780;
else if (BREG != 0) \DSP48E1.PCOUT_arrival = 1765;
end
//else
// $error("Invalid DSP48E1 configuration");
end
endfunction
`endif
initial begin initial begin
`ifndef YOSYS `ifndef YOSYS
if (AUTORESET_PATDET != "NO_RESET") $fatal(1, "Unsupported AUTORESET_PATDET value"); if (AUTORESET_PATDET != "NO_RESET") $fatal(1, "Unsupported AUTORESET_PATDET value");
@ -2440,8 +2517,8 @@ module DSP48E1 (
if (CEB2) Br2 <= Br1; if (CEB2) Br2 <= Br1;
end end
end else if (BREG == 1) begin end else if (BREG == 1) begin
//initial Br1 = 25'b0; //initial Br1 = 18'b0;
initial Br2 = 25'b0; initial Br2 = 18'b0;
always @(posedge CLK) always @(posedge CLK)
if (RSTB) begin if (RSTB) begin
Br1 <= 18'b0; Br1 <= 18'b0;
@ -2488,7 +2565,7 @@ module DSP48E1 (
endgenerate endgenerate
// A/D input selection and pre-adder // A/D input selection and pre-adder
wire signed [29:0] Ar12_muxed = INMODEr[0] ? Ar1 : Ar2; wire signed [24:0] Ar12_muxed = INMODEr[0] ? Ar1 : Ar2;
wire signed [24:0] Ar12_gated = INMODEr[1] ? 25'b0 : Ar12_muxed; wire signed [24:0] Ar12_gated = INMODEr[1] ? 25'b0 : Ar12_muxed;
wire signed [24:0] Dr_gated = INMODEr[2] ? Dr : 25'b0; wire signed [24:0] Dr_gated = INMODEr[2] ? Dr : 25'b0;
wire signed [24:0] AD_result = INMODEr[3] ? (Dr_gated - Ar12_gated) : (Dr_gated + Ar12_gated); wire signed [24:0] AD_result = INMODEr[3] ? (Dr_gated - Ar12_gated) : (Dr_gated + Ar12_gated);

View File

@ -33,7 +33,7 @@ design -import gold -as gold
design -import gate -as gate design -import gate -as gate
miter -equiv -flatten -make_assert -make_outputs gold gate miter miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -verify -prove-asserts -show-ports -seq 16 miter sat -verify -prove-asserts -show-ports -seq 16 miter
" " -l ${aag}.log
done done
for aig in *.aig; do for aig in *.aig; do
@ -50,5 +50,5 @@ design -import gold -as gold
design -import gate -as gate design -import gate -as gate
miter -equiv -flatten -make_assert -make_outputs gold gate miter miter -equiv -flatten -make_assert -make_outputs gold gate miter
sat -verify -prove-asserts -show-ports -seq 16 miter sat -verify -prove-asserts -show-ports -seq 16 miter
" " -l ${aig}.log
done done

9
tests/aiger/symbols.aag Normal file
View File

@ -0,0 +1,9 @@
aag 2 1 1 1 0
2
4 2 1
4
i0 d
l0 q
o0 q
c
Generated by Yosys 0.9+932 (git sha1 baba33fb, clang 9.0.0-2 -fPIC -Os)

8
tests/aiger/symbols.aig Normal file
View File

@ -0,0 +1,8 @@
aig 2 1 1 1 0
2 1
4
i0 d
l0 q
o0 q
c
Generated by Yosys 0.9+932 (git sha1 baba33fb, clang 9.0.0-2 -fPIC -Os)