aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG4
-rw-r--r--Makefile2
-rw-r--r--backends/aiger/Makefile.inc1
-rw-r--r--backends/aiger/aiger.cc2
-rw-r--r--backends/aiger/xaiger.cc1015
-rw-r--r--frontends/aiger/aigerparse.cc692
-rw-r--r--frontends/aiger/aigerparse.h10
-rw-r--r--kernel/rtlil.h6
-rw-r--r--passes/techmap/Makefile.inc1
-rw-r--r--passes/techmap/abc.cc2
-rw-r--r--passes/techmap/abc9.cc1173
-rw-r--r--techlibs/common/synth.cc21
-rw-r--r--techlibs/ecp5/Makefile.inc3
-rw-r--r--techlibs/ecp5/abc_5g.box36
-rw-r--r--techlibs/ecp5/abc_5g.lut25
-rw-r--r--techlibs/ecp5/cells_map.v133
-rw-r--r--techlibs/ecp5/cells_sim.v32
-rw-r--r--techlibs/ecp5/synth_ecp5.cc25
-rw-r--r--techlibs/ice40/Makefile.inc6
-rw-r--r--techlibs/ice40/abc_hx.box113
-rw-r--r--techlibs/ice40/abc_hx.lut6
-rw-r--r--techlibs/ice40/abc_lp.box113
-rw-r--r--techlibs/ice40/abc_lp.lut6
-rw-r--r--techlibs/ice40/abc_u.box113
-rw-r--r--techlibs/ice40/abc_u.lut6
-rw-r--r--techlibs/ice40/cells_map.v20
-rw-r--r--techlibs/ice40/cells_sim.v59
-rw-r--r--techlibs/ice40/synth_ice40.cc35
-rw-r--r--techlibs/xilinx/Makefile.inc2
-rw-r--r--techlibs/xilinx/abc.box62
-rw-r--r--techlibs/xilinx/abc.lut14
-rw-r--r--techlibs/xilinx/arith_map.v4
-rw-r--r--techlibs/xilinx/brams_bb.v16
-rw-r--r--techlibs/xilinx/cells_map.v20
-rw-r--r--techlibs/xilinx/cells_sim.v27
-rw-r--r--techlibs/xilinx/cells_xtra.sh4
-rw-r--r--techlibs/xilinx/cells_xtra.v18
-rw-r--r--techlibs/xilinx/synth_xilinx.cc66
-rw-r--r--tests/simple_abc9/abc.box2
-rw-r--r--tests/simple_abc9/abc9.v264
-rwxr-xr-xtests/simple_abc9/run-test.sh22
41 files changed, 3950 insertions, 231 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 839fefcf1..44e32c6a8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -17,6 +17,10 @@ Yosys 0.8 .. Yosys 0.8-dev
- Added "rename -src"
- Added "equiv_opt" pass
- Added "read_aiger" frontend
+ - Added "abc9" pass for timing-aware techmapping (experimental, FPGA only, no FFs)
+ - Added "synth_xilinx -abc9" (experimental)
+ - Added "synth_ice40 -abc9" (experimental)
+ - Added "synth -abc9" (experimental)
- "synth_xilinx" to now infer hard shift registers, using new "shregmap -tech xilinx"
diff --git a/Makefile b/Makefile
index 76dac48a5..fb0eaf14d 100644
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ OBJS = kernel/version_$(GIT_REV).o
# is just a symlink to your actual ABC working directory, as 'make mrproper'
# will remove the 'abc' directory and you do not want to accidentally
# delete your work on ABC..
-ABCREV = 3709744
+ABCREV = 62487de
ABCPULL = 1
ABCURL ?= https://github.com/berkeley-abc/abc
ABCMKARGS = CC="$(CXX)" CXX="$(CXX)" ABC_USE_LIBSTDCXX=1
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/aiger.cc b/backends/aiger/aiger.cc
index dfe506c66..516e538a5 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -685,7 +685,7 @@ struct AigerBackend : public Backend {
log("invariant constraints.\n");
log("\n");
log(" -ascii\n");
- log(" write ASCII version of AGIER format\n");
+ log(" write ASCII version of AIGER format\n");
log("\n");
log(" -zinit\n");
log(" convert FFs to zero-initialized FFs, adding additional inputs for\n");
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
new file mode 100644
index 000000000..daefd3a73
--- /dev/null
+++ b/backends/aiger/xaiger.cc
@@ -0,0 +1,1015 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.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;
+ vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int>> ci_bits;
+ vector<std::tuple<SigBit,RTLIL::Cell*,RTLIL::IdString,int,int>> co_bits;
+ vector<std::pair<SigBit,SigBit>> ff_bits;
+
+ 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;
+
+ vector<Cell*> box_list;
+
+ //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 holes_mode=false) : 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;
+ }
+
+ bool keep = wire->attributes.count("\\keep");
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wirebit(wire, i);
+ SigBit bit = sigmap(wirebit);
+
+ if (bit.wire) {
+ undriven_bits.insert(bit);
+ unused_bits.insert(bit);
+ }
+
+ if (wire->port_input || keep) {
+ if (bit != wirebit)
+ alias_map[bit] = wirebit;
+ input_bits.insert(wirebit);
+ }
+
+ if (wire->port_output || keep) {
+ if (bit != wirebit)
+ alias_map[wirebit] = bit;
+ output_bits.insert(wirebit);
+ }
+ }
+ }
+
+ for (auto bit : input_bits)
+ undriven_bits.erase(sigmap(bit));
+ for (auto bit : output_bits)
+ if (!bit.wire->port_input)
+ unused_bits.erase(bit);
+
+ SigMap topomap;
+ topomap.database = sigmap.database;
+
+ bool abc_box_seen = false;
+
+ 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;
+ if (!holes_mode)
+ topomap.add(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);
+ if (!holes_mode) {
+ topomap.add(Y, A);
+ topomap.add(Y, B);
+ }
+ continue;
+ }
+
+ //if (cell->type == "$initstate")
+ //{
+ // SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
+ // undriven_bits.erase(Y);
+ // initstate_bits.insert(Y);
+ // continue;
+ //}
+
+ RTLIL::Module* inst_module = module->design->module(cell->type);
+ bool inst_flop = inst_module ? inst_module->attributes.count("\\abc_flop") : false;
+ if (inst_flop) {
+ SigBit d, q;
+ for (const auto &c : cell->connections()) {
+ auto is_input = cell->input(c.first);
+ auto is_output = cell->output(c.first);
+ log_assert(is_input || is_output);
+ RTLIL::Wire* port = inst_module->wire(c.first);
+ for (auto b : c.second.bits()) {
+ if (is_input && port->attributes.count("\\abc_flop_d")) {
+ d = b;
+ SigBit I = sigmap(d);
+ if (I != d)
+ alias_map[I] = d;
+ unused_bits.erase(d);
+ }
+ if (is_output && port->attributes.count("\\abc_flop_q")) {
+ q = b;
+ SigBit O = sigmap(q);
+ if (O != q)
+ alias_map[O] = q;
+ undriven_bits.erase(O);
+ }
+ }
+ }
+ if (!abc_box_seen)
+ abc_box_seen = inst_module->attributes.count("\\abc_box_id");
+
+ ff_bits.emplace_back(d, q);
+ }
+ else if (inst_module && inst_module->attributes.count("\\abc_box_id")) {
+ abc_box_seen = true;
+ }
+ else {
+ for (const auto &c : cell->connections()) {
+ if (c.second.is_fully_const()) continue;
+ auto is_input = cell->input(c.first);
+ auto is_output = cell->output(c.first);
+ log_assert(is_input || is_output);
+
+ if (is_input) {
+ for (auto b : c.second.bits()) {
+ Wire *w = b.wire;
+ if (!w) continue;
+ if (!w->port_output) {
+ SigBit I = sigmap(b);
+ if (I != b)
+ alias_map[b] = I;
+ output_bits.insert(b);
+ unused_bits.erase(b);
+ }
+ }
+ }
+ if (is_output) {
+ for (auto b : c.second.bits()) {
+ Wire *w = b.wire;
+ if (!w) continue;
+ input_bits.insert(b);
+ SigBit O = sigmap(b);
+ if (O != b)
+ alias_map[O] = b;
+ undriven_bits.erase(O);
+ }
+ }
+ }
+ }
+
+ //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
+ }
+
+ if (abc_box_seen && !holes_mode) {
+ TopoSort<IdString, RTLIL::sort_by_id_str> toposort;
+ dict<SigBit, pool<IdString>> bit_drivers, bit_users;
+
+ for (auto cell : module->cells()) {
+ RTLIL::Module* inst_module = module->design->module(cell->type);
+ if (!inst_module || !inst_module->attributes.count("\\abc_box_id"))
+ continue;
+ toposort.node(cell->name);
+ for (const auto &conn : cell->connections()) {
+ if (cell->input(conn.first)) {
+ // Ignore inout for the sake of topographical ordering
+ if (cell->output(conn.first)) continue;
+ for (auto bit : topomap(conn.second))
+ if (bit.wire)
+ bit_users[bit].insert(cell->name);
+ }
+
+ if (cell->output(conn.first)) {
+ RTLIL::Wire* inst_module_port = inst_module->wire(conn.first);
+ log_assert(inst_module_port);
+ if (inst_module_port->attributes.count("\\abc_flop_q"))
+ continue;
+ for (auto bit : topomap(conn.second))
+ bit_drivers[bit].insert(cell->name);
+ }
+ }
+ }
+
+ for (auto &it : bit_users)
+ if (bit_drivers.count(it.first))
+ for (auto driver_cell : bit_drivers.at(it.first))
+ for (auto user_cell : it.second)
+ toposort.edge(driver_cell, user_cell);
+
+#if 1
+ toposort.analyze_loops = true;
+#endif
+ bool no_loops = toposort.sort();
+#if 1
+ unsigned i = 0;
+ for (auto &it : toposort.loops) {
+ log(" loop %d", i++);
+ for (auto cell : it)
+ log(" %s", log_id(cell));
+ log("\n");
+ }
+#endif
+ log_assert(no_loops);
+
+ pool<RTLIL::Module*> abc_carry_modules;
+ for (auto cell_name : toposort.sorted) {
+ RTLIL::Cell *cell = module->cell(cell_name);
+ RTLIL::Module* box_module = module->design->module(cell->type);
+ log_assert(box_module);
+ log_assert(box_module->attributes.count("\\abc_box_id"));
+
+ if (!abc_carry_modules.count(box_module) && box_module->attributes.count("\\abc_carry")) {
+ RTLIL::Wire* carry_in = nullptr, *carry_out = nullptr;
+ RTLIL::Wire* last_in = nullptr, *last_out = nullptr;
+ for (const auto &port_name : box_module->ports) {
+ RTLIL::Wire* w = box_module->wire(port_name);
+ log_assert(w);
+ if (w->port_input) {
+ if (w->attributes.count("\\abc_carry_in")) {
+ log_assert(!carry_in);
+ carry_in = w;
+ }
+ log_assert(!last_in || last_in->port_id < w->port_id);
+ last_in = w;
+ }
+ if (w->port_output) {
+ if (w->attributes.count("\\abc_carry_out")) {
+ log_assert(!carry_out);
+ carry_out = w;
+ }
+ log_assert(!last_out || last_out->port_id < w->port_id);
+ last_out = w;
+ }
+ }
+
+ if (carry_in) {
+ log_assert(last_in);
+ std::swap(box_module->ports[carry_in->port_id-1], box_module->ports[last_in->port_id-1]);
+ std::swap(carry_in->port_id, last_in->port_id);
+ }
+ if (carry_out) {
+ log_assert(last_out);
+ std::swap(box_module->ports[carry_out->port_id-1], box_module->ports[last_out->port_id-1]);
+ std::swap(carry_out->port_id, last_out->port_id);
+ }
+ }
+
+ // Fully pad all unused input connections of this box cell with S0
+ // Fully pad all undriven output connections of this box cell with anonymous wires
+ // NB: Assume box_module->ports are sorted alphabetically
+ // (as RTLIL::Module::fixup_ports() would do)
+ for (const auto &port_name : box_module->ports) {
+ RTLIL::Wire* w = box_module->wire(port_name);
+ log_assert(w);
+ auto it = cell->connections_.find(port_name);
+ if (w->port_input) {
+ RTLIL::SigSpec rhs;
+ if (it != cell->connections_.end()) {
+ if (GetSize(it->second) < GetSize(w))
+ it->second.append(RTLIL::SigSpec(RTLIL::S0, GetSize(w)-GetSize(it->second)));
+ rhs = it->second;
+ }
+ else {
+ rhs = RTLIL::SigSpec(RTLIL::S0, GetSize(w));
+ cell->setPort(port_name, rhs);
+ }
+
+ int offset = 0;
+ for (const auto &b : rhs.bits()) {
+ SigBit I = sigmap(b);
+ if (I != b)
+ alias_map[b] = I;
+ co_bits.emplace_back(b, cell, port_name, offset++, 0);
+ unused_bits.erase(b);
+ }
+ }
+ if (w->port_output) {
+ RTLIL::SigSpec rhs;
+ auto it = cell->connections_.find(w->name);
+ if (it != cell->connections_.end()) {
+ if (GetSize(it->second) < GetSize(w))
+ it->second.append(module->addWire(NEW_ID, GetSize(w)-GetSize(it->second)));
+ rhs = it->second;
+ }
+ else {
+ rhs = module->addWire(NEW_ID, GetSize(w));
+ cell->setPort(port_name, rhs);
+ }
+
+ int offset = 0;
+ for (const auto &b : rhs.bits()) {
+ ci_bits.emplace_back(b, cell, port_name, offset++);
+ SigBit O = sigmap(b);
+ if (O != b)
+ alias_map[O] = b;
+ undriven_bits.erase(O);
+
+ auto jt = input_bits.find(b);
+ if (jt != input_bits.end()) {
+ log_assert(b.wire->attributes.count("\\keep"));
+ input_bits.erase(b);
+ }
+ }
+ }
+ }
+ box_list.emplace_back(cell);
+ }
+ }
+
+ for (auto bit : input_bits) {
+ RTLIL::Wire *wire = bit.wire;
+ // If encountering an inout port, or a keep-ed wire, then create a new wire
+ // with $inout.out suffix, make it a PO driven by the existing inout, and
+ // inherit existing inout's drivers
+ if ((wire->port_input && wire->port_output && !undriven_bits.count(bit))
+ || wire->attributes.count("\\keep")) {
+ log_assert(input_bits.count(bit) && output_bits.count(bit));
+ RTLIL::IdString wire_name = wire->name.str() + "$inout.out";
+ RTLIL::Wire *new_wire = module->wire(wire_name);
+ if (!new_wire)
+ new_wire = module->addWire(wire_name, GetSize(wire));
+ SigBit new_bit(new_wire, bit.offset);
+ module->connect(new_bit, bit);
+ if (not_map.count(bit))
+ not_map[new_bit] = not_map.at(bit);
+ else if (and_map.count(bit))
+ and_map[new_bit] = and_map.at(bit);
+ else if (alias_map.count(bit))
+ alias_map[new_bit] = alias_map.at(bit);
+ else
+ //log_abort();
+ alias_map[new_bit] = bit;
+ output_bits.erase(bit);
+ output_bits.insert(new_bit);
+ }
+ }
+
+ for (auto bit : unused_bits)
+ undriven_bits.erase(bit);
+
+ if (!undriven_bits.empty() && !holes_mode) {
+ 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();
+ if (holes_mode) {
+ struct sort_by_port_id {
+ bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const {
+ return a.wire->port_id < b.wire->port_id;
+ }
+ };
+ input_bits.sort(sort_by_port_id());
+ output_bits.sort(sort_by_port_id());
+ }
+ else {
+ 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 : input_bits) {
+ aig_m++, aig_i++;
+ log_assert(!aig_map.count(bit));
+ aig_map[bit] = 2*aig_m;
+ }
+
+ for (auto &f : ff_bits) {
+ RTLIL::SigBit bit = f.second;
+ aig_m++, aig_i++;
+ log_assert(!aig_map.count(bit));
+ aig_map[bit] = 2*aig_m;
+ }
+
+ dict<SigBit, int> ff_aig_map;
+ for (auto &c : ci_bits) {
+ RTLIL::SigBit bit = std::get<0>(c);
+ aig_m++, aig_i++;
+ auto r = aig_map.insert(std::make_pair(bit, 2*aig_m));
+ if (!r.second)
+ ff_aig_map[bit] = 2*aig_m;
+ }
+
+ //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 &c : co_bits) {
+ RTLIL::SigBit bit = std::get<0>(c);
+ std::get<4>(c) = ordered_outputs[bit] = aig_o++;
+ aig_outputs.push_back(bit2aig(bit));
+ }
+
+ for (auto bit : output_bits) {
+ ordered_outputs[bit] = aig_o++;
+ aig_outputs.push_back(bit2aig(bit));
+ }
+
+ for (auto &f : ff_bits) {
+ aig_o++;
+ RTLIL::SigBit bit = f.second;
+ aig_outputs.push_back(ff_aig_map.at(bit));
+ }
+
+ }
+
+ void write_aiger(std::ostream &f, bool ascii_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);
+ }
+ }
+
+ f << "c";
+
+ if (!box_list.empty() || !ff_bits.empty()) {
+ std::stringstream h_buffer;
+ auto write_h_buffer = [&h_buffer](int i32) {
+ // TODO: Don't assume we're on little endian
+#ifdef _WIN32
+ int i32_be = _byteswap_ulong(i32);
+#else
+ int i32_be = __builtin_bswap32(i32);
+#endif
+ h_buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
+ };
+ write_h_buffer(1);
+ log_debug("ciNum = %zu\n", input_bits.size() + ff_bits.size() + ci_bits.size());
+ write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
+ log_debug("coNum = %zu\n", output_bits.size() + ff_bits.size() + co_bits.size());
+ write_h_buffer(output_bits.size() + ff_bits.size()+ co_bits.size());
+ log_debug("piNum = %zu\n", input_bits.size() + ff_bits.size());
+ write_h_buffer(input_bits.size()+ ff_bits.size());
+ log_debug("poNum = %zu\n", output_bits.size() + ff_bits.size());
+ write_h_buffer(output_bits.size() + ff_bits.size());
+ log_debug("boxNum = %zu\n", box_list.size());
+ write_h_buffer(box_list.size());
+
+ RTLIL::Module *holes_module = nullptr;
+ holes_module = module->design->addModule("\\__holes__");
+ log_assert(holes_module);
+
+ int port_id = 1;
+ int box_count = 0;
+ for (auto cell : box_list) {
+ RTLIL::Module* box_module = module->design->module(cell->type);
+ int box_inputs = 0, box_outputs = 0;
+ Cell *holes_cell = nullptr;
+ if (box_module->get_bool_attribute("\\whitebox")) {
+ holes_cell = holes_module->addCell(cell->name, cell->type);
+ holes_cell->parameters = cell->parameters;
+ }
+
+ // NB: Assume box_module->ports are sorted alphabetically
+ // (as RTLIL::Module::fixup_ports() would do)
+ for (const auto &port_name : box_module->ports) {
+ RTLIL::Wire *w = box_module->wire(port_name);
+ log_assert(w);
+ RTLIL::Wire *holes_wire;
+ RTLIL::SigSpec port_wire;
+ if (w->port_input) {
+ for (int i = 0; i < GetSize(w); i++) {
+ box_inputs++;
+ holes_wire = holes_module->wire(stringf("\\i%d", box_inputs));
+ if (!holes_wire) {
+ holes_wire = holes_module->addWire(stringf("\\i%d", box_inputs));
+ holes_wire->port_input = true;
+ holes_wire->port_id = port_id++;
+ holes_module->ports.push_back(holes_wire->name);
+ }
+ if (holes_cell)
+ port_wire.append(holes_wire);
+ }
+ if (!port_wire.empty())
+ holes_cell->setPort(w->name, port_wire);
+ }
+ if (w->port_output) {
+ box_outputs += GetSize(w);
+ for (int i = 0; i < GetSize(w); i++) {
+ if (GetSize(w) == 1)
+ holes_wire = holes_module->addWire(stringf("%s.%s", cell->name.c_str(), w->name.c_str()));
+ else
+ holes_wire = holes_module->addWire(stringf("%s.%s[%d]", cell->name.c_str(), w->name.c_str(), i));
+ holes_wire->port_output = true;
+ holes_wire->port_id = port_id++;
+ holes_module->ports.push_back(holes_wire->name);
+ if (holes_cell)
+ port_wire.append(holes_wire);
+ else
+ holes_module->connect(holes_wire, RTLIL::S0);
+ }
+ if (!port_wire.empty())
+ holes_cell->setPort(w->name, port_wire);
+ }
+ }
+
+ write_h_buffer(box_inputs);
+ write_h_buffer(box_outputs);
+ write_h_buffer(box_module->attributes.at("\\abc_box_id").as_int());
+ write_h_buffer(box_count++);
+ }
+
+ f << "h";
+ std::string buffer_str = h_buffer.str();
+ // TODO: Don't assume we're on little endian
+#ifdef _WIN32
+ int buffer_size_be = _byteswap_ulong(buffer_str.size());
+#else
+ int buffer_size_be = __builtin_bswap32(buffer_str.size());
+#endif
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+
+ /*if (!ff_bits.empty())*/ {
+ std::stringstream r_buffer;
+ auto write_r_buffer = [&r_buffer](int i32) {
+ // TODO: Don't assume we're on little endian
+#ifdef _WIN32
+ int i32_be = _byteswap_ulong(i32);
+#else
+ int i32_be = __builtin_bswap32(i32);
+#endif
+ r_buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
+ };
+ log_debug("flopNum = %zu\n", ff_bits.size());
+ write_r_buffer(ff_bits.size());
+ int mergeability_class = 1;
+ for (auto cell : ff_bits)
+ write_r_buffer(mergeability_class++);
+
+ f << "r";
+ std::string buffer_str = r_buffer.str();
+ // TODO: Don't assume we're on little endian
+#ifdef _WIN32
+ int buffer_size_be = _byteswap_ulong(buffer_str.size());
+#else
+ int buffer_size_be = __builtin_bswap32(buffer_str.size());
+#endif
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+ }
+
+ if (holes_module) {
+ // NB: fixup_ports() will sort ports by name
+ //holes_module->fixup_ports();
+ holes_module->check();
+
+ holes_module->design->selection_stack.emplace_back(false);
+ RTLIL::Selection& sel = holes_module->design->selection_stack.back();
+ sel.select(holes_module);
+
+ // TODO: Should not need to opt_merge if we only instantiate
+ // each box type once...
+ Pass::call(holes_module->design, "opt_merge -share_all");
+
+ Pass::call(holes_module->design, "flatten -wb");
+
+ // TODO: Should techmap all lib_whitebox-es once
+ Pass::call(holes_module->design, "techmap");
+ Pass::call(holes_module->design, "aigmap");
+ Pass::call(holes_module->design, "clean -purge");
+
+ holes_module->design->selection_stack.pop_back();
+
+ std::stringstream a_buffer;
+ XAigerWriter writer(holes_module, false /*zinit_mode*/, true /* holes_mode */);
+ writer.write_aiger(a_buffer, false /*ascii_mode*/);
+
+ f << "a";
+ std::string buffer_str = a_buffer.str();
+ // TODO: Don't assume we're on little endian
+#ifdef _WIN32
+ int buffer_size_be = _byteswap_ulong(buffer_str.size());
+#else
+ int buffer_size_be = __builtin_bswap32(buffer_str.size());
+#endif
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+ holes_module->design->remove(holes_module);
+ }
+ }
+
+ f << stringf("Generated by %s\n", yosys_version_str);
+ }
+
+ void write_map(std::ostream &f, bool verbose_map)
+ {
+ 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)) {
+ int a = aig_map.at(b);
+ log_assert((a & 1) == 0);
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire));
+ }
+
+ if (output_bits.count(b)) {
+ int o = ordered_outputs.at(b);
+ output_lines[o] += stringf("output %lu %d %s\n", o - co_bits.size(), 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());
+
+ init_lines.sort();
+ for (auto &it : init_lines)
+ f << it.second;
+
+ int box_count = 0;
+ for (auto cell : box_list)
+ f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
+
+ output_lines.sort();
+ for (auto &it : output_lines)
+ f << it.second;
+ log_assert(output_lines.size() == output_bits.size());
+
+ 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 AIGER 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(" -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");
+ }
+ 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 verbose_map = 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 (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;
+ }
+ 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);
+ writer.write_aiger(*f, ascii_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);
+ }
+ }
+} XAigerBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index 68552fd06..a72a82926 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -2,7 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- * Eddie Hung <eddie@fpgeh.com>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -22,19 +22,166 @@
// 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
-#ifndef _WIN32
+#ifdef _WIN32
#include <libgen.h>
+#include <stdlib.h>
#endif
#include <array>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
#include "aigerparse.h"
YOSYS_NAMESPACE_BEGIN
-AigerReader::AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name)
- : design(design), f(f), clk_name(clk_name)
+struct ConstEvalAig
+{
+ RTLIL::Module *module;
+ dict<RTLIL::SigBit, RTLIL::State> values_map;
+ dict<RTLIL::SigBit, RTLIL::Cell*> sig2driver;
+ dict<SigBit, pool<RTLIL::SigBit>> sig2deps;
+
+ ConstEvalAig(RTLIL::Module *module) : module(module)
+ {
+ for (auto &it : module->cells_) {
+ if (!yosys_celltypes.cell_known(it.second->type))
+ continue;
+ for (auto &it2 : it.second->connections())
+ if (yosys_celltypes.cell_output(it.second->type, it2.first)) {
+ auto r = sig2driver.insert(std::make_pair(it2.second, it.second));
+ log_assert(r.second);
+ }
+ }
+ }
+
+ void clear()
+ {
+ values_map.clear();
+ sig2deps.clear();
+ }
+
+ void set(RTLIL::SigBit sig, RTLIL::State value)
+ {
+ auto it = values_map.find(sig);
+#ifndef NDEBUG
+ if (it != values_map.end()) {
+ RTLIL::State current_val = it->second;
+ log_assert(current_val == value);
+ }
+#endif
+ if (it != values_map.end())
+ it->second = value;
+ else
+ values_map[sig] = value;
+ }
+
+ void set_incremental(RTLIL::SigSpec sig, RTLIL::Const value)
+ {
+ log_assert(GetSize(sig) == GetSize(value));
+
+ for (int i = 0; i < GetSize(sig); i++) {
+ auto it = values_map.find(sig[i]);
+ if (it != values_map.end()) {
+ RTLIL::State current_val = it->second;
+ if (current_val != value[i])
+ for (auto dep : sig2deps[sig[i]])
+ values_map.erase(dep);
+ it->second = value[i];
+ }
+ else
+ values_map[sig[i]] = value[i];
+ }
+ }
+
+ void compute_deps(RTLIL::SigBit output, const pool<RTLIL::SigBit> &inputs)
+ {
+ sig2deps[output].insert(output);
+
+ RTLIL::Cell *cell = sig2driver.at(output);
+ RTLIL::SigBit sig_a = cell->getPort("\\A");
+ sig2deps[sig_a].insert(sig2deps[output].begin(), sig2deps[output].end());
+ if (!inputs.count(sig_a))
+ compute_deps(sig_a, inputs);
+
+ if (cell->type == "$_AND_") {
+ RTLIL::SigSpec sig_b = cell->getPort("\\B");
+ sig2deps[sig_b].insert(sig2deps[output].begin(), sig2deps[output].end());
+ if (!inputs.count(sig_b))
+ compute_deps(sig_b, inputs);
+ }
+ else if (cell->type == "$_NOT_") {
+ }
+ else log_abort();
+ }
+
+ bool eval(RTLIL::Cell *cell)
+ {
+ RTLIL::SigBit sig_y = cell->getPort("\\Y");
+ if (values_map.count(sig_y))
+ return true;
+
+ RTLIL::SigBit sig_a = cell->getPort("\\A");
+ if (!eval(sig_a))
+ return false;
+
+ RTLIL::State eval_ret = RTLIL::Sx;
+ if (cell->type == "$_NOT_") {
+ if (sig_a == RTLIL::S0) eval_ret = RTLIL::S1;
+ else if (sig_a == RTLIL::S1) eval_ret = RTLIL::S0;
+ }
+ else if (cell->type == "$_AND_") {
+ if (sig_a == RTLIL::S0) {
+ eval_ret = RTLIL::S0;
+ goto eval_end;
+ }
+
+ {
+ RTLIL::SigBit sig_b = cell->getPort("\\B");
+ if (!eval(sig_b))
+ return false;
+ if (sig_b == RTLIL::S0) {
+ eval_ret = RTLIL::S0;
+ goto eval_end;
+ }
+
+ if (sig_a != RTLIL::S1 || sig_b != RTLIL::S1)
+ goto eval_end;
+
+ eval_ret = RTLIL::S1;
+ }
+ }
+ else log_abort();
+
+eval_end:
+ set(sig_y, eval_ret);
+ return true;
+ }
+
+ bool eval(RTLIL::SigBit &sig)
+ {
+ auto it = values_map.find(sig);
+ if (it != values_map.end()) {
+ sig = it->second;
+ return true;
+ }
+
+ RTLIL::Cell *cell = sig2driver.at(sig);
+ if (!eval(cell))
+ return false;
+
+ it = values_map.find(sig);
+ if (it != values_map.end()) {
+ sig = it->second;
+ return true;
+ }
+
+ return false;
+ }
+};
+
+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;
@@ -73,6 +220,8 @@ end_of_header:
log_debug("M=%u I=%u L=%u O=%u A=%u B=%u C=%u J=%u F=%u\n", M, I, L, O, A, B, C, J, F);
line_count = 1;
+ piNum = 0;
+ flopNum = 0;
if (header == "aag")
parse_aiger_ascii();
@@ -81,21 +230,10 @@ end_of_header:
else
log_abort();
- RTLIL::Wire* n0 = module->wire("\\n0");
+ RTLIL::Wire* n0 = module->wire("\\__0__");
if (n0)
module->connect(n0, RTLIL::S0);
- for (unsigned i = 0; i < outputs.size(); ++i) {
- RTLIL::Wire *wire = outputs[i];
- if (wire->port_input) {
- RTLIL::Wire *o_wire = module->addWire(wire->name.str() + "_o");
- o_wire->port_output = true;
- wire->port_output = false;
- module->connect(o_wire, wire);
- outputs[i] = o_wire;
- }
- }
-
// Parse footer (symbol table, comments, etc.)
unsigned l1;
std::string s;
@@ -131,21 +269,35 @@ end_of_header:
std::getline(f, line); // Ignore up to start of next line
}
- module->fixup_ports();
- design->add(module);
+ post_process();
+}
+
+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;
@@ -153,14 +305,163 @@ 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());
- module->addNotGate(stringf("\\n%d_not", variable), wire_inv, wire); // FIXME: is "_not" the right suffix?
+ 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;
+ piNum = 0;
+ flopNum = 0;
+
+ if (header == "aag")
+ parse_aiger_ascii();
+ else if (header == "aig")
+ parse_aiger_binary();
+ else
+ log_abort();
+
+ RTLIL::Wire* n0 = module->wire("\\__0__");
+ if (n0)
+ module->connect(n0, RTLIL::S0);
+
+ dict<int,IdString> box_lookup;
+ for (auto m : design->modules()) {
+ auto it = m->attributes.find("\\abc_box_id");
+ if (it == m->attributes.end())
+ continue;
+ if (m->name[0] == '$') continue;
+ auto r = box_lookup.insert(std::make_pair(it->second.as_int(), m->name));
+ log_assert(r.second);
+ }
+
+ // Parse footer (symbol table, comments, etc.)
+ 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);
+ ConstEvalAig 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);
+ }
+ // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size())
+ ce.clear();
+ ce.compute_deps(output_sig, input_sig.to_sigbit_pool());
+ RTLIL::Const lut_mask(RTLIL::State::Sx, 1 << input_sig.size());
+ for (int j = 0; j < (1 << cutLeavesM); ++j) {
+ int gray = j ^ (j >> 1);
+ ce.set_incremental(input_sig, RTLIL::Const{gray, static_cast<int>(cutLeavesM)});
+ RTLIL::SigBit o(output_sig);
+ bool success = ce.eval(o);
+ log_assert(success);
+ log_assert(o.wire == nullptr);
+ lut_mask[gray] = o.data;
+ }
+ RTLIL::Cell *output_cell = module->cell(stringf("\\__%d__$and", rootNodeID));
+ log_assert(output_cell);
+ module->remove(output_cell);
+ module->addLut(stringf("\\__%d__$lut", rootNodeID), input_sig, output_sig, std::move(lut_mask));
+ }
+ }
+ else if (c == 'r') {
+ uint32_t dataSize = parse_xaiger_literal(f);
+ flopNum = parse_xaiger_literal(f);
+ log_assert(dataSize == (flopNum+1) * sizeof(uint32_t));
+ f.ignore(flopNum * sizeof(uint32_t));
+ }
+ else if (c == 'n') {
+ parse_xaiger_literal(f);
+ f >> s;
+ log_debug("n: '%s'\n", s.c_str());
+ }
+ else if (c == 'h') {
+ f.ignore(sizeof(uint32_t));
+ uint32_t version = parse_xaiger_literal(f);
+ log_assert(version == 1);
+ uint32_t ciNum = parse_xaiger_literal(f);
+ log_debug("ciNum = %u\n", ciNum);
+ uint32_t coNum = parse_xaiger_literal(f);
+ log_debug("coNum = %u\n", coNum);
+ piNum = parse_xaiger_literal(f);
+ log_debug("piNum = %u\n", piNum);
+ uint32_t poNum = parse_xaiger_literal(f);
+ log_debug("poNum = %u\n", poNum);
+ uint32_t boxNum = parse_xaiger_literal(f);
+ log_debug("boxNum = %u\n", poNum);
+ for (unsigned i = 0; i < boxNum; i++) {
+ f.ignore(2*sizeof(uint32_t));
+ uint32_t boxUniqueId = parse_xaiger_literal(f);
+ log_assert(boxUniqueId > 0);
+ uint32_t oldBoxNum = parse_xaiger_literal(f);
+ RTLIL::Cell* cell = module->addCell(stringf("$__box%u__", oldBoxNum), box_lookup.at(boxUniqueId));
+ boxes.emplace_back(cell);
+ }
+ }
+ else if (c == 'a' || c == 'i' || c == 'o') {
+ uint32_t dataSize = parse_xaiger_literal(f);
+ f.ignore(dataSize);
+ }
+ else {
+ break;
+ }
+ }
+ else
+ log_error("Line %u: cannot interpret first character '%c'!\n", line_count, c);
+ }
+
+ post_process();
+}
+
void AigerReader::parse_aiger_ascii()
{
std::string line;
@@ -173,7 +474,7 @@ void AigerReader::parse_aiger_ascii()
if (!(f >> l1))
log_error("Line %u cannot be interpreted as an input!\n", line_count);
log_debug("%d is an input\n", l1);
- log_assert(!(l1 & 1)); // TODO: Inputs can't be inverted?
+ log_assert(!(l1 & 1)); // Inputs can't be inverted
RTLIL::Wire *wire = createWireIfNotExists(module, l1);
wire->port_input = true;
inputs.push_back(wire);
@@ -182,11 +483,13 @@ void AigerReader::parse_aiger_ascii()
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
if (L > 0) {
+ log_assert(clk_name != "");
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug("Creating %s\n", clk_name.c_str());
clk_wire = module->addWire(clk_name);
clk_wire->port_input = true;
+ clk_wire->port_output = false;
}
for (unsigned i = 0; i < L; ++i, ++line_count) {
if (!(f >> l1 >> l2))
@@ -208,7 +511,7 @@ void AigerReader::parse_aiger_ascii()
else if (l3 == 1)
q_wire->attributes["\\init"] = RTLIL::S1;
else if (l3 == l1) {
- //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
+ //q_wire->attributes["\\init"] = RTLIL::Sx;
}
else
log_error("Line %u has invalid reset literal for latch!\n", line_count);
@@ -226,7 +529,17 @@ void AigerReader::parse_aiger_ascii()
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);
+ 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?
+ RTLIL::Wire *wire = module->wire(wire_name);
+ if (!wire)
+ wire = createWireIfNotExists(module, l1);
+ else if (wire->port_input || wire->port_output) {
+ RTLIL::Wire *new_wire = module->addWire(NEW_ID);
+ module->connect(new_wire, wire);
+ wire = new_wire;
+ }
wire->port_output = true;
outputs.push_back(wire);
}
@@ -260,11 +573,11 @@ void AigerReader::parse_aiger_ascii()
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);
- module->addAndGate(NEW_ID, i1_wire, i2_wire, 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
}
@@ -285,19 +598,23 @@ 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);
}
// Parse latches
RTLIL::Wire *clk_wire = nullptr;
if (L > 0) {
+ log_assert(clk_name != "");
clk_wire = module->wire(clk_name);
log_assert(!clk_wire);
log_debug("Creating %s\n", clk_name.c_str());
clk_wire = module->addWire(clk_name);
clk_wire->port_input = true;
+ clk_wire->port_output = false;
}
l1 = (I+1) * 2;
for (unsigned i = 0; i < L; ++i, ++line_count, l1 += 2) {
@@ -319,7 +636,7 @@ void AigerReader::parse_aiger_binary()
else if (l3 == 1)
q_wire->attributes["\\init"] = RTLIL::S1;
else if (l3 == l1) {
- //q_wire->attributes["\\init"] = RTLIL::Const(RTLIL::State::Sx);
+ //q_wire->attributes["\\init"] = RTLIL::Sx;
}
else
log_error("Line %u has invalid reset literal for latch!\n", line_count);
@@ -337,7 +654,17 @@ void AigerReader::parse_aiger_binary()
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);
+ 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?
+ RTLIL::Wire *wire = module->wire(wire_name);
+ if (!wire)
+ wire = createWireIfNotExists(module, l1);
+ else if (wire->port_input || wire->port_output) {
+ RTLIL::Wire *new_wire = module->addWire(NEW_ID);
+ module->connect(new_wire, wire);
+ wire = new_wire;
+ }
wire->port_output = true;
outputs.push_back(wire);
}
@@ -375,15 +702,283 @@ 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);
+ module->addAndGate(o_wire->name.str() + "$and", i1_wire, i2_wire, o_wire);
+ }
+}
+
+void AigerReader::post_process()
+{
+ pool<RTLIL::Module*> abc_carry_modules;
+ unsigned ci_count = 0, co_count = 0, flop_count = 0;
+ for (auto cell : boxes) {
+ RTLIL::Module* box_module = design->module(cell->type);
+ log_assert(box_module);
+
+ if (box_module->attributes.count("\\abc_carry") && !abc_carry_modules.count(box_module)) {
+ RTLIL::Wire* carry_in = nullptr, *carry_out = nullptr;
+ RTLIL::Wire* last_in = nullptr, *last_out = nullptr;
+ for (const auto &port_name : box_module->ports) {
+ RTLIL::Wire* w = box_module->wire(port_name);
+ log_assert(w);
+ if (w->port_input) {
+ if (w->attributes.count("\\abc_carry_in")) {
+ log_assert(!carry_in);
+ carry_in = w;
+ }
+ log_assert(!last_in || last_in->port_id < w->port_id);
+ last_in = w;
+ }
+ if (w->port_output) {
+ if (w->attributes.count("\\abc_carry_out")) {
+ log_assert(!carry_out);
+ carry_out = w;
+ }
+ log_assert(!last_out || last_out->port_id < w->port_id);
+ last_out = w;
+ }
+ }
+
+ if (carry_in != last_in) {
+ std::swap(box_module->ports[carry_in->port_id], box_module->ports[last_in->port_id]);
+ std::swap(carry_in->port_id, last_in->port_id);
+ }
+ if (carry_out != last_out) {
+ log_assert(last_out);
+ std::swap(box_module->ports[carry_out->port_id], box_module->ports[last_out->port_id]);
+ std::swap(carry_out->port_id, last_out->port_id);
+ }
+ }
+
+ bool flop = box_module->attributes.count("\\abc_flop");
+ log_assert(!flop || flop_count < flopNum);
+
+ // NB: Assume box_module->ports are sorted alphabetically
+ // (as RTLIL::Module::fixup_ports() would do)
+ for (auto port_name : box_module->ports) {
+ RTLIL::Wire* w = box_module->wire(port_name);
+ log_assert(w);
+ RTLIL::SigSpec rhs;
+ RTLIL::Wire* wire = nullptr;
+ for (int i = 0; i < GetSize(w); i++) {
+ if (w->port_input) {
+ log_assert(co_count < outputs.size());
+ wire = outputs[co_count++];
+ log_assert(wire);
+ log_assert(wire->port_output);
+ wire->port_output = false;
- 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);
+ if (flop && w->attributes.count("\\abc_flop_d")) {
+ RTLIL::Wire* d = outputs[outputs.size() - flopNum + flop_count];
+ log_assert(d);
+ log_assert(d->port_output);
+ d->port_output = false;
+ }
+ }
+ if (w->port_output) {
+ log_assert((piNum + ci_count) < inputs.size());
+ wire = inputs[piNum + ci_count++];
+ log_assert(wire);
+ log_assert(wire->port_input);
+ wire->port_input = false;
+
+ if (flop && w->attributes.count("\\abc_flop_q")) {
+ wire = inputs[piNum - flopNum + flop_count];
+ log_assert(wire);
+ log_assert(wire->port_input);
+ wire->port_input = false;
+ }
+ }
+ rhs.append(wire);
+ }
+ cell->setPort(port_name, rhs);
+ }
+
+ if (flop) flop_count++;
+ }
+
+ 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_s = 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) {
+ // Cope with the fact that a CI might be identical
+ // to a PI (necessary due to ABC); in those cases
+ // simply connect the latter to the former
+ RTLIL::Wire* existing = module->wire(escaped_s);
+ if (!existing)
+ module->rename(wire, escaped_s);
+ else {
+ wire->port_input = false;
+ module->connect(wire, existing);
+ }
+ }
+ else if (index > 0) {
+ std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
+ RTLIL::Wire* existing = module->wire(indexed_name);
+ if (!existing) {
+ module->rename(wire, indexed_name);
+ if (wideports)
+ wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
+ }
+ else {
+ module->connect(wire, existing);
+ wire->port_input = false;
+ }
+ }
+ }
+ else if (type == "output") {
+ log_assert(static_cast<unsigned>(variable + co_count) < outputs.size());
+ RTLIL::Wire* wire = outputs[variable + co_count];
+ log_assert(wire);
+ log_assert(wire->port_output);
+
+ if (index == 0) {
+ // Cope with the fact that a CO might be identical
+ // to a PO (necessary due to ABC); in those cases
+ // simply connect the latter to the former
+ RTLIL::Wire* existing = module->wire(escaped_s);
+ if (!existing) {
+ if (escaped_s.ends_with("$inout.out")) {
+ wire->port_output = false;
+ RTLIL::Wire *in_wire = module->wire(escaped_s.substr(0, escaped_s.size()-10));
+ log_assert(in_wire);
+ log_assert(in_wire->port_input && !in_wire->port_output);
+ in_wire->port_output = true;
+ module->connect(in_wire, wire);
+ }
+ else
+ module->rename(wire, escaped_s);
+ }
+ else {
+ wire->port_output = false;
+ module->connect(wire, existing);
+ }
+ }
+ else if (index > 0) {
+ std::string indexed_name = stringf("%s[%d]", escaped_s.c_str(), index);
+ RTLIL::Wire* existing = module->wire(indexed_name);
+ if (!existing) {
+ if (escaped_s.ends_with("$inout.out")) {
+ wire->port_output = false;
+ RTLIL::Wire *in_wire = module->wire(stringf("%s[%d]", escaped_s.substr(0, escaped_s.size()-10).c_str(), index));
+ log_assert(in_wire);
+ log_assert(in_wire->port_input && !in_wire->port_output);
+ in_wire->port_output = true;
+ module->connect(in_wire, wire);
+ }
+ else {
+ module->rename(wire, indexed_name);
+ if (wideports)
+ wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
+ }
+ }
+ else {
+ module->connect(wire, existing);
+ wire->port_output = false;
+ }
+ }
+ }
+ else if (type == "box") {
+ RTLIL::Cell* cell = module->cell(stringf("$__box%d__", variable));
+ if (cell) { // ABC could have optimised this box away
+ module->rename(cell, escaped_s);
+ RTLIL::Module* box_module = design->module(cell->type);
+ log_assert(box_module);
+
+ for (const auto &i : cell->connections()) {
+ RTLIL::IdString port_name = i.first;
+ RTLIL::SigSpec rhs = i.second;
+ int index = 0;
+ for (auto bit : rhs.bits()) {
+ RTLIL::Wire* wire = bit.wire;
+ RTLIL::IdString escaped_s = RTLIL::escape_id(stringf("%s.%s", log_id(cell), log_id(port_name)));
+ if (index == 0)
+ module->rename(wire, escaped_s);
+ else if (index > 0) {
+ module->rename(wire, stringf("%s[%d]", escaped_s.c_str(), index));
+ if (wideports)
+ wideports_cache[escaped_s] = std::max(wideports_cache[escaped_s], index);
+ }
+ index++;
+ }
+ }
+ }
+ }
+ else
+ 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)
+ module->rename(wire, RTLIL::escape_id(stringf("%s[%d]", name.c_str(), 0)));
+
+ // 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;
+ }
+ }
+
+ 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);
+
+ design->selection_stack.emplace_back(false);
+ RTLIL::Selection& sel = design->selection_stack.back();
+ sel.select(module);
+
+ Pass::call(design, "clean");
+
+ design->selection_stack.pop_back();
+
+ for (auto cell : module->cells().to_vector()) {
+ if (cell->type != "$lut") continue;
+ auto y_port = cell->getPort("\\Y").as_bit();
+ if (y_port.wire->width == 1)
+ module->rename(cell, stringf("%s$lut", y_port.wire->name.c_str()));
+ else
+ module->rename(cell, stringf("%s[%d]$lut", y_port.wire->name.c_str(), y_port.offset));
}
}
@@ -398,18 +993,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: "
-#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
{
@@ -417,6 +1013,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++) {
@@ -429,13 +1027,23 @@ 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
char* bn = strdup(filename.c_str());
module_name = RTLIL::escape_id(bn);
@@ -443,7 +1051,7 @@ struct AigerFrontend : public Frontend {
#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 0e3719cc4..de3c3efbc 100644
--- a/frontends/aiger/aigerparse.h
+++ b/frontends/aiger/aigerparse.h
@@ -2,7 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- * Eddie Hung <eddie@fpgeh.com>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -31,20 +31,26 @@ 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
unsigned line_count;
+ uint32_t piNum, flopNum;
std::vector<RTLIL::Wire*> inputs;
std::vector<RTLIL::Wire*> latches;
std::vector<RTLIL::Wire*> outputs;
std::vector<RTLIL::Wire*> bad_properties;
+ std::vector<RTLIL::Cell*> boxes;
- 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();
+ void post_process();
};
YOSYS_NAMESPACE_END
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 8509670ff..d3ad57d72 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -276,6 +276,12 @@ namespace RTLIL
return std::string(c_str() + pos, len);
}
+ bool ends_with(const char* suffix) const {
+ size_t len = strlen(suffix);
+ if (size() < len) return false;
+ return substr(size()-len) == suffix;
+ }
+
size_t size() const {
return str().size();
}
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 5b19d84fb..15e79f9d1 100644
--- a/passes/techmap/abc.cc
+++ b/passes/techmap/abc.cc
@@ -1453,7 +1453,7 @@ struct AbcPass : public Pass {
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("design as BLIF file with write_blif and then load that into ABC externally if\n");
log("you want to use ABC to convert your design into another format.\n");
log("\n");
log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc
new file mode 100644
index 000000000..99083c20a
--- /dev/null
+++ b/passes/techmap/abc9.cc
@@ -0,0 +1,1173 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// [[CITE]] ABC
+// Berkeley Logic Synthesis and Verification Group, ABC: A System for Sequential Synthesis and Verification
+// http://www.eecs.berkeley.edu/~alanmi/abc/
+
+#if 0
+// Based on &flow3 - better QoR but more experimental
+#define ABC_COMMAND_LUT "&st; &ps -l; "/*"&sweep -v;"*/" &scorr; " \
+ "&st; &if {W}; &save; &st; &syn2; &if {W}; &save; &load; "\
+ "&st; &if -g -K 6; &dch -f; &if {W}; &save; &load; "\
+ "&st; &if -g -K 6; &synch2; &if {W}; &save; &load"
+#else
+#define ABC_COMMAND_LUT "&st; &sweep; &scorr; "/*"&dc2; "*/"&retime; &dch -f; &ps -l; &if {W} -v; "/*"&mfs; "*/"&ps -l"
+#endif
+
+
+#define ABC_FAST_COMMAND_LUT "&st; &retime; &if {W}"
+
+#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);
+ }
+};
+
+void abc9_module(RTLIL::Design *design, RTLIL::Module *current_module, std::string script_file, std::string exe_file,
+ bool cleanup, vector<int> lut_costs, bool dff_mode, std::string clk_str,
+ bool keepff, std::string delay_target, std::string lutin_shared, bool fast_mode,
+ const std::vector<RTLIL::Cell*> &cells, bool show_tempdir, bool sop_mode, std::string box_file, std::string lut_file,
+ std::string wire_delay)
+{
+ 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;
+
+ if (!lut_costs.empty()) {
+ abc_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str());
+ if (!box_file.empty())
+ abc_script += stringf("read_box -v %s; ", box_file.c_str());
+ }
+ else
+ if (!lut_file.empty()) {
+ abc_script += stringf("read_lut %s; ", lut_file.c_str());
+ if (!box_file.empty())
+ abc_script += stringf("read_box -v %s; ", box_file.c_str());
+ }
+ else
+ log_abort();
+
+ abc_script += stringf("&read %s/input.xaig; &ps; ", tempdir_name.c_str());
+
+ if (!script_file.empty()) {
+ if (script_file[0] == '+') {
+ for (size_t i = 1; i < script_file.size(); i++)
+ if (script_file[i] == '\'')
+ 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() || !lut_file.empty()) {
+ //bool all_luts_cost_same = true;
+ //for (int this_cost : lut_costs)
+ // if (this_cost != lut_costs.front())
+ // all_luts_cost_same = false;
+ abc_script += fast_mode ? ABC_FAST_COMMAND_LUT : ABC_COMMAND_LUT;
+ //if (all_luts_cost_same && !fast_mode)
+ // abc_script += "; lutpack {S}";
+ } else
+ log_abort();
+
+ 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("{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);
+
+ for (size_t pos = abc_script.find("{W}"); pos != std::string::npos; pos = abc_script.find("{W}", pos))
+ abc_script = abc_script.substr(0, pos) + wire_delay + abc_script.substr(pos+3);
+
+ abc_script += stringf("; &write %s/output.aig", tempdir_name.c_str());
+ abc_script = add_echos_to_abc_cmd(abc_script);
+
+ for (size_t i = 0; i+1 < abc_script.size(); i++)
+ 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");
+ }
+ }
+
+ bool count_output = false;
+ for (auto port_name : module->ports) {
+ RTLIL::Wire *port_wire = module->wire(port_name);
+ log_assert(port_wire);
+ if (port_wire->port_output) {
+ count_output = true;
+ break;
+ }
+ }
+
+ log_push();
+
+ if (count_output)
+ {
+ design->selection_stack.emplace_back(false);
+ RTLIL::Selection& sel = design->selection_stack.back();
+ sel.select(module);
+
+ // Behave as for "abc" where BLIF writer implicitly outputs all undef as zero
+ Pass::call(design, "setundef -zero");
+
+ Pass::call(design, "aigmap");
+
+ handle_loops(design);
+
+ //log("Extracted %d gates and %d wires to a netlist network with %d inputs and %d outputs.\n",
+ // count_gates, GetSize(signal_list), count_input, count_output);
+
+ Pass::call(design, stringf("write_xaiger -map %s/input.sym %s/input.xaig", tempdir_name.c_str(), tempdir_name.c_str()));
+
+ std::string buffer;
+ std::ifstream ifs;
+#if 0
+ buffer = stringf("%s/%s", tempdir_name.c_str(), "input.xaig");
+ ifs.open(buffer);
+ if (ifs.fail())
+ log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
+ buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym");
+ log_assert(!design->module("$__abc9__"));
+ {
+ AigerReader reader(design, ifs, "$__abc9__", "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */);
+ reader.parse_xaiger();
+ }
+ ifs.close();
+ Pass::call(design, stringf("write_verilog -noexpr -norename %s/%s", tempdir_name.c_str(), "input.v"));
+ design->remove(design->module("$__abc9__"));
+#endif
+
+ 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_header(design, "Executing ABC9.\n");
+
+ if (!lut_costs.empty()) {
+ buffer = stringf("%s/lutdefs.txt", tempdir_name.c_str());
+ f = fopen(buffer.c_str(), "wt");
+ if (f == NULL)
+ log_error("Opening %s for writing failed: %s\n", buffer.c_str(), strerror(errno));
+ for (int i = 0; i < GetSize(lut_costs); i++)
+ fprintf(f, "%d %d.00 1.00\n", i+1, lut_costs.at(i));
+ fclose(f);
+ }
+
+ buffer = stringf("%s -s -f %s/abc.script 2>&1", exe_file.c_str(), tempdir_name.c_str());
+ log("Running ABC command: %s\n", replace_tempdir(buffer, tempdir_name, show_tempdir).c_str());
+
+#ifndef YOSYS_LINK_ABC
+ 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.aig");
+ ifs.open(buffer);
+ if (ifs.fail())
+ log_error("Can't open ABC output file `%s'.\n", buffer.c_str());
+
+ buffer = stringf("%s/%s", tempdir_name.c_str(), "input.sym");
+ log_assert(!design->module("$__abc9__"));
+ AigerReader reader(design, ifs, "$__abc9__", "" /* clk_name */, buffer.c_str() /* map_filename */, true /* wideports */);
+ reader.parse_xaiger();
+ ifs.close();
+
+#if 0
+ Pass::call(design, stringf("write_verilog -noexpr -norename %s/%s", tempdir_name.c_str(), "output.v"));
+#endif
+
+ log_header(design, "Re-integrating ABC9 results.\n");
+ RTLIL::Module *mapped_mod = design->module("$__abc9__");
+ if (mapped_mod == NULL)
+ log_error("ABC output file does not contain a module `$__abc9__'.\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;
+ if (w->port_output) {
+ RTLIL::Wire *wire = module->wire(w->name);
+ log_assert(wire);
+ for (int i = 0; i < GetSize(wire); i++)
+ output_bits.insert({wire, i});
+ }
+ }
+
+ // Remove all AND, NOT, and ABC box instances
+ // in preparation for stitching mapped_mod in
+ dict<IdString, decltype(RTLIL::Cell::parameters)> erased_boxes;
+ for (auto it = module->cells_.begin(); it != module->cells_.end(); ) {
+ RTLIL::Cell* cell = it->second;
+ if (cell->type.in("$_AND_", "$_NOT_")) {
+ it = module->cells_.erase(it);
+ continue;
+ }
+ RTLIL::Module* box_module = design->module(cell->type);
+ if (box_module && box_module->attributes.count("\\abc_box_id")) {
+ erased_boxes.insert(std::make_pair(it->first, std::move(cell->parameters)));
+ it = module->cells_.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ // 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);
+ }
+
+ std::map<std::string, int> cell_stats;
+ for (auto c : mapped_mod->cells())
+ {
+ 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 (!a_bit.wire) {
+ c->setPort("\\Y", module->addWire(NEW_ID));
+ RTLIL::Wire *wire = module->wire(remap_name(y_bit.wire->name));
+ log_assert(wire);
+ module->connect(RTLIL::SigBit(wire, y_bit.offset), RTLIL::S1);
+ }
+ else if (!lut_costs.empty() || !lut_file.empty()) {
+ RTLIL::Cell* driving_lut = nullptr;
+ // ABC can return NOT gates that drive POs
+ if (!a_bit.wire->port_input) {
+ // If it's not a NOT gate that that comes from a PI directly,
+ // find the driving LUT and clone that to guarantee that we won't
+ // increase the max logic depth
+ // (TODO: Optimise by not cloning unless will increase depth)
+ RTLIL::IdString driver_name;
+ if (GetSize(a_bit.wire) == 1)
+ driver_name = stringf("%s$lut", a_bit.wire->name.c_str());
+ else
+ driver_name = stringf("%s[%d]$lut", a_bit.wire->name.c_str(), a_bit.offset);
+ driving_lut = mapped_mod->cell(driver_name);
+ }
+
+ if (!driving_lut) {
+ // If a driver couldn't be found (could be from PI,
+ // or from a box) then implement 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 {
+ auto driver_a = driving_lut->getPort("\\A").chunks();
+ for (auto &chunk : driver_a)
+ chunk.wire = module->wires_[remap_name(chunk.wire->name)];
+ RTLIL::Const driver_lut = driving_lut->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);
+ }
+ }
+ else {
+ cell = module->addCell(remap_name(c->name), "$_NOT_");
+ cell->setPort("\\A", RTLIL::SigBit(module->wires_[remap_name(a_bit.wire->name)], a_bit.offset));
+ cell->setPort("\\Y", RTLIL::SigBit(module->wires_[remap_name(y_bit.wire->name)], y_bit.offset));
+ cell_stats[RTLIL::unescape_id(c->type)]++;
+ }
+ if (markgroups) cell->attributes["\\abcgroup"] = map_autoidx;
+ continue;
+ }
+ cell_stats[RTLIL::unescape_id(c->type)]++;
+
+ if (c->type == "$lut") {
+ if (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;
+ }
+ }
+ else {
+ auto it = erased_boxes.find(c->name);
+ log_assert(it != erased_boxes.end());
+ c->parameters = std::move(it->second);
+ }
+
+ 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);
+ if (c.wire)
+ c.wire = module->wires_[remap_name(c.wire->name)];
+ newsig.append(c);
+ }
+ cell->setPort(conn.first, newsig);
+ }
+ }
+
+ // 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;
+
+ // 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);
+ log_assert(wire);
+ RTLIL::Wire *remap_wire = module->wire(remap_name(w->name));
+ RTLIL::SigSpec signal = RTLIL::SigSpec(wire, 0, GetSize(remap_wire));
+ log_assert(GetSize(signal) >= GetSize(remap_wire));
+
+ log_assert(w->port_input || w->port_output);
+ RTLIL::SigSig conn;
+ if (w->port_input) {
+ conn.first = remap_wire;
+ conn.second = signal;
+ in_wires++;
+ module->connect(conn);
+ }
+ if (w->port_output) {
+ conn.first = signal;
+ conn.second = remap_wire;
+ out_wires++;
+ module->connect(conn);
+ }
+ }
+
+ //log("ABC RESULTS: internal signals: %8d\n", int(signal_list.size()) - in_wires - out_wires);
+ log("ABC RESULTS: input signals: %8d\n", in_wires);
+ log("ABC RESULTS: output signals: %8d\n", out_wires);
+
+ design->remove(mapped_mod);
+ }
+ else
+ {
+ log("Don't call ABC as there is nothing to map.\n");
+ }
+
+ if (cleanup)
+ {
+ log("Removing temp directory.\n");
+ remove_directory(tempdir_name);
+ }
+
+ log_pop();
+}
+
+struct Abc9Pass : public Pass {
+ Abc9Pass() : Pass("abc9", "use ABC9 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 -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(" -fast\n");
+ log(" use different default scripts that are slightly faster (at the cost\n");
+ log(" of output quality):\n");
+ log("\n");
+ log(" for -lut/-luts:\n");
+ log("%s\n", fold_abc_cmd(ABC_FAST_COMMAND_LUT).c_str());
+ log("\n");
+// log(" -D <picoseconds>\n");
+// log(" set delay target. the string {D} in the default scripts above is\n");
+// log(" replaced by this option when used, and an empty string otherwise.\n");
+// log(" this also replaces 'dretime' with 'dretime; retime -o {D}' in the\n");
+// log(" default scripts above.\n");
+// log("\n");
+// log(" -S <num>\n");
+// log(" maximum number of LUT inputs shared.\n");
+// log(" (replaces {S} in the default scripts above, default: -S 1)\n");
+// log("\n");
+ log(" -lut <width>\n");
+ log(" generate netlist using luts of (max) the specified width.\n");
+ log("\n");
+ log(" -lut <w1>:<w2>\n");
+ log(" generate netlist using luts of (max) the specified width <w2>. All\n");
+ log(" luts with width <= <w1> have constant cost. for luts larger than <w1>\n");
+ log(" the area cost doubles with each additional input bit. the delay cost\n");
+ log(" is still constant for all lut widths.\n");
+ log("\n");
+ log(" -lut <file>\n");
+ log(" pass this file with lut library to ABC.\n");
+ log("\n");
+ log(" -luts <cost1>,<cost2>,<cost3>,<sizeN>:<cost4-N>,..\n");
+ log(" generate netlist using luts. Use the specified costs for luts with 1,\n");
+ log(" 2, 3, .. inputs.\n");
+ log("\n");
+// log(" -dff\n");
+// log(" also pass $_DFF_?_ and $_DFFE_??_ cells through ABC. modules with many\n");
+// log(" clock domains are automatically partitioned in clock domains and each\n");
+// log(" domain is passed through ABC independently.\n");
+// log("\n");
+// log(" -clk [!]<clock-signal-name>[,[!]<enable-signal-name>]\n");
+// log(" use only the specified clock domain. this is like -dff, but only FF\n");
+// log(" cells that belong to the specified clock domain are used.\n");
+// log("\n");
+// log(" -keepff\n");
+// log(" set the \"keep\" attribute on flip-flop output wires. (and thus preserve\n");
+// log(" them, for example for equivalence checking.)\n");
+// log("\n");
+ log(" -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(" -box <file>\n");
+ log(" pass this file with box library to ABC. Use with -lut.\n");
+ log("\n");
+ log("Note that this is a logic optimization pass within Yosys that is calling ABC\n");
+ log("internally. This is not going to \"run ABC on your design\". It will instead run\n");
+ log("ABC on logic snippets extracted from your design. You will not get any useful\n");
+ log("output when passing an ABC script that writes a file. Instead write your full\n");
+ log("design as BLIF file with write_blif and then load that into ABC externally if\n");
+ log("you want to use ABC to convert your design into another format.\n");
+ log("\n");
+ log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing ABC9 pass (technology mapping using ABC9).\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, clk_str, box_file, lut_file;
+ std::string delay_target, lutin_shared = "-S 1", wire_delay;
+ bool fast_mode = false, dff_mode = false, keepff = false, cleanup = true;
+ bool show_tempdir = false, sop_mode = false;
+ vector<int> lut_costs;
+ markgroups = false;
+
+#if 0
+ cleanup = false;
+ show_tempdir = true;
+#endif
+
+ 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 == "-D" && argidx+1 < args.size()) {
+ delay_target = "-D " + args[++argidx];
+ continue;
+ }
+ //if (arg == "-S" && argidx+1 < args.size()) {
+ // lutin_shared = "-S " + args[++argidx];
+ // continue;
+ //}
+ if (arg == "-lut" && argidx+1 < args.size()) {
+ string arg = args[++argidx];
+ 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 {
+ pos = arg.find_first_of('.');
+ if (pos != string::npos) {
+ lut_file = arg;
+ rewrite_filename(lut_file);
+ if (!lut_file.empty() && !is_absolute_path(lut_file))
+ lut_file = std::string(pwd) + "/" + lut_file;
+ }
+ 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 == "-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;
+ }
+ if (arg == "-box" && argidx+1 < args.size()) {
+ box_file = args[++argidx];
+ rewrite_filename(box_file);
+ if (!box_file.empty() && !is_absolute_path(box_file))
+ box_file = std::string(pwd) + "/" + box_file;
+ continue;
+ }
+ if (arg == "-W" && argidx+1 < args.size()) {
+ wire_delay = "-W " + args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ for (auto mod : design->selected_modules())
+ {
+ if (mod->attributes.count("\\abc_box_id"))
+ continue;
+
+ 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, cleanup, lut_costs, dff_mode, clk_str, keepff,
+ delay_target, lutin_shared, fast_mode, mod->selected_cells(), show_tempdir, sop_mode,
+ box_file, lut_file, wire_delay);
+ 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, cleanup, lut_costs, !clk_sig.empty(), "$",
+ keepff, delay_target, lutin_shared, fast_mode, it.second, show_tempdir, sop_mode,
+ box_file, lut_file, wire_delay);
+ assign_map.set(mod);
+ }
+ }
+
+ Pass::call(design, "clean");
+
+ 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 e41c0fe97..555de9fba 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 new ABC9 flow (EXPERIMENTAL)\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);
@@ -166,6 +174,9 @@ struct SynthPass : public ScriptPass
if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n");
+ if (abc == "abc9" && !lut)
+ log_cmd_error("ABC9 flow only supported for FPGA synthesis (using '-lut' option)");
+
log_header(design, "Executing SYNTH pass.\n");
log_push();
@@ -241,15 +252,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/ecp5/Makefile.inc b/techlibs/ecp5/Makefile.inc
index 4db087e87..eee3b418f 100644
--- a/techlibs/ecp5/Makefile.inc
+++ b/techlibs/ecp5/Makefile.inc
@@ -11,6 +11,9 @@ $(eval $(call add_share_file,share/ecp5,techlibs/ecp5/bram.txt))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/arith_map.v))
$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/latches_map.v))
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.box))
+$(eval $(call add_share_file,share/ecp5,techlibs/ecp5/abc_5g.lut))
+
EXTRA_OBJS += techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
.SECONDARY: techlibs/ecp5/brams_init.mk techlibs/ecp5/brams_connect.mk
diff --git a/techlibs/ecp5/abc_5g.box b/techlibs/ecp5/abc_5g.box
new file mode 100644
index 000000000..72af6d9cb
--- /dev/null
+++ b/techlibs/ecp5/abc_5g.box
@@ -0,0 +1,36 @@
+# Box 1 : CCU2C (2xCARRY + 2xLUT4)
+# Outputs: S0, S1, COUT
+# name ID w/b ins outs
+CCU2C 1 1 9 3
+
+#A0 A1 B0 B1 C0 C1 D0 D1 CIN
+379 - 379 - 275 - 141 - 257
+630 379 630 379 526 275 392 141 273
+516 516 516 516 412 412 278 278 43
+
+# Box 2 : TRELLIS_DPR16X4 (16x4 dist ram)
+# Outputs: DO0, DO1, DO2, DO3, DO4
+# name ID w/b ins outs
+TRELLIS_DPR16X4 2 0 14 4
+
+#DI0 DI1 DI2 DI3 RAD0 RAD1 RAD2 RAD3 WAD0 WAD1 WAD2 WAD3 WCK WRE
+- - - - 141 379 275 379 - - - - - -
+- - - - 141 379 275 379 - - - - - -
+- - - - 141 379 275 379 - - - - - -
+- - - - 141 379 275 379 - - - - - -
+
+# Box 3 : PFUMX (MUX2)
+# Outputs: Z
+# name ID w/b ins outs
+PFUMX 3 1 3 1
+
+#ALUT BLUT C0
+98 98 151
+
+# Box 4 : L6MUX21 (MUX2)
+# Outputs: Z
+# name ID w/b ins outs
+L6MUX21 4 1 3 1
+
+#D0 D1 SD
+140 141 148
diff --git a/techlibs/ecp5/abc_5g.lut b/techlibs/ecp5/abc_5g.lut
new file mode 100644
index 000000000..e8aa9b35d
--- /dev/null
+++ b/techlibs/ecp5/abc_5g.lut
@@ -0,0 +1,25 @@
+# ECP5-5G LUT library for ABC
+# Note that ECP5 architecture assigns difference
+# in LUT input delay to interconnect, so this is
+# considered too
+
+
+# Simple LUTs
+# area D C B A
+1 1 141
+2 1 141 275
+3 1 141 275 379
+4 1 141 275 379 379
+
+# LUT5 = 2x LUT4 + PFUMX
+# area M0 D C B A
+5 2 151 239 373 477 477
+
+# LUT6 = 2x LUT5 + MUX2
+# area M1 M0 D C B A
+6 4 148 292 380 514 618 618
+
+# LUT7 = 2x LUT6 + MUX2
+# area M2 M1 M0 D C B A
+7 8 148 289 433 521 655 759 759
+
diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v
index f6c71a03d..53a89e8a3 100644
--- a/techlibs/ecp5/cells_map.v
+++ b/techlibs/ecp5/cells_map.v
@@ -70,80 +70,107 @@ module \$lut (A, Y);
parameter WIDTH = 0;
parameter LUT = 0;
+
+ // Need to swap input ordering, and fix init accordingly,
+ // to match ABC's expectation of LUT inputs in non-decreasing
+ // delay order
+ localparam P_WIDTH = WIDTH < 4 ? 4 : WIDTH;
+ function [P_WIDTH-1:0] permute_index;
+ input [P_WIDTH-1:0] i;
+ integer j;
+ begin
+ permute_index = 0;
+ for (j = 0; j < P_WIDTH; j = j + 1)
+ permute_index[P_WIDTH-1 - j] = i[j];
+ end
+ endfunction
+
+ function [2**P_WIDTH-1:0] permute_init;
+ input [2**P_WIDTH-1:0] orig;
+ integer i;
+ begin
+ permute_init = 0;
+ for (i = 0; i < 2**P_WIDTH; i = i + 1)
+ permute_init[i] = orig[permute_index(i)];
+ end
+ endfunction
+
+ parameter [2**P_WIDTH-1:0] P_LUT = permute_init(LUT);
+
input [WIDTH-1:0] A;
output Y;
generate
if (WIDTH == 1) begin
- LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(A[0]), .B(1'b0), .C(1'b0), .D(1'b0));
+ LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(1'b0), .B(1'b0), .C(1'b0), .D(A[0]));
end else
if (WIDTH == 2) begin
- LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(A[0]), .B(A[1]), .C(1'b0), .D(1'b0));
+ LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(1'b0), .B(1'b0), .C(A[1]), .D(A[0]));
end else
if (WIDTH == 3) begin
- LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(1'b0));
+ LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(1'b0), .B(A[2]), .C(A[1]), .D(A[0]));
end else
if (WIDTH == 4) begin
- LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
+ LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y),
+ .A(A[3]), .B(A[2]), .C(A[1]), .D(A[0]));
`ifndef NO_PFUMUX
end else
if (WIDTH == 5) begin
wire f0, f1;
- LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y));
+ LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
+ .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
+ LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
+ .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1]));
+ PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y));
end else
if (WIDTH == 6) begin
wire f0, f1, f2, f3, g0, g1;
- LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
-
- LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
-
- PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
- PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
- L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y));
+ LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
+ .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
+ LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
+ .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
+
+ LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
+ .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
+ LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
+ .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2]));
+
+ PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0));
+ PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1));
+ L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y));
end else
if (WIDTH == 7) begin
wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1;
- LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
-
- LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
-
- LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
-
- LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
- LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7),
- .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
-
- PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0));
- PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1));
- PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2));
- PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3));
- L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0));
- L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1));
- L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y));
+ LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+ LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+
+ LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+ LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+
+ LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+ LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+
+ LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+ LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7),
+ .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3]));
+
+ PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0));
+ PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1));
+ PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2));
+ PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3));
+ L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0));
+ L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1));
+ L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y));
`endif
end else begin
wire _TECHMAP_FAIL_ = 1;
diff --git a/techlibs/ecp5/cells_sim.v b/techlibs/ecp5/cells_sim.v
index 1e4002ee0..430aadbd7 100644
--- a/techlibs/ecp5/cells_sim.v
+++ b/techlibs/ecp5/cells_sim.v
@@ -9,15 +9,17 @@ module LUT4(input A, B, C, D, output Z);
endmodule
// ---------------------------------------
-
+(* abc_box_id=4, lib_whitebox *)
module L6MUX21 (input D0, D1, SD, output Z);
assign Z = SD ? D1 : D0;
endmodule
// ---------------------------------------
-
-module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1,
- output S0, S1, COUT);
+(* abc_box_id=1, abc_carry, lib_whitebox *)
+module CCU2C((* abc_carry_in *) input CIN,
+ input A0, B0, C0, D0, A1, B1, C1, D1,
+ output S0, S1,
+ (* abc_carry_out *) output COUT);
parameter [15:0] INIT0 = 16'h0000;
parameter [15:0] INIT1 = 16'h0000;
@@ -26,9 +28,13 @@ module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1,
// First half
wire LUT4_0, LUT2_0;
+`ifdef _ABC
+ assign LUT4_0 = INIT0[{D0, C0, B0, A0}];
+ assign LUT2_0 = INIT0[{2'b00, B0, A0}];
+`else
LUT4 #(.INIT(INIT0)) lut4_0(.A(A0), .B(B0), .C(C0), .D(D0), .Z(LUT4_0));
LUT2 #(.INIT(INIT0[3:0])) lut2_0(.A(A0), .B(B0), .Z(LUT2_0));
-
+`endif
wire gated_cin_0 = (INJECT1_0 == "YES") ? 1'b0 : CIN;
assign S0 = LUT4_0 ^ gated_cin_0;
@@ -37,9 +43,13 @@ module CCU2C(input CIN, A0, B0, C0, D0, A1, B1, C1, D1,
// Second half
wire LUT4_1, LUT2_1;
+`ifdef _ABC
+ assign LUT4_1 = INIT1[{D1, C1, B1, A1}];
+ assign LUT2_1 = INIT1[{2'b00, B1, A1}];
+`else
LUT4 #(.INIT(INIT1)) lut4_1(.A(A1), .B(B1), .C(C1), .D(D1), .Z(LUT4_1));
LUT2 #(.INIT(INIT1[3:0])) lut2_1(.A(A1), .B(B1), .Z(LUT2_1));
-
+`endif
wire gated_cin_1 = (INJECT1_1 == "YES") ? 1'b0 : cout_0;
assign S1 = LUT4_1 ^ gated_cin_1;
@@ -90,13 +100,13 @@ module TRELLIS_RAM16X2 (
endmodule
// ---------------------------------------
-
+(* abc_box_id=3, lib_whitebox *)
module PFUMX (input ALUT, BLUT, C0, output Z);
assign Z = C0 ? ALUT : BLUT;
endmodule
// ---------------------------------------
-
+(* abc_box_id=2 *)
module TRELLIS_DPR16X4 (
input [3:0] DI,
input [3:0] WAD,
@@ -203,7 +213,7 @@ endmodule
// ---------------------------------------
-module TRELLIS_FF(input CLK, LSR, CE, DI, M, output reg Q);
+module TRELLIS_FF(input CLK, LSR, CE, DI, M, (* abc_flop_q *) output reg Q);
parameter GSR = "ENABLED";
parameter [127:0] CEMUX = "1";
parameter CLKMUX = "CLK";
@@ -464,13 +474,13 @@ module DP16KD(
input ADA13, ADA12, ADA11, ADA10, ADA9, ADA8, ADA7, ADA6, ADA5, ADA4, ADA3, ADA2, ADA1, ADA0,
input CEA, OCEA, CLKA, WEA, RSTA,
input CSA2, CSA1, CSA0,
- output DOA17, DOA16, DOA15, DOA14, DOA13, DOA12, DOA11, DOA10, DOA9, DOA8, DOA7, DOA6, DOA5, DOA4, DOA3, DOA2, DOA1, DOA0,
+ (* abc_flop_q *) output DOA17, DOA16, DOA15, DOA14, DOA13, DOA12, DOA11, DOA10, DOA9, DOA8, DOA7, DOA6, DOA5, DOA4, DOA3, DOA2, DOA1, DOA0,
input DIB17, DIB16, DIB15, DIB14, DIB13, DIB12, DIB11, DIB10, DIB9, DIB8, DIB7, DIB6, DIB5, DIB4, DIB3, DIB2, DIB1, DIB0,
input ADB13, ADB12, ADB11, ADB10, ADB9, ADB8, ADB7, ADB6, ADB5, ADB4, ADB3, ADB2, ADB1, ADB0,
input CEB, OCEB, CLKB, WEB, RSTB,
input CSB2, CSB1, CSB0,
- output DOB17, DOB16, DOB15, DOB14, DOB13, DOB12, DOB11, DOB10, DOB9, DOB8, DOB7, DOB6, DOB5, DOB4, DOB3, DOB2, DOB1, DOB0
+ (* abc_flop_q *) output DOB17, DOB16, DOB15, DOB14, DOB13, DOB12, DOB11, DOB10, DOB9, DOB8, DOB7, DOB6, DOB5, DOB4, DOB3, DOB2, DOB1, DOB0
);
parameter DATA_WIDTH_A = 18;
parameter DATA_WIDTH_B = 18;
diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc
index c6e12248e..b271500f1 100644
--- a/techlibs/ecp5/synth_ecp5.cc
+++ b/techlibs/ecp5/synth_ecp5.cc
@@ -82,6 +82,9 @@ struct SynthEcp5Pass : public ScriptPass
log(" -abc2\n");
log(" run two passes of 'abc' for slightly improved logic density\n");
log("\n");
+ log(" -abc9\n");
+ log(" use new ABC9 flow (EXPERIMENTAL)\n");
+ log("\n");
log(" -vpr\n");
log(" generate an output netlist (and BLIF file) suitable for VPR\n");
log(" (this feature is experimental and incomplete)\n");
@@ -93,7 +96,7 @@ struct SynthEcp5Pass : public ScriptPass
}
string top_opt, blif_file, edif_file, json_file;
- bool noccu2, nodffe, nobram, nodram, nomux, flatten, retime, abc2, vpr;
+ bool noccu2, nodffe, nobram, nodram, nomux, flatten, retime, abc2, abc9, vpr;
void clear_flags() YS_OVERRIDE
{
@@ -110,6 +113,7 @@ struct SynthEcp5Pass : public ScriptPass
retime = false;
abc2 = false;
vpr = false;
+ abc9 = false;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -184,6 +188,10 @@ struct SynthEcp5Pass : public ScriptPass
vpr = true;
continue;
}
+ if (args[argidx] == "-abc9") {
+ abc9 = true;
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -203,7 +211,7 @@ struct SynthEcp5Pass : public ScriptPass
{
if (check_label("begin"))
{
- run("read_verilog -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
+ run("read_verilog -D_ABC -lib +/ecp5/cells_sim.v +/ecp5/cells_bb.v");
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
}
@@ -264,10 +272,15 @@ struct SynthEcp5Pass : public ScriptPass
run("abc", " (only if -abc2)");
}
run("techmap -map +/ecp5/latches_map.v");
- if (nomux)
- run("abc -lut 4 -dress");
- else
- run("abc -lut 4:7 -dress");
+ if (abc9) {
+ run("abc9 -lut +/ecp5/abc_5g.lut -box +/ecp5/abc_5g.box -W 200");
+ } else {
+ if (nomux)
+ run("abc -lut 4 -dress");
+ else
+ run("abc -lut 4:7 -dress");
+ }
+
run("clean");
}
diff --git a/techlibs/ice40/Makefile.inc b/techlibs/ice40/Makefile.inc
index 723b59d6f..d258d5a5d 100644
--- a/techlibs/ice40/Makefile.inc
+++ b/techlibs/ice40/Makefile.inc
@@ -28,6 +28,12 @@ $(eval $(call add_share_file,share/ice40,techlibs/ice40/cells_sim.v))
$(eval $(call add_share_file,share/ice40,techlibs/ice40/latches_map.v))
$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams.txt))
$(eval $(call add_share_file,share/ice40,techlibs/ice40/brams_map.v))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.box))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_hx.lut))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.box))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_lp.lut))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.box))
+$(eval $(call add_share_file,share/ice40,techlibs/ice40/abc_u.lut))
$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init1.vh))
$(eval $(call add_gen_share_file,share/ice40,techlibs/ice40/brams_init2.vh))
diff --git a/techlibs/ice40/abc_hx.box b/techlibs/ice40/abc_hx.box
new file mode 100644
index 000000000..a0655643d
--- /dev/null
+++ b/techlibs/ice40/abc_hx.box
@@ -0,0 +1,113 @@
+# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
+
+# NB: Inputs/Outputs must be ordered alphabetically
+
+# Inputs: C D
+# Outputs: Q
+SB_DFF 1 0 2 1
+- -
+
+# Inputs: C D E
+# Outputs: Q
+SB_DFFE 2 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFSR 3 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFR 4 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFSS 5 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFS 6 0 3 1
+- - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFESR 7 0 4 1
+- - - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFER 8 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFESS 9 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFES 10 0 4 1
+- - - -
+
+# Inputs: C D
+# Outputs: Q
+SB_DFFN 11 0 2 1
+- -
+
+# Inputs: C D E
+# Outputs: Q
+SB_DFFNE 12 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFNSR 13 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFNR 14 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFNSS 15 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFNS 16 0 3 1
+- - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFNESR 17 0 4 1
+- - - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFNER 18 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFNESS 19 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFNES 20 0 4 1
+- - - -
+
+# Inputs: CI I0 I1
+# Outputs: CO
+SB_CARRY 21 1 3 1
+126 259 231
+
+# Inputs: I0 I1 I2 I3
+# Outputs: O
+SB_LUT4 22 1 4 1
+449 400 379 316
diff --git a/techlibs/ice40/abc_hx.lut b/techlibs/ice40/abc_hx.lut
new file mode 100644
index 000000000..3b3bb11e2
--- /dev/null
+++ b/techlibs/ice40/abc_hx.lut
@@ -0,0 +1,6 @@
+# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_hx8k.txt
+# I3 I2 I1 I0
+1 1 316
+2 1 316 379
+3 1 316 379 400
+4 1 316 379 400 449
diff --git a/techlibs/ice40/abc_lp.box b/techlibs/ice40/abc_lp.box
new file mode 100644
index 000000000..eb1cd0937
--- /dev/null
+++ b/techlibs/ice40/abc_lp.box
@@ -0,0 +1,113 @@
+# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt
+
+# NB: Inputs/Outputs must be ordered alphabetically
+
+# Inputs: C D
+# Outputs: Q
+SB_DFF 1 0 2 1
+- -
+
+# Inputs: C D E
+# Outputs: Q
+SB_DFFE 2 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFSR 3 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFR 4 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFSS 5 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFS 6 0 3 1
+- - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFESR 7 0 4 1
+- - - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFER 8 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFESS 9 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFES 10 0 4 1
+- - - -
+
+# Inputs: C D
+# Outputs: Q
+SB_DFFN 11 0 2 1
+- -
+
+# Inputs: C D E
+# Outputs: Q
+SB_DFFNE 12 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFNSR 13 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFNR 14 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFNSS 15 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFNS 16 0 3 1
+- - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFNESR 17 0 4 1
+- - - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFNER 18 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFNESS 19 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFNES 20 0 4 1
+- - - -
+
+# Inputs: CI I0 I1
+# Outputs: CO
+SB_CARRY 21 1 3 1
+186 675 609
+
+# Inputs: I0 I1 I2 I3
+# Outputs: O
+SB_LUT4 22 1 4 1
+661 589 558 465
diff --git a/techlibs/ice40/abc_lp.lut b/techlibs/ice40/abc_lp.lut
new file mode 100644
index 000000000..e72f760a2
--- /dev/null
+++ b/techlibs/ice40/abc_lp.lut
@@ -0,0 +1,6 @@
+# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_lp8k.txt
+# I3 I2 I1 I0
+1 1 465
+2 1 465 558
+3 1 465 558 589
+4 1 465 558 589 661
diff --git a/techlibs/ice40/abc_u.box b/techlibs/ice40/abc_u.box
new file mode 100644
index 000000000..3b5834e40
--- /dev/null
+++ b/techlibs/ice40/abc_u.box
@@ -0,0 +1,113 @@
+# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
+
+# NB: Inputs/Outputs must be ordered alphabetically
+
+# Inputs: C D
+# Outputs: Q
+SB_DFF 1 0 2 1
+- -
+
+# Inputs: C D E
+# Outputs: Q
+SB_DFFE 2 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFSR 3 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFR 4 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFSS 5 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFS 6 0 3 1
+- - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFESR 7 0 4 1
+- - - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFER 8 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFESS 9 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFES 10 0 4 1
+- - - -
+
+# Inputs: C D
+# Outputs: Q
+SB_DFFN 11 0 2 1
+- -
+
+# Inputs: C D E
+# Outputs: Q
+SB_DFFNE 12 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFNSR 13 0 3 1
+- - -
+
+# Inputs: C D R
+# Outputs: Q
+SB_DFFNR 14 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFNSS 15 0 3 1
+- - -
+
+# Inputs: C D S
+# Outputs: Q
+SB_DFFNS 16 0 3 1
+- - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFNESR 17 0 4 1
+- - - -
+
+# Inputs: C D E R
+# Outputs: Q
+SB_DFFNER 18 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFNESS 19 0 4 1
+- - - -
+
+# Inputs: C D E S
+# Outputs: Q
+SB_DFFNES 20 0 4 1
+- - - -
+
+# Inputs: CI I0 I1
+# Outputs: CO
+SB_CARRY 21 1 3 1
+278 675 609
+
+# Inputs: I0 I1 I2 I3
+# Outputs: O
+SB_LUT4 22 1 4 1
+1285 1231 1205 874
diff --git a/techlibs/ice40/abc_u.lut b/techlibs/ice40/abc_u.lut
new file mode 100644
index 000000000..1e4fcadb6
--- /dev/null
+++ b/techlibs/ice40/abc_u.lut
@@ -0,0 +1,6 @@
+# From https://github.com/cliffordwolf/icestorm/blob/be0bca0/icefuzz/timings_up5k.txt
+# I3 I2 I1 I0
+1 1 874
+2 1 874 1205
+3 1 874 1205 1231
+4 1 874 1205 1231 1285
diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v
index d0ddfd02e..759549e30 100644
--- a/techlibs/ice40/cells_map.v
+++ b/techlibs/ice40/cells_map.v
@@ -37,20 +37,24 @@ module \$lut (A, Y);
generate
if (WIDTH == 1) begin
- SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[0]), .I1(1'b0), .I2(1'b0), .I3(1'b0));
+ localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}};
+ SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0]));
end else
if (WIDTH == 2) begin
- SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[0]), .I1(A[1]), .I2(1'b0), .I3(1'b0));
+ localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}};
+ SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0]));
end else
if (WIDTH == 3) begin
- SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(1'b0));
+ localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[3]}}, {2{LUT[5]}}, {2{LUT[1]}}, {2{LUT[6]}}, {2{LUT[2]}}, {2{LUT[4]}}, {2{LUT[0]}}};
+ SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0]));
end else
if (WIDTH == 4) begin
- SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y),
- .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3]));
+ localparam [15:0] INIT = {LUT[15], LUT[7], LUT[11], LUT[3], LUT[13], LUT[5], LUT[9], LUT[1], LUT[14], LUT[6], LUT[10], LUT[2], LUT[12], LUT[4], LUT[8], LUT[0]};
+ SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y),
+ .I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0]));
end else begin
wire _TECHMAP_FAIL_ = 1;
end
diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v
index f9945b2b5..55b4d98c9 100644
--- a/techlibs/ice40/cells_sim.v
+++ b/techlibs/ice40/cells_sim.v
@@ -127,6 +127,7 @@ endmodule
// SiliconBlue Logic Cells
+(* abc_box_id = 22, lib_whitebox *)
module SB_LUT4 (output O, input I0, I1, I2, I3);
parameter [15:0] LUT_INIT = 0;
wire [7:0] s3 = I3 ? LUT_INIT[15:8] : LUT_INIT[7:0];
@@ -135,24 +136,29 @@ module SB_LUT4 (output O, input I0, I1, I2, I3);
assign O = I0 ? s1[1] : s1[0];
endmodule
-module SB_CARRY (output CO, input I0, I1, CI);
+(* abc_box_id = 21, abc_carry, lib_whitebox *)
+module SB_CARRY ((* abc_carry_out *) output CO, input I0, I1, (* abc_carry_in *) input CI);
assign CO = (I0 && I1) || ((I0 || I1) && CI);
endmodule
// Positive Edge SiliconBlue FF Cells
-module SB_DFF (output `SB_DFF_REG, input C, D);
+module SB_DFF ((* abc_flop_q *) output `SB_DFF_REG, input C, D);
+`ifndef _ABC
always @(posedge C)
Q <= D;
+`else
+ always @* Q <= D;
+`endif
endmodule
-module SB_DFFE (output `SB_DFF_REG, input C, E, D);
+module SB_DFFE ((* abc_flop_q *) output `SB_DFF_REG, input C, E, D);
always @(posedge C)
if (E)
Q <= D;
endmodule
-module SB_DFFSR (output `SB_DFF_REG, input C, R, D);
+module SB_DFFSR ((* abc_flop_q *) output `SB_DFF_REG, input C, R, D);
always @(posedge C)
if (R)
Q <= 0;
@@ -160,7 +166,7 @@ module SB_DFFSR (output `SB_DFF_REG, input C, R, D);
Q <= D;
endmodule
-module SB_DFFR (output `SB_DFF_REG, input C, R, D);
+module SB_DFFR ((* abc_flop_q *) output `SB_DFF_REG, input C, R, D);
always @(posedge C, posedge R)
if (R)
Q <= 0;
@@ -168,7 +174,7 @@ module SB_DFFR (output `SB_DFF_REG, input C, R, D);
Q <= D;
endmodule
-module SB_DFFSS (output `SB_DFF_REG, input C, S, D);
+module SB_DFFSS ((* abc_flop_q *) output `SB_DFF_REG, input C, S, D);
always @(posedge C)
if (S)
Q <= 1;
@@ -176,7 +182,7 @@ module SB_DFFSS (output `SB_DFF_REG, input C, S, D);
Q <= D;
endmodule
-module SB_DFFS (output `SB_DFF_REG, input C, S, D);
+module SB_DFFS ((* abc_flop_q *) output `SB_DFF_REG, input C, S, D);
always @(posedge C, posedge S)
if (S)
Q <= 1;
@@ -184,7 +190,7 @@ module SB_DFFS (output `SB_DFF_REG, input C, S, D);
Q <= D;
endmodule
-module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D);
+module SB_DFFESR ((* abc_flop_q *) output `SB_DFF_REG, input C, E, R, D);
always @(posedge C)
if (E) begin
if (R)
@@ -194,7 +200,7 @@ module SB_DFFESR (output `SB_DFF_REG, input C, E, R, D);
end
endmodule
-module SB_DFFER (output `SB_DFF_REG, input C, E, R, D);
+module SB_DFFER ((* abc_flop_q *) output `SB_DFF_REG, input C, E, R, D);
always @(posedge C, posedge R)
if (R)
Q <= 0;
@@ -202,7 +208,7 @@ module SB_DFFER (output `SB_DFF_REG, input C, E, R, D);
Q <= D;
endmodule
-module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D);
+module SB_DFFESS ((* abc_flop_q *) output `SB_DFF_REG, input C, E, S, D);
always @(posedge C)
if (E) begin
if (S)
@@ -212,7 +218,7 @@ module SB_DFFESS (output `SB_DFF_REG, input C, E, S, D);
end
endmodule
-module SB_DFFES (output `SB_DFF_REG, input C, E, S, D);
+module SB_DFFES ((* abc_flop_q *) output `SB_DFF_REG, input C, E, S, D);
always @(posedge C, posedge S)
if (S)
Q <= 1;
@@ -222,18 +228,18 @@ endmodule
// Negative Edge SiliconBlue FF Cells
-module SB_DFFN (output `SB_DFF_REG, input C, D);
+module SB_DFFN ((* abc_flop_q *) output `SB_DFF_REG, input C, D);
always @(negedge C)
Q <= D;
endmodule
-module SB_DFFNE (output `SB_DFF_REG, input C, E, D);
+module SB_DFFNE ((* abc_flop_q *) output `SB_DFF_REG, input C, E, D);
always @(negedge C)
if (E)
Q <= D;
endmodule
-module SB_DFFNSR (output `SB_DFF_REG, input C, R, D);
+module SB_DFFNSR ((* abc_flop_q *) output `SB_DFF_REG, input C, R, D);
always @(negedge C)
if (R)
Q <= 0;
@@ -241,7 +247,7 @@ module SB_DFFNSR (output `SB_DFF_REG, input C, R, D);
Q <= D;
endmodule
-module SB_DFFNR (output `SB_DFF_REG, input C, R, D);
+module SB_DFFNR ((* abc_flop_q *) output `SB_DFF_REG, input C, R, D);
always @(negedge C, posedge R)
if (R)
Q <= 0;
@@ -249,7 +255,7 @@ module SB_DFFNR (output `SB_DFF_REG, input C, R, D);
Q <= D;
endmodule
-module SB_DFFNSS (output `SB_DFF_REG, input C, S, D);
+module SB_DFFNSS ((* abc_flop_q *) output `SB_DFF_REG, input C, S, D);
always @(negedge C)
if (S)
Q <= 1;
@@ -257,7 +263,7 @@ module SB_DFFNSS (output `SB_DFF_REG, input C, S, D);
Q <= D;
endmodule
-module SB_DFFNS (output `SB_DFF_REG, input C, S, D);
+module SB_DFFNS ((* abc_flop_q *) output `SB_DFF_REG, input C, S, D);
always @(negedge C, posedge S)
if (S)
Q <= 1;
@@ -265,7 +271,7 @@ module SB_DFFNS (output `SB_DFF_REG, input C, S, D);
Q <= D;
endmodule
-module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D);
+module SB_DFFNESR ((* abc_flop_q *) output `SB_DFF_REG, input C, E, R, D);
always @(negedge C)
if (E) begin
if (R)
@@ -275,7 +281,7 @@ module SB_DFFNESR (output `SB_DFF_REG, input C, E, R, D);
end
endmodule
-module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D);
+module SB_DFFNER ((* abc_flop_q *) output `SB_DFF_REG, input C, E, R, D);
always @(negedge C, posedge R)
if (R)
Q <= 0;
@@ -283,7 +289,7 @@ module SB_DFFNER (output `SB_DFF_REG, input C, E, R, D);
Q <= D;
endmodule
-module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D);
+module SB_DFFNESS ((* abc_flop_q *) output `SB_DFF_REG, input C, E, S, D);
always @(negedge C)
if (E) begin
if (S)
@@ -293,7 +299,7 @@ module SB_DFFNESS (output `SB_DFF_REG, input C, E, S, D);
end
endmodule
-module SB_DFFNES (output `SB_DFF_REG, input C, E, S, D);
+module SB_DFFNES ((* abc_flop_q *) output `SB_DFF_REG, input C, E, S, D);
always @(negedge C, posedge S)
if (S)
Q <= 1;
@@ -304,7 +310,7 @@ endmodule
// SiliconBlue RAM Cells
module SB_RAM40_4K (
- output [15:0] RDATA,
+ (* abc_flop_q *) output [15:0] RDATA,
input RCLK, RCLKE, RE,
input [10:0] RADDR,
input WCLK, WCLKE, WE,
@@ -472,7 +478,7 @@ module SB_RAM40_4K (
endmodule
module SB_RAM40_4KNR (
- output [15:0] RDATA,
+ (* abc_flop_q *) output [15:0] RDATA,
input RCLKN, RCLKE, RE,
input [10:0] RADDR,
input WCLK, WCLKE, WE,
@@ -537,7 +543,7 @@ module SB_RAM40_4KNR (
endmodule
module SB_RAM40_4KNW (
- output [15:0] RDATA,
+ (* abc_flop_q *) output [15:0] RDATA,
input RCLK, RCLKE, RE,
input [10:0] RADDR,
input WCLKN, WCLKE, WE,
@@ -602,7 +608,7 @@ module SB_RAM40_4KNW (
endmodule
module SB_RAM40_4KNRNW (
- output [15:0] RDATA,
+ (* abc_flop_q *) output [15:0] RDATA,
input RCLKN, RCLKE, RE,
input [10:0] RADDR,
input WCLKN, WCLKE, WE,
@@ -890,12 +896,13 @@ module SB_WARMBOOT (
);
endmodule
+(* nomem2reg *)
module SB_SPRAM256KA (
input [13:0] ADDRESS,
input [15:0] DATAIN,
input [3:0] MASKWREN,
input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF,
- output reg [15:0] DATAOUT
+ (* abc_flop_q *) output reg [15:0] DATAOUT
);
`ifndef BLACKBOX
`ifndef EQUIV
diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc
index bb96d66d1..a60829764 100644
--- a/techlibs/ice40/synth_ice40.cc
+++ b/techlibs/ice40/synth_ice40.cc
@@ -37,6 +37,10 @@ struct SynthIce40Pass : public ScriptPass
log("\n");
log("This command runs synthesis for iCE40 FPGAs.\n");
log("\n");
+ log(" -device < hx | lp | u >\n");
+ log(" optimise the synthesis netlist for the specified device.\n");
+ log(" HX is the default target if no device argument specified.\n");
+ log("\n");
log(" -top <module>\n");
log(" use the specified module as top module\n");
log("\n");
@@ -92,13 +96,17 @@ 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 new ABC9 flow (EXPERIMENTAL)\n");
+ log("\n");
log("\n");
log("The following commands are executed by this synthesis command:\n");
help_script();
log("\n");
}
- string top_opt, blif_file, edif_file, json_file;
+
+ string top_opt, blif_file, edif_file, json_file, abc, device_opt;
bool nocarry, nodffe, nobram, dsp, flatten, retime, relut, noabc, abc2, vpr;
int min_ce_use;
@@ -119,6 +127,8 @@ struct SynthIce40Pass : public ScriptPass
noabc = false;
abc2 = false;
vpr = false;
+ abc = "abc";
+ device_opt = "hx";
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -201,12 +211,22 @@ struct SynthIce40Pass : public ScriptPass
vpr = true;
continue;
}
+ if (args[argidx] == "-abc9") {
+ abc = "abc9";
+ continue;
+ }
+ if (args[argidx] == "-device" && argidx+1 < args.size()) {
+ device_opt = args[++argidx];
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n");
+ if (device_opt != "hx" && device_opt != "lp" && device_opt !="u")
+ log_cmd_error("Invalid or no device specified: '%s'\n", device_opt.c_str());
log_header(design, "Executing SYNTH_ICE40 pass.\n");
log_push();
@@ -220,7 +240,7 @@ struct SynthIce40Pass : public ScriptPass
{
if (check_label("begin"))
{
- run("read_verilog -lib +/ice40/cells_sim.v");
+ run("read_verilog -lib -D_ABC +/ice40/cells_sim.v");
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
run("proc");
}
@@ -277,8 +297,8 @@ struct SynthIce40Pass : public ScriptPass
run("techmap");
else
run("techmap -map +/techmap.v -map +/ice40/arith_map.v");
- if (retime || help_mode)
- run("abc -dff", "(only if -retime)");
+ if ((retime || help_mode) && abc != "abc9")
+ run(abc + " -dff", "(only if -retime)");
run("ice40_opt");
}
@@ -302,7 +322,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");
@@ -311,7 +331,10 @@ struct SynthIce40Pass : public ScriptPass
run("techmap -map +/gate2lut.v -D LUT_WIDTH=4", "(only if -noabc)");
}
if (!noabc) {
- run("abc -dress -lut 4", "(skip if -noabc)");
+ if (abc == "abc9")
+ run(abc + stringf(" -dress -lut +/ice40/abc_%s.lut -box +/ice40/abc_%s.box", device_opt.c_str(), device_opt.c_str()), "(skip if -noabc)");
+ else
+ run(abc + " -dress -lut 4", "(skip if -noabc)");
}
run("clean");
if (relut || help_mode) {
diff --git a/techlibs/xilinx/Makefile.inc b/techlibs/xilinx/Makefile.inc
index d68f03bb4..296edace9 100644
--- a/techlibs/xilinx/Makefile.inc
+++ b/techlibs/xilinx/Makefile.inc
@@ -30,6 +30,8 @@ $(eval $(call add_share_file,share/xilinx,techlibs/xilinx/drams_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/arith_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/ff_map.v))
$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/lut_map.v))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc.box))
+$(eval $(call add_share_file,share/xilinx,techlibs/xilinx/abc.lut))
$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_36.vh))
$(eval $(call add_gen_share_file,share/xilinx,techlibs/xilinx/brams_init_32.vh))
diff --git a/techlibs/xilinx/abc.box b/techlibs/xilinx/abc.box
new file mode 100644
index 000000000..a4182ed63
--- /dev/null
+++ b/techlibs/xilinx/abc.box
@@ -0,0 +1,62 @@
+# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf
+
+# F7BMUX slower than F7AMUX
+# Inputs: I0 I1 S0
+# Outputs: O
+F7BMUX 1 1 3 1
+217 223 296
+
+# Inputs: I0 I1 S0
+# Outputs: O
+MUXF8 2 1 3 1
+104 94 273
+
+# CARRY4 + CARRY4_[ABCD]X
+# Inputs: S0 S1 S2 S3 CYINIT DI0 DI1 DI2 DI3 CI
+# Outputs: O0 O1 O2 O3 CO0 CO1 CO2 CO3
+# (NB: carry chain input/output must be last input/output,
+# swapped with what normally would have been the last
+# output, here: CI <-> S, CO <-> O
+CARRY4 3 1 10 8
+223 - - - 482 - - - - 222
+400 205 - - 598 407 - - - 334
+523 558 226 - 584 556 537 - - 239
+582 618 330 227 642 615 596 438 - 313
+340 - - - 536 379 - - - 271
+433 469 - - 494 465 445 - - 157
+512 548 292 - 592 540 520 356 - 228
+508 528 378 380 580 526 507 398 385 114
+
+# SLICEM/A6LUT
+# Inputs: A0 A1 A2 A3 A4 A5 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 WCLK WE
+# Outputs: DPO SPO
+RAM64X1D 4 0 15 2
+- - - - - - - 124 124 124 124 124 124 - -
+124 124 124 124 124 124 - - - - - - 124 - -
+
+# SLICEM/A6LUT + F7[AB]MUX
+# Inputs: A0 A1 A2 A3 A4 A5 A6 D DPRA0 DPRA1 DPRA2 DPRA3 DPRA4 DPRA5 DPRA6 WCLK WE
+# Outputs: DPO SPO
+RAM128X1D 5 0 17 2
+- - - - - - - - 314 314 314 314 314 314 292 - -
+347 347 347 347 347 347 296 - - - - - - - - - -
+
+# Inputs: C CE D R
+# Outputs: Q
+FDRE 6 0 4 1
+- - - -
+
+# Inputs: C CE D S
+# Outputs: Q
+FDSE 7 0 4 1
+- - - -
+
+# Inputs: C CE CLR D
+# Outputs: Q
+FDCE 8 0 4 1
+- - 404 -
+
+# Inputs: C CE D PRE
+# Outputs: Q
+FDPE 9 0 4 1
+- - - 404
diff --git a/techlibs/xilinx/abc.lut b/techlibs/xilinx/abc.lut
new file mode 100644
index 000000000..3a7dc268d
--- /dev/null
+++ b/techlibs/xilinx/abc.lut
@@ -0,0 +1,14 @@
+# Max delays from https://github.com/SymbiFlow/prjxray-db/blob/34ea6eb08a63d21ec16264ad37a0a7b142ff6031/artix7/timings/CLBLL_L.sdf
+
+# K area delay
+1 1 124
+2 2 124 235
+3 3 124 235 399
+4 3 124 235 399 490
+5 3 124 235 399 490 620
+6 5 124 235 399 490 620 632
+ # F7BMUX
+7 10 296 420 531 695 756 916 928
+ # F8MUX
+ # F8MUX+F7BMUX
+8 20 273 569 693 804 968 1029 1189 1201
diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v
index 09a5f07e8..5c848d4e6 100644
--- a/techlibs/xilinx/arith_map.v
+++ b/techlibs/xilinx/arith_map.v
@@ -180,7 +180,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
// First one
if (i == 0) begin
- CARRY4 #(.IS_INITIALIZED(1'd1)) carry4_1st_part
+ CARRY4 carry4_1st_part
(
.CYINIT(CI),
.CI (1'd0),
@@ -207,7 +207,7 @@ module _80_xilinx_alu (A, B, CI, BI, X, Y, CO);
// First one
if (i == 0) begin
- CARRY4 #(.IS_INITIALIZED(1'd1)) carry4_1st_full
+ CARRY4 carry4_1st_full
(
.CYINIT(CI),
.CI (1'd0),
diff --git a/techlibs/xilinx/brams_bb.v b/techlibs/xilinx/brams_bb.v
index a682ba4a7..f540d299d 100644
--- a/techlibs/xilinx/brams_bb.v
+++ b/techlibs/xilinx/brams_bb.v
@@ -19,10 +19,10 @@ module RAMB18E1 (
input [1:0] WEA,
input [3:0] WEBWE,
- output [15:0] DOADO,
- output [15:0] DOBDO,
- output [1:0] DOPADOP,
- output [1:0] DOPBDOP
+ (* abc_flop_q *) output [15:0] DOADO,
+ (* abc_flop_q *) output [15:0] DOBDO,
+ (* abc_flop_q *) output [1:0] DOPADOP,
+ (* abc_flop_q *) output [1:0] DOPBDOP
);
parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
@@ -143,10 +143,10 @@ module RAMB36E1 (
input [3:0] WEA,
input [7:0] WEBWE,
- output [31:0] DOADO,
- output [31:0] DOBDO,
- output [3:0] DOPADOP,
- output [3:0] DOPBDOP
+ (* abc_flop_q *) output [31:0] DOADO,
+ (* abc_flop_q *) output [31:0] DOBDO,
+ (* abc_flop_q *) output [3:0] DOPADOP,
+ (* abc_flop_q *) output [3:0] DOPBDOP
);
parameter INITP_00 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INITP_01 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
diff --git a/techlibs/xilinx/cells_map.v b/techlibs/xilinx/cells_map.v
index 40789ddbe..b5114758c 100644
--- a/techlibs/xilinx/cells_map.v
+++ b/techlibs/xilinx/cells_map.v
@@ -2,6 +2,7 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -19,14 +20,16 @@
// Convert negative-polarity reset to positive-polarity
(* techmap_celltype = "$_DFF_NN0_" *)
-module _90_dff_nn0_to_np0 (input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+module _90_dff_nn0_to_np0(input D, C, R, output Q); \$_DFF_NP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
(* techmap_celltype = "$_DFF_PN0_" *)
-module _90_dff_pn0_to_pp0 (input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+module _90_dff_pn0_to_pp0(input D, C, R, output Q); \$_DFF_PP0_ _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+
(* techmap_celltype = "$_DFF_NN1_" *)
module _90_dff_nn1_to_np1 (input D, C, R, output Q); \$_DFF_NP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
(* techmap_celltype = "$_DFF_PN1_" *)
module _90_dff_pn1_to_pp1 (input D, C, R, output Q); \$_DFF_PP1 _TECHMAP_REPLACE_ (.D(D), .Q(Q), .C(C), .R(~R)); endmodule
+
module \$__SHREG_ (input C, input D, input E, output Q);
parameter DEPTH = 0;
parameter [DEPTH-1:0] INIT = 0;
@@ -88,7 +91,7 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
end else
if (DEPTH > 65 && DEPTH <= 96) begin
wire T0, T1, T2, T3, T4, T5, T6;
- SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
+ SRLC32E #(.INIT(INIT_R[32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1));
SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-64), .INIT(INIT[DEPTH-64-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_2 (.C(C), .D(T3), .L(L[4:0]), .E(E), .Q(T4));
if (&_TECHMAP_CONSTMSK_L_)
@@ -101,7 +104,7 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
end else
if (DEPTH > 97 && DEPTH < 128) begin
wire T0, T1, T2, T3, T4, T5, T6, T7, T8;
- SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
+ SRLC32E #(.INIT(INIT_R[32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1));
SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
\$__XILINX_SHREG_ #(.DEPTH(DEPTH-96), .INIT(INIT[DEPTH-96-1:0]), .CLKPOL(CLKPOL), .ENPOL(ENPOL)) fpga_srl_3 (.C(C), .D(T5), .L(L[4:0]), .E(E), .Q(T6));
@@ -115,9 +118,9 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
end
else if (DEPTH == 128) begin
wire T0, T1, T2, T3, T4, T5, T6;
- SRLC32E #(.INIT(INIT_R[32-1:0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D(D), .Q(T0), .Q31(T1));
- SRLC32E #(.INIT(INIT_R[64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
- SRLC32E #(.INIT(INIT_R[96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
+ SRLC32E #(.INIT(INIT_R[ 32-1: 0]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_0 (.A(L[4:0]), .CE(CE), .CLK(C), .D( D), .Q(T0), .Q31(T1));
+ SRLC32E #(.INIT(INIT_R[ 64-1:32]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_1 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T1), .Q(T2), .Q31(T3));
+ SRLC32E #(.INIT(INIT_R[ 96-1:64]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_2 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T3), .Q(T4), .Q31(T5));
SRLC32E #(.INIT(INIT_R[128-1:96]), .IS_CLK_INVERTED(~CLKPOL[0])) fpga_srl_3 (.A(L[4:0]), .CE(CE), .CLK(C), .D(T5), .Q(T6), .Q31(SO));
if (&_TECHMAP_CONSTMSK_L_)
assign Q = T6;
@@ -151,6 +154,3 @@ module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, o
end
endgenerate
endmodule
-
-`ifndef SRL_ONLY
-`endif
diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v
index 3a4540b83..d9aa36666 100644
--- a/techlibs/xilinx/cells_sim.v
+++ b/techlibs/xilinx/cells_sim.v
@@ -159,10 +159,12 @@ module MUXCY(output O, input CI, DI, S);
assign O = S ? CI : DI;
endmodule
+(* abc_box_id = 1, lib_whitebox *)
module MUXF7(output O, input I0, I1, S);
assign O = S ? I1 : I0;
endmodule
+(* abc_box_id = 2, lib_whitebox *)
module MUXF8(output O, input I0, I1, S);
assign O = S ? I1 : I0;
endmodule
@@ -171,7 +173,8 @@ module XORCY(output O, input CI, LI);
assign O = CI ^ LI;
endmodule
-module CARRY4(output [3:0] CO, O, input CI, CYINIT, input [3:0] DI, S);
+(* abc_box_id = 3, abc_carry, lib_whitebox *)
+module CARRY4((* abc_carry_out *) output [3:0] CO, output [3:0] O, (* abc_carry_in *) input CI, input CYINIT, input [3:0] DI, S);
assign O = S ^ {CO[2:0], CI | CYINIT};
assign CO[0] = S[0] ? CI | CYINIT : DI[0];
assign CO[1] = S[1] ? CO[0] : DI[1];
@@ -202,7 +205,7 @@ endmodule
`endif
-module FDRE (output reg Q, input C, CE, D, R);
+module FDRE ((* abc_flop_q *) output reg Q, input C, CE, D, R);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -214,7 +217,7 @@ module FDRE (output reg Q, input C, CE, D, R);
endcase endgenerate
endmodule
-module FDSE (output reg Q, input C, CE, D, S);
+module FDSE ((* abc_flop_q *) output reg Q, input C, CE, D, S);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -226,7 +229,7 @@ module FDSE (output reg Q, input C, CE, D, S);
endcase endgenerate
endmodule
-module FDCE (output reg Q, input C, CE, D, CLR);
+module FDCE ((* abc_flop_q *) output reg Q, input C, CE, D, CLR);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -240,7 +243,7 @@ module FDCE (output reg Q, input C, CE, D, CLR);
endcase endgenerate
endmodule
-module FDPE (output reg Q, input C, CE, D, PRE);
+module FDPE ((* abc_flop_q *) output reg Q, input C, CE, D, PRE);
parameter [0:0] INIT = 1'b0;
parameter [0:0] IS_C_INVERTED = 1'b0;
parameter [0:0] IS_D_INVERTED = 1'b0;
@@ -254,30 +257,31 @@ module FDPE (output reg Q, input C, CE, D, PRE);
endcase endgenerate
endmodule
-module FDRE_1 (output reg Q, input C, CE, D, R);
+module FDRE_1 ((* abc_flop_q *) output reg Q, input C, CE, D, R);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C) if (R) Q <= 1'b0; else if(CE) Q <= D;
endmodule
-module FDSE_1 (output reg Q, input C, CE, D, S);
+module FDSE_1 ((* abc_flop_q *) output reg Q, input C, CE, D, S);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C) if (S) Q <= 1'b1; else if(CE) Q <= D;
endmodule
-module FDCE_1 (output reg Q, input C, CE, D, CLR);
+module FDCE_1 ((* abc_flop_q *) output reg Q, input C, CE, D, CLR);
parameter [0:0] INIT = 1'b0;
initial Q <= INIT;
always @(negedge C, posedge CLR) if (CLR) Q <= 1'b0; else if (CE) Q <= D;
endmodule
-module FDPE_1 (output reg Q, input C, CE, D, PRE);
+module FDPE_1 ((* abc_flop_q *) output reg Q, input C, CE, D, PRE);
parameter [0:0] INIT = 1'b1;
initial Q <= INIT;
always @(negedge C, posedge PRE) if (PRE) Q <= 1'b1; else if (CE) Q <= D;
endmodule
+//(* abc_box_id = 4 /*, lib_whitebox*/ *)
module RAM64X1D (
output DPO, SPO,
input D, WCLK, WE,
@@ -295,6 +299,7 @@ module RAM64X1D (
always @(posedge clk) if (WE) mem[a] <= D;
endmodule
+//(* abc_box_id = 5 /*, lib_whitebox*/ *)
module RAM128X1D (
output DPO, SPO,
input D, WCLK, WE,
@@ -310,7 +315,7 @@ module RAM128X1D (
endmodule
module SRL16E (
- output Q,
+ (* abc_flop_q *) output Q,
input A0, A1, A2, A3, CE, CLK, D
);
parameter [15:0] INIT = 16'h0000;
@@ -328,7 +333,7 @@ module SRL16E (
endmodule
module SRLC32E (
- output Q,
+ (* abc_flop_q *) output Q,
output Q31,
input [4:0] A,
input CE, CLK, D
diff --git a/techlibs/xilinx/cells_xtra.sh b/techlibs/xilinx/cells_xtra.sh
index 8e39b440d..2b384f405 100644
--- a/techlibs/xilinx/cells_xtra.sh
+++ b/techlibs/xilinx/cells_xtra.sh
@@ -116,7 +116,7 @@ function xtract_cell_decl()
xtract_cell_decl PS7 "(* keep *)"
xtract_cell_decl PULLDOWN
xtract_cell_decl PULLUP
- xtract_cell_decl RAM128X1D
+ #xtract_cell_decl RAM128X1D
xtract_cell_decl RAM128X1S
xtract_cell_decl RAM256X1S
xtract_cell_decl RAM32M
@@ -125,7 +125,7 @@ function xtract_cell_decl()
xtract_cell_decl RAM32X1S_1
xtract_cell_decl RAM32X2S
xtract_cell_decl RAM64M
- xtract_cell_decl RAM64X1D
+ #xtract_cell_decl RAM64X1D
xtract_cell_decl RAM64X1S
xtract_cell_decl RAM64X1S_1
xtract_cell_decl RAM64X2S
diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v
index fbcc74682..0ec3d0df0 100644
--- a/techlibs/xilinx/cells_xtra.v
+++ b/techlibs/xilinx/cells_xtra.v
@@ -3655,17 +3655,6 @@ module PULLUP (...);
output O;
endmodule
-module RAM128X1D (...);
- parameter [127:0] INIT = 128'h00000000000000000000000000000000;
- parameter [0:0] IS_WCLK_INVERTED = 1'b0;
- output DPO, SPO;
- input [6:0] A;
- input [6:0] DPRA;
- input D;
- input WCLK;
- input WE;
-endmodule
-
module RAM128X1S (...);
parameter [127:0] INIT = 128'h00000000000000000000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
@@ -3756,13 +3745,6 @@ module RAM64M (...);
input WE;
endmodule
-module RAM64X1D (...);
- parameter [63:0] INIT = 64'h0000000000000000;
- parameter [0:0] IS_WCLK_INVERTED = 1'b0;
- output DPO, SPO;
- input A0, A1, A2, A3, A4, A5, D, DPRA0, DPRA1, DPRA2, DPRA3, DPRA4, DPRA5, WCLK, WE;
-endmodule
-
module RAM64X1S (...);
parameter [63:0] INIT = 64'h0000000000000000;
parameter [0:0] IS_WCLK_INVERTED = 1'b0;
diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc
index a293081f1..69a3d99c2 100644
--- a/techlibs/xilinx/synth_xilinx.cc
+++ b/techlibs/xilinx/synth_xilinx.cc
@@ -58,6 +58,9 @@ struct SynthXilinxPass : 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(" -nocarry\n");
+ log(" disable inference of carry chains\n");
+ log("\n");
log(" -nobram\n");
log(" disable inference of block rams\n");
log("\n");
@@ -78,23 +81,28 @@ struct SynthXilinxPass : public ScriptPass
log(" -retime\n");
log(" run 'abc' with -dff option\n");
log("\n");
+ log(" -abc9\n");
+ log(" use new ABC9 flow (EXPERIMENTAL)\n");
+ log("\n");
log("\n");
log("The following commands are executed by this synthesis command:\n");
help_script();
log("\n");
}
- std::string top_opt, edif_file, blif_file, arch;
- bool flatten, retime, vpr, nobram, nodram, nosrl;
+ std::string top_opt, edif_file, blif_file, abc, arch;
+ bool flatten, retime, vpr, nocarry, nobram, nodram, nosrl;
void clear_flags() YS_OVERRIDE
{
top_opt = "-auto-top";
edif_file.clear();
blif_file.clear();
+ abc = "abc";
flatten = false;
retime = false;
vpr = false;
+ nocarry = false;
nobram = false;
nodram = false;
nosrl = false;
@@ -145,6 +153,10 @@ struct SynthXilinxPass : public ScriptPass
vpr = true;
continue;
}
+ if (args[argidx] == "-nocarry") {
+ nocarry = true;
+ continue;
+ }
if (args[argidx] == "-nobram") {
nobram = true;
continue;
@@ -157,6 +169,10 @@ struct SynthXilinxPass : public ScriptPass
nosrl = true;
continue;
}
+ if (args[argidx] == "-abc9") {
+ abc = "abc9";
+ continue;
+ }
break;
}
extra_args(args, argidx, design);
@@ -179,9 +195,9 @@ struct SynthXilinxPass : public ScriptPass
{
if (check_label("begin")) {
if (vpr)
- run("read_verilog -lib -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
+ run("read_verilog -lib -D _ABC -D_EXPLICIT_CARRY +/xilinx/cells_sim.v");
else
- run("read_verilog -lib +/xilinx/cells_sim.v");
+ run("read_verilog -lib -D _ABC +/xilinx/cells_sim.v");
run("read_verilog -lib +/xilinx/cells_xtra.v");
@@ -200,6 +216,17 @@ struct SynthXilinxPass : public ScriptPass
if (check_label("coarse")) {
run("synth -run coarse");
+
+ // shregmap -tech xilinx can cope with $shiftx and $mux
+ // cells for identifying variable-length shift registers,
+ // so attempt to convert $pmux-es to the former
+ if (!nosrl || help_mode)
+ run("pmux2shiftx", "(skip if '-nosrl')");
+
+ // Run a number of peephole optimisations, including one
+ // that optimises $mul cells driving $shiftx's B input
+ // and that aids wide mux analysis
+ run("peepopt");
}
if (check_label("bram", "(skip if '-nobram')")) {
@@ -217,12 +244,6 @@ struct SynthXilinxPass : public ScriptPass
}
if (check_label("fine")) {
- // shregmap -tech xilinx can cope with $shiftx and $mux
- // cells for identifiying variable-length shift registers,
- // so attempt to convert $pmux-es to the former
- if (!nosrl || help_mode)
- run("pmux2shiftx", "(skip if '-nosrl')");
-
run("opt -fast -full");
run("memory_map");
run("dffsr2dff");
@@ -237,11 +258,17 @@ struct SynthXilinxPass : public ScriptPass
run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
}
- if (!vpr || help_mode)
- run("techmap -map +/techmap.v -map +/xilinx/arith_map.v");
- else
- run("techmap -map +/techmap.v +/xilinx/arith_map.v -D _EXPLICIT_CARRY");
-
+ std::string techmap_files = " -map +/techmap.v";
+ if (help_mode)
+ techmap_files += " [-map +/xilinx/arith_map.v]";
+ else if (!nocarry) {
+ techmap_files += " -map +/xilinx/arith_map.v";
+ if (vpr)
+ techmap_files += " -D _EXPLICIT_CARRY";
+ else if (abc == "abc9")
+ techmap_files += " -D _CLB_CARRY";
+ }
+ run("techmap " + techmap_files);
run("opt -fast");
}
@@ -251,11 +278,14 @@ struct SynthXilinxPass : public ScriptPass
}
if (check_label("map_luts")) {
- if (help_mode)
- run("abc -luts 2:2,3,6:5,10,20 [-dff]");
+ if (abc == "abc9")
+ run(abc + " -lut +/xilinx/abc.lut -box +/xilinx/abc.box -W 160" + string(retime ? " -dff" : ""));
+ else if (help_mode)
+ run(abc + " -luts 2:2,3,6:5,10,20 [-dff]");
else
- run("abc -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
+ run(abc + " -luts 2:2,3,6:5,10,20" + string(retime ? " -dff" : ""));
run("clean");
+
// This shregmap call infers fixed length shift registers after abc
// has performed any necessary retiming
if (!nosrl || help_mode)
diff --git a/tests/simple_abc9/abc.box b/tests/simple_abc9/abc.box
new file mode 100644
index 000000000..a8801d807
--- /dev/null
+++ b/tests/simple_abc9/abc.box
@@ -0,0 +1,2 @@
+MUXF8 1 0 3 1
+1 1 1
diff --git a/tests/simple_abc9/abc9.v b/tests/simple_abc9/abc9.v
new file mode 100644
index 000000000..0b83c34a3
--- /dev/null
+++ b/tests/simple_abc9/abc9.v
@@ -0,0 +1,264 @@
+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
+
+module abc9_test009(inout io, input oe);
+reg latch;
+always @(io or oe)
+ if (!oe)
+ latch <= io;
+assign io = oe ? ~latch : 1'bz;
+endmodule
+
+module abc9_test010(inout [7:0] io, input oe);
+reg [7:0] latch;
+always @(io or oe)
+ if (!oe)
+ latch <= io;
+assign io = oe ? ~latch : 8'bz;
+endmodule
+
+module abc9_test011(inout io, input oe);
+reg latch;
+always @(io or oe)
+ if (!oe)
+ latch <= io;
+//assign io = oe ? ~latch : 8'bz;
+endmodule
+
+module abc9_test012(inout io, input oe);
+reg latch;
+//always @(io or oe)
+// if (!oe)
+// latch <= io;
+assign io = oe ? ~latch : 8'bz;
+endmodule
+
+module abc9_test013(inout [3:0] io, input oe);
+reg [3:0] latch;
+always @(io or oe)
+ if (!oe)
+ latch[3:0] <= io[3:0];
+ else
+ latch[7:4] <= io;
+assign io[3:0] = oe ? ~latch[3:0] : 4'bz;
+assign io[7:4] = !oe ? {latch[4], latch[7:3]} : 4'bz;
+endmodule
+
+module abc9_test014(inout [7:0] io, input oe);
+abc9_test012_sub sub(io, oe);
+endmodule
+
+module abc9_test012_sub(inout [7:0] io, input oe);
+reg [7:0] latch;
+always @(io or oe)
+ if (!oe)
+ latch[3:0] <= io;
+ else
+ latch[7:4] <= io;
+assign io[3:0] = oe ? ~latch[3:0] : 4'bz;
+assign io[7:4] = !oe ? {latch[4], latch[7:3]} : 4'bz;
+endmodule
+
+module abc9_test015(input a, output b, input c);
+assign b = ~a;
+(* keep *) wire d;
+assign d = ~c;
+endmodule
+
+module abc9_test016(input a, output b);
+assign b = ~a;
+(* keep *) reg c;
+always @* c <= ~a;
+endmodule
+
+module abc9_test017(input a, output b);
+assign b = ~a;
+(* keep *) reg c;
+always @* c = b;
+endmodule
+
+module abc9_test018(input a, output b, output c);
+assign b = ~a;
+(* keep *) wire [1:0] d;
+assign c = &d;
+endmodule
+
+module abc9_test019(input a, output b);
+assign b = ~a;
+(* keep *) reg [1:0] c;
+reg d;
+always @* d <= &c;
+endmodule
+
+module abc9_test020(input a, output b);
+assign b = ~a;
+(* keep *) reg [1:0] c;
+(* keep *) reg d;
+always @* d <= &c;
+endmodule
+
+// Citation: https://github.com/alexforencich/verilog-ethernet
+module abc9_test021(clk, rst, s_eth_hdr_valid, s_eth_hdr_ready, s_eth_dest_mac, s_eth_src_mac, s_eth_type, s_eth_payload_axis_tdata, s_eth_payload_axis_tkeep, s_eth_payload_axis_tvalid, s_eth_payload_axis_tready, s_eth_payload_axis_tlast, s_eth_payload_axis_tid, s_eth_payload_axis_tdest, s_eth_payload_axis_tuser, m_eth_hdr_valid, m_eth_hdr_ready, m_eth_dest_mac, m_eth_src_mac, m_eth_type, m_eth_payload_axis_tdata, m_eth_payload_axis_tkeep, m_eth_payload_axis_tvalid, m_eth_payload_axis_tready, m_eth_payload_axis_tlast, m_eth_payload_axis_tid, m_eth_payload_axis_tdest, m_eth_payload_axis_tuser);
+ input clk;
+ output [47:0] m_eth_dest_mac;
+ input m_eth_hdr_ready;
+ output m_eth_hdr_valid;
+ output [7:0] m_eth_payload_axis_tdata;
+ output [7:0] m_eth_payload_axis_tdest;
+ output [7:0] m_eth_payload_axis_tid;
+ output m_eth_payload_axis_tkeep;
+ output m_eth_payload_axis_tlast;
+ input m_eth_payload_axis_tready;
+ output m_eth_payload_axis_tuser;
+ output m_eth_payload_axis_tvalid;
+ output [47:0] m_eth_src_mac;
+ output [15:0] m_eth_type;
+ input rst;
+ input [191:0] s_eth_dest_mac;
+ output [3:0] s_eth_hdr_ready;
+ input [3:0] s_eth_hdr_valid;
+ input [31:0] s_eth_payload_axis_tdata;
+ input [31:0] s_eth_payload_axis_tdest;
+ input [31:0] s_eth_payload_axis_tid;
+ input [3:0] s_eth_payload_axis_tkeep;
+ input [3:0] s_eth_payload_axis_tlast;
+ output [3:0] s_eth_payload_axis_tready;
+ input [3:0] s_eth_payload_axis_tuser;
+ input [3:0] s_eth_payload_axis_tvalid;
+ input [191:0] s_eth_src_mac;
+ input [63:0] s_eth_type;
+ (* keep *)
+ wire [0:0] grant, request;
+ wire a;
+ not u0 (
+ a,
+ grant[0]
+ );
+ and u1 (
+ request[0],
+ s_eth_hdr_valid[0],
+ a
+ );
+ (* keep *)
+ MUXF8 u2 (
+ .I0(1'bx),
+ .I1(1'bx),
+ .O(o),
+ .S(1'bx)
+ );
+ arbiter arb_inst (
+ .acknowledge(acknowledge),
+ .clk(clk),
+ .grant(grant),
+ .grant_encoded(grant_encoded),
+ .grant_valid(grant_valid),
+ .request(request),
+ .rst(rst)
+ );
+endmodule
+
+module arbiter (clk, rst, request, acknowledge, grant, grant_valid, grant_encoded);
+ input [3:0] acknowledge;
+ input clk;
+ output [3:0] grant;
+ output [1:0] grant_encoded;
+ output grant_valid;
+ input [3:0] request;
+ input rst;
+endmodule
+
+(* abc_box_id=1 *)
+module MUXF8(input I0, I1, S, output O);
+endmodule
+
+// Citation: https://github.com/alexforencich/verilog-ethernet
+// TODO: yosys -p "synth_xilinx -abc9 -top abc9_test022" abc9.v -q
+// returns before b4321a31
+// Warning: Wire abc9_test022.\m_eth_payload_axis_tkeep [7] is used but has no
+// driver.
+// Warning: Wire abc9_test022.\m_eth_payload_axis_tkeep [3] is used but has no
+// driver.
+module abc9_test022
+(
+ input wire clk,
+ input wire i,
+ output wire [7:0] m_eth_payload_axis_tkeep
+);
+ reg [7:0] m_eth_payload_axis_tkeep_reg = 8'd0;
+ assign m_eth_payload_axis_tkeep = m_eth_payload_axis_tkeep_reg;
+ always @(posedge clk)
+ m_eth_payload_axis_tkeep_reg <= i ? 8'hff : 8'h0f;
+endmodule
+
+// Citation: https://github.com/riscv/riscv-bitmanip
+// TODO: yosys -p "synth_xilinx -abc9 -top abc9_test023" abc9.v -q
+// returns before 14233843
+// Warning: Wire abc9_test023.\dout [1] is used but has no driver.
+module abc9_test023 #(
+ parameter integer N = 2,
+ parameter integer M = 2
+) (
+ input [7:0] din,
+ output [M-1:0] dout
+);
+ wire [2*M-1:0] mask = {M{1'b1}};
+ assign dout = (mask << din[N-1:0]) >> M;
+endmodule
+
+module abc9_test024(input [3:0] i, output [3:0] o);
+abc9_test024_sub a(i[1:0], o[1:0]);
+endmodule
+
+module abc9_test024_sub(input [1:0] i, output [1:0] o);
+assign o = i;
+endmodule
+
+module abc9_test025(input [3:0] i, output [3:0] o);
+abc9_test024_sub a(i[2:1], o[2:1]);
+endmodule
diff --git a/tests/simple_abc9/run-test.sh b/tests/simple_abc9/run-test.sh
new file mode 100755
index 000000000..4935d41ad
--- /dev/null
+++ b/tests/simple_abc9/run-test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+OPTIND=1
+seed="" # default to no seed specified
+while getopts "S:" opt
+do
+ case "$opt" in
+ S) arg="${OPTARG#"${OPTARG%%[![:space:]]*}"}" # remove leading space
+ seed="SEED=$arg" ;;
+ esac
+done
+shift "$((OPTIND-1))"
+
+# check for Icarus Verilog
+if ! which iverilog > /dev/null ; then
+ echo "$0: Error: Icarus Verilog 'iverilog' not found."
+ exit 1
+fi
+
+cp ../simple/*.v .
+DOLLAR='?'
+exec ${MAKE:-make} -f ../tools/autotest.mk $seed *.v EXTRA_FLAGS="-p 'hierarchy; synth -run coarse; opt -full; techmap; abc9 -lut 4 -box ../abc.box; stat; check -assert; select -assert-none t:${DOLLAR}_NOT_ t:${DOLLAR}_AND_ %%'"