diff options
47 files changed, 3408 insertions, 65 deletions
@@ -430,6 +430,7 @@ $(eval $(call add_include_file,backends/ilang/ilang_backend.h)) OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o OBJS += kernel/cellaigs.o kernel/celledges.o +OBJS += kernel/cost.o kernel/log.o: CXXFLAGS += -DYOSYS_SRC='"$(YOSYS_SRC)"' kernel/yosys.o: CXXFLAGS += -DYOSYS_DATDIR='"$(DATDIR)"' diff --git a/backends/aiger/Makefile.inc b/backends/aiger/Makefile.inc index 0fc37e95c..4a4cf30bd 100644 --- a/backends/aiger/Makefile.inc +++ b/backends/aiger/Makefile.inc @@ -1,3 +1,4 @@ OBJS += backends/aiger/aiger.o +OBJS += backends/aiger/xaiger.o diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc new file mode 100644 index 000000000..56d361fff --- /dev/null +++ b/backends/aiger/xaiger.cc @@ -0,0 +1,703 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void aiger_encode(std::ostream &f, int x) +{ + log_assert(x >= 0); + + while (x & ~0x7f) { + f.put((x & 0x7f) | 0x80); + x = x >> 7; + } + + f.put(x); +} + +struct XAigerWriter +{ + Module *module; + bool zinit_mode; + SigMap sigmap; + + dict<SigBit, bool> init_map; + pool<SigBit> input_bits, output_bits; + dict<SigBit, SigBit> not_map, ff_map, alias_map; + dict<SigBit, pair<SigBit, SigBit>> and_map; + pool<SigBit> initstate_bits; + pool<SigBit> ci_bits, co_bits; + dict<IdString, unsigned> type_map; + + vector<pair<int, int>> aig_gates; + vector<int> aig_latchin, aig_latchinit, aig_outputs; + int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; + + dict<SigBit, int> aig_map; + dict<SigBit, int> ordered_outputs; + dict<SigBit, int> ordered_latches; + + dict<SigBit, int> init_inputs; + int initstate_ff = 0; + + int mkgate(int a0, int a1) + { + aig_m++, aig_a++; + aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); + return 2*aig_m; + } + + int bit2aig(SigBit bit) + { + if (aig_map.count(bit) == 0) + { + aig_map[bit] = -1; + + if (initstate_bits.count(bit)) { + log_assert(initstate_ff > 0); + aig_map[bit] = initstate_ff; + } else + if (not_map.count(bit)) { + int a = bit2aig(not_map.at(bit)) ^ 1; + aig_map[bit] = a; + } else + if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + aig_map[bit] = mkgate(a0, a1); + } else + if (alias_map.count(bit)) { + aig_map[bit] = bit2aig(alias_map.at(bit)); + } + + if (bit == State::Sx || bit == State::Sz) + log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); + } + + log_assert(aig_map.at(bit) >= 0); + return aig_map.at(bit); + } + + XAigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode) : module(module), zinit_mode(zinit_mode), sigmap(module) + { + pool<SigBit> undriven_bits; + pool<SigBit> unused_bits; + + // promote public wires + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + sigmap.add(wire); + + // promote input wires + for (auto wire : module->wires()) + if (wire->port_input) + sigmap.add(wire); + + // promote output wires + for (auto wire : module->wires()) + if (wire->port_output) + sigmap.add(wire); + + for (auto wire : module->wires()) + { + if (wire->attributes.count("\\init")) { + SigSpec initsig = sigmap(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(wire) && i < GetSize(initval); i++) + if (initval[i] == State::S0 || initval[i] == State::S1) + init_map[initsig[i]] = initval[i] == State::S1; + } + + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wirebit(wire, i); + SigBit bit = sigmap(wirebit); + + if (bit.wire == nullptr) { + if (wire->port_output) { + aig_map[wirebit] = (bit == State::S1) ? 1 : 0; + output_bits.insert(wirebit); + } + continue; + } + + undriven_bits.insert(bit); + unused_bits.insert(bit); + + if (wire->port_input) + input_bits.insert(bit); + + if (wire->port_output) { + if (bit != wirebit) + alias_map[wirebit] = bit; + output_bits.insert(wirebit); + } + } + } + + for (auto bit : input_bits) + undriven_bits.erase(bit); + + for (auto bit : output_bits) + unused_bits.erase(bit); + + for (auto cell : module->cells()) + { + if (cell->type == "$_NOT_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + undriven_bits.erase(Y); + not_map[Y] = A; + continue; + } + + //if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_")) + //{ + // SigBit D = sigmap(cell->getPort("\\D").as_bit()); + // SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); + // unused_bits.erase(D); + // undriven_bits.erase(Q); + // ff_map[Q] = D; + // continue; + //} + + if (cell->type == "$_AND_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit B = sigmap(cell->getPort("\\B").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + unused_bits.erase(B); + undriven_bits.erase(Y); + and_map[Y] = make_pair(A, B); + continue; + } + + if (cell->type == "$initstate") + { + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + undriven_bits.erase(Y); + initstate_bits.insert(Y); + continue; + } + + for (const auto &c : cell->connections()) { + if (c.second.is_fully_const()) continue; + for (auto b : c.second.bits()) { + Wire *w = b.wire; + if (!w) continue; + if (cell->input(c.first)) { + SigBit I = sigmap(b); + if (!w->port_input) + co_bits.insert(I); + } + else if (cell->output(c.first)) { + SigBit O = sigmap(b); + ci_bits.insert(O); + } + else log_abort(); + } + if (!type_map.count(cell->type)) + type_map[cell->type] = type_map.size()+1; + } + //log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + } + + // Do some CI/CO post-processing: + // Erase all POs and COs that are undriven + for (auto bit : undriven_bits) { + co_bits.erase(bit); + output_bits.erase(bit); + } + // Erase all POs and CIs that are also PIs + for (auto bit : input_bits) { + output_bits.erase(bit); + ci_bits.erase(bit); + } + for (auto bit : output_bits) { + ci_bits.erase(bit); + // POs override COs + co_bits.erase(bit); + } + // Erase all CIs that are also COs + for (auto bit : co_bits) + ci_bits.erase(bit); + // CIs cannot be undriven + for (auto bit : ci_bits) + undriven_bits.erase(bit); + + for (auto bit : unused_bits) + undriven_bits.erase(bit); + + if (!undriven_bits.empty()) { + undriven_bits.sort(); + for (auto bit : undriven_bits) { + log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit)); + input_bits.insert(bit); + } + log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module)); + } + + init_map.sort(); + input_bits.sort(); + output_bits.sort(); + not_map.sort(); + ff_map.sort(); + and_map.sort(); + + aig_map[State::S0] = 0; + aig_map[State::S1] = 1; + + for (auto bit : ci_bits) { + aig_m++, aig_i++; + aig_map[bit] = 2*aig_m; + } + + for (auto bit : input_bits) { + aig_m++, aig_i++; + aig_map[bit] = 2*aig_m; + } + + if (imode && input_bits.empty()) { + aig_m++, aig_i++; + } + + if (zinit_mode) + { + for (auto it : ff_map) { + if (init_map.count(it.first)) + continue; + aig_m++, aig_i++; + init_inputs[it.first] = 2*aig_m; + } + } + + for (auto it : ff_map) { + aig_m++, aig_l++; + aig_map[it.first] = 2*aig_m; + ordered_latches[it.first] = aig_l-1; + if (init_map.count(it.first) == 0) + aig_latchinit.push_back(2); + else + aig_latchinit.push_back(init_map.at(it.first) ? 1 : 0); + } + + if (!initstate_bits.empty() || !init_inputs.empty()) { + aig_m++, aig_l++; + initstate_ff = 2*aig_m+1; + aig_latchinit.push_back(0); + } + + if (zinit_mode) + { + for (auto it : ff_map) + { + int l = ordered_latches[it.first]; + + if (aig_latchinit.at(l) == 1) + aig_map[it.first] ^= 1; + + if (aig_latchinit.at(l) == 2) + { + int gated_ffout = mkgate(aig_map[it.first], initstate_ff^1); + int gated_initin = mkgate(init_inputs[it.first], initstate_ff); + aig_map[it.first] = mkgate(gated_ffout^1, gated_initin^1)^1; + } + } + } + + for (auto it : ff_map) { + int a = bit2aig(it.second); + int l = ordered_latches[it.first]; + if (zinit_mode && aig_latchinit.at(l) == 1) + aig_latchin.push_back(a ^ 1); + else + aig_latchin.push_back(a); + } + + if (!initstate_bits.empty() || !init_inputs.empty()) + aig_latchin.push_back(1); + + for (auto bit : co_bits) { + aig_o++; + ordered_outputs[bit] = aig_o-1; + aig_outputs.push_back(bit2aig(bit)); + } + + for (auto bit : output_bits) { + aig_o++; + ordered_outputs[bit] = aig_o-1; + aig_outputs.push_back(bit2aig(bit)); + } + + if (omode && output_bits.empty() && co_bits.empty()) { + aig_o++; + aig_outputs.push_back(0); + } + + if (bmode) { + //aig_b++; + aig_outputs.push_back(0); + } + } + + void write_aiger(std::ostream &f, bool ascii_mode, bool miter_mode, bool symbols_mode) + { + int aig_obc = aig_o; + int aig_obcj = aig_obc; + int aig_obcjf = aig_obcj; + + log_assert(aig_m == aig_i + aig_l + aig_a); + log_assert(aig_l == GetSize(aig_latchin)); + log_assert(aig_l == GetSize(aig_latchinit)); + log_assert(aig_obcjf == GetSize(aig_outputs)); + + f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); + f << stringf("\n"); + + if (ascii_mode) + { + for (int i = 0; i < aig_i; i++) + f << stringf("%d\n", 2*i+2); + + for (int i = 0; i < aig_l; i++) { + if (zinit_mode || aig_latchinit.at(i) == 0) + f << stringf("%d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 1) + f << stringf("%d %d 1\n", 2*(aig_i+i)+2, aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 2) + f << stringf("%d %d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i), 2*(aig_i+i)+2); + } + + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) + f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); + } + else + { + for (int i = 0; i < aig_l; i++) { + if (zinit_mode || aig_latchinit.at(i) == 0) + f << stringf("%d\n", aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 1) + f << stringf("%d 1\n", aig_latchin.at(i)); + else if (aig_latchinit.at(i) == 2) + f << stringf("%d %d\n", aig_latchin.at(i), 2*(aig_i+i)+2); + } + + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) { + int lhs = 2*(aig_i+aig_l+i)+2; + int rhs0 = aig_gates.at(i).first; + int rhs1 = aig_gates.at(i).second; + int delta0 = lhs - rhs0; + int delta1 = rhs0 - rhs1; + aiger_encode(f, delta0); + aiger_encode(f, delta1); + } + } + + if (symbols_mode) + { + dict<string, vector<string>> symbols; + + for (auto wire : module->wires()) + { + //if (wire->name[0] == '$') + // continue; + + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(wire); i++) + { + if (sig[i].wire == nullptr) { + if (wire->port_output) + sig[i] = SigBit(wire, i); + else + continue; + } + + if (input_bits.count(sig[i]) || ci_bits.count(SigSpec(sig[i]))) { + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + if (GetSize(wire) != 1) + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i)); + else + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire))); + } + + if (output_bits.count(SigSpec(wire, i)) || co_bits.count(SigSpec(wire, i))) { + int o = ordered_outputs.at(SigSpec(wire, i)); + if (GetSize(wire) != 1) + symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i)); + else + symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire))); + } + + if (init_inputs.count(sig[i])) { + int a = init_inputs.at(sig[i]); + log_assert((a & 1) == 0); + if (GetSize(wire) != 1) + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i)); + else + symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire))); + } + + if (ordered_latches.count(sig[i])) { + int l = ordered_latches.at(sig[i]); + const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : ""; + if (GetSize(wire) != 1) + symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i)); + else + symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire))); + } + } + } + + symbols.sort(); + + for (auto &sym : symbols) { + f << sym.first; + std::sort(sym.second.begin(), sym.second.end()); + for (auto &s : sym.second) + f << " " << s; + f << std::endl; + } + } + + f << stringf("c\nGenerated by %s\n", yosys_version_str); + } + + void write_map(std::ostream &f, bool verbose_map, bool omode) + { + dict<int, string> input_lines; + dict<int, string> init_lines; + dict<int, string> output_lines; + dict<int, string> latch_lines; + dict<int, string> wire_lines; + + for (auto wire : module->wires()) + { + //if (!verbose_map && wire->name[0] == '$') + // continue; + + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(wire); i++) + { + RTLIL::SigBit b(wire, i); + if (input_bits.count(b) || ci_bits.count(b)) { + int a = aig_map.at(sig[i]); + log_assert((a & 1) == 0); + input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); + continue; + } + + if (output_bits.count(b) || co_bits.count(b)) { + int o = ordered_outputs.at(b); + output_lines[o] += stringf("output %d %d %s\n", o, i, log_id(wire)); + continue; + } + + if (init_inputs.count(sig[i])) { + int a = init_inputs.at(sig[i]); + log_assert((a & 1) == 0); + init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, i, log_id(wire)); + continue; + } + + if (ordered_latches.count(sig[i])) { + int l = ordered_latches.at(sig[i]); + if (zinit_mode && (aig_latchinit.at(l) == 1)) + latch_lines[l] += stringf("invlatch %d %d %s\n", l, i, log_id(wire)); + else + latch_lines[l] += stringf("latch %d %d %s\n", l, i, log_id(wire)); + 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)); + } + } + } + + input_lines.sort(); + for (auto &it : input_lines) + f << it.second; + log_assert(input_lines.size() == input_bits.size() + ci_bits.size()); + + init_lines.sort(); + for (auto &it : init_lines) + f << it.second; + + output_lines.sort(); + for (auto &it : output_lines) + f << it.second; + log_assert(output_lines.size() == output_bits.size() + co_bits.size()); + if (omode && output_lines.empty()) + f << "output 0 0 __dummy_o__\n"; + + latch_lines.sort(); + for (auto &it : latch_lines) + f << it.second; + + wire_lines.sort(); + for (auto &it : wire_lines) + f << it.second; + } +}; + +struct XAigerBackend : public Backend { + XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_xaiger [options] [filename]\n"); + log("\n"); + log("Write the current design to an XAIGER file. The design must be flattened and\n"); + log("all unsupported cells will be converted into psuedo-inputs and pseudo-outputs.\n"); + log("\n"); + log(" -ascii\n"); + log(" write ASCII version of AGIER format\n"); + log("\n"); + log(" -zinit\n"); + log(" convert FFs to zero-initialized FFs, adding additional inputs for\n"); + log(" uninitialized FFs.\n"); + log("\n"); + log(" -symbols\n"); + log(" include a symbol table in the generated AIGER file\n"); + log("\n"); + log(" -map <filename>\n"); + log(" write an extra file with port and latch symbols\n"); + log("\n"); + log(" -vmap <filename>\n"); + log(" like -map, but more verbose\n"); + log("\n"); + log(" -I, -O, -B\n"); + log(" If the design contains no input/output/assert then create one\n"); + log(" dummy input/output/bad_state pin to make the tools reading the\n"); + log(" AIGER file happy.\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool ascii_mode = false; + bool zinit_mode = false; + bool miter_mode = false; + bool symbols_mode = false; + bool verbose_map = false; + bool imode = false; + bool omode = false; + bool bmode = false; + std::string map_filename; + + log_header(design, "Executing XAIGER backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ascii") { + ascii_mode = true; + continue; + } + if (args[argidx] == "-zinit") { + zinit_mode = true; + continue; + } + if (args[argidx] == "-symbols") { + symbols_mode = true; + continue; + } + if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + verbose_map = true; + continue; + } + if (args[argidx] == "-I") { + imode = true; + continue; + } + if (args[argidx] == "-O") { + omode = true; + continue; + } + if (args[argidx] == "-B") { + bmode = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + Module *top_module = design->top_module(); + + if (top_module == nullptr) + log_error("Can't find top module in current design!\n"); + + XAigerWriter writer(top_module, zinit_mode, imode, omode, bmode); + writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); + + if (!map_filename.empty()) { + std::ofstream mapf; + mapf.open(map_filename.c_str(), std::ofstream::trunc); + if (mapf.fail()) + log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); + writer.write_map(mapf, verbose_map, omode); + } + } +} XAigerBackend; + +PRIVATE_NAMESPACE_END diff --git a/frontends/aiger/Makefile.inc b/frontends/aiger/Makefile.inc new file mode 100644 index 000000000..bc1112452 --- /dev/null +++ b/frontends/aiger/Makefile.inc @@ -0,0 +1,3 @@ + +OBJS += frontends/aiger/aigerparse.o + diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc new file mode 100644 index 000000000..6fa77282e --- /dev/null +++ b/frontends/aiger/aigerparse.cc @@ -0,0 +1,791 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 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]] The AIGER And-Inverter Graph (AIG) Format Version 20071012 +// Armin Biere. The AIGER And-Inverter Graph (AIG) Format Version 20071012. Technical Report 07/1, October 2011, FMV Reports Series, Institute for Formal Models and Verification, Johannes Kepler University, Altenbergerstr. 69, 4040 Linz, Austria. +// http://fmv.jku.at/papers/Biere-FMV-TR-07-1.pdf + +#ifdef _WIN32 +#include <stdlib.h> +#endif +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/consteval.h" +#include "aigerparse.h" + +YOSYS_NAMESPACE_BEGIN + +//#define log_debug log +#define log_debug(...) ; + +AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports) + : design(design), f(f), clk_name(clk_name), map_filename(map_filename), wideports(wideports) +{ + module = new RTLIL::Module; + module->name = module_name; + if (design->module(module->name)) + log_error("Duplicate definition of module %s!\n", log_id(module->name)); +} + +void AigerReader::parse_aiger() +{ + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + for (auto &i : std::array<std::reference_wrapper<unsigned>,4>{B, C, J, F}) { + if (f.peek() != ' ') break; + if (!(f >> i)) + log_error("Invalid AIGER header\n"); + } + + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F); + + line_count = 1; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + // Parse footer (symbol table, comments, etc.) + unsigned l1; + std::string s; + for (int c = f.peek(); c != EOF; c = f.peek(), ++line_count) { + if (c == 'i' || c == 'l' || c == 'o') { + f.ignore(1); + if (!(f >> l1 >> s)) + log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); + + 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); + + RTLIL::Wire* wire; + if (c == 'i') wire = inputs[l1]; + else if (c == 'l') wire = latches[l1]; + else if (c == 'o') wire = outputs[l1]; + else log_abort(); + + module->rename(wire, stringf("\\%s", s.c_str())); + } + else if (c == 'b' || c == 'j' || c == 'f') { + // TODO + } + else if (c == 'c') { + f.ignore(1); + if (f.peek() == '\n') + break; + // Else constraint (TODO) + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + std::getline(f, line); // Ignore up to start of next line + } + + dict<RTLIL::IdString, int> wideports_cache; + + if (!map_filename.empty()) { + std::ifstream mf(map_filename); + std::string type, symbol; + int variable, index; + while (mf >> type >> variable >> index >> symbol) { + RTLIL::IdString escaped_symbol = RTLIL::escape_id(symbol); + if (type == "input") { + log_assert(static_cast<unsigned>(variable) < inputs.size()); + RTLIL::Wire* wire = inputs[variable]; + log_assert(wire); + log_assert(wire->port_input); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable) < outputs.size()); + RTLIL::Wire* wire = outputs[variable]; + log_assert(wire); + // Ignore direct output -> input connections + if (!wire->port_output) + continue; + log_assert(wire->port_output); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + } + else + log_error("Symbol type '%s' not recognised.\n", type.c_str()); + } + } + + for (auto &wp : wideports_cache) { + auto name = wp.first; + int width = wp.second + 1; + + RTLIL::Wire *wire = module->wire(name); + if (wire) { + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + if (driver) + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + } + + // Do not make ports with a mix of input/output into + // wide ports + bool port_input = false, port_output = false; + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + port_input = port_input || other_wire->port_input; + port_output = port_output || other_wire->port_output; + } + } + if ((port_input && port_output) || (!port_input && !port_output)) + continue; + + wire = module->addWire(name, width); + wire->port_input = port_input; + wire->port_output = port_output; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + if (wire->port_input) + module->connect(other_wire, SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), other_wire); + } + } + } + + module->fixup_ports(); + design->add(module); + + Pass::call(design, "clean"); +} + +static uint32_t parse_xaiger_literal(std::istream &f) +{ + uint32_t l; + f.read(reinterpret_cast<char*>(&l), sizeof(l)); + if (f.gcount() != sizeof(l)) + log_error("Offset %ld: unable to read literal!\n", static_cast<int64_t>(f.tellg())); + // TODO: Don't assume we're on little endian +#ifdef _WIN32 + return _byteswap_ulong(l); +#else + return __builtin_bswap32(l); +#endif +} + +static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned literal) +{ + const unsigned variable = literal >> 1; + const bool invert = literal & 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) return wire; + log_debug("Creating %s\n", wire_name.c_str()); + wire = module->addWire(wire_name); + wire->port_input = wire->port_output = false; + if (!invert) return wire; + RTLIL::IdString wire_inv_name(stringf("\\__%d__", variable)); + RTLIL::Wire *wire_inv = module->wire(wire_inv_name); + if (wire_inv) { + if (module->cell(wire_inv_name)) return wire; + } + else { + log_debug("Creating %s\n", wire_inv_name.c_str()); + wire_inv = module->addWire(wire_inv_name); + wire_inv->port_input = wire_inv->port_output = false; + } + + log_debug("Creating %s = ~%s\n", wire_name.c_str(), wire_inv_name.c_str()); + module->addNotGate(stringf("\\__%d__$not", variable), wire_inv, wire); // FIXME: is "$not" the right suffix? + + return wire; +} + +void AigerReader::parse_xaiger() +{ + std::string header; + f >> header; + if (header != "aag" && header != "aig") + log_error("Unsupported AIGER file!\n"); + + // Parse rest of header + if (!(f >> M >> I >> L >> O >> A)) + log_error("Invalid AIGER header\n"); + + // Optional values + B = C = J = F = 0; + + std::string line; + std::getline(f, line); // Ignore up to start of next line, as standard + // says anything that follows could be used for + // optional sections + + log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); + + line_count = 1; + + if (header == "aag") + parse_aiger_ascii(); + else if (header == "aig") + parse_aiger_binary(); + else + log_abort(); + + // Parse footer (symbol table, comments, etc.) + unsigned l1; + std::string s; + bool comment_seen = false; + for (int c = f.peek(); c != EOF; c = f.peek()) { + if (comment_seen || c == 'c') { + if (!comment_seen) { + f.ignore(1); + c = f.peek(); + comment_seen = true; + } + if (c == '\n') + break; + f.ignore(1); + // XAIGER extensions + if (c == 'm') { + uint32_t dataSize = parse_xaiger_literal(f); + uint32_t lutNum = parse_xaiger_literal(f); + uint32_t lutSize = parse_xaiger_literal(f); + log_debug("m: dataSize=%u lutNum=%u lutSize=%u\n", dataSize, lutNum, lutSize); + ConstEval ce(module); + for (unsigned i = 0; i < lutNum; ++i) { + uint32_t rootNodeID = parse_xaiger_literal(f); + uint32_t cutLeavesM = parse_xaiger_literal(f); + log_debug("rootNodeID=%d cutLeavesM=%d\n", rootNodeID, cutLeavesM); + RTLIL::Wire *output_sig = module->wire(stringf("\\__%d__", rootNodeID)); + uint32_t nodeID; + RTLIL::SigSpec input_sig; + for (unsigned j = 0; j < cutLeavesM; ++j) { + nodeID = parse_xaiger_literal(f); + log_debug("\t%u\n", nodeID); + RTLIL::Wire *wire = module->wire(stringf("\\__%d__", nodeID)); + log_assert(wire); + input_sig.append(wire); + } + RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size()); + for (int j = 0; j < (1 << cutLeavesM); ++j) { + ce.push(); + ce.set(input_sig, RTLIL::Const{j, static_cast<int>(cutLeavesM)}); + RTLIL::SigSpec o(output_sig); + ce.eval(o); + lut_mask[j] = o.as_const()[0]; + ce.pop(); + } + RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID)); + log_assert(output_cell); + module->remove(output_cell); + module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask)); + } + } + else if (c == 'n') { + parse_xaiger_literal(f); + f >> s; + log_debug("n: '%s'\n", s.c_str()); + } + } + else if (c == 'i' || c == 'l' || c == 'o') { + f.ignore(1); + if (!(f >> l1 >> s)) + log_error("Line %u cannot be interpreted as a symbol entry!\n", line_count); + + 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); + + RTLIL::Wire* wire; + if (c == 'i') wire = inputs[l1]; + else if (c == 'l') wire = latches[l1]; + else if (c == 'o') wire = outputs[l1]; + else log_abort(); + + module->rename(wire, stringf("\\%s", s.c_str())); + + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + + std::getline(f, line); // Ignore up to start of next line + ++line_count; + } + else + log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c); + } + + dict<RTLIL::IdString, int> wideports_cache; + + if (!map_filename.empty()) { + std::ifstream mf(map_filename); + std::string type, symbol; + int variable, index; + while (mf >> type >> variable >> index >> symbol) { + RTLIL::IdString escaped_symbol = RTLIL::escape_id(symbol); + if (type == "input") { + log_assert(static_cast<unsigned>(variable) < inputs.size()); + RTLIL::Wire* wire = inputs[variable]; + log_assert(wire); + log_assert(wire->port_input); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + } + else if (type == "output") { + log_assert(static_cast<unsigned>(variable) < outputs.size()); + RTLIL::Wire* wire = outputs[variable]; + log_assert(wire); + log_assert(wire->port_output); + + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + + if (index == 0) + module->rename(wire, RTLIL::escape_id(symbol)); + else if (index > 0) { + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", symbol.c_str(), index))); + if (wideports) + wideports_cache[escaped_symbol] = std::max(wideports_cache[escaped_symbol], index); + } + + if (driver) + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + } + else + log_error("Symbol type '%s' not recognised.\n", type.c_str()); + } + } + + for (auto &wp : wideports_cache) { + auto name = wp.first; + int width = wp.second + 1; + + RTLIL::Wire *wire = module->wire(name); + if (wire) { + RTLIL::Cell* driver = module->cell(stringf("%s$lut", wire->name.c_str())); + + module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0))); + + if (driver) + module->rename(driver, stringf("%s$lut", wire->name.c_str())); + } + + // Do not make ports with a mix of input/output into + // wide ports + bool port_input = false, port_output = false; + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + port_input = port_input || other_wire->port_input; + port_output = port_output || other_wire->port_output; + } + } + if ((port_input && port_output) || (!port_input && !port_output)) + continue; + + wire = module->addWire(name, width); + wire->port_input = port_input; + wire->port_output = port_output; + + for (int i = 0; i < width; i++) { + RTLIL::IdString other_name = name.str() + stringf("[%d]", i); + RTLIL::Wire *other_wire = module->wire(other_name); + if (other_wire) { + other_wire->port_input = false; + other_wire->port_output = false; + if (wire->port_input) + module->connect(other_wire, SigSpec(wire, i)); + else + module->connect(SigSpec(wire, i), other_wire); + } + } + } + + module->fixup_ports(); + design->add(module); + + Pass::call(design, "clean"); +} + +void AigerReader::parse_aiger_ascii() +{ + std::string line; + std::stringstream ss; + + unsigned l1, l2, l3; + + // Parse inputs + for (unsigned i = 0; i < I; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an input!\n", line_count); + log_debug("%d is an input\n", l1); + log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted? + RTLIL::Wire *wire = createWireIfNotExists(module, l1); + wire->port_input = true; + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + clk_wire = module->wire(clk_name); + log_assert(!clk_wire); + log_debug("Creating %s\n", clk_name.c_str()); + clk_wire = module->addWire(clk_name); + clk_wire->port_input = true; + clk_wire->port_output = false; + } + for (unsigned i = 0; i < L; ++i, ++line_count) { + if (!(f >> l1 >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug("%d %d is a latch\n", l1, l2); + log_assert(!(l1 & 1)); // TODO: Latch outputs can't be inverted? + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDffGate(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0 || l3 == 1) + q_wire->attributes["\\init"] = RTLIL::Const(l3); + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::Const(0); + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an output!\n", line_count); + + RTLIL::Wire *wire; + if (l1 == 0 || l1 == 1) { + wire = module->addWire(NEW_ID); + if (l1 == 0) + module->connect(wire, RTLIL::State::S0); + else if (l1 == 1) + module->connect(wire, RTLIL::State::S1); + else + log_abort(); + } + else { + log_debug("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? + 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; + outputs.push_back(wire); + } + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse bad state properties + for (unsigned i = 0; i < B; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + for (unsigned i = 0; i < A; ++i) { + if (!(f >> l1 >> l2 >> l3)) + log_error("Line %u cannot be interpreted as an AND!\n", line_count); + + log_debug("%d %d %d is an AND\n", l1, l2, l3); + log_assert(!(l1 & 1)); + RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); + RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } + std::getline(f, line); +} + +static unsigned parse_next_delta_literal(std::istream &f, unsigned ref) +{ + unsigned x = 0, i = 0; + unsigned char ch; + while ((ch = f.get()) & 0x80) + x |= (ch & 0x7f) << (7 * i++); + return ref - (x | (ch << (7 * i))); +} + +void AigerReader::parse_aiger_binary() +{ + unsigned l1, l2, l3; + std::string line; + + // Parse inputs + for (unsigned i = 1; i <= I; ++i) { + log_debug("%d is an input\n", i); + RTLIL::Wire *wire = createWireIfNotExists(module, i << 1); + wire->port_input = true; + log_assert(!wire->port_output); + inputs.push_back(wire); + } + + // Parse latches + RTLIL::Wire *clk_wire = nullptr; + if (L > 0) { + clk_wire = module->wire(clk_name); + log_assert(!clk_wire); + log_debug("Creating %s\n", clk_name.c_str()); + clk_wire = module->addWire(clk_name); + clk_wire->port_input = true; + clk_wire->port_output = false; + } + l1 = (I+1) * 2; + for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) { + if (!(f >> l2)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + log_debug("%d %d is a latch\n", l1, l2); + RTLIL::Wire *q_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *d_wire = createWireIfNotExists(module, l2); + + module->addDff(NEW_ID, clk_wire, d_wire, q_wire); + + // Reset logic is optional in AIGER 1.9 + if (f.peek() == ' ') { + if (!(f >> l3)) + log_error("Line %u cannot be interpreted as a latch!\n", line_count); + + if (l3 == 0 || l3 == 1) + q_wire->attributes["\\init"] = RTLIL::Const(l3); + else if (l3 == l1) { + //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx); + } + else + log_error("Line %u has invalid reset literal for latch!\n", line_count); + } + else { + // AIGER latches are assumed to be initialized to zero + q_wire->attributes["\\init"] = RTLIL::Const(0); + } + latches.push_back(q_wire); + } + + // Parse outputs + for (unsigned i = 0; i < O; ++i, ++line_count) { + if (!(f >> l1)) + log_error("Line %u cannot be interpreted as an output!\n", line_count); + + RTLIL::Wire *wire; + if (l1 == 0 || l1 == 1) { + wire = module->addWire(NEW_ID); + if (l1 == 0) + module->connect(wire, RTLIL::State::S0); + else if (l1 == 1) + module->connect(wire, RTLIL::State::S1); + else + log_abort(); + } + else { + log_debug("%d is an output\n", l1); + const unsigned variable = l1 >> 1; + const bool invert = l1 & 1; + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "_inv" the right suffix? + 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; + outputs.push_back(wire); + } + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse bad state properties + for (unsigned i = 0; i < B; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse invariant constraints + for (unsigned i = 0; i < C; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse justice properties + for (unsigned i = 0; i < J; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // TODO: Parse fairness constraints + for (unsigned i = 0; i < F; ++i, ++line_count) + std::getline(f, line); // Ignore up to start of next line + + // Parse AND + l1 = (I+L+1) << 1; + for (unsigned i = 0; i < A; ++i, ++line_count, l1 += 2) { + l2 = parse_next_delta_literal(f, l1); + l3 = parse_next_delta_literal(f, l2); + + log_debug("%d %d %d is an AND\n", l1, l2, l3); + log_assert(!(l1 & 1)); + RTLIL::Wire *o_wire = createWireIfNotExists(module, l1); + RTLIL::Wire *i1_wire = createWireIfNotExists(module, l2); + RTLIL::Wire *i2_wire = createWireIfNotExists(module, l3); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); + } +} + +struct AigerFrontend : public Frontend { + AigerFrontend() : Frontend("aiger", "read AIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_aiger [options] [filename]\n"); + log("\n"); + log("Load module from an AIGER file into the current design.\n"); + log("\n"); + log(" -module_name <module_name>\n"); + log(" Name of module to be created (default: <filename>)\n"); + log("\n"); + log(" -clk_name <wire_name>\n"); + log(" AIGER latches to be transformed into posedge DFFs clocked by wire of"); + log(" this name (default: clk)\n"); + log("\n"); + log(" -map <filename>\n"); + log(" read file with port and latch symbols\n"); + log("\n"); + log(" -wideports\n"); + log(" Merge ports that match the pattern 'name[int]' into a single\n"); + log(" multi-bit port 'name'.\n"); + log("\n"); + } + 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"); + + RTLIL::IdString clk_name = "\\clk"; + RTLIL::IdString module_name; + std::string map_filename; + bool wideports = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-module_name" && argidx+1 < args.size()) { + module_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (arg == "-clk_name" && argidx+1 < args.size()) { + clk_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (map_filename.empty() && arg == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + if (arg == "-wideports") { + wideports = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx); + + if (module_name.empty()) { +#ifdef _WIN32 + char fname[_MAX_FNAME]; + _splitpath(filename.c_str(), NULL /* drive */, NULL /* dir */, fname, NULL /* ext */) + module_name = fname; +#else + module_name = RTLIL::escape_id(basename(filename.c_str())); +#endif + } + + AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); + reader.parse_aiger(); + } +} AigerFrontend; + +YOSYS_NAMESPACE_END diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h new file mode 100644 index 000000000..42c87a298 --- /dev/null +++ b/frontends/aiger/aigerparse.h @@ -0,0 +1,53 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef ABC_AIGERPARSE +#define ABC_AIGERPARSE + +#include "kernel/yosys.h" + +YOSYS_NAMESPACE_BEGIN + +struct AigerReader +{ + RTLIL::Design *design; + std::istream &f; + RTLIL::IdString clk_name; + RTLIL::Module *module; + std::string map_filename; + bool wideports; + + unsigned M, I, L, O, A; + unsigned B, C, J, F; // Optional in AIGER 1.9 + unsigned line_count; + + std::vector<RTLIL::Wire*> inputs; + std::vector<RTLIL::Wire*> latches; + std::vector<RTLIL::Wire*> outputs; + + AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); + void parse_aiger(); + void parse_xaiger(); + void parse_aiger_ascii(); + void parse_aiger_binary(); +}; + +YOSYS_NAMESPACE_END + +#endif diff --git a/frontends/blif/blifparse.cc b/frontends/blif/blifparse.cc index 9116b257f..a6a07863f 100644 --- a/frontends/blif/blifparse.cc +++ b/frontends/blif/blifparse.cc @@ -584,7 +584,7 @@ struct BlifFrontend : public Frontend { { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" read_blif [filename]\n"); + log(" read_blif [options] [filename]\n"); log("\n"); log("Load modules from a BLIF file into the current design.\n"); log("\n"); diff --git a/kernel/cost.cc b/kernel/cost.cc new file mode 100644 index 000000000..175f01e64 --- /dev/null +++ b/kernel/cost.cc @@ -0,0 +1,75 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/cost.h" + +YOSYS_NAMESPACE_BEGIN + +int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> ¶meters, + RTLIL::Design *design, dict<RTLIL::IdString, int> *mod_cost_cache) +{ + static dict<RTLIL::IdString, int> gate_cost = { + { "$_BUF_", 1 }, + { "$_NOT_", 2 }, + { "$_AND_", 4 }, + { "$_NAND_", 4 }, + { "$_OR_", 4 }, + { "$_NOR_", 4 }, + { "$_ANDNOT_", 4 }, + { "$_ORNOT_", 4 }, + { "$_XOR_", 8 }, + { "$_XNOR_", 8 }, + { "$_AOI3_", 6 }, + { "$_OAI3_", 6 }, + { "$_AOI4_", 8 }, + { "$_OAI4_", 8 }, + { "$_MUX_", 4 } + }; + + if (gate_cost.count(type)) + return gate_cost.at(type); + + if (parameters.empty() && design && design->module(type)) + { + RTLIL::Module *mod = design->module(type); + + if (mod->attributes.count("\\cost")) + return mod->attributes.at("\\cost").as_int(); + + dict<RTLIL::IdString, int> local_mod_cost_cache; + if (mod_cost_cache == nullptr) + mod_cost_cache = &local_mod_cost_cache; + + if (mod_cost_cache->count(mod->name)) + return mod_cost_cache->at(mod->name); + + int module_cost = 1; + for (auto c : mod->cells()) + module_cost += get_cell_cost(c, mod_cost_cache); + + (*mod_cost_cache)[mod->name] = module_cost; + return module_cost; + } + + log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(type), GetSize(parameters)); + return 1; +} + +YOSYS_NAMESPACE_END diff --git a/kernel/cost.h b/kernel/cost.h index e795b571b..7d7822fa0 100644 --- a/kernel/cost.h +++ b/kernel/cost.h @@ -27,56 +27,9 @@ YOSYS_NAMESPACE_BEGIN int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr); int get_cell_cost(RTLIL::IdString type, const dict<RTLIL::IdString, RTLIL::Const> ¶meters = dict<RTLIL::IdString, RTLIL::Const>(), - RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr) -{ - static dict<RTLIL::IdString, int> gate_cost = { - { "$_BUF_", 1 }, - { "$_NOT_", 2 }, - { "$_AND_", 4 }, - { "$_NAND_", 4 }, - { "$_OR_", 4 }, - { "$_NOR_", 4 }, - { "$_ANDNOT_", 4 }, - { "$_ORNOT_", 4 }, - { "$_XOR_", 8 }, - { "$_XNOR_", 8 }, - { "$_AOI3_", 6 }, - { "$_OAI3_", 6 }, - { "$_AOI4_", 8 }, - { "$_OAI4_", 8 }, - { "$_MUX_", 4 } - }; - - if (gate_cost.count(type)) - return gate_cost.at(type); - - if (parameters.empty() && design && design->module(type)) - { - RTLIL::Module *mod = design->module(type); - - if (mod->attributes.count("\\cost")) - return mod->attributes.at("\\cost").as_int(); - - dict<RTLIL::IdString, int> local_mod_cost_cache; - if (mod_cost_cache == nullptr) - mod_cost_cache = &local_mod_cost_cache; - - if (mod_cost_cache->count(mod->name)) - return mod_cost_cache->at(mod->name); - - int module_cost = 1; - for (auto c : mod->cells()) - module_cost += get_cell_cost(c, mod_cost_cache); - - (*mod_cost_cache)[mod->name] = module_cost; - return module_cost; - } - - log_warning("Can't determine cost of %s cell (%d parameters).\n", log_id(type), GetSize(parameters)); - return 1; -} + RTLIL::Design *design = nullptr, dict<RTLIL::IdString, int> *mod_cost_cache = nullptr); -int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache) +inline int get_cell_cost(RTLIL::Cell *cell, dict<RTLIL::IdString, int> *mod_cost_cache) { return get_cell_cost(cell->type, cell->parameters, cell->module->design, mod_cost_cache); } diff --git a/passes/sat/expose.cc b/passes/sat/expose.cc index 809345486..3add9a9eb 100644 --- a/passes/sat/expose.cc +++ b/passes/sat/expose.cc @@ -42,7 +42,7 @@ struct dff_map_bit_info_t { bool consider_wire(RTLIL::Wire *wire, std::map<RTLIL::IdString, dff_map_info_t> &dff_dq_map) { - if (wire->name[0] == '$' || dff_dq_map.count(wire->name)) + if (/*wire->name[0] == '$' ||*/ dff_dq_map.count(wire->name)) return false; if (wire->port_input) return false; diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index cf9e198ad..c45571b01 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -7,6 +7,7 @@ OBJS += passes/techmap/libparse.o ifeq ($(ENABLE_ABC),1) OBJS += passes/techmap/abc.o +OBJS += passes/techmap/abc9.o ifneq ($(ABCEXTERNAL),) passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index d2d15a4a9..b215b1ea4 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -52,6 +52,8 @@ #include <cerrno> #include <sstream> #include <climits> +#include <array> +#include <functional> #ifndef _WIN32 # include <unistd.h> diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc new file mode 100644 index 000000000..4b045cbea --- /dev/null +++ b/passes/techmap/abc9.cc @@ -0,0 +1,1558 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// [[CITE]] ABC +// 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_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 "&st; &retime; &if" +#define ABC_FAST_COMMAND_SOP "strash; dretime; cover -I {I} -P {P}" +#define ABC_FAST_COMMAND_DFL "strash; dretime; map" + +#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" + +#ifdef YOSYS_LINK_ABC +extern "C" int Abc_RealMain(int argc, char *argv[]); +#endif + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +bool map_mux4; +bool map_mux8; +bool map_mux16; + +bool markgroups; +int map_autoidx; +SigMap assign_map; +RTLIL::Module *module; +std::map<RTLIL::SigBit, int> signal_map; +std::map<RTLIL::SigBit, RTLIL::State> signal_init; +pool<std::string> enabled_gates; +bool recover_init; + +bool clk_polarity, en_polarity; +RTLIL::SigSpec clk_sig, en_sig; +dict<int, std::string> pi_map, po_map; + +std::string remap_name(RTLIL::IdString abc_name) +{ + std::stringstream sstr; + sstr << "$abc$" << map_autoidx << "$" << abc_name.substr(1); + 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; + 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_abc_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 abc_output_filter +{ + bool got_cr; + int escape_seq_state; + std::string linebuf; + std::string tempdir_name; + bool show_tempdir; + + abc_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); + } +}; + +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, + const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode) +{ + module = current_module; + map_autoidx = autoidx++; + + signal_map.clear(); + pi_map.clear(); + po_map.clear(); + recover_init = false; + + if (clk_str != "$") + { + clk_polarity = true; + clk_sig = RTLIL::SigSpec(); + + en_polarity = true; + en_sig = RTLIL::SigSpec(); + } + + if (!clk_str.empty() && clk_str != "$") + { + if (clk_str.find(',') != std::string::npos) { + int pos = clk_str.find(','); + std::string en_str = clk_str.substr(pos+1); + clk_str = clk_str.substr(0, pos); + if (en_str[0] == '!') { + en_polarity = false; + en_str = en_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(en_str)) != 0) + en_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(en_str)), 0)); + } + if (clk_str[0] == '!') { + clk_polarity = false; + clk_str = clk_str.substr(1); + } + if (module->wires_.count(RTLIL::escape_id(clk_str)) != 0) + clk_sig = assign_map(RTLIL::SigSpec(module->wires_.at(RTLIL::escape_id(clk_str)), 0)); + } + + if (dff_mode && clk_sig.empty()) + log_cmd_error("Clock domain %s not found.\n", clk_str.c_str()); + + std::string tempdir_name = "/tmp/yosys-abc-XXXXXX"; + if (!cleanup) + tempdir_name[0] = tempdir_name[4] = '_'; + tempdir_name = make_temp_dir(tempdir_name); + log_header(design, "Extracting gate netlist of module `%s' to `%s/input.xaig'..\n", + module->name.c_str(), replace_tempdir(tempdir_name, tempdir_name, show_tempdir).c_str()); + + std::string abc_script = stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str()); + + if (!liberty_file.empty()) { + abc_script += stringf("read_lib -w %s; ", liberty_file.c_str()); + if (!constr_file.empty()) + abc_script += stringf("read_constr -v %s; ", constr_file.c_str()); + } else + if (!lut_costs.empty()) + abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); + else + abc_script += stringf("read_library %s/stdcells.genlib; ", 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] == '\'') + abc_script += "'\\''"; + else if (script_file[i] == ',') + abc_script += " "; + else + abc_script += script_file[i]; + } else + abc_script += stringf("source %s", script_file.c_str()); + } else if (!lut_costs.empty()) { + bool all_luts_cost_same = true; + for (int this_cost : lut_costs) + if (this_cost != lut_costs.front()) + all_luts_cost_same = false; + abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT; + //if (all_luts_cost_same && !fast_mode) + // abc_script += "; lutpack {S}"; + } else if (!liberty_file.empty()) + abc_script += constr_file.empty() ? (fast_mode ? ABC_FAST_COMMAND_LIB : ABC_COMMAND_LIB) : (fast_mode ? ABC_FAST_COMMAND_CTR : ABC_COMMAND_CTR); + else if (sop_mode) + abc_script += fast_mode ? ABC_FAST_COMMAND_SOP : ABC_COMMAND_SOP; + else + abc_script += fast_mode ? ABC_FAST_COMMAND_DFL : ABC_COMMAND_DFL; + + if (script_file.empty() && !delay_target.empty()) + for (size_t pos = abc_script.find("dretime;"); pos != std::string::npos; pos = abc_script.find("dretime;", pos+1)) + abc_script = abc_script.substr(0, pos) + "dretime; retime -o {D};" + abc_script.substr(pos+8); + + for (size_t pos = abc_script.find("{D}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + delay_target + abc_script.substr(pos+3); + + for (size_t pos = abc_script.find("{I}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_inputs + abc_script.substr(pos+3); + + for (size_t pos = abc_script.find("{P}"); pos != std::string::npos; pos = abc_script.find("{D}", pos)) + abc_script = abc_script.substr(0, pos) + sop_products + abc_script.substr(pos+3); + + 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 %s/output.xaig", tempdir_name.c_str()); + abc_script = add_echos_to_abc_cmd(abc_script); + + for (size_t i = 0; i+1 < abc_script.size(); i++) + if (abc_script[i] == ';' && abc_script[i+1] == ' ') + abc_script[i+1] = '\n'; + + FILE *f = fopen(stringf("%s/abc.script", tempdir_name.c_str()).c_str(), "wt"); + fprintf(f, "%s\n", abc_script.c_str()); + fclose(f); + + if (dff_mode || !clk_str.empty()) + { + if (clk_sig.size() == 0) + log("No%s clock domain found. Not extracting any FF cells.\n", clk_str.empty() ? "" : " matching"); + else { + log("Found%s %s clock domain: %s", clk_str.empty() ? "" : " matching", clk_polarity ? "posedge" : "negedge", log_signal(clk_sig)); + if (en_sig.size() != 0) + log(", enabled by %s%s", en_polarity ? "" : "!", log_signal(en_sig)); + log("\n"); + } + } + + design->selection_stack.emplace_back(false); + RTLIL::Selection& sel = design->selection_stack.back(); + sel.select(module); + + Pass::call(design, "aigmap; clean;"); + + handle_loops(design); + + Pass::call(design, stringf("write_xaiger -O -map %s/input.symbols %s/input.xaig; ", tempdir_name.c_str(), 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_push(); + + //if (count_output > 0) + { + log_header(design, "Executing ABC9.\n"); + + std::string buffer = stringf("%s/stdcells.genlib", 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)); + fprintf(f, "GATE ZERO 1 Y=CONST0;\n"); + fprintf(f, "GATE ONE 1 Y=CONST1;\n"); + fprintf(f, "GATE BUF %d Y=A; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_BUF_")); + fprintf(f, "GATE NOT %d Y=!A; PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOT_")); + if (enabled_gates.empty() || enabled_gates.count("AND")) + fprintf(f, "GATE AND %d Y=A*B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_AND_")); + if (enabled_gates.empty() || enabled_gates.count("NAND")) + fprintf(f, "GATE NAND %d Y=!(A*B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NAND_")); + if (enabled_gates.empty() || enabled_gates.count("OR")) + fprintf(f, "GATE OR %d Y=A+B; PIN * NONINV 1 999 1 0 1 0\n", get_cell_cost("$_OR_")); + if (enabled_gates.empty() || enabled_gates.count("NOR")) + fprintf(f, "GATE NOR %d Y=!(A+B); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_NOR_")); + if (enabled_gates.empty() || enabled_gates.count("XOR")) + fprintf(f, "GATE XOR %d Y=(A*!B)+(!A*B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XOR_")); + if (enabled_gates.empty() || enabled_gates.count("XNOR")) + fprintf(f, "GATE XNOR %d Y=(A*B)+(!A*!B); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_XNOR_")); + if (enabled_gates.empty() || enabled_gates.count("ANDNOT")) + fprintf(f, "GATE ANDNOT %d Y=A*!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ANDNOT_")); + if (enabled_gates.empty() || enabled_gates.count("ORNOT")) + fprintf(f, "GATE ORNOT %d Y=A+!B; PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_ORNOT_")); + if (enabled_gates.empty() || enabled_gates.count("AOI3")) + fprintf(f, "GATE AOI3 %d Y=!((A*B)+C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI3_")); + if (enabled_gates.empty() || enabled_gates.count("OAI3")) + fprintf(f, "GATE OAI3 %d Y=!((A+B)*C); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI3_")); + if (enabled_gates.empty() || enabled_gates.count("AOI4")) + fprintf(f, "GATE AOI4 %d Y=!((A*B)+(C*D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_AOI4_")); + if (enabled_gates.empty() || enabled_gates.count("OAI4")) + fprintf(f, "GATE OAI4 %d Y=!((A+B)*(C+D)); PIN * INV 1 999 1 0 1 0\n", get_cell_cost("$_OAI4_")); + if (enabled_gates.empty() || enabled_gates.count("MUX")) + fprintf(f, "GATE MUX %d Y=(A*B)+(S*B)+(!S*A); PIN * UNKNOWN 1 999 1 0 1 0\n", get_cell_cost("$_MUX_")); + if (map_mux4) + fprintf(f, "GATE MUX4 %d Y=(!S*!T*A)+(S*!T*B)+(!S*T*C)+(S*T*D); PIN * UNKNOWN 1 999 1 0 1 0\n", 2*get_cell_cost("$_MUX_")); + if (map_mux8) + fprintf(f, "GATE MUX8 %d Y=(!S*!T*!U*A)+(S*!T*!U*B)+(!S*T*!U*C)+(S*T*!U*D)+(!S*!T*U*E)+(S*!T*U*F)+(!S*T*U*G)+(S*T*U*H); PIN * UNKNOWN 1 999 1 0 1 0\n", 4*get_cell_cost("$_MUX_")); + if (map_mux16) + fprintf(f, "GATE MUX16 %d Y=(!S*!T*!U*!V*A)+(S*!T*!U*!V*B)+(!S*T*!U*!V*C)+(S*T*!U*!V*D)+(!S*!T*U*!V*E)+(S*!T*U*!V*F)+(!S*T*U*!V*G)+(S*T*U*!V*H)+(!S*!T*!U*V*I)+(S*!T*!U*V*J)+(!S*T*!U*V*K)+(S*T*!U*V*L)+(!S*!T*U*V*M)+(S*!T*U*V*N)+(!S*T*U*V*O)+(S*T*U*V*P); PIN * UNKNOWN 1 999 1 0 1 0\n", 8*get_cell_cost("$_MUX_")); + fclose(f); + + 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 + abc_output_filter filt(tempdir_name, show_tempdir); + int ret = run_command(buffer, std::bind(&abc_output_filter::next_line, filt, std::placeholders::_1)); +#else + // These needs to be mutable, supposedly due to getopt + char *abc_argv[5]; + string tmp_script_name = stringf("%s/abc.script", tempdir_name.c_str()); + abc_argv[0] = strdup(exe_file.c_str()); + abc_argv[1] = strdup("-s"); + abc_argv[2] = strdup("-f"); + abc_argv[3] = strdup(tmp_script_name.c_str()); + abc_argv[4] = 0; + int ret = Abc_RealMain(4, abc_argv); + free(abc_argv[0]); + free(abc_argv[1]); + free(abc_argv[2]); + free(abc_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.xaig"); + std::ifstream ifs; + ifs.open(buffer); + if (ifs.fail()) + log_error("Can't open ABC output file `%s'.\n", buffer.c_str()); + + 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 */); + reader.parse_xaiger(); + + ifs.close(); + + 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 *remap_wire = module->addWire(remap_name(w->name), GetSize(w)); + if (markgroups) remap_wire->attributes["\\abcgroup"] = map_autoidx; + design->select(module, remap_wire); + 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; + } + + 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; + for (auto c : mapped_mod->cells()) + { + 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; + design->select(module, cell); + continue; + } + + cell_stats[RTLIL::unescape_id(c->type)]++; + if (c->type == "\\ZERO" || c->type == "\\ONE") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\ZERO" ? 0 : 1, 1); + module->connect(conn); + continue; + } + if (c->type == "\\BUF") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]); + conn.second = RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]); + module->connect(conn); + 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) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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("\\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") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX4_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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("\\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("\\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") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX8_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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("\\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("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)])); + cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)])); + cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)])); + cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)])); + 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("\\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") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_MUX16_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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("\\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("\\E", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\E").as_wire()->name)])); + cell->setPort("\\F", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\F").as_wire()->name)])); + cell->setPort("\\G", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\G").as_wire()->name)])); + cell->setPort("\\H", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\H").as_wire()->name)])); + cell->setPort("\\I", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\I").as_wire()->name)])); + cell->setPort("\\J", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\J").as_wire()->name)])); + cell->setPort("\\K", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\K").as_wire()->name)])); + cell->setPort("\\L", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\L").as_wire()->name)])); + cell->setPort("\\M", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\M").as_wire()->name)])); + cell->setPort("\\N", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\N").as_wire()->name)])); + cell->setPort("\\O", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\O").as_wire()->name)])); + cell->setPort("\\P", RTLIL::SigSpec(module->wires_[remap_name(c->getPort("\\P").as_wire()->name)])); + 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("\\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") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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("\\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") { + RTLIL::Cell *cell = module->addCell(remap_name(c->name), "$_" + c->type.substr(1) + "_"); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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("\\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") { + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); + } + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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; + } + } + else + cell_stats[RTLIL::unescape_id(c->type)]++; + + if (c->type == "\\_const0_" || c->type == "\\_const1_") { + RTLIL::SigSig conn; + conn.first = RTLIL::SigSpec(module->wires_[remap_name(c->connections().begin()->second.as_wire()->name)]); + conn.second = RTLIL::SigSpec(c->type == "\\_const0_" ? 0 : 1, 1); + module->connect(conn); + continue; + } + + if (c->type == "\\_dff_") { + log_assert(clk_sig.size() == 1); + RTLIL::Cell *cell; + if (en_sig.size() == 0) { + cell = module->addCell(remap_name(c->name), clk_polarity ? "$_DFF_P_" : "$_DFF_N_"); + } else { + log_assert(en_sig.size() == 1); + cell = module->addCell(remap_name(c->name), stringf("$_DFFE_%c%c_", clk_polarity ? 'P' : 'N', en_polarity ? 'P' : 'N')); + cell->setPort("\\E", en_sig); + } + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + 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; + } + + if (c->type == "$lut" && GetSize(c->getPort("\\A")) == 1 && c->getParam("\\LUT").as_int() == 2) { + SigSpec my_a = module->wires_[remap_name(c->getPort("\\A").as_wire()->name)]; + SigSpec my_y = module->wires_[remap_name(c->getPort("\\Y").as_wire()->name)]; + module->connect(my_y, my_a); + continue; + } + + RTLIL::Cell *cell = module->addCell(remap_name(c->name), c->type); + if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx; + cell->parameters = c->parameters; + for (auto &conn : c->connections()) { + RTLIL::SigSpec newsig; + for (auto c : conn.second.chunks()) { + if (c.width == 0) + continue; + //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); + } + + // 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_[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_[remap_name(c.wire->name)]; + conn.second = std::move(chunks); + } + module->connect(conn); + } + + if (recover_init) + for (auto wire : mapped_mod->wires()) { + if (wire->attributes.count("\\init")) { + Wire *w = module->wires_[remap_name(wire->name)]; + log_assert(w->attributes.count("\\init") == 0); + w->attributes["\\init"] = wire->attributes.at("\\init"); + } + } + + 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; + //for (auto &si : signal_list) + // if (si.is_port) { + // char buffer[100]; + // snprintf(buffer, 100, "\\n%d", si.id); + // RTLIL::SigSig conn; + // if (si.type != G(NONE)) { + // conn.first = si.bit; + // conn.second = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + // out_wires++; + // } else { + // conn.first = RTLIL::SigSpec(module->wires_[remap_name(buffer)]); + // conn.second = si.bit; + // in_wires++; + // } + // module->connect(conn); + // } + + // Go through all cell output connections, + // and for those output ports driving wires + // also driven by mapped_mod, disconnect them + for (auto cell : module->cells()) { + 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(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 { + 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)); + + if (w->port_input) { + RTLIL::SigSig conn; + conn.first = remap_wire; + conn.second = signal; + in_wires++; + module->connect(conn); + } + else if (w->port_output) { + RTLIL::SigSig conn; + conn.first = signal; + conn.second = remap_wire; + out_wires++; + module->connect(conn); + } + else log_abort(); + } + + //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); + + delete mapped_design; + } + //else + //{ + // log("Don't call ABC as there is nothing to map.\n"); + //} + + Pass::call(design, "clean"); + +cleanup: + if (cleanup) + { + log("Removing temp directory.\n"); + remove_directory(tempdir_name); + } + + log_pop(); +} + +struct Abc9Pass : public Pass { + Abc9Pass() : Pass("abc9", "use ABC for technology mapping") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" abc9 [options] [selection]\n"); + log("\n"); + log("This pass uses the ABC tool [1] for technology mapping of yosys's internal gate\n"); + log("library to a target architecture.\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 -liberty without -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LIB).c_str()); + log("\n"); + log(" for -liberty with -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_CTR).c_str()); + log("\n"); + log(" for -lut/-luts (only one LUT size):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT "; lutpack {S}").c_str()); + log("\n"); + log(" for -lut/-luts (different LUT sizes):\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_LUT).c_str()); + log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_SOP).c_str()); + log("\n"); + log(" otherwise:\n"); + log("%s\n", fold_abc_cmd(ABC_COMMAND_DFL).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 -liberty without -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LIB).c_str()); + log("\n"); + log(" for -liberty with -constr:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_CTR).c_str()); + log("\n"); + log(" for -lut/-luts:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str()); + log("\n"); + log(" for -sop:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_SOP).c_str()); + log("\n"); + log(" otherwise:\n"); + log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_DFL).c_str()); + log("\n"); + log(" -liberty <file>\n"); + log(" generate netlists for the specified cell library (using the liberty\n"); + log(" file format).\n"); + log("\n"); + log(" -constr <file>\n"); + log(" pass this file with timing constraints to ABC. use with -liberty.\n"); + log("\n"); + log(" a constr file contains two lines:\n"); + log(" set_driving_cell <cell_name>\n"); + log(" set_load <floating_point_number>\n"); + log("\n"); + log(" the set_driving_cell statement defines which cell type is assumed to\n"); + log(" drive the primary inputs and the set_load statement sets the load in\n"); + log(" femtofarads for each primary output.\n"); + 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(" this also replaces 'dretime' with 'dretime; retime -o {D}' in the\n"); + log(" default scripts above.\n"); + log("\n"); + log(" -I <num>\n"); + log(" maximum number of SOP inputs.\n"); + log(" (replaces {I} in the default scripts above)\n"); + log("\n"); + log(" -P <num>\n"); + log(" maximum number of SOP products.\n"); + log(" (replaces {P} in the 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(" -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(" -sop\n"); + log(" map to sum-of-product cells and inverters\n"); + log("\n"); + // log(" -mux4, -mux8, -mux16\n"); + // log(" try to extract 4-input, 8-input, and/or 16-input muxes\n"); + // log(" (ignored when used with -liberty or -lut)\n"); + // log("\n"); + log(" -g type1,type2,...\n"); + log(" Map to the specified list of gate types. Supported gates types are:\n"); + log(" AND, NAND, OR, NOR, XOR, XNOR, ANDNOT, ORNOT, MUX, AOI3, OAI3, AOI4, OAI4.\n"); + log(" (The NOT gate is always added to this list automatically.)\n"); + log("\n"); + log(" The following aliases can be used to reference common sets of gate types:\n"); + log(" simple: AND OR XOR MUX\n"); + log(" cmos2: NAND NOR\n"); + log(" cmos3: NAND NOR AOI3 OAI3\n"); + log(" cmos4: NAND NOR AOI3 OAI3 AOI4 OAI4\n"); + log(" gates: AND NAND OR NOR XOR XNOR ANDNOT ORNOT\n"); + log(" aig: AND NAND OR NOR ANDNOT ORNOT\n"); + log("\n"); + log(" Prefix a gate type with a '-' to remove it from the list. For example\n"); + log(" the arguments 'AND,OR,XOR' and 'simple,-MUX' are equivalent.\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(" -nocleanup\n"); + log(" when this option is used, the temporary files created by this pass\n"); + log(" are not removed. this is useful for debugging.\n"); + log("\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("When neither -liberty nor -lut is used, the Yosys standard cell library is\n"); + log("loaded into ABC before the ABC script is executed.\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 the 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 pass (technology mapping using ABC).\n"); + log_push(); + + assign_map.clear(); + signal_map.clear(); + signal_init.clear(); + pi_map.clear(); + po_map.clear(); + +#ifdef ABCEXTERNAL + std::string exe_file = ABCEXTERNAL; +#else + std::string exe_file = proc_self_dirname() + "yosys-abc"; +#endif + std::string script_file, liberty_file, constr_file, clk_str; + 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 = true; + vector<int> lut_costs; + markgroups = false; + + map_mux4 = false; + map_mux8 = false; + map_mux16 = false; + enabled_gates.clear(); + +#ifdef _WIN32 +#ifndef ABCEXTERNAL + if (!check_file_exists(exe_file + ".exe") && check_file_exists(proc_self_dirname() + "..\\yosys-abc.exe")) + exe_file = proc_self_dirname() + "..\\yosys-abc"; +#endif +#endif + + 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 == "-liberty" && argidx+1 < args.size()) { + 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; + } + if (arg == "-constr" && argidx+1 < args.size()) { + rewrite_filename(constr_file); + constr_file = args[++argidx]; + if (!constr_file.empty() && !is_absolute_path(constr_file)) + constr_file = std::string(pwd) + "/" + constr_file; + continue; + } + if (arg == "-D" && argidx+1 < args.size()) { + delay_target = "-D " + args[++argidx]; + continue; + } + if (arg == "-I" && argidx+1 < args.size()) { + sop_inputs = "-I " + args[++argidx]; + continue; + } + if (arg == "-P" && argidx+1 < args.size()) { + sop_products = "-P " + 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]; + 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; + } + 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 == "-sop") { + sop_mode = true; + continue; + } + if (arg == "-mux4") { + map_mux4 = true; + continue; + } + if (arg == "-mux8") { + map_mux8 = true; + continue; + } + if (arg == "-mux16") { + map_mux16 = true; + continue; + } + if (arg == "-g" && argidx+1 < args.size()) { + for (auto g : split_tokens(args[++argidx], ",")) { + vector<string> gate_list; + bool remove_gates = false; + if (GetSize(g) > 0 && g[0] == '-') { + remove_gates = true; + g = g.substr(1); + } + if (g == "AND") goto ok_gate; + if (g == "NAND") goto ok_gate; + if (g == "OR") goto ok_gate; + if (g == "NOR") goto ok_gate; + if (g == "XOR") goto ok_gate; + if (g == "XNOR") goto ok_gate; + if (g == "ANDNOT") goto ok_gate; + if (g == "ORNOT") goto ok_gate; + if (g == "MUX") goto ok_gate; + if (g == "AOI3") goto ok_gate; + if (g == "OAI3") goto ok_gate; + if (g == "AOI4") goto ok_gate; + if (g == "OAI4") goto ok_gate; + if (g == "simple") { + gate_list.push_back("AND"); + gate_list.push_back("OR"); + gate_list.push_back("XOR"); + gate_list.push_back("MUX"); + goto ok_alias; + } + if (g == "cmos2") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + goto ok_alias; + } + if (g == "cmos3") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + gate_list.push_back("AOI3"); + gate_list.push_back("OAI3"); + goto ok_alias; + } + if (g == "cmos4") { + gate_list.push_back("NAND"); + gate_list.push_back("NOR"); + gate_list.push_back("AOI3"); + gate_list.push_back("OAI3"); + gate_list.push_back("AOI4"); + gate_list.push_back("OAI4"); + goto ok_alias; + } + if (g == "gates") { + gate_list.push_back("AND"); + gate_list.push_back("NAND"); + gate_list.push_back("OR"); + gate_list.push_back("NOR"); + gate_list.push_back("XOR"); + gate_list.push_back("XNOR"); + gate_list.push_back("ANDNOT"); + gate_list.push_back("ORNOT"); + goto ok_alias; + } + if (g == "aig") { + gate_list.push_back("AND"); + gate_list.push_back("NAND"); + gate_list.push_back("OR"); + gate_list.push_back("NOR"); + gate_list.push_back("ANDNOT"); + gate_list.push_back("ORNOT"); + goto ok_alias; + } + cmd_error(args, argidx, stringf("Unsupported gate type: %s", g.c_str())); + ok_gate: + gate_list.push_back(g); + ok_alias: + for (auto gate : gate_list) { + if (remove_gates) + enabled_gates.erase(gate); + else + 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()) + log_cmd_error("Got -lut and -liberty! This two options are exclusive.\n"); + if (!constr_file.empty() && liberty_file.empty()) + log_cmd_error("Got -constr but no -liberty!\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; + } + + assign_map.set(mod); + signal_init.clear(); + + for (Wire *wire : mod->wires()) + if (wire->attributes.count("\\init")) { + SigSpec initsig = assign_map(wire); + Const initval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++) + switch (initval[i]) { + case State::S0: + signal_init[initsig[i]] = State::S0; + break; + case State::S1: + signal_init[initsig[i]] = State::S0; + break; + default: + break; + } + } + + if (!dff_mode || !clk_str.empty()) { + abc9_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, dff_mode, clk_str, keepff, + delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode); + continue; + } + + CellTypes ct(design); + + std::vector<RTLIL::Cell*> all_cells = mod->selected_cells(); + std::set<RTLIL::Cell*> unassigned_cells(all_cells.begin(), all_cells.end()); + + std::set<RTLIL::Cell*> expand_queue, next_expand_queue; + std::set<RTLIL::Cell*> expand_queue_up, next_expand_queue_up; + std::set<RTLIL::Cell*> expand_queue_down, next_expand_queue_down; + + typedef tuple<bool, RTLIL::SigSpec, bool, RTLIL::SigSpec> clkdomain_t; + std::map<clkdomain_t, std::vector<RTLIL::Cell*>> assigned_cells; + std::map<RTLIL::Cell*, clkdomain_t> assigned_cells_reverse; + + std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_bit, cell_to_bit_up, cell_to_bit_down; + std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> bit_to_cell, bit_to_cell_up, bit_to_cell_down; + + for (auto cell : all_cells) + { + clkdomain_t key; + + for (auto &conn : cell->connections()) + for (auto bit : conn.second) { + bit = assign_map(bit); + if (bit.wire != nullptr) { + cell_to_bit[cell].insert(bit); + bit_to_cell[bit].insert(cell); + if (ct.cell_input(cell->type, conn.first)) { + cell_to_bit_up[cell].insert(bit); + bit_to_cell_down[bit].insert(cell); + } + if (ct.cell_output(cell->type, conn.first)) { + cell_to_bit_down[cell].insert(bit); + bit_to_cell_up[bit].insert(cell); + } + } + } + + if (cell->type == "$_DFF_N_" || cell->type == "$_DFF_P_") + { + key = clkdomain_t(cell->type == "$_DFF_P_", assign_map(cell->getPort("\\C")), true, RTLIL::SigSpec()); + } + else + if (cell->type == "$_DFFE_NN_" || cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_") + { + bool this_clk_pol = cell->type == "$_DFFE_PN_" || cell->type == "$_DFFE_PP_"; + bool this_en_pol = cell->type == "$_DFFE_NP_" || cell->type == "$_DFFE_PP_"; + key = clkdomain_t(this_clk_pol, assign_map(cell->getPort("\\C")), this_en_pol, assign_map(cell->getPort("\\E"))); + } + else + continue; + + unassigned_cells.erase(cell); + expand_queue.insert(cell); + expand_queue_up.insert(cell); + expand_queue_down.insert(cell); + + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + while (!expand_queue_up.empty() || !expand_queue_down.empty()) + { + if (!expand_queue_up.empty()) + { + RTLIL::Cell *cell = *expand_queue_up.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_up.erase(cell); + + for (auto bit : cell_to_bit_up[cell]) + for (auto c : bit_to_cell_up[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (!expand_queue_down.empty()) + { + RTLIL::Cell *cell = *expand_queue_down.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue_down.erase(cell); + + for (auto bit : cell_to_bit_down[cell]) + for (auto c : bit_to_cell_down[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue_up.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + expand_queue.insert(c); + } + } + + if (expand_queue_up.empty() && expand_queue_down.empty()) { + expand_queue_up.swap(next_expand_queue_up); + expand_queue_down.swap(next_expand_queue_down); + } + } + + while (!expand_queue.empty()) + { + RTLIL::Cell *cell = *expand_queue.begin(); + clkdomain_t key = assigned_cells_reverse.at(cell); + expand_queue.erase(cell); + + for (auto bit : cell_to_bit.at(cell)) { + for (auto c : bit_to_cell[bit]) + if (unassigned_cells.count(c)) { + unassigned_cells.erase(c); + next_expand_queue.insert(c); + assigned_cells[key].push_back(c); + assigned_cells_reverse[c] = key; + } + bit_to_cell[bit].clear(); + } + + if (expand_queue.empty()) + expand_queue.swap(next_expand_queue); + } + + clkdomain_t key(true, RTLIL::SigSpec(), true, RTLIL::SigSpec()); + for (auto cell : unassigned_cells) { + assigned_cells[key].push_back(cell); + assigned_cells_reverse[cell] = key; + } + + log_header(design, "Summary of detected clock domains:\n"); + for (auto &it : assigned_cells) + log(" %d cells in clk=%s%s, en=%s%s\n", GetSize(it.second), + std::get<0>(it.first) ? "" : "!", log_signal(std::get<1>(it.first)), + std::get<2>(it.first) ? "" : "!", log_signal(std::get<3>(it.first))); + + for (auto &it : assigned_cells) { + clk_polarity = std::get<0>(it.first); + clk_sig = assign_map(std::get<1>(it.first)); + en_polarity = std::get<2>(it.first); + en_sig = assign_map(std::get<3>(it.first)); + abc9_module(design, mod, script_file, exe_file, liberty_file, constr_file, cleanup, lut_costs, !clk_sig.empty(), "$", + keepff, delay_target, sop_inputs, sop_products, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode); + assign_map.set(mod); + } + } + + assign_map.clear(); + signal_map.clear(); + signal_init.clear(); + pi_map.clear(); + po_map.clear(); + + log_pop(); + } +} Abc9Pass; + +PRIVATE_NAMESPACE_END diff --git a/techlibs/common/synth.cc b/techlibs/common/synth.cc index ccfa76e02..349605f8c 100644 --- a/techlibs/common/synth.cc +++ b/techlibs/common/synth.cc @@ -75,13 +75,16 @@ struct SynthPass : public ScriptPass log(" from label is synonymous to 'begin', and empty to label is\n"); log(" synonymous to the end of the command list.\n"); log("\n"); + log(" -abc9\n"); + log(" use abc9 instead of abc\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - string top_module, fsm_opts, memory_opts; + string top_module, fsm_opts, memory_opts, abc; bool autotop, flatten, noalumacc, nofsm, noabc, noshare; int lut; @@ -98,6 +101,7 @@ struct SynthPass : public ScriptPass nofsm = false; noabc = false; noshare = false; + abc = "abc"; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -159,6 +163,10 @@ struct SynthPass : public ScriptPass noshare = true; continue; } + if (args[argidx] == "-abc9") { + abc = "abc9"; + continue; + } break; } extra_args(args, argidx, design); @@ -239,15 +247,15 @@ struct SynthPass : public ScriptPass #ifdef YOSYS_ENABLE_ABC if (help_mode) { - run("abc -fast", " (unless -noabc, unless -lut)"); - run("abc -fast -lut k", "(unless -noabc, if -lut)"); + run(abc + " -fast", " (unless -noabc, unless -lut)"); + run(abc + " -fast -lut k", "(unless -noabc, if -lut)"); } else { if (lut) - run(stringf("abc -fast -lut %d", lut)); + run(stringf("%s -fast -lut %d", abc.c_str(), lut)); else - run("abc -fast"); + run(abc + " -fast"); } run("opt -fast", " (unless -noabc)"); #endif diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index f900453e8..f77ea7f80 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -89,13 +89,16 @@ struct SynthIce40Pass : public ScriptPass log(" generate an output netlist (and BLIF file) suitable for VPR\n"); log(" (this feature is experimental and incomplete)\n"); log("\n"); + log(" -abc9\n"); + log(" use abc9 instead of abc\n"); + log("\n"); log("\n"); log("The following commands are executed by this synthesis command:\n"); help_script(); log("\n"); } - string top_opt, blif_file, edif_file, json_file; + string top_opt, blif_file, edif_file, json_file, abc; bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr; int min_ce_use; @@ -115,6 +118,7 @@ struct SynthIce40Pass : public ScriptPass noabc = false; abc2 = false; vpr = false; + abc = "abc"; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -193,6 +197,10 @@ struct SynthIce40Pass : public ScriptPass vpr = true; continue; } + if (args[argidx] == "-abc9") { + abc = "abc9"; + continue; + } break; } extra_args(args, argidx, design); @@ -249,7 +257,7 @@ struct SynthIce40Pass : public ScriptPass else run("techmap -map +/techmap.v -map +/ice40/arith_map.v"); if (retime || help_mode) - run("abc -dff", "(only if -retime)"); + run(abc + " -dff", "(only if -retime)"); run("ice40_opt"); } @@ -273,7 +281,7 @@ struct SynthIce40Pass : public ScriptPass if (check_label("map_luts")) { if (abc2 || help_mode) { - run("abc", " (only if -abc2)"); + run(abc, " (only if -abc2)"); run("ice40_opt", "(only if -abc2)"); } run("techmap -map +/ice40/latches_map.v"); @@ -282,7 +290,7 @@ struct SynthIce40Pass : public ScriptPass run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)"); } if (!noabc) { - run("abc -lut 4", "(skip if -noabc)"); + run(abc + " -lut 4", "(skip if -noabc)"); } run("clean"); if (relut || help_mode) { diff --git a/tests/aiger/and.aag b/tests/aiger/and.aag new file mode 100644 index 000000000..d1ef2c5a5 --- /dev/null +++ b/tests/aiger/and.aag @@ -0,0 +1,5 @@ +aag 3 2 0 1 1 +2 +4 +6 +6 2 4 diff --git a/tests/aiger/and.aig b/tests/aiger/and.aig new file mode 100644 index 000000000..da0fa0719 --- /dev/null +++ b/tests/aiger/and.aig @@ -0,0 +1,3 @@ +aig 3 2 0 1 1 +6 +
\ No newline at end of file diff --git a/tests/aiger/buffer.aag b/tests/aiger/buffer.aag new file mode 100644 index 000000000..94a6fb1ed --- /dev/null +++ b/tests/aiger/buffer.aag @@ -0,0 +1,3 @@ +aag 1 1 0 1 0 +2 +2 diff --git a/tests/aiger/buffer.aig b/tests/aiger/buffer.aig new file mode 100644 index 000000000..0c715fdeb --- /dev/null +++ b/tests/aiger/buffer.aig @@ -0,0 +1,2 @@ +aig 1 1 0 1 0 +2 diff --git a/tests/aiger/cnt1.aag b/tests/aiger/cnt1.aag new file mode 100644 index 000000000..ce4f28fcb --- /dev/null +++ b/tests/aiger/cnt1.aag @@ -0,0 +1,3 @@ +aag 1 0 1 0 0 1 +2 3 +2 diff --git a/tests/aiger/cnt1.aig b/tests/aiger/cnt1.aig new file mode 100644 index 000000000..8d0ba13b1 --- /dev/null +++ b/tests/aiger/cnt1.aig @@ -0,0 +1,3 @@ +aig 1 0 1 0 0 1 +3 +2 diff --git a/tests/aiger/cnt1e.aag b/tests/aiger/cnt1e.aag new file mode 100644 index 000000000..6db3f0ffd --- /dev/null +++ b/tests/aiger/cnt1e.aag @@ -0,0 +1,8 @@ +aag 5 1 1 0 3 1 +2 +4 10 +4 +6 5 3 +8 4 2 +10 9 7 +b0 AIGER_NEVER diff --git a/tests/aiger/cnt1e.aig b/tests/aiger/cnt1e.aig new file mode 100644 index 000000000..d8d159f11 --- /dev/null +++ b/tests/aiger/cnt1e.aig @@ -0,0 +1,4 @@ +aig 5 1 1 0 3 1 +10 +4 +b0 AIGER_NEVER diff --git a/tests/aiger/empty.aag b/tests/aiger/empty.aag new file mode 100644 index 000000000..40c0f00cb --- /dev/null +++ b/tests/aiger/empty.aag @@ -0,0 +1 @@ +aag 0 0 0 0 0 diff --git a/tests/aiger/empty.aig b/tests/aiger/empty.aig new file mode 100644 index 000000000..a28373cd3 --- /dev/null +++ b/tests/aiger/empty.aig @@ -0,0 +1 @@ +aig 0 0 0 0 0 diff --git a/tests/aiger/false.aag b/tests/aiger/false.aag new file mode 100644 index 000000000..421e64a91 --- /dev/null +++ b/tests/aiger/false.aag @@ -0,0 +1,2 @@ +aag 0 0 0 1 0 +0 diff --git a/tests/aiger/false.aig b/tests/aiger/false.aig new file mode 100644 index 000000000..ad7d039fa --- /dev/null +++ b/tests/aiger/false.aig @@ -0,0 +1,2 @@ +aig 0 0 0 1 0 +0 diff --git a/tests/aiger/halfadder.aag b/tests/aiger/halfadder.aag new file mode 100644 index 000000000..5bf54d38d --- /dev/null +++ b/tests/aiger/halfadder.aag @@ -0,0 +1,14 @@ +aag 7 2 0 2 3 +2 +4 +6 +12 +6 13 15 +12 2 4 +14 3 5 +i0 x +i1 y +o0 s +o1 c +c +half adder diff --git a/tests/aiger/halfadder.aig b/tests/aiger/halfadder.aig new file mode 100644 index 000000000..83727ee63 --- /dev/null +++ b/tests/aiger/halfadder.aig @@ -0,0 +1,9 @@ +aig 5 2 0 2 3 +10 +6 +i0 x +i1 y +o0 s +o1 c +c +half adder diff --git a/tests/aiger/inverter.aag b/tests/aiger/inverter.aag new file mode 100644 index 000000000..ff7c28542 --- /dev/null +++ b/tests/aiger/inverter.aag @@ -0,0 +1,3 @@ +aag 1 1 0 1 0 +2 +3 diff --git a/tests/aiger/inverter.aig b/tests/aiger/inverter.aig new file mode 100644 index 000000000..525d82392 --- /dev/null +++ b/tests/aiger/inverter.aig @@ -0,0 +1,2 @@ +aig 1 1 0 1 0 +3 diff --git a/tests/aiger/notcnt1.aag b/tests/aiger/notcnt1.aag new file mode 100644 index 000000000..e92815f23 --- /dev/null +++ b/tests/aiger/notcnt1.aag @@ -0,0 +1,4 @@ +aag 1 0 1 0 0 1 +2 3 +3 +b0 AIGER_NEVER diff --git a/tests/aiger/notcnt1.aig b/tests/aiger/notcnt1.aig new file mode 100644 index 000000000..f8a667f1f --- /dev/null +++ b/tests/aiger/notcnt1.aig @@ -0,0 +1,4 @@ +aig 1 0 1 0 0 1 +3 +3 +b0 AIGER_NEVER diff --git a/tests/aiger/notcnt1e.aag b/tests/aiger/notcnt1e.aag new file mode 100644 index 000000000..141c864f7 --- /dev/null +++ b/tests/aiger/notcnt1e.aag @@ -0,0 +1,8 @@ +aag 5 1 1 0 3 1 +2 +4 10 +5 +6 5 3 +8 4 2 +10 9 7 +b0 AIGER_NEVER diff --git a/tests/aiger/notcnt1e.aig b/tests/aiger/notcnt1e.aig new file mode 100644 index 000000000..7c85a7290 --- /dev/null +++ b/tests/aiger/notcnt1e.aig @@ -0,0 +1,4 @@ +aig 5 1 1 0 3 1 +10 +5 +b0 AIGER_NEVER diff --git a/tests/aiger/or.aag b/tests/aiger/or.aag new file mode 100644 index 000000000..f780e339f --- /dev/null +++ b/tests/aiger/or.aag @@ -0,0 +1,5 @@ +aag 3 2 0 1 1 +2 +4 +7 +6 3 5 diff --git a/tests/aiger/or.aig b/tests/aiger/or.aig new file mode 100644 index 000000000..75c9e4480 --- /dev/null +++ b/tests/aiger/or.aig @@ -0,0 +1,3 @@ +aig 3 2 0 1 1 +7 +
\ No newline at end of file diff --git a/tests/aiger/run-test.sh b/tests/aiger/run-test.sh new file mode 100755 index 000000000..e0a34f023 --- /dev/null +++ b/tests/aiger/run-test.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +OPTIND=1 +seed="" # default to no seed specified +while getopts "S:" opt +do + case "$opt" in + S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space + seed="SEED=$arg" ;; + esac +done +shift "$((OPTIND-1))" + +# check for Icarus Verilog +if ! which iverilog > /dev/null ; then + echo "$0: Error: Icarus Verilog 'iverilog' not found." + exit 1 +fi + +echo "===== AAG ======" +${MAKE:-make} -f ../tools/autotest.mk $seed *.aag EXTRA_FLAGS="-f aiger" + +echo "===== AIG ======" +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.aig EXTRA_FLAGS="-f aiger" diff --git a/tests/aiger/toggle-re.aag b/tests/aiger/toggle-re.aag new file mode 100644 index 000000000..b662bb386 --- /dev/null +++ b/tests/aiger/toggle-re.aag @@ -0,0 +1,14 @@ +aag 7 2 1 2 4 +2 +4 +6 8 +6 +7 +8 4 10 +10 13 15 +12 2 6 +14 3 7 +i0 enable +i1 reset +o0 Q +o1 !Q diff --git a/tests/aiger/toggle-re.aig b/tests/aiger/toggle-re.aig new file mode 100644 index 000000000..9d6730f21 --- /dev/null +++ b/tests/aiger/toggle-re.aig @@ -0,0 +1,8 @@ +aig 7 2 1 2 4 +14 +6 +7 +i0 enable +i1 reset +o0 Q +o1 !Q diff --git a/tests/aiger/toggle.aag b/tests/aiger/toggle.aag new file mode 100644 index 000000000..09651012d --- /dev/null +++ b/tests/aiger/toggle.aag @@ -0,0 +1,4 @@ +aag 1 0 1 2 0 +2 3 +2 +3 diff --git a/tests/aiger/toggle.aig b/tests/aiger/toggle.aig new file mode 100644 index 000000000..b69e21aaf --- /dev/null +++ b/tests/aiger/toggle.aig @@ -0,0 +1,4 @@ +aig 1 0 1 2 0 +3 +2 +3 diff --git a/tests/aiger/true.aag b/tests/aiger/true.aag new file mode 100644 index 000000000..366893648 --- /dev/null +++ b/tests/aiger/true.aag @@ -0,0 +1,2 @@ +aag 0 0 0 1 0 +1 diff --git a/tests/aiger/true.aig b/tests/aiger/true.aig new file mode 100644 index 000000000..10086f389 --- /dev/null +++ b/tests/aiger/true.aig @@ -0,0 +1,2 @@ +aig 0 0 0 1 0 +1 diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v new file mode 100644 index 000000000..2d9aea366 --- /dev/null +++ b/tests/simple_abc9/abc9.v @@ -0,0 +1,6 @@ +module top(input [1:0] a, output [1:0] b, output c, output d, output e); +assign b = a; +assign c = ^a; +assign d = ~c; +assign e = d; +endmodule diff --git a/tests/simple_abc9/run-test.sh b/tests/simple_abc9/run-test.sh new file mode 100755 index 000000000..1baf31244 --- /dev/null +++ b/tests/simple_abc9/run-test.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +OPTIND=1 +seed="" # default to no seed specified +while getopts "S:" opt +do + case "$opt" in + S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space + seed="SEED=$arg" ;; + esac +done +shift "$((OPTIND-1))" + +# check for Icarus Verilog +if ! which iverilog > /dev/null ; then + echo "$0: Error: Icarus Verilog 'iverilog' not found." + exit 1 +fi + +cp ../simple/*.v . +rm dff_different_styles.v # FIXME: dffsr1 fails because opt_rmdff does something fishy (#816) +rm partsel.v # FIXME: Contains 1'hx, thus write_xaiger fails +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-p \"hierarchy; synth -run coarse; techmap; opt -full; abc9 -lut 4\"" diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index 6fdd1e80a..65fd4cb1f 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -110,8 +110,9 @@ shift $((OPTIND - 1)) for fn do - bn=${fn%.v} - if [ "$bn" == "$fn" ]; then + bn=${fn%.*} + ext=${fn##*.} + if [[ "$ext" != "v" ]] && [[ "$ext" != "aag" ]] && [[ "$ext" != "aig" ]]; then echo "Invalid argument: $fn" >&2 exit 1 fi @@ -133,17 +134,22 @@ do fn=$(basename $fn) bn=$(basename $bn) + if [[ "$ext" == "v" ]]; then + egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.${ext} + else + "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "verilog" -o ${bn}_ref.v ../${fn} + frontend="verilog" + fi rm -f ${bn}_ref.fir - egrep -v '^\s*`timescale' ../$fn > ${bn}_ref.v - if [ ! -f ../${bn}_tb.v ]; then "$toolsdir"/../../yosys -f "$frontend $include_opts" -b "test_autotb $autotb_opts" -o ${bn}_tb.v ${bn}_ref.v else cp ../${bn}_tb.v ${bn}_tb.v fi if $genvcd; then sed -i 's,// \$dump,$dump,g' ${bn}_tb.v; fi - compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs + compile_and_run ${bn}_tb_ref ${bn}_out_ref ${bn}_tb.v ${bn}_ref.v $libs \ + "$toolsdir"/../../techlibs/common/simlib.v if $genvcd; then mv testbench.vcd ${bn}_ref.vcd; fi test_count=0 |