* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
// http://www.eecs.berkeley.edu/~alanmi/abc/
-// [[CITE]] Berkeley Logic Interchange Format (BLIF)
-// University of California. Berkeley. July 28, 1992
-// http://www.ece.cmu.edu/~ee760/760docs/blif.pdf
-
-// [[CITE]] Kahn's Topological sorting algorithm
-// Kahn, Arthur B. (1962), "Topological sorting of large networks", Communications of the ACM 5 (11): 558-562, doi:10.1145/368996.369025
-// http://en.wikipedia.org/wiki/Topological_sorting
-
#define ABC_COMMAND_LIB "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
#define ABC_COMMAND_CTR "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_COMMAND_LUT "&st; &fraig; &scorr; &dc2; &retime; &dch -f; &if;"/*" &mfs"*/
+//#define ABC_COMMAND_LUT "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2"
+#define ABC_COMMAND_LUT "&st; &fraig; &scorr; &dc2; &retime; &dch -f; &if; &mfs"
#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}"
#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put"
#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}"
#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p"
-#define ABC_FAST_COMMAND_LUT "strash; dretime; if"
+#define ABC_FAST_COMMAND_LUT "&st; &retime; &if"
#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}"
#define ABC_FAST_COMMAND_DFL "strash; dretime; map"
return sstr.str();
}
+void handle_loops(RTLIL::Design *design)
+{
+ Pass::call(design, "scc -set_attr abc_scc_id {}");
+
+ design->selection_stack.emplace_back(false);
+ RTLIL::Selection& sel = design->selection_stack.back();
+
+ // For every unique SCC found, (arbitrarily) find the first
+ // cell in the component, and select (and mark) all its output
+ // wires
+ pool<RTLIL::Const> ids_seen;
+ for (auto cell : module->cells()) {
+ auto it = cell->attributes.find("\\abc_scc_id");
+ if (it != cell->attributes.end()) {
+ auto r = ids_seen.insert(it->second);
+ if (r.second) {
+ for (const auto &c : cell->connections()) {
+ if (c.second.is_fully_const()) continue;
+ if (cell->output(c.first)) {
+ SigBit b = c.second.as_bit();
+ Wire *w = b.wire;
+ w->set_bool_attribute("\\abc_scc_break");
+ sel.select(module, w);
+ }
+ }
+ }
+ cell->attributes.erase(it);
+ }
+ }
+
+ // Then cut those selected wires to expose them as new PO/PI
+ Pass::call(design, "expose -cut -sep .abc");
+
+ design->selection_stack.pop_back();
+}
+
std::string add_echos_to_abc_cmd(std::string str)
{
std::string new_str, token;
}
};
+static std::pair<RTLIL::IdString, int> wideports_split(std::string name)
+{
+ int pos = -1;
+
+ if (name.empty() || name.back() != ']')
+ goto failed;
+
+ for (int i = 0; i+1 < GetSize(name); i++) {
+ if (name[i] == '[')
+ pos = i;
+ else if (name[i] < '0' || name[i] > '9')
+ pos = -1;
+ else if (i == pos+1 && name[i] == '0' && name[i+1] != ']')
+ pos = -1;
+ }
+
+ if (pos >= 0)
+ return std::pair<RTLIL::IdString, int>(RTLIL::escape_id(name.substr(0, pos)), atoi(name.c_str() + pos+1));
+
+failed:
+ return std::pair<RTLIL::IdString, int>(name, 0);
+}
+
void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
std::string liberty_file, std::string constr_file, bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
bool keepff, std::string delay_target, std::string sop_inputs, std::string sop_products, std::string lutin_shared, bool fast_mode,
if (!cleanup)
tempdir_name[0] = tempdir_name[4] = '_';
tempdir_name = make_temp_dir(tempdir_name);
- log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n",
+ log_header(design, "Extracting gate netlist of module `%s' to `%s/input.aig'..\n",
module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str());
- std::string abc_script = stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
+ std::string abc_script = stringf("read %s/input.aig; &get -n; ", tempdir_name.c_str());
if (!liberty_file.empty()) {
abc_script += stringf("read_lib -w %s; ", liberty_file.c_str());
for (size_t pos = abc_script.find("{S}"); pos != std::string::npos; pos = abc_script.find("{S}", pos))
abc_script = abc_script.substr(0, pos) + lutin_shared + abc_script.substr(pos+3);
- abc_script += stringf("; &ps; &write -v %s/output.xaig", tempdir_name.c_str());
+ abc_script += stringf("; &write %s/output.aig", tempdir_name.c_str());
abc_script = add_echos_to_abc_cmd(abc_script);
for (size_t i = 0; i+1 < abc_script.size(); i++)
}
}
- Pass::call(design, stringf("aigmap; write_xaiger -map %s/input.symbols %s/input.xaig; ", tempdir_name.c_str(), tempdir_name.c_str()));
+ design->selection_stack.emplace_back(false);
+ RTLIL::Selection& sel = design->selection_stack.back();
+ sel.select(module);
+
+ Pass::call(design, "aigmap");
+
+ handle_loops(design);
+
+ Pass::call(design, stringf("write_xaiger -O -symbols %s/input.aig; ", tempdir_name.c_str()));
+
+ design->selection_stack.pop_back();
+
+ // Now 'unexpose' those wires by undoing
+ // the expose operation -- remove them from PO/PI
+ // and re-connecting them back together
+ for (auto wire : module->wires()) {
+ auto it = wire->attributes.find("\\abc_scc_break");
+ if (it != wire->attributes.end()) {
+ wire->attributes.erase(it);
+ log_assert(wire->port_output);
+ wire->port_output = false;
+ RTLIL::Wire *i_wire = module->wire(wire->name.str() + ".abci");
+ log_assert(i_wire);
+ log_assert(i_wire->port_input);
+ i_wire->port_input = false;
+ module->connect(i_wire, wire);
+ }
+ }
+ module->fixup_ports();
+
+ //log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
+ // count_gates, GetSize(signal_list), count_input, count_output);
log_push();
//if (count_output > 0)
{
- log_header(design, "Executing ABC.\n");
+ log_header(design, "Executing ABC9.\n");
std::string buffer = stringf("%s/stdcells.genlib", tempdir_name.c_str());
f = fopen(buffer.c_str(), "wt");
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.xaig");
+ buffer = stringf("%s/%s", tempdir_name.c_str(), "output.aig");
std::ifstream ifs;
ifs.open(buffer);
if (ifs.fail())
bool builtin_lib = liberty_file.empty();
RTLIL::Design *mapped_design = new RTLIL::Design;
//parse_blif(mapped_design, ifs, builtin_lib ? "\\DFF" : "\\_dff_", false, sop_mode);
- buffer = stringf("%s/%s", tempdir_name.c_str(), "input.symbols");
- AigerReader reader(mapped_design, ifs, "\\netlist", "\\clk", buffer, true /* wideports */);
+ AigerReader reader(mapped_design, ifs, "\\netlist", "" /* clk_name */, "" /* map_filename */, true /* wideports */);
reader.parse_xaiger();
ifs.close();
- log_header(design, "Re-integrating ABC results.\n");
+ log_header(design, "Re-integrating ABC9 results.\n");
RTLIL::Module *mapped_mod = mapped_design->modules_["\\netlist"];
if (mapped_mod == NULL)
log_error("ABC output file does not contain a module `netlist'.\n");
+
+ pool<RTLIL::SigBit> output_bits;
for (auto &it : mapped_mod->wires_) {
RTLIL::Wire *w = it.second;
- RTLIL::Wire *wire = module->addWire(remap_name(w->name), GetSize(w));
- if (markgroups) wire->attributes["\\abcgroup"] = map_autoidx;
- design->select(module, wire);
+ RTLIL::Wire *remap_wire = module->addWire(remap_name(w->name), GetSize(w));
+ if (markgroups) remap_wire->attributes["\\abcgroup"] = map_autoidx;
+ if (w->port_output) {
+ RTLIL::Wire *wire = module->wire(w->name);
+ if (wire) {
+ for (int i = 0; i < GetSize(wire); i++)
+ output_bits.insert({wire, i});
+ }
+ else {
+ if (w->name.str() == "\\__dummy_o__") {
+ log("Don't call ABC as there is nothing to map.\n");
+ goto cleanup;
+ }
+
+ // Attempt another wideports_split here because there
+ // exists the possibility that different bits of a port
+ // could be an input and output, therefore parse_xiager()
+ // could not combine it into a wideport
+ auto r = wideports_split(w->name.str());
+ wire = module->wire(r.first);
+ log_assert(wire);
+ int i = r.second;
+ output_bits.insert({wire, i});
+ }
+ }
}
std::map<std::string, int> cell_stats;
{
if (builtin_lib)
{
+ if (c->type == "$_NOT_") {
+ RTLIL::Cell *cell;
+ RTLIL::SigBit a_bit = c->getPort("\\A").as_bit();
+ RTLIL::SigBit y_bit = c->getPort("\\Y").as_bit();
+ if (!lut_costs.empty()) {
+ // ABC can return NOT gates that drive POs
+ if (a_bit.wire->port_input) {
+ // If it's a NOT gate that comes from a primary input directly
+ // then implement it using a LUT
+ cell = module->addLut(remap_name(stringf("%s$lut", c->name.c_str())),
+ RTLIL::SigBit(module->wires_[remap_name(a_bit.wire->name)], a_bit.offset),
+ RTLIL::SigBit(module->wires_[remap_name(y_bit.wire->name)], y_bit.offset),
+ 1);
+ }
+ else {
+ // Otherwise, clone the driving LUT 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);
+ RTLIL::Cell* driver = mapped_mod->cell(driver_name);
+ log_assert(driver);
+ auto driver_a = driver->getPort("\\A").chunks();
+ for (auto &chunk : driver_a)
+ chunk.wire = module->wires_[remap_name(chunk.wire->name)];
+ RTLIL::Const driver_lut = driver->getParam("\\LUT");
+ for (auto &b : driver_lut.bits) {
+ if (b == RTLIL::State::S0) b = RTLIL::State::S1;
+ else if (b == RTLIL::State::S1) b = RTLIL::State::S0;
+ }
+ cell = module->addLut(remap_name(stringf("%s$lut", c->name.c_str())),
+ driver_a,
+ RTLIL::SigBit(module->wires_[remap_name(y_bit.wire->name)], y_bit.offset),
+ driver_lut);
+ }
+ cell_stats["$lut"]++;
+ }
+ else {
+ cell = module->addCell(remap_name(c->name), "$_NOT_");
+ cell->setPort("\\A", RTLIL::SigBit(module->wires_[remap_name(a_bit.wire->name)], a_bit.offset));
+ cell->setPort("\\Y", RTLIL::SigBit(module->wires_[remap_name(y_bit.wire->name)], y_bit.offset));
+ cell_stats[RTLIL::unescape_id(c->type)]++;
+ }
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ continue;
+ }
+
cell_stats[RTLIL::unescape_id(c->type)]++;
if (c->type == "\\ZERO" || c->type == "\\ONE") {
RTLIL::SigSig conn;
module->connect(conn);
continue;
}
- if (c->type == "\\NOT") {
- RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_NOT_");
- if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
- cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
- cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
- continue;
- }
+
if (c->type == "\\AND" || c->type == "\\OR" || c->type == "\\XOR" || c->type == "\\NAND" || c->type == "\\NOR" ||
c->type == "\\XNOR" || c->type == "\\ANDNOT" || c->type == "\\ORNOT") {
RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_");
cell->setPort("\\A", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]));
cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\MUX") {
cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\MUX4") {
cell->setPort("\\S", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\S").as_wire()->name)]));
cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\MUX8") {
cell->setPort("\\T", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\T").as_wire()->name)]));
cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\MUX16") {
cell->setPort("\\U", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\U").as_wire()->name)]));
cell->setPort("\\V", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\V").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\AOI3" || c->type == "\\OAI3") {
cell->setPort("\\B", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\B").as_wire()->name)]));
cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\AOI4" || c->type == "\\OAI4") {
cell->setPort("\\C", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\C").as_wire()->name)]));
cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
cell->setPort("\\Y", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]));
- design->select(module, cell);
continue;
}
if (c->type == "\\DFF") {
cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
cell->setPort("\\C", clk_sig);
- design->select(module, cell);
continue;
}
}
-
- cell_stats[RTLIL::unescape_id(c->type)]++;
+ else
+ cell_stats[RTLIL::unescape_id(c->type)]++;
if (c->type == "\\_const0_" || c->type == "\\_const1_") {
RTLIL::SigSig conn;
cell->setPort("\\D", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\D").as_wire()->name)]));
cell->setPort("\\Q", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Q").as_wire()->name)]));
cell->setPort("\\C", clk_sig);
- design->select(module, cell);
continue;
}
cell->parameters = c->parameters;
for (auto &conn : c->connections()) {
RTLIL::SigSpec newsig;
- for (auto &c : conn.second.chunks()) {
+ for (auto c : conn.second.chunks()) {
if (c.width == 0)
continue;
- log_assert(c.width == 1);
- newsig.append(module->wires_[remap_name(c.wire->name)]);
+ //log_assert(c.width == 1);
+ c.wire = module->wires_[remap_name(c.wire->name)];
+ newsig.append(c);
}
cell->setPort(conn.first, newsig);
}
- design->select(module, cell);
}
- // FIXME: Better way to clean out module contents?
- module->connections_.clear();
-
+ // 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();
if (!conn.second.is_fully_const()) {
auto chunks = conn.second.chunks();
for (auto &c : chunks)
- c.wire = module->wires_[remap_name(c.wire->name)];
+ if (c.wire)
+ c.wire = module->wires_[remap_name(c.wire->name)];
conn.second = std::move(chunks);
}
module->connect(conn);
// module->connect(conn);
// }
+ // Go through all AND and NOT output connections,
+ // and for those output ports driving wires
+ // also driven by mapped_mod, disconnect them
+ for (auto cell : module->cells()) {
+ if (!cell->type.in("$_AND_", "$_NOT_"))
+ continue;
+ for (auto &it : cell->connections_) {
+ auto port_name = it.first;
+ if (!cell->output(port_name)) continue;
+ auto &signal = it.second;
+ auto bits = signal.bits();
+ for (auto &b : bits)
+ if (output_bits.count(b))
+ b = module->addWire(NEW_ID);
+ signal = std::move(bits);
+ }
+ }
+ // Do the same for module connections
+ for (auto &it : module->connections_) {
+ auto &signal = it.first;
+ auto bits = signal.bits();
+ for (auto &b : bits)
+ if (output_bits.count(b))
+ b = module->addWire(NEW_ID);
+ signal = std::move(bits);
+ }
+
+ // Stitch in mapped_mod's inputs/outputs into module
for (auto &it : mapped_mod->wires_) {
RTLIL::Wire *w = it.second;
if (!w->port_input && !w->port_output)
continue;
- RTLIL::Wire *wire = module->wire(remap_name(w->name));
+ RTLIL::Wire *wire = module->wire(w->name);
+ RTLIL::Wire *remap_wire = module->wire(remap_name(w->name));
+ RTLIL::SigSpec signal;
+ if (wire) {
+ signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
+ }
+ else {
+ // Attempt another wideports_split here because there
+ // exists the possibility that different bits of a port
+ // could be an input and output, therefore parse_xiager()
+ // could not combine it into a wideport
+ auto r = wideports_split(w->name.str());
+ wire = module->wire(r.first);
+ log_assert(wire);
+ int i = r.second;
+ signal = RTLIL::SigSpec(wire, i);
+ }
+ log_assert(GetSize(signal) >= GetSize(remap_wire));
+
+ log_assert(w->port_input || w->port_output);
+ RTLIL::SigSig conn;
if (w->port_input) {
- RTLIL::SigSig conn;
- conn.first = wire;
- conn.second = module->wire(w->name);
- if (conn.second.empty())
- log_error("Input port %s not found in original module.\n", w->name.c_str());
+ conn.first = remap_wire;
+ conn.second = signal;
in_wires++;
module->connect(conn);
}
- else if (w->port_output) {
- RTLIL::SigSig conn;
- conn.first = module->wire(w->name);
- if (conn.first.empty())
- log_error("Output port %s not found in original module.\n", w->name.c_str());
- conn.second = wire;
+ if (w->port_output) {
+ conn.first = signal;
+ conn.second = remap_wire;
out_wires++;
module->connect(conn);
}
}
+
//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);
// log("Don't call ABC as there is nothing to map.\n");
//}
+cleanup:
if (cleanup)
{
log("Removing temp directory.\n");
std::string delay_target, sop_inputs, sop_products, lutin_shared = "-S 1";
bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
bool show_tempdir = false, sop_mode = false;
- show_tempdir = true; cleanup = false;
vector<int> lut_costs;
markgroups = false;
}
}
+ Pass::call(design, "clean");
+
assign_map.clear();
signal_map.clear();
signal_init.clear();