diff options
31 files changed, 4724 insertions, 168 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..b0602dbd8 --- /dev/null +++ b/backends/aiger/xaiger.cc @@ -0,0 +1,706 @@ +/* + * 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)) { + if (!w->port_input) { + SigBit I = sigmap(b); + if (I != b) + alias_map[b] = I; + co_bits.insert(b); + } + } + 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/aigerparse.cc b/frontends/aiger/aigerparse.cc index c45de8531..6fa77282e 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -22,16 +22,21 @@ // 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 log +#define log_debug(...) ; -AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name) - : design(design), f(f), clk_name(clk_name) +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; @@ -102,28 +107,134 @@ void AigerReader::parse_aiger() if (f.peek() == '\n') break; // Else constraint (TODO) - break; } 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("\\n%d%s", variable, invert ? "_inv" : "")); // FIXME: is "_inv" the right suffix? + RTLIL::IdString wire_name(stringf("\\__%d%s__", variable, invert ? "b" : "")); // FIXME: is "b" the right suffix? RTLIL::Wire *wire = module->wire(wire_name); 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("\\n%d", variable)); + 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; @@ -131,16 +242,225 @@ static RTLIL::Wire* createWireIfNotExists(RTLIL::Module *module, unsigned litera 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()); - RTLIL::Cell *inv = module->addCell(stringf("\\n%d_not", variable), "$_NOT_"); // FIXME: is "_not" the right suffix? - inv->setPort("\\A", wire_inv); - inv->setPort("\\Y", wire); + 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; @@ -167,6 +487,7 @@ void AigerReader::parse_aiger_ascii() 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)) @@ -203,8 +524,32 @@ void AigerReader::parse_aiger_ascii() if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); - log_debug("%d is an output\n", l1); - RTLIL::Wire *wire = createWireIfNotExists(module, l1); + 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); } @@ -227,22 +572,18 @@ void AigerReader::parse_aiger_ascii() std::getline(f, line); // Ignore up to start of next line // Parse AND - for (unsigned i = 0; i < A; ++i, ++line_count) { + 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)); // TODO: Output of ANDs can't be inverted? + 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); - - RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_"); - and_cell->setPort("\\A", i1_wire); - and_cell->setPort("\\B", i2_wire); - and_cell->setPort("\\Y", o_wire); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); } - std::getline(f, line); // Ignore up to start of next line + std::getline(f, line); } static unsigned parse_next_delta_literal(std::istream &f, unsigned ref) @@ -261,8 +602,10 @@ void AigerReader::parse_aiger_binary() // 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); } @@ -274,6 +617,7 @@ void AigerReader::parse_aiger_binary() 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) { @@ -310,8 +654,32 @@ void AigerReader::parse_aiger_binary() if (!(f >> l1)) log_error("Line %u cannot be interpreted as an output!\n", line_count); - log_debug("%d is an output\n", l1); - RTLIL::Wire *wire = createWireIfNotExists(module, l1); + 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); } @@ -340,18 +708,12 @@ void AigerReader::parse_aiger_binary() l3 = parse_next_delta_literal(f, l2); log_debug("%d %d %d is an AND\n", l1, l2, l3); - log_assert(!(l1 & 1)); // TODO: Output of ANDs can't be inverted? + 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); - - RTLIL::Cell *and_cell = module->addCell(NEW_ID, "$_AND_"); - and_cell->setPort("\\A", i1_wire); - and_cell->setPort("\\B", i2_wire); - and_cell->setPort("\\Y", o_wire); + module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire); } - std::getline(f, line); // Ignore up to start of next line - } struct AigerFrontend : public Frontend { @@ -365,18 +727,19 @@ struct AigerFrontend : public Frontend { log("Load module from an AIGER file into the current design.\n"); log("\n"); log(" -module_name <module_name>\n"); - log(" Name of module to be created (default: <filename>)" -#ifdef _WIN32 - "top" // FIXME -#else - "<filename>" -#endif - ")\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 { @@ -384,6 +747,8 @@ struct AigerFrontend : public Frontend { 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++) { @@ -396,19 +761,29 @@ struct AigerFrontend : public Frontend { 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 - module_name = "top"; // FIXME: basename equivalent on 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); + AigerReader reader(design, *f, module_name, clk_name, map_filename, wideports); reader.parse_aiger(); } } AigerFrontend; diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 39a77bd93..42c87a298 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -30,6 +30,8 @@ struct AigerReader 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 @@ -39,8 +41,9 @@ struct AigerReader 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); + 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(); }; 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/kernel/rtlil.cc b/kernel/rtlil.cc index 8404db5e9..d4aebcda9 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2410,6 +2410,9 @@ void RTLIL::Cell::fixup_parameters(bool set_a_signed, bool set_b_signed) if (connections_.count("\\Y")) parameters["\\Y_WIDTH"] = GetSize(connections_["\\Y"]); + if (connections_.count("\\Q")) + parameters["\\WIDTH"] = GetSize(connections_["\\Q"]); + check(); } diff --git a/passes/cmds/setundef.cc b/passes/cmds/setundef.cc index 56ef2d125..f6949c820 100644 --- a/passes/cmds/setundef.cc +++ b/passes/cmds/setundef.cc @@ -143,6 +143,9 @@ struct SetundefPass : public Pass { log(" -init\n"); log(" also create/update init values for flip-flops\n"); log("\n"); + log(" -params\n"); + log(" replace undef in cell parameters\n"); + log("\n"); } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { @@ -150,6 +153,7 @@ struct SetundefPass : public Pass { bool undriven_mode = false; bool expose_mode = false; bool init_mode = false; + bool params_mode = false; SetundefWorker worker; log_header(design, "Executing SETUNDEF pass (replace undef values with defined constants).\n"); @@ -199,6 +203,10 @@ struct SetundefPass : public Pass { init_mode = true; continue; } + if (args[argidx] == "-params") { + params_mode = true; + continue; + } if (args[argidx] == "-random" && !got_value && argidx+1 < args.size()) { got_value = true; worker.next_bit_mode = MODE_RANDOM; @@ -228,6 +236,18 @@ struct SetundefPass : public Pass { for (auto module : design->selected_modules()) { + if (params_mode) + { + for (auto *cell : module->selected_cells()) { + for (auto ¶meter : cell->parameters) { + for (auto &bit : parameter.second.bits) { + if (bit > RTLIL::State::S1) + bit = worker.next_bit(); + } + } + } + } + if (undriven_mode) { if (!module->processes.empty()) diff --git a/passes/opt/opt_rmdff.cc b/passes/opt/opt_rmdff.cc index 5880254c1..e8570f0eb 100644 --- a/passes/opt/opt_rmdff.cc +++ b/passes/opt/opt_rmdff.cc @@ -174,8 +174,6 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell) cell->unsetParam("\\CLR_POLARITY"); cell->unsetPort("\\SET"); cell->unsetPort("\\CLR"); - - return true; } else { @@ -186,11 +184,12 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell) cell->unsetParam("\\CLR_POLARITY"); cell->unsetPort("\\SET"); cell->unsetPort("\\CLR"); - - return true; } + + return true; } - else + + if (!hasreset) { IdString new_type; @@ -207,8 +206,10 @@ bool handle_dffsr(RTLIL::Module *mod, RTLIL::Cell *cell) cell->unsetPort("\\S"); cell->unsetPort("\\R"); - return did_something; + return true; } + + return did_something; } bool handle_dlatch(RTLIL::Module *mod, RTLIL::Cell *dlatch) diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 8063b86a6..3aa916ec2 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -38,7 +38,8 @@ struct WreduceConfig "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx", "$lt", "$le", "$eq", "$ne", "$eqx", "$nex", "$ge", "$gt", "$add", "$sub", "$mul", // "$div", "$mod", "$pow", - "$mux", "$pmux" + "$mux", "$pmux", + "$dff", "$adff" }); } }; @@ -134,6 +135,71 @@ struct WreduceWorker module->connect(sig_y.extract(n_kept, n_removed), sig_removed); } + void run_cell_dff(Cell *cell) + { + // Reduce size of FF if inputs are just sign/zero extended or output bit is not used + + SigSpec sig_d = mi.sigmap(cell->getPort("\\D")); + SigSpec sig_q = mi.sigmap(cell->getPort("\\Q")); + + int width_before = GetSize(sig_q); + + if (width_before == 0) + return; + + bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0; + bool sign_ext = !zero_ext; + + for (int i = GetSize(sig_q)-1; i >= 0; i--) + { + if (zero_ext && sig_d[i] == State::S0) { + module->connect(sig_q[i], State::S0); + sig_d.remove(i); + sig_q.remove(i); + continue; + } + + if (sign_ext && i > 0 && sig_d[i] == sig_d[i-1]) { + module->connect(sig_q[i], sig_q[i-1]); + sig_d.remove(i); + sig_q.remove(i); + continue; + } + + auto info = mi.query(sig_q[i]); + if (!info->is_output && GetSize(info->ports) <= 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { + sig_d.remove(i); + sig_q.remove(i); + zero_ext = false; + sign_ext = false; + continue; + } + + break; + } + + if (width_before == GetSize(sig_q)) + return; + + if (GetSize(sig_q) == 0) { + log("Removed cell %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type)); + return; + } + + log("Removed top %d bits (of %d) from mux cell %s.%s (%s).\n", width_before - GetSize(sig_q), width_before, + log_id(module), log_id(cell), log_id(cell->type)); + + for (auto bit : sig_d) + work_queue_bits.insert(bit); + + for (auto bit : sig_q) + work_queue_bits.insert(bit); + + cell->setPort("\\D", sig_d); + cell->setPort("\\Q", sig_q); + cell->fixup_parameters(); + } + void run_reduce_inport(Cell *cell, char port, int max_port_size, bool &port_signed, bool &did_something) { port_signed = cell->getParam(stringf("\\%c_SIGNED", port)).as_bool(); @@ -176,6 +242,9 @@ struct WreduceWorker if (cell->type.in("$mux", "$pmux")) return run_cell_mux(cell); + if (cell->type.in("$dff", "$adff")) + return run_cell_dff(cell); + SigSpec sig = mi.sigmap(cell->getPort("\\Y")); if (sig.has_const()) diff --git a/passes/pmgen/.gitignore b/passes/pmgen/.gitignore new file mode 100644 index 000000000..c9263057e --- /dev/null +++ b/passes/pmgen/.gitignore @@ -0,0 +1 @@ +/ice40_dsp_pm.h diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc new file mode 100644 index 000000000..33baaca30 --- /dev/null +++ b/passes/pmgen/Makefile.inc @@ -0,0 +1,8 @@ +OBJS += passes/pmgen/ice40_dsp.o + +passes/pmgen/ice40_dsp.o: passes/pmgen/ice40_dsp_pm.h +EXTRA_OBJS += passes/pmgen/ice40_dsp_pm.h +.SECONDARY: passes/pmgen/ice40_dsp_pm.h + +passes/pmgen/ice40_dsp_pm.h: passes/pmgen/ice40_dsp.pmg passes/pmgen/pmgen.py + $(P) cd passes/pmgen && python3 pmgen.py ice40_dsp diff --git a/passes/pmgen/README.md b/passes/pmgen/README.md new file mode 100644 index 000000000..a1007dc62 --- /dev/null +++ b/passes/pmgen/README.md @@ -0,0 +1,224 @@ +Pattern Matcher Generator +========================= + +The program `pmgen.py` reads a `.pmg` (Pattern Matcher Generator) file and +writes a header-only C++ library that implements that pattern matcher. + +The "patterns" in this context are subgraphs in a Yosys RTLIL netlist. + +The algorithm used in the generated pattern matcher is a simple recursive +search with backtracking. It is left to the author of the `.pmg` file to +determine an efficient cell order for the search that allows for maximum +use of indices and early backtracking. + + +API of Generated Matcher +======================== + +When `pmgen.py` reads a `foobar.pmg` file, it writes `foobar_pm.h` containing +a class `foobar_pm`. That class is instanciated with an RTLIL module and a +list of cells from that module: + + foobar_pm pm(module, module->selected_cells()); + +The caller must make sure that none of the cells in the 2nd argument are +deleted for as long as the patter matcher instance is used. + +At any time it is possible to disable cells, preventing them from showing +up in any future matches: + + pm.blacklist(some_cell); + +The `.run(callback_function)` method searches for all matches and calls the +callback function for each found match: + + pm.run([&](){ + log("found matching 'foo' cell: %s\n", log_id(pm.st.foo)); + log(" with 'bar' cell: %s\n", log_id(pm.st.bar)); + }); + +The `.pmg` file declares matcher state variables that are accessible via the +`.st.<state_name>` members. (The `.st` member is of type `foobar_pm::state_t`.) + +Similarly the `.pmg` file declares user data variables that become members of +`.ud`, a struct of type `foobar_pm::udata_t`. + + +The .pmg File Format +==================== + +The `.pmg` file format is a simple line-based file format. For the most part +lines consist of whitespace-separated tokens. + +Lines in `.pmg` files starting with `//` are comments. + +Declaring state variables +------------------------- + +One or more state variables can be declared using the `state` statement, +followed by a C++ type in angle brackets, followed by a whitespace separated +list of variable names. For example: + + state <bool> flag1 flag2 happy big + state <SigSpec> sigA sigB sigY + +State variables are automatically managed by the generated backtracking algorithm +and saved and restored as needed. + +They are automatically initialized to the default constructed value of their type +when `.run(callback_function)` is called. + +Declaring udata variables +------------------------- + +Udata (user-data) variables can be used for example to configure the matcher or +the callback function used to perform actions on found matches. + +There is no automatic management of udata variables. For this reason it is +recommended that the user-supplied matcher code treats them as read-only +variables. + +They are declared like state variables, just using the `udata` statement: + + udata <int> min_data_width max_data_width + udata <IdString> data_port_name + +They are atomatically initialzed to the default constructed value of their type +when ther pattern matcher object is constructed. + +Embedded C++ code +----------------- + +Many statements in a `.pmg` file contain C++ code. However, there are some +slight additions to regular C++/Yosys/RTLIL code that make it a bit easier to +write matchers: + +- Identifiers starting with a dollar sign or backslash are automatically + converted to special IdString variables that are initialized when the + matcher object is constructed. + +- The `port(<cell>, <portname>)` function is a handy alias for + `sigmap(<cell>->getPort(<portname>))`. + +- Similarly `param(<cell>, <paramname>)` looks up a parameter on a cell. + +- The function `nusers(<sigspec>)` returns the number of different cells + connected to any of the given signal bits, plus one if any of the signal + bits is also a primary input or primary output. + +- In `code..endcode` blocks there exist `accept`, `reject`, and `branch` + statements. + +- In `index` statements there is a special `===` operator for the index + lookup. + +Matching cells +-------------- + +Cells are matched using `match..endmatch` blocks. For example: + + match mul + if ff + select mul->type == $mul + select nusers(port(mul, \Y) == 2 + index <SigSpec> port(mul, \Y) === port(ff, \D) + filter some_weird_function(mul) < other_weird_function(ff) + optional + endmatch + +A `match` block starts with `match <statevar>` and implicitly generates +a state variable `<statevar>` of type `RTLIL::Cell*`. + +All statements in the match block are optional. (An empty match block +would simply match each and every cell in the module.) + +The `if <expression>` statement makes the match block conditional. If +`<expression>` evaluates to `false` then the match block will be ignored +and the corresponding state variable is set to `nullptr`. In our example +we only try to match the `mul` cell if the `ff` state variable points +to a cell. (Presumably `ff` is provided by a prior `match` block.) + +The `select` lines are evaluated once for each cell when the matcher is +initialized. A `match` block will only consider cells for which all `select` +expressions evaluated to `true`. Note that the state variable corresponding to +the match (in the example `mul`) is the only state variable that may be used +`select` lines. + +Index lines are using the `index <type> expr1 === expr2` syntax. `expr1` is +evaluated during matcher initialization and the same restrictions apply as for +`select` expressions. `expr2` is evaluated when the match is calulated. It is a +function of any state variables assigned to by previous blocks. Both expression +are converted to the given type and compared for equality. Only cells for which +all `index` statements in the block pass are considered by the match. + +Note that `select` and `index` are fast operations. Thus `select` and `index` +should be used whenever possible to create efficient matchers. + +Finally, `filter <expression>` narrows down the remaining list of cells. For +performance reasons `filter` statements should only be used for things that +can't be done using `select` and `index`. + +The `optional` statement marks optional matches. I.e. the matcher will also +explore the case where `mul` is set to `nullptr`. Without the `optional` +statement a match may only be assigned nullptr when one of the `if` expressions +evaluates to `false`. + +Additional code +--------------- + +Interleaved with `match..endmatch` blocks there may be `code..endcode` blocks. +Such a block starts with the keyword `code` followed by a list of state variables +that the block may modify. For example: + + code addAB sigS + if (addA) { + addAB = addA; + sigS = port(addA, \B); + } + if (addB) { + addAB = addB; + sigS = port(addB, \A); + } + endcode + +The special keyword `reject` can be used to reject the current state and +backtrack. For example: + + code + if (ffA && ffB) { + if (port(ffA, \CLK) != port(ffB, \CLK)) + reject; + if (param(ffA, \CLK_POLARITY) != param(ffB, \CLK_POLARITY)) + reject; + } + endcode + +Similarly, the special keyword `accept` can be used to accept the current +state. (`accept` will not backtrack. This means it continues with the current +branch and may accept a larger match later.) + +The special keyword `branch` can be used to explore different cases. Note that +each code block has an implicit `branch` at the end. So most use-cases of the +`branch` keyword need to end the block with `reject` to avoid the implicit +branch at the end. For example: + + state <int> mode + + code mode + for (mode = 0; mode < 8; mode++) + branch; + reject; + endcode + +But in some cases it is more natural to utilize the implicit branch statement: + + state <IdString> portAB + + code portAB + portAB = \A; + branch; + portAB = \B; + endcode + +There is an implicit `code..endcode` block at the end of each `.pgm` file +that just accepts everything that gets all the way there. diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc new file mode 100644 index 000000000..3a054a463 --- /dev/null +++ b/passes/pmgen/ice40_dsp.cc @@ -0,0 +1,237 @@ +/* + * 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" +#include "passes/pmgen/ice40_dsp_pm.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +void create_ice40_dsp(ice40_dsp_pm &pm) +{ +#if 0 + log("\n"); + log("ffA: %s\n", log_id(pm.st.ffA, "--")); + log("ffB: %s\n", log_id(pm.st.ffB, "--")); + log("mul: %s\n", log_id(pm.st.mul, "--")); + log("ffY: %s\n", log_id(pm.st.ffY, "--")); + log("addAB: %s\n", log_id(pm.st.addAB, "--")); + log("muxAB: %s\n", log_id(pm.st.muxAB, "--")); + log("ffS: %s\n", log_id(pm.st.ffS, "--")); +#endif + + log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul)); + + if (GetSize(pm.st.sigA) > 16) { + log(" input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA)); + return; + } + + if (GetSize(pm.st.sigB) > 16) { + log(" input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB)); + return; + } + + if (GetSize(pm.st.sigS) > 32) { + log(" accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS)); + return; + } + + if (GetSize(pm.st.sigY) > 32) { + log(" output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY)); + return; + } + + bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool(); + + if (mul_signed) { + log(" inference of signed iCE40 DSP arithmetic is currently not supported.\n"); + return; + } + + log(" replacing $mul with SB_MAC16 cell.\n"); + + Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16"); + pm.module->swap_names(cell, pm.st.mul); + + // SB_MAC16 Input Interface + + SigSpec A = pm.st.sigA; + A.extend_u0(16, mul_signed); + + SigSpec B = pm.st.sigB; + B.extend_u0(16, mul_signed); + + SigSpec CD; + if (pm.st.muxA) + CD = pm.st.muxA->getPort("\\B"); + if (pm.st.muxB) + CD = pm.st.muxB->getPort("\\A"); + CD.extend_u0(32, mul_signed); + + cell->setPort("\\A", A); + cell->setPort("\\B", B); + cell->setPort("\\C", CD.extract(0, 16)); + cell->setPort("\\D", CD.extract(16, 16)); + + cell->setParam("\\A_REG", pm.st.ffA ? State::S1 : State::S0); + cell->setParam("\\B_REG", pm.st.ffB ? State::S1 : State::S0); + + cell->setPort("\\AHOLD", State::S0); + cell->setPort("\\BHOLD", State::S0); + cell->setPort("\\CHOLD", State::S0); + cell->setPort("\\DHOLD", State::S0); + + cell->setPort("\\IRSTTOP", State::S0); + cell->setPort("\\IRSTBOT", State::S0); + + if (pm.st.clock_vld) + { + cell->setPort("\\CLK", pm.st.clock); + cell->setPort("\\CE", State::S1); + cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1); + + log(" clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge"); + + if (pm.st.ffA) + log(" ffA:%s", log_id(pm.st.ffA)); + + if (pm.st.ffB) + log(" ffB:%s", log_id(pm.st.ffB)); + + if (pm.st.ffY) + log(" ffY:%s", log_id(pm.st.ffY)); + + if (pm.st.ffS) + log(" ffS:%s", log_id(pm.st.ffS)); + + log("\n"); + } + else + { + cell->setPort("\\CLK", State::S0); + cell->setPort("\\CE", State::S0); + cell->setParam("\\NEG_TRIGGER", State::S0); + } + + // SB_MAC16 Cascade Interface + + cell->setPort("\\SIGNEXTIN", State::Sx); + cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID)); + + cell->setPort("\\CI", State::Sx); + cell->setPort("\\CO", pm.module->addWire(NEW_ID)); + + cell->setPort("\\ACCUMCI", State::Sx); + cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID)); + + // SB_MAC16 Output Interface + + SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY; + if (GetSize(O) < 32) + O.append(pm.module->addWire(NEW_ID, 32-GetSize(O))); + + cell->setPort("\\O", O); + + if (pm.st.addAB) { + log(" accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type)); + cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1); + cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1); + } else { + cell->setPort("\\ADDSUBTOP", State::S0); + cell->setPort("\\ADDSUBBOT", State::S0); + } + + cell->setPort("\\ORSTTOP", State::S0); + cell->setPort("\\ORSTBOT", State::S0); + + cell->setPort("\\OHOLDTOP", State::S0); + cell->setPort("\\OHOLDBOT", State::S0); + + SigSpec acc_reset = State::S0; + if (pm.st.muxA) + acc_reset = pm.st.muxA->getPort("\\S"); + if (pm.st.muxB) + acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S")); + + cell->setPort("\\OLOADTOP", acc_reset); + cell->setPort("\\OLOADBOT", acc_reset); + + // SB_MAC16 Remaining Parameters + + cell->setParam("\\C_REG", State::S0); + cell->setParam("\\D_REG", State::S0); + + cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); + cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0); + cell->setParam("\\PIPELINE_16x16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0); + cell->setParam("\\PIPELINE_16x16_MULT_REG2", State::S0); + + cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); + cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2)); + cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0); + cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2)); + + cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2)); + cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2)); + cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0); + cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2)); + + cell->setParam("\\MODE_8x8", State::S0); + cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0); + cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0); + + pm.autoremove(pm.st.mul); + pm.autoremove(pm.st.ffY); + pm.autoremove(pm.st.ffS); +} + +struct Ice40DspPass : public Pass { + Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" ice40_dsp [options] [selection]\n"); + log("\n"); + log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing ICE40_DSP pass (map multipliers).\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + // if (args[argidx] == "-singleton") { + // singleton_mode = true; + // continue; + // } + break; + } + extra_args(args, argidx, design); + + for (auto module : design->selected_modules()) + ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp); + } +} Ice40DspPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg new file mode 100644 index 000000000..96c62e313 --- /dev/null +++ b/passes/pmgen/ice40_dsp.pmg @@ -0,0 +1,160 @@ +state <SigBit> clock +state <bool> clock_pol clock_vld +state <SigSpec> sigA sigB sigY sigS +state <Cell*> addAB muxAB + +match mul + select mul->type.in($mul) + select GetSize(mul->getPort(\A)) + GetSize(mul->getPort(\B)) > 10 + select GetSize(mul->getPort(\Y)) > 10 +endmatch + +match ffA + select ffA->type.in($dff) + // select nusers(port(ffA, \Q)) == 2 + index <SigSpec> port(ffA, \Q) === port(mul, \A) + optional +endmatch + +code sigA clock clock_pol clock_vld + sigA = port(mul, \A); + + if (ffA) { + sigA = port(ffA, \D); + + clock = port(ffA, \CLK).as_bit(); + clock_pol = param(ffA, \CLK_POLARITY).as_bool(); + clock_vld = true; + } +endcode + +match ffB + select ffB->type.in($dff) + // select nusers(port(ffB, \Q)) == 2 + index <SigSpec> port(ffB, \Q) === port(mul, \B) + optional +endmatch + +code sigB clock clock_pol clock_vld + sigB = port(mul, \B); + + if (ffB) { + sigB = port(ffB, \D); + SigBit c = port(ffB, \CLK).as_bit(); + bool cp = param(ffB, \CLK_POLARITY).as_bool(); + + if (clock_vld && (c != clock || cp != clock_pol)) + reject; + + clock = c; + clock_pol = cp; + clock_vld = true; + } +endcode + +match ffY + select ffY->type.in($dff) + select nusers(port(ffY, \D)) == 2 + index <SigSpec> port(ffY, \D) === port(mul, \Y) + optional +endmatch + +code sigY clock clock_pol clock_vld + sigY = port(mul, \Y); + + if (ffY) { + sigY = port(ffY, \Q); + SigBit c = port(ffY, \CLK).as_bit(); + bool cp = param(ffY, \CLK_POLARITY).as_bool(); + + if (clock_vld && (c != clock || cp != clock_pol)) + reject; + + clock = c; + clock_pol = cp; + clock_vld = true; + } +endcode + +match addA + select addA->type.in($add) + select nusers(port(addA, \A)) == 2 + index <SigSpec> port(addA, \A) === sigY + optional +endmatch + +match addB + if !addA + select addB->type.in($add, $sub) + select nusers(port(addB, \B)) == 2 + index <SigSpec> port(addB, \B) === sigY + optional +endmatch + +code addAB sigS + if (addA) { + addAB = addA; + sigS = port(addA, \B); + } + if (addB) { + addAB = addB; + sigS = port(addB, \A); + } + if (addAB) { + int natural_mul_width = GetSize(sigA) + GetSize(sigB); + int actual_mul_width = GetSize(sigY); + int actual_acc_width = GetSize(sigS); + + if ((actual_acc_width > actual_mul_width) && (natural_mul_width > actual_mul_width)) + reject; + if ((actual_acc_width != actual_mul_width) && (param(mul, \A_SIGNED).as_bool() != param(addAB, \A_SIGNED).as_bool())) + reject; + } +endcode + +match muxA + if addAB + select muxA->type.in($mux) + select nusers(port(muxA, \A)) == 2 + index <SigSpec> port(muxA, \A) === port(addAB, \Y) + optional +endmatch + +match muxB + if addAB + if !muxA + select muxB->type.in($mux) + select nusers(port(muxB, \B)) == 2 + index <SigSpec> port(muxB, \B) === port(addAB, \Y) + optional +endmatch + +code muxAB + muxAB = addAB; + if (muxA) + muxAB = muxA; + if (muxB) + muxAB = muxB; +endcode + +match ffS + if muxAB + select ffS->type.in($dff) + select nusers(port(ffS, \D)) == 2 + index <SigSpec> port(ffS, \D) === port(muxAB, \Y) + index <SigSpec> port(ffS, \Q) === sigS +endmatch + +code clock clock_pol clock_vld + if (ffS) { + SigBit c = port(ffS, \CLK).as_bit(); + bool cp = param(ffS, \CLK_POLARITY).as_bool(); + + if (clock_vld && (c != clock || cp != clock_pol)) + reject; + + clock = c; + clock_pol = cp; + clock_vld = true; + } +endcode diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py new file mode 100644 index 000000000..e688a4567 --- /dev/null +++ b/passes/pmgen/pmgen.py @@ -0,0 +1,482 @@ +#!/usr/bin/env python3 + +import re +import sys +import pprint + +pp = pprint.PrettyPrinter(indent=4) + +prefix = sys.argv[1] + +state_types = dict() +udata_types = dict() +blocks = list() +ids = dict() + +def rewrite_cpp(s): + t = list() + i = 0 + while i < len(s): + if s[i] in ("'", '"') and i + 1 < len(s): + j = i + 1 + while j + 1 < len(s) and s[j] != s[i]: + if s[j] == '\\' and j + 1 < len(s): + j += 1 + j += 1 + t.append(s[i:j+1]) + i = j + 1 + continue + + if s[i] in ('$', '\\') and i + 1 < len(s): + j = i + 1 + while True: + if j == len(s): + j -= 1 + break + if ord('a') <= ord(s[j]) <= ord('z'): + j += 1 + continue + if ord('A') <= ord(s[j]) <= ord('Z'): + j += 1 + continue + if ord('0') <= ord(s[j]) <= ord('9'): + j += 1 + continue + if s[j] == '_': + j += 1 + continue + j -= 1 + break + + n = s[i:j+1] + i = j + 1 + + if n[0] == '$': + v = "id_d_" + n[1:] + else: + v = "id_b_" + n[1:] + + if v not in ids: + ids[v] = n + else: + assert ids[v] == n + + t.append(v) + continue + + if s[i] == "\t": + t.append(" ") + else: + t.append(s[i]) + + i += 1 + + return "".join(t) + +with open("%s.pmg" % prefix, "r") as f: + while True: + line = f.readline() + if line == "": break + line = line.strip() + + cmd = line.split() + if len(cmd) == 0 or cmd[0].startswith("//"): continue + cmd = cmd[0] + + if cmd == "state": + m = re.match(r"^state\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) + assert m + type_str = m.group(1) + states_str = m.group(2) + for s in re.split(r"\s+", states_str): + assert s not in state_types + state_types[s] = type_str + continue + + if cmd == "udata": + m = re.match(r"^udata\s+<(.*?)>\s+(([A-Za-z_][A-Za-z_0-9]*\s+)*[A-Za-z_][A-Za-z_0-9]*)\s*$", line) + assert m + type_str = m.group(1) + udatas_str = m.group(2) + for s in re.split(r"\s+", udatas_str): + assert s not in udata_types + udata_types[s] = type_str + continue + + if cmd == "match": + block = dict() + block["type"] = "match" + + line = line.split() + assert len(line) == 2 + assert line[1] not in state_types + block["cell"] = line[1] + state_types[line[1]] = "Cell*"; + + block["if"] = list() + block["select"] = list() + block["index"] = list() + block["filter"] = list() + block["optional"] = False + + while True: + l = f.readline() + assert l != "" + a = l.split() + if len(a) == 0 or a[0].startswith("//"): continue + if a[0] == "endmatch": break + + if a[0] == "if": + b = l.lstrip()[2:] + block["if"].append(rewrite_cpp(b.strip())) + continue + + if a[0] == "select": + b = l.lstrip()[6:] + block["select"].append(rewrite_cpp(b.strip())) + continue + + if a[0] == "index": + m = re.match(r"^\s*index\s+<(.*?)>\s+(.*?)\s*===\s*(.*?)\s*$", l) + assert m + block["index"].append((m.group(1), rewrite_cpp(m.group(2)), rewrite_cpp(m.group(3)))) + continue + + if a[0] == "filter": + b = l.lstrip()[6:] + block["filter"].append(rewrite_cpp(b.strip())) + continue + + if a[0] == "optional": + block["optional"] = True + continue + + assert False + + blocks.append(block) + + if cmd == "code": + block = dict() + block["type"] = "code" + block["code"] = list() + block["states"] = set() + + for s in line.split()[1:]: + assert s in state_types + block["states"].add(s) + + while True: + l = f.readline() + assert l != "" + a = l.split() + if len(a) == 0: continue + if a[0] == "endcode": break + + block["code"].append(rewrite_cpp(l.rstrip())) + + blocks.append(block) + +with open("%s_pm.h" % prefix, "w") as f: + print("// Generated by pmgen.py from {}.pgm".format(prefix), file=f) + print("", file=f) + + print("#include \"kernel/yosys.h\"", file=f) + print("#include \"kernel/sigtools.h\"", file=f) + print("", file=f) + + print("YOSYS_NAMESPACE_BEGIN", file=f) + print("", file=f) + + print("struct {}_pm {{".format(prefix), file=f) + print(" Module *module;", file=f) + print(" SigMap sigmap;", file=f) + print(" std::function<void()> on_accept;".format(prefix), file=f) + print("", file=f) + + for index in range(len(blocks)): + block = blocks[index] + if block["type"] == "match": + index_types = list() + for entry in block["index"]: + index_types.append(entry[0]) + print(" typedef std::tuple<{}> index_{}_key_type;".format(", ".join(index_types), index), file=f) + print(" dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f) + print(" dict<SigBit, pool<Cell*>> sigusers;", file=f) + print(" pool<Cell*> blacklist_cells;", file=f) + print(" pool<Cell*> autoremove_cells;", file=f) + print(" bool blacklist_dirty;", file=f) + print(" int rollback;", file=f) + print("", file=f) + + print(" struct state_t {", file=f) + for s, t in sorted(state_types.items()): + print(" {} {};".format(t, s), file=f) + print(" } st;", file=f) + print("", file=f) + + print(" struct udata_t {", file=f) + for s, t in sorted(udata_types.items()): + print(" {} {};".format(t, s), file=f) + print(" } ud;", file=f) + print("", file=f) + + for v, n in sorted(ids.items()): + if n[0] == "\\": + print(" IdString {}{{\"\\{}\"}};".format(v, n), file=f) + else: + print(" IdString {}{{\"{}\"}};".format(v, n), file=f) + print("", file=f) + + print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f) + print(" for (auto bit : sigmap(sig)) {", file=f) + print(" if (bit.wire == nullptr) continue;", file=f) + print(" if (sigusers.count(bit) == 0 && bit.wire->port_id)", file=f) + print(" sigusers[bit].insert(nullptr);", file=f) + print(" sigusers[bit].insert(cell);", file=f) + print(" }", file=f) + print(" }", file=f) + print("", file=f) + + print(" void blacklist(Cell *cell) {", file=f) + print(" if (cell != nullptr) {", file=f) + print(" if (blacklist_cells.insert(cell).second)", file=f) + print(" blacklist_dirty = true;", file=f) + print(" }", file=f) + print(" }", file=f) + print("", file=f) + + print(" void autoremove(Cell *cell) {", file=f) + print(" if (cell != nullptr) {", file=f) + print(" if (blacklist_cells.insert(cell).second)", file=f) + print(" blacklist_dirty = true;", file=f) + print(" autoremove_cells.insert(cell);", file=f) + print(" }", file=f) + print(" }", file=f) + print("", file=f) + + print(" void check_blacklist() {", file=f) + print(" if (!blacklist_dirty)", file=f) + print(" return;", file=f) + print(" blacklist_dirty = false;", file=f) + for index in range(len(blocks)): + block = blocks[index] + if block["type"] == "match": + print(" if (st.{} != nullptr && blacklist_cells.count(st.{})) {{".format(block["cell"], block["cell"]), file=f) + print(" rollback = {};".format(index+1), file=f) + print(" return;", file=f) + print(" }", file=f) + print(" rollback = 0;", file=f) + print(" }", file=f) + print("", file=f) + + print(" SigSpec port(Cell *cell, IdString portname) {", file=f) + print(" return sigmap(cell->getPort(portname));", file=f) + print(" }", file=f) + print("", file=f) + + print(" Const param(Cell *cell, IdString paramname) {", file=f) + print(" return cell->getParam(paramname);", file=f) + print(" }", file=f) + print("", file=f) + + print(" int nusers(const SigSpec &sig) {", file=f) + print(" pool<Cell*> users;", file=f) + print(" for (auto bit : sigmap(sig))", file=f) + print(" for (auto user : sigusers[bit])", file=f) + print(" users.insert(user);", file=f) + print(" return GetSize(users);", file=f) + print(" }", file=f) + print("", file=f) + + print(" {}_pm(Module *module, const vector<Cell*> &cells) :".format(prefix), file=f) + print(" module(module), sigmap(module) {", file=f) + for s, t in sorted(udata_types.items()): + if t.endswith("*"): + print(" ud.{} = nullptr;".format(s), file=f) + else: + print(" ud.{} = {}();".format(s, t), file=f) + print(" for (auto cell : module->cells()) {", file=f) + print(" for (auto &conn : cell->connections())", file=f) + print(" add_siguser(conn.second, cell);", file=f) + print(" }", file=f) + print(" for (auto cell : cells) {", file=f) + + for index in range(len(blocks)): + block = blocks[index] + if block["type"] == "match": + print(" do {", file=f) + print(" Cell *{} = cell;".format(block["cell"]), file=f) + for expr in block["select"]: + print(" if (!({})) break;".format(expr), file=f) + print(" index_{}_key_type key;".format(index), file=f) + for field, entry in enumerate(block["index"]): + print(" std::get<{}>(key) = {};".format(field, entry[1]), file=f) + print(" index_{}[key].push_back(cell);".format(index), file=f) + print(" } while (0);", file=f) + + print(" }", file=f) + print(" }", file=f) + print("", file=f) + + print(" ~{}_pm() {{".format(prefix), file=f) + print(" for (auto cell : autoremove_cells)", file=f) + print(" module->remove(cell);", file=f) + print(" }", file=f) + print("", file=f) + + print(" void run(std::function<void()> on_accept_f) {", file=f) + print(" on_accept = on_accept_f;", file=f) + print(" rollback = 0;", file=f) + print(" blacklist_dirty = false;", file=f) + for s, t in sorted(state_types.items()): + if t.endswith("*"): + print(" st.{} = nullptr;".format(s), file=f) + else: + print(" st.{} = {}();".format(s, t), file=f) + print(" block_0();", file=f) + print(" }", file=f) + print("", file=f) + + print(" void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f) + print(" run([&](){on_accept_f(*this);});", file=f) + print(" }", file=f) + print("", file=f) + + for index in range(len(blocks)): + block = blocks[index] + + print(" void block_{}() {{".format(index), file=f) + + const_st = set() + nonconst_st = set() + restore_st = set() + + for i in range(index): + if blocks[i]["type"] == "code": + for s in blocks[i]["states"]: + const_st.add(s) + elif blocks[i]["type"] == "match": + const_st.add(blocks[i]["cell"]) + else: + assert False + + if block["type"] == "code": + for s in block["states"]: + if s in const_st: + const_st.remove(s) + restore_st.add(s) + nonconst_st.add(s) + elif block["type"] == "match": + s = block["cell"] + assert s not in const_st + nonconst_st.add(s) + else: + assert False + + for s in sorted(const_st): + t = state_types[s] + if t.endswith("*"): + print(" {} const &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + else: + print(" const {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + + for s in sorted(nonconst_st): + t = state_types[s] + print(" {} &{} YS_ATTRIBUTE(unused) = st.{};".format(t, s, s), file=f) + + if len(restore_st): + print("", file=f) + for s in sorted(restore_st): + t = state_types[s] + print(" {} backup_{} = {};".format(t, s, s), file=f) + + if block["type"] == "code": + print("", file=f) + print(" do {", file=f) + print("#define reject do { check_blacklist(); goto rollback_label; } while(0)", file=f) + print("#define accept do { on_accept(); check_blacklist(); if (rollback) goto rollback_label; } while(0)", file=f) + print("#define branch do {{ block_{}(); if (rollback) goto rollback_label; }} while(0)".format(index+1), file=f) + + for line in block["code"]: + print(" " + line, file=f) + + print("", file=f) + print(" block_{}();".format(index+1), file=f) + print("#undef reject", file=f) + print("#undef accept", file=f) + print("#undef branch", file=f) + print(" } while (0);", file=f) + print("", file=f) + print("rollback_label:", file=f) + print(" YS_ATTRIBUTE(unused);", file=f) + + if len(restore_st) or len(nonconst_st): + print("", file=f) + for s in sorted(restore_st): + t = state_types[s] + print(" {} = backup_{};".format(s, s), file=f) + for s in sorted(nonconst_st): + if s not in restore_st: + t = state_types[s] + if t.endswith("*"): + print(" {} = nullptr;".format(s), file=f) + else: + print(" {} = {}();".format(s, t), file=f) + + elif block["type"] == "match": + assert len(restore_st) == 0 + + if len(block["if"]): + for expr in block["if"]: + print("", file=f) + print(" if (!({})) {{".format(expr), file=f) + print(" {} = nullptr;".format(block["cell"]), file=f) + print(" block_{}();".format(index+1), file=f) + print(" return;", file=f) + print(" }", file=f) + + print("", file=f) + print(" index_{}_key_type key;".format(index), file=f) + for field, entry in enumerate(block["index"]): + print(" std::get<{}>(key) = {};".format(field, entry[2]), file=f) + print(" const vector<Cell*> &cells = index_{}[key];".format(index), file=f) + + print("", file=f) + print(" for (int idx = 0; idx < GetSize(cells); idx++) {", file=f) + print(" {} = cells[idx];".format(block["cell"]), file=f) + print(" if (blacklist_cells.count({})) continue;".format(block["cell"]), file=f) + for expr in block["filter"]: + print(" if (!({})) continue;".format(expr), file=f) + print(" block_{}();".format(index+1), file=f) + print(" if (rollback) {", file=f) + print(" if (rollback != {}) {{".format(index+1), file=f) + print(" {} = nullptr;".format(block["cell"]), file=f) + print(" return;", file=f) + print(" }", file=f) + print(" rollback = 0;", file=f) + print(" }", file=f) + print(" }", file=f) + + print("", file=f) + print(" {} = nullptr;".format(block["cell"]), file=f) + + if block["optional"]: + print(" block_{}();".format(index+1), file=f) + + else: + assert False + + + print(" }", file=f) + print("", file=f) + + print(" void block_{}() {{".format(len(blocks)), file=f) + print(" on_accept();", file=f) + print(" check_blacklist();", file=f) + print(" }", file=f) + print("};", file=f) + + print("", file=f) + print("YOSYS_NAMESPACE_END", file=f) + +# pp.pprint(blocks) 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..abf0167b5 --- /dev/null +++ b/passes/techmap/abc9.cc @@ -0,0 +1,1561 @@ +/* + * 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 "strash; ifraig; scorr; dc2; dretime; strash; dch -f; if; mfs2" +#define ABC_COMMAND_LUT "&st; &fraig; &scorr; &dc2; &retime; &dch -f; &if; &mfs" +#define ABC_COMMAND_SOP "strash; ifraig; scorr; dc2; dretime; strash; dch -f; cover {I} {P}" +#define ABC_COMMAND_DFL "strash; ifraig; scorr; dc2; dretime; strash; &get -n; &dch -f; &nf {D}; &put" + +#define ABC_FAST_COMMAND_LIB "strash; dretime; map {D}" +#define ABC_FAST_COMMAND_CTR "strash; dretime; map {D}; buffer; upsize {D}; dnsize {D}; stime -p" +#define ABC_FAST_COMMAND_LUT "&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 AND and NOT output connections, + // and for those output ports driving wires + // also driven by mapped_mod, disconnect them + for (auto cell : module->cells()) { + if (!cell->type.in("$_AND_", "$_NOT_")) + continue; + for (auto &it : cell->connections_) { + auto port_name = it.first; + if (!cell->output(port_name)) continue; + auto &signal = it.second; + auto bits = signal.bits(); + for (auto &b : bits) + if (output_bits.count(b)) + b = module->addWire(NEW_ID); + signal = std::move(bits); + } + } + // Do the same for module connections + for (auto &it : module->connections_) { + auto &signal = it.first; + auto bits = signal.bits(); + for (auto &b : bits) + if (output_bits.count(b)) + b = module->addWire(NEW_ID); + signal = std::move(bits); + } + + // Stitch in mapped_mod's inputs/outputs into module + for (auto &it : mapped_mod->wires_) { + RTLIL::Wire *w = it.second; + if (!w->port_input && !w->port_output) + continue; + RTLIL::Wire *wire = module->wire(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; + 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/cells_sim.v b/techlibs/ice40/cells_sim.v index 38ed45981..2041693cc 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -886,59 +886,6 @@ module SB_WARMBOOT ( ); endmodule -// UltraPlus feature cells -(* blackbox *) -module SB_MAC16 ( - input CLK, - input CE, - input [15:0] C, - input [15:0] A, - input [15:0] B, - input [15:0] D, - input AHOLD, - input BHOLD, - input CHOLD, - input DHOLD, - input IRSTTOP, - input IRSTBOT, - input ORSTTOP, - input ORSTBOT, - input OLOADTOP, - input OLOADBOT, - input ADDSUBTOP, - input ADDSUBBOT, - input OHOLDTOP, - input OHOLDBOT, - input CI, - input ACCUMCI, - input SIGNEXTIN, - output [31:0] O, - output CO, - output ACCUMCO, - output SIGNEXTOUT -); -parameter NEG_TRIGGER = 1'b0; -parameter C_REG = 1'b0; -parameter A_REG = 1'b0; -parameter B_REG = 1'b0; -parameter D_REG = 1'b0; -parameter TOP_8x8_MULT_REG = 1'b0; -parameter BOT_8x8_MULT_REG = 1'b0; -parameter PIPELINE_16x16_MULT_REG1 = 1'b0; -parameter PIPELINE_16x16_MULT_REG2 = 1'b0; -parameter TOPOUTPUT_SELECT = 2'b00; -parameter TOPADDSUB_LOWERINPUT = 2'b00; -parameter TOPADDSUB_UPPERINPUT = 1'b0; -parameter TOPADDSUB_CARRYSELECT = 2'b00; -parameter BOTOUTPUT_SELECT = 2'b00; -parameter BOTADDSUB_LOWERINPUT = 2'b00; -parameter BOTADDSUB_UPPERINPUT = 1'b0; -parameter BOTADDSUB_CARRYSELECT = 2'b00; -parameter MODE_8x8 = 1'b0; -parameter A_SIGNED = 1'b0; -parameter B_SIGNED = 1'b0; -endmodule - module SB_SPRAM256KA ( input [13:0] ADDRESS, input [15:0] DATAIN, @@ -1273,3 +1220,171 @@ module SB_IO_OD ( endgenerate `endif endmodule + +module SB_MAC16 ( + input CLK, CE, + input [15:0] C, A, B, D, + input AHOLD, BHOLD, CHOLD, DHOLD, + input IRSTTOP, IRSTBOT, + input ORSTTOP, ORSTBOT, + input OLOADTOP, OLOADBOT, + input ADDSUBTOP, ADDSUBBOT, + input OHOLDTOP, OHOLDBOT, + input CI, ACCUMCI, SIGNEXTIN, + output [31:0] O, + output CO, ACCUMCO, SIGNEXTOUT +); + parameter [0:0] NEG_TRIGGER = 0; + parameter [0:0] C_REG = 0; + parameter [0:0] A_REG = 0; + parameter [0:0] B_REG = 0; + parameter [0:0] D_REG = 0; + parameter [0:0] TOP_8x8_MULT_REG = 0; + parameter [0:0] BOT_8x8_MULT_REG = 0; + parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0; + parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0; + parameter [1:0] TOPOUTPUT_SELECT = 0; + parameter [1:0] TOPADDSUB_LOWERINPUT = 0; + parameter [0:0] TOPADDSUB_UPPERINPUT = 0; + parameter [1:0] TOPADDSUB_CARRYSELECT = 0; + parameter [1:0] BOTOUTPUT_SELECT = 0; + parameter [1:0] BOTADDSUB_LOWERINPUT = 0; + parameter [0:0] BOTADDSUB_UPPERINPUT = 0; + parameter [1:0] BOTADDSUB_CARRYSELECT = 0; + parameter [0:0] MODE_8x8 = 0; + parameter [0:0] A_SIGNED = 0; + parameter [0:0] B_SIGNED = 0; + + wire clock = CLK ^ NEG_TRIGGER; + + // internal wires, compare Figure on page 133 of ICE Technology Library 3.0 and Fig 2 on page 2 of Lattice TN1295-DSP + // http://www.latticesemi.com/~/media/LatticeSemi/Documents/TechnicalBriefs/SBTICETechnologyLibrary201608.pdf + // https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/AD/DSPFunctionUsageGuideforICE40Devices.ashx + wire [15:0] iA, iB, iC, iD; + wire [15:0] iF, iJ, iK, iG; + wire [31:0] iL, iH; + wire [15:0] iW, iX, iP, iQ; + wire [15:0] iY, iZ, iR, iS; + wire HCI, LCI, LCO; + + // Regs C and A + reg [15:0] rC, rA; + always @(posedge clock, posedge IRSTTOP) begin + if (IRSTTOP) begin + rC <= 0; + rA <= 0; + end else if (CE) begin + if (!CHOLD) rC <= C; + if (!AHOLD) rA <= A; + end + end + assign iC = C_REG ? rC : C; + assign iA = A_REG ? rA : A; + + // Regs B and D + reg [15:0] rB, rD; + always @(posedge clock, posedge IRSTBOT) begin + if (IRSTBOT) begin + rB <= 0; + rD <= 0; + end else if (CE) begin + if (!BHOLD) rB <= B; + if (!DHOLD) rD <= D; + end + end + assign iB = B_REG ? rB : B; + assign iD = D_REG ? rD : D; + + // Multiplier Stage + wire [15:0] p_Ah_Bh, p_Al_Bh, p_Ah_Bl, p_Al_Bl; + wire [15:0] Ah, Al, Bh, Bl; + assign Ah = {A_SIGNED ? {8{iA[15]}} : 8'b0, iA[15: 8]}; + assign Al = {A_SIGNED ? {8{iA[ 7]}} : 8'b0, iA[ 7: 0]}; + assign Bh = {B_SIGNED ? {8{iB[15]}} : 8'b0, iB[15: 8]}; + assign Bl = {B_SIGNED ? {8{iB[ 7]}} : 8'b0, iB[ 7: 0]}; + assign p_Ah_Bh = Ah * Bh; + assign p_Al_Bh = Al * Bh; + assign p_Ah_Bl = Ah * Bl; + assign p_Al_Bl = Al * Bl; + + // Regs F and J + reg [15:0] rF, rJ; + always @(posedge clock, posedge IRSTTOP) begin + if (IRSTTOP) begin + rF <= 0; + rJ <= 0; + end else if (CE) begin + rF <= p_Ah_Bh; + if (!MODE_8x8) rJ <= p_Al_Bh; + end + end + assign iF = TOP_8x8_MULT_REG ? rF : p_Ah_Bh; + assign iJ = PIPELINE_16x16_MULT_REG1 ? rJ : p_Al_Bh; + + // Regs K and G + reg [15:0] rK, rG; + always @(posedge clock, posedge IRSTBOT) begin + if (IRSTBOT) begin + rK <= 0; + rG <= 0; + end else if (CE) begin + if (!MODE_8x8) rK <= p_Ah_Bl; + rG <= p_Al_Bl; + end + end + assign iK = PIPELINE_16x16_MULT_REG1 ? rK : p_Ah_Bl; + assign iG = BOT_8x8_MULT_REG ? rG : p_Al_Bl; + + // Adder Stage + assign iL = iG + (iK << 8) + (iJ << 8) + (iF << 16); + + // Reg H + reg [31:0] rH; + always @(posedge clock, posedge IRSTBOT) begin + if (IRSTBOT) begin + rH <= 0; + end else if (CE) begin + if (!MODE_8x8) rH <= iL; + end + end + assign iH = PIPELINE_16x16_MULT_REG2 ? rH : iL; + + // Hi Output Stage + wire [15:0] XW, Oh; + reg [15:0] rQ; + assign iW = TOPADDSUB_UPPERINPUT ? iC : iQ; + assign iX = (TOPADDSUB_LOWERINPUT == 0) ? iA : (TOPADDSUB_LOWERINPUT == 1) ? iF : (TOPADDSUB_LOWERINPUT == 2) ? iH[31:16] : {16{iZ[15]}}; + assign {ACCUMCO, XW} = iX + (iW ^ {16{ADDSUBTOP}}) + HCI; + assign CO = ACCUMCO ^ ADDSUBTOP; + assign iP = OLOADTOP ? iC : XW ^ {16{ADDSUBTOP}}; + always @(posedge clock, posedge ORSTTOP) begin + if (ORSTTOP) begin + rQ <= 0; + end else if (CE) begin + if (!OHOLDTOP) rQ <= iP; + end + end + assign iQ = rQ; + assign Oh = (TOPOUTPUT_SELECT == 0) ? iP : (TOPOUTPUT_SELECT == 1) ? iQ : (TOPOUTPUT_SELECT == 2) ? iF : iH[31:16]; + assign HCI = (TOPADDSUB_CARRYSELECT == 0) ? 1'b0 : (TOPADDSUB_CARRYSELECT == 1) ? 1'b1 : (TOPADDSUB_CARRYSELECT == 2) ? LCO : LCO ^ ADDSUBBOT; + assign SIGNEXTOUT = iX[15]; + + // Lo Output Stage + wire [15:0] YZ, Ol; + reg [15:0] rS; + assign iY = BOTADDSUB_UPPERINPUT ? iD : iS; + assign iZ = (BOTADDSUB_LOWERINPUT == 0) ? iB : (BOTADDSUB_LOWERINPUT == 1) ? iG : (BOTADDSUB_LOWERINPUT == 2) ? iH[15:0] : {16{SIGNEXTIN}}; + assign {LCO, YZ} = iZ + (iY ^ {16{ADDSUBBOT}}) + LCI; + assign iR = OLOADBOT ? iD : YZ ^ {16{ADDSUBBOT}}; + always @(posedge clock, posedge ORSTBOT) begin + if (ORSTBOT) begin + rS <= 0; + end else if (CE) begin + if (!OHOLDBOT) rS <= iR; + end + end + assign iS = rS; + assign Ol = (BOTOUTPUT_SELECT == 0) ? iR : (BOTOUTPUT_SELECT == 1) ? iS : (BOTOUTPUT_SELECT == 2) ? iG : iH[15:0]; + assign LCI = (BOTADDSUB_CARRYSELECT == 0) ? 1'b0 : (BOTADDSUB_CARRYSELECT == 1) ? 1'b1 : (BOTADDSUB_CARRYSELECT == 2) ? ACCUMCI : CI; + assign O = {Oh, Ol}; +endmodule diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index f900453e8..a13a81246 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -79,6 +79,9 @@ struct SynthIce40Pass : public ScriptPass log(" -nobram\n"); log(" do not use SB_RAM40_4K* cells in output netlist\n"); log("\n"); + log(" -dsp\n"); + log(" use iCE40 UltraPlus DSP cells for large arithmetic\n"); + log("\n"); log(" -noabc\n"); log(" use built-in Yosys LUT techmapping instead of abc\n"); log("\n"); @@ -89,14 +92,18 @@ 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; - bool nocarry, nodffe, nobram, flatten, retime, relut, noabc, abc2, vpr; + + string top_opt, blif_file, edif_file, json_file, abc; + bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr; int min_ce_use; void clear_flags() YS_OVERRIDE @@ -109,12 +116,14 @@ struct SynthIce40Pass : public ScriptPass nodffe = false; min_ce_use = -1; nobram = false; + dsp = false; flatten = true; retime = false; relut = false; noabc = false; abc2 = false; vpr = false; + abc = "abc"; } void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE @@ -181,6 +190,10 @@ struct SynthIce40Pass : public ScriptPass nobram = true; continue; } + if (args[argidx] == "-dsp") { + dsp = true; + continue; + } if (args[argidx] == "-noabc") { noabc = true; continue; @@ -193,6 +206,10 @@ struct SynthIce40Pass : public ScriptPass vpr = true; continue; } + if (args[argidx] == "-abc9") { + abc = "abc9"; + continue; + } break; } extra_args(args, argidx, design); @@ -214,11 +231,11 @@ struct SynthIce40Pass : public ScriptPass { run("read_verilog -lib +/ice40/cells_sim.v"); run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str())); + run("proc"); } if (flatten && check_label("flatten", "(unless -noflatten)")) { - run("proc"); run("flatten"); run("tribuf -logic"); run("deminout"); @@ -226,7 +243,23 @@ struct SynthIce40Pass : public ScriptPass if (check_label("coarse")) { - run("synth -lut 4 -run coarse"); + run("opt_expr"); + run("opt_clean"); + run("check"); + run("opt"); + run("wreduce"); + run("share"); + run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4"); + run("opt_expr"); + run("opt_clean"); + if (help_mode || dsp) + run("ice40_dsp", "(if -dsp)"); + run("alumacc"); + run("opt"); + run("fsm"); + run("opt -fast"); + run("memory -nomap"); + run("opt_clean"); } if (!nobram && check_label("bram", "(skip if -nobram)")) @@ -249,7 +282,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 +306,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 +315,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/techlibs/ice40/tests/.gitignore b/techlibs/ice40/tests/.gitignore index b58f9ad4a..120286550 100644 --- a/techlibs/ice40/tests/.gitignore +++ b/techlibs/ice40/tests/.gitignore @@ -1,2 +1,11 @@ -test_ffs_[01][01][01][01][01]_* -test_bram_[0-9]* +/test_ffs_[01][01][01][01][01]_* +/test_bram_[0-9]* +/test_dsp_model +/test_dsp_model.vcd +/test_dsp_model_ref.v +/test_dsp_model_uut.v +/test_dsp_map +/test_dsp_map.vcd +/test_dsp_map_tb.v +/test_dsp_map_top.v +/test_dsp_map_syn.v diff --git a/techlibs/ice40/tests/test_dsp_map.sh b/techlibs/ice40/tests/test_dsp_map.sh new file mode 100644 index 000000000..3f7f134e4 --- /dev/null +++ b/techlibs/ice40/tests/test_dsp_map.sh @@ -0,0 +1,107 @@ +#!/bin/bash +set -ex + +for iter in {1..100} +do + SZA=$(( 3 + $RANDOM % 13 )) + SZB=$(( 3 + $RANDOM % 13 )) + SZO=$(( 3 + $RANDOM % 29 )) + + C0=clk$(( $RANDOM & 1)) + C1=clk$(( $RANDOM & 1)) + C2=clk$(( $RANDOM & 1)) + C3=clk$(( $RANDOM & 1)) + + E0=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) + E1=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) + E2=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) + E3=$( test $(( $RANDOM & 1 )) -eq 0 && echo posedge || echo negedge ) + + SP=$( test $(( $RANDOM & 1 )) -eq 0 && echo S || echo P ) + + RC=$( test $(( $RANDOM & 1 )) -eq 0 && echo "reset" || echo "!reset" ) + RV="32'h$( echo $RANDOM | md5sum | cut -c1-8 )" + + cat > test_dsp_map_top.v << EOT +module top ( + input clk0, clk1, reset, + input [$SZA:0] A, + input [$SZB:0] B, + output [$SZO:0] O +); + reg [15:0] AA, BB; + reg [31:0] P, S; + + always @($E0 $C0) AA <= A; + always @($E1 $C1) BB <= B; + always @($E2 $C2) P <= AA * BB; + always @($E3 $C3) S <= $RC ? $RV : S + P; + assign O = $SP; +endmodule +EOT + + cat > test_dsp_map_tb.v << EOT +\`timescale 1ns / 1ps +module testbench; + reg clk1, clk0, reset; + reg [$SZA:0] A; + reg [$SZB:0] B; + + wire [$SZO:0] O_top, O_syn; + + top top_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_top)); + syn syn_inst (.clk0(clk0), .clk1(clk1), .reset(reset), .A(A), .B(B), .O(O_syn)); + + initial begin + // \$dumpfile("test_dsp_map.vcd"); + // \$dumpvars(0, testbench); + + #2; + clk0 = 0; + clk1 = 0; + reset = 1; + reset = $RC; + A = 0; + B = 0; + + repeat (3) begin + #2; clk0 = ~clk0; + #2; clk0 = ~clk0; + #2; clk1 = ~clk1; + #2; clk1 = ~clk1; + end + + repeat (100) begin + #2; + A = \$urandom; + B = \$urandom; + reset = \$urandom & \$urandom & \$urandom & \$urandom; + if (\$urandom & 1) begin + #2; clk0 = ~clk0; + #2; clk0 = ~clk0; + end else begin + #2; clk1 = ~clk1; + #2; clk1 = ~clk1; + end + #2; + if (O_top !== O_syn) begin + \$display("ERROR: O_top=%b O_syn=%b", O_top, O_syn); + \$stop; + end + // \$display("OK O_top=O_syn=%b", O_top); + end + + \$display("Test passed."); + \$finish; + end +endmodule +EOT + + ../../../yosys -p 'read_verilog test_dsp_map_top.v; synth_ice40 -dsp; rename top syn; write_verilog test_dsp_map_syn.v' + iverilog -o test_dsp_map -s testbench test_dsp_map_tb.v test_dsp_map_top.v test_dsp_map_syn.v ../cells_sim.v + vvp -N test_dsp_map +done + +: "" +: "#### All tests passed. ####" +: "" diff --git a/techlibs/ice40/tests/test_dsp_model.sh b/techlibs/ice40/tests/test_dsp_model.sh new file mode 100644 index 000000000..1bc0cc688 --- /dev/null +++ b/techlibs/ice40/tests/test_dsp_model.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -ex +sed 's/SB_MAC16/SB_MAC16_UUT/; /SB_MAC16_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp_model_uut.v +cat /opt/lscc/iCEcube2.2017.01/verilog/sb_ice_syn.v > test_dsp_model_ref.v +for tb in testbench \ + testbench_comb_8x8_A testbench_comb_8x8_B testbench_comb_16x16 \ + testbench_seq_16x16_A testbench_seq_16x16_B +do + iverilog -s $tb -o test_dsp_model test_dsp_model.v test_dsp_model_uut.v test_dsp_model_ref.v + vvp -N ./test_dsp_model +done diff --git a/techlibs/ice40/tests/test_dsp_model.v b/techlibs/ice40/tests/test_dsp_model.v new file mode 100644 index 000000000..594bd4ad3 --- /dev/null +++ b/techlibs/ice40/tests/test_dsp_model.v @@ -0,0 +1,342 @@ +`timescale 1ns / 1ps + +module testbench; + parameter [0:0] NEG_TRIGGER = 0; + parameter [0:0] C_REG = 0; + parameter [0:0] A_REG = 0; + parameter [0:0] B_REG = 0; + parameter [0:0] D_REG = 0; + parameter [0:0] TOP_8x8_MULT_REG = 0; + parameter [0:0] BOT_8x8_MULT_REG = 0; + parameter [0:0] PIPELINE_16x16_MULT_REG1 = 0; + parameter [0:0] PIPELINE_16x16_MULT_REG2 = 0; + parameter [1:0] TOPOUTPUT_SELECT = 0; + parameter [1:0] TOPADDSUB_LOWERINPUT = 0; + parameter [0:0] TOPADDSUB_UPPERINPUT = 1; + parameter [1:0] TOPADDSUB_CARRYSELECT = 0; + parameter [1:0] BOTOUTPUT_SELECT = 0; + parameter [1:0] BOTADDSUB_LOWERINPUT = 0; + parameter [0:0] BOTADDSUB_UPPERINPUT = 1; + parameter [1:0] BOTADDSUB_CARRYSELECT = 0; + parameter [0:0] MODE_8x8 = 0; + parameter [0:0] A_SIGNED = 0; + parameter [0:0] B_SIGNED = 0; + + reg CLK, CE; + reg [15:0] C, A, B, D; + reg AHOLD, BHOLD, CHOLD, DHOLD; + reg IRSTTOP, IRSTBOT; + reg ORSTTOP, ORSTBOT; + reg OLOADTOP, OLOADBOT; + reg ADDSUBTOP, ADDSUBBOT; + reg OHOLDTOP, OHOLDBOT; + reg CI, ACCUMCI, SIGNEXTIN; + + output [31:0] REF_O, UUT_O; + output REF_CO, REF_ACCUMCO, REF_SIGNEXTOUT; + output UUT_CO, UUT_ACCUMCO, UUT_SIGNEXTOUT; + + integer errcount = 0; + + task clkcycle; + begin + #5; + CLK = ~CLK; + #10; + CLK = ~CLK; + #2; + if (REF_O !== UUT_O) begin + $display("ERROR at %1t: REF_O=%b UUT_O=%b DIFF=%b", $time, REF_O, UUT_O, REF_O ^ UUT_O); + errcount = errcount + 1; + end + if (REF_CO !== UUT_CO) begin + $display("ERROR at %1t: REF_CO=%b UUT_CO=%b", $time, REF_CO, UUT_CO); + errcount = errcount + 1; + end + if (REF_ACCUMCO !== UUT_ACCUMCO) begin + $display("ERROR at %1t: REF_ACCUMCO=%b UUT_ACCUMCO=%b", $time, REF_ACCUMCO, UUT_ACCUMCO); + errcount = errcount + 1; + end + if (REF_SIGNEXTOUT !== UUT_SIGNEXTOUT) begin + $display("ERROR at %1t: REF_SIGNEXTOUT=%b UUT_SIGNEXTOUT=%b", $time, REF_SIGNEXTOUT, UUT_SIGNEXTOUT); + errcount = errcount + 1; + end + #3; + end + endtask + + initial begin + $dumpfile("test_dsp_model.vcd"); + $dumpvars(0, testbench); + + #2; + CLK = NEG_TRIGGER; + CE = 1; + {C, A, B, D} = 0; + {AHOLD, BHOLD, CHOLD, DHOLD} = 0; + {OLOADTOP, OLOADBOT} = 0; + {ADDSUBTOP, ADDSUBBOT} = 0; + {OHOLDTOP, OHOLDBOT} = 0; + {CI, ACCUMCI, SIGNEXTIN} = 0; + + {IRSTTOP, IRSTBOT} = ~0; + {ORSTTOP, ORSTBOT} = ~0; + + #3; + {IRSTTOP, IRSTBOT} = 0; + {ORSTTOP, ORSTBOT} = 0; + + repeat (300) begin + clkcycle; + + A = $urandom; + B = $urandom; + C = $urandom; + D = $urandom; + + {AHOLD, BHOLD, CHOLD, DHOLD} = $urandom & $urandom & $urandom; + {OLOADTOP, OLOADBOT} = $urandom & $urandom & $urandom; + {ADDSUBTOP, ADDSUBBOT} = $urandom & $urandom & $urandom; + {OHOLDTOP, OHOLDBOT} = $urandom & $urandom & $urandom; + {CI, ACCUMCI, SIGNEXTIN} = $urandom & $urandom & $urandom; + + {IRSTTOP, IRSTBOT} = $urandom & $urandom & $urandom; + {ORSTTOP, ORSTBOT} = $urandom & $urandom & $urandom; + end + + if (errcount == 0) begin + $display("All tests passed."); + $finish; + end else begin + $display("Caught %1d errors.", errcount); + $stop; + end + end + + SB_MAC16 #( + .NEG_TRIGGER (NEG_TRIGGER ), + .C_REG (C_REG ), + .A_REG (A_REG ), + .B_REG (B_REG ), + .D_REG (D_REG ), + .TOP_8x8_MULT_REG (TOP_8x8_MULT_REG ), + .BOT_8x8_MULT_REG (BOT_8x8_MULT_REG ), + .PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1), + .PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2), + .TOPOUTPUT_SELECT (TOPOUTPUT_SELECT ), + .TOPADDSUB_LOWERINPUT (TOPADDSUB_LOWERINPUT ), + .TOPADDSUB_UPPERINPUT (TOPADDSUB_UPPERINPUT ), + .TOPADDSUB_CARRYSELECT (TOPADDSUB_CARRYSELECT ), + .BOTOUTPUT_SELECT (BOTOUTPUT_SELECT ), + .BOTADDSUB_LOWERINPUT (BOTADDSUB_LOWERINPUT ), + .BOTADDSUB_UPPERINPUT (BOTADDSUB_UPPERINPUT ), + .BOTADDSUB_CARRYSELECT (BOTADDSUB_CARRYSELECT ), + .MODE_8x8 (MODE_8x8 ), + .A_SIGNED (A_SIGNED ), + .B_SIGNED (B_SIGNED ) + ) ref ( + .CLK (CLK ), + .CE (CE ), + .C (C ), + .A (A ), + .B (B ), + .D (D ), + .AHOLD (AHOLD ), + .BHOLD (BHOLD ), + .CHOLD (CHOLD ), + .DHOLD (DHOLD ), + .IRSTTOP (IRSTTOP ), + .IRSTBOT (IRSTBOT ), + .ORSTTOP (ORSTTOP ), + .ORSTBOT (ORSTBOT ), + .OLOADTOP (OLOADTOP ), + .OLOADBOT (OLOADBOT ), + .ADDSUBTOP (ADDSUBTOP ), + .ADDSUBBOT (ADDSUBBOT ), + .OHOLDTOP (OHOLDTOP ), + .OHOLDBOT (OHOLDBOT ), + .CI (CI ), + .ACCUMCI (ACCUMCI ), + .SIGNEXTIN (SIGNEXTIN ), + .O (REF_O ), + .CO (REF_CO ), + .ACCUMCO (REF_ACCUMCO ), + .SIGNEXTOUT (REF_SIGNEXTOUT) + ); + + SB_MAC16_UUT #( + .NEG_TRIGGER (NEG_TRIGGER ), + .C_REG (C_REG ), + .A_REG (A_REG ), + .B_REG (B_REG ), + .D_REG (D_REG ), + .TOP_8x8_MULT_REG (TOP_8x8_MULT_REG ), + .BOT_8x8_MULT_REG (BOT_8x8_MULT_REG ), + .PIPELINE_16x16_MULT_REG1 (PIPELINE_16x16_MULT_REG1), + .PIPELINE_16x16_MULT_REG2 (PIPELINE_16x16_MULT_REG2), + .TOPOUTPUT_SELECT (TOPOUTPUT_SELECT ), + .TOPADDSUB_LOWERINPUT (TOPADDSUB_LOWERINPUT ), + .TOPADDSUB_UPPERINPUT (TOPADDSUB_UPPERINPUT ), + .TOPADDSUB_CARRYSELECT (TOPADDSUB_CARRYSELECT ), + .BOTOUTPUT_SELECT (BOTOUTPUT_SELECT ), + .BOTADDSUB_LOWERINPUT (BOTADDSUB_LOWERINPUT ), + .BOTADDSUB_UPPERINPUT (BOTADDSUB_UPPERINPUT ), + .BOTADDSUB_CARRYSELECT (BOTADDSUB_CARRYSELECT ), + .MODE_8x8 (MODE_8x8 ), + .A_SIGNED (A_SIGNED ), + .B_SIGNED (B_SIGNED ) + ) uut ( + .CLK (CLK ), + .CE (CE ), + .C (C ), + .A (A ), + .B (B ), + .D (D ), + .AHOLD (AHOLD ), + .BHOLD (BHOLD ), + .CHOLD (CHOLD ), + .DHOLD (DHOLD ), + .IRSTTOP (IRSTTOP ), + .IRSTBOT (IRSTBOT ), + .ORSTTOP (ORSTTOP ), + .ORSTBOT (ORSTBOT ), + .OLOADTOP (OLOADTOP ), + .OLOADBOT (OLOADBOT ), + .ADDSUBTOP (ADDSUBTOP ), + .ADDSUBBOT (ADDSUBBOT ), + .OHOLDTOP (OHOLDTOP ), + .OHOLDBOT (OHOLDBOT ), + .CI (CI ), + .ACCUMCI (ACCUMCI ), + .SIGNEXTIN (SIGNEXTIN ), + .O (UUT_O ), + .CO (UUT_CO ), + .ACCUMCO (UUT_ACCUMCO ), + .SIGNEXTOUT (UUT_SIGNEXTOUT) + ); +endmodule + +module testbench_comb_8x8_A; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (2), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (0), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (2), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (0), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_8x8_B; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (1), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (1), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (0), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_comb_16x16; + testbench #( + .NEG_TRIGGER (0), + .C_REG (0), + .A_REG (0), + .B_REG (0), + .D_REG (0), + .TOP_8x8_MULT_REG (0), + .BOT_8x8_MULT_REG (0), + .PIPELINE_16x16_MULT_REG1 (0), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_seq_16x16_A; + testbench #( + .NEG_TRIGGER (0), + .C_REG (1), + .A_REG (1), + .B_REG (1), + .D_REG (1), + .TOP_8x8_MULT_REG (1), + .BOT_8x8_MULT_REG (1), + .PIPELINE_16x16_MULT_REG1 (1), + .PIPELINE_16x16_MULT_REG2 (1), + .TOPOUTPUT_SELECT (0), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (1), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (0), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (1), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (0) + ) testbench (); +endmodule + +module testbench_seq_16x16_B; + testbench #( + .NEG_TRIGGER (0), + .C_REG (1), + .A_REG (1), + .B_REG (1), + .D_REG (1), + .TOP_8x8_MULT_REG (1), + .BOT_8x8_MULT_REG (1), + .PIPELINE_16x16_MULT_REG1 (1), + .PIPELINE_16x16_MULT_REG2 (0), + .TOPOUTPUT_SELECT (1), // 0=P, 1=Q, 2=8x8, 3=16x16 + .TOPADDSUB_LOWERINPUT (2), // 0=A, 1=8x8, 2=16x16, 3=S-EXT + .TOPADDSUB_UPPERINPUT (0), // 0=Q, 1=C + .TOPADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .BOTOUTPUT_SELECT (1), // 0=R, 1=S, 2=8x8, 3=16x16 + .BOTADDSUB_LOWERINPUT (2), // 0=B, 1=8x8, 2=16x16, 3=S-EXT + .BOTADDSUB_UPPERINPUT (0), // 0=S, 1=D + .BOTADDSUB_CARRYSELECT (2), // 0=0, 1=1, 2=ACI, 3=CI + .MODE_8x8 (0), + .A_SIGNED (0), + .B_SIGNED (0) + ) testbench (); +endmodule diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v new file mode 100644 index 000000000..d387b99eb --- /dev/null +++ b/tests/simple_abc9/abc9.v @@ -0,0 +1,48 @@ +module abc9_test001(input a, output o); +assign o = a; +endmodule + +module abc9_test002(input [1:0] a, output o); +assign o = a[1]; +endmodule + +module abc9_test003(input [1:0] a, output [1:0] o); +assign o = a; +endmodule + +module abc9_test004(input [1:0] a, output o); +assign o = ^a; +endmodule + +module abc9_test005(input [1:0] a, output o, output p); +assign o = ^a; +assign p = ~o; +endmodule + +module abc9_test006(input [1:0] a, output [2:0] o); +assign o[0] = ^a; +assign o[1] = ~o[0]; +assign o[2] = o[1]; +endmodule + +module abc9_test007(input a, output o); +wire b, c; +assign c = ~a; +assign b = c; +abc9_test007_sub s(b, o); +endmodule + +module abc9_test007_sub(input a, output b); +assign b = a; +endmodule + +module abc9_test008(input a, output o); +wire b, c; +assign b = ~a; +assign c = b; +abc9_test008_sub s(b, o); +endmodule + +module abc9_test008_sub(input a, output b); +assign b = ~a; +endmodule diff --git a/tests/simple_defparam/run-test.sh b/tests/simple_abc9/run-test.sh index 137e15076..bf48d007d 100755 --- a/tests/simple_defparam/run-test.sh +++ b/tests/simple_abc9/run-test.sh @@ -18,4 +18,6 @@ if ! which iverilog > /dev/null ; then fi cp ../simple/*.v . -exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-B \"-defparam\"" +rm partsel.v # FIXME: Contains 1'hx, thus write_xaiger fails +DOLLAR='?' +exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-p 'hierarchy; synth -run coarse; techmap; opt -full; abc9 -lut 4; stat; check -assert; select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_'" diff --git a/tests/tools/autotest.sh b/tests/tools/autotest.sh index 65fd4cb1f..13c25432f 100755 --- a/tests/tools/autotest.sh +++ b/tests/tools/autotest.sh @@ -28,7 +28,7 @@ if [ ! -f $toolsdir/cmp_tbdata -o $toolsdir/cmp_tbdata.c -nt $toolsdir/cmp_tbdat ( set -ex; ${CC:-gcc} -Wall -o $toolsdir/cmp_tbdata $toolsdir/cmp_tbdata.c; ) || exit 1 fi -while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do +while getopts xmGl:wkjvref:s:p:n:S:I:-: opt; do case "$opt" in x) use_xsim=true ;; @@ -65,8 +65,6 @@ while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do include_opts="$include_opts -I $OPTARG" xinclude_opts="$xinclude_opts -i $OPTARG" minclude_opts="$minclude_opts +incdir+$OPTARG" ;; - B) - backend_opts="$backend_opts $OPTARG" ;; -) case "${OPTARG}" in xfirrtl) @@ -84,7 +82,7 @@ while getopts xmGl:wkjvref:s:p:n:S:I:B:-: opt; do ;; esac;; *) - echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [-B backend_opt] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2 + echo "Usage: $0 [-x|-m] [-G] [-w] [-k] [-j] [-v] [-r] [-e] [-l libs] [-f frontend] [-s script] [-p cmdstring] [-n iters] [-S seed] [-I incdir] [--xfirrtl FIRRTL test exclude file] [--firrtl2verilog command to generate verilog from firrtl] verilog-files\n" >&2 exit 1 esac done |