From 38f9e6c3a2dd09f46508e3fba1c69238ddc8d9f4 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Tue, 27 Aug 2024 08:50:10 +0300 Subject: [PATCH 01/58] -y flag for libyosys Python scripts This adds a Python equivalent to the `-c` option, where scripts importing `libyosys` can be imported and used. Most of the work for this was already done to enable Python passes a couple years back, so this is a relatively small changeset. --- kernel/driver.cc | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index 1e4cd0052..bd32872a2 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -241,6 +241,7 @@ int main(int argc, char **argv) std::string topmodule = ""; std::string perffile = ""; bool scriptfile_tcl = false; + bool scriptfile_python = false; bool print_banner = true; bool print_stats = true; bool call_abort = false; @@ -305,6 +306,11 @@ int main(int argc, char **argv) printf("\n"); printf(" -C\n"); printf(" enters TCL interatcive shell mode\n"); +#endif +#ifdef WITH_PYTHON + printf("\n"); + printf(" -y python_scriptfile\n"); + printf(" execute the python script"); #endif printf("\n"); printf(" -p command\n"); @@ -379,7 +385,7 @@ int main(int argc, char **argv) } int opt; - while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:r:D:P:E:x:B:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVCSgm:f:Hh:b:o:p:l:L:qv:tds:c:y:W:w:e:r:D:P:E:x:B:")) != -1) { switch (opt) { @@ -464,11 +470,19 @@ int main(int argc, char **argv) case 's': scriptfile = optarg; scriptfile_tcl = false; + scriptfile_python = false; run_shell = false; break; case 'c': scriptfile = optarg; scriptfile_tcl = true; + scriptfile_python = false; + run_shell = false; + break; + case 'y': + scriptfile = optarg; + scriptfile_tcl = false; + scriptfile_python = true; run_shell = false; break; case 'W': @@ -607,8 +621,9 @@ int main(int argc, char **argv) run_pass(vdef_cmd); } - if (scriptfile.empty() || !scriptfile_tcl) { - // Without a TCL script, arguments following '--' are also treated as frontend files + if (scriptfile.empty() || (!scriptfile_tcl && !scriptfile_python)) { + // Without a TCL or Python script, arguments following '--' are also + // treated as frontend files for (int i = optind; i < argc; ++i) frontend_files.push_back(argv[i]); } @@ -636,7 +651,28 @@ int main(int argc, char **argv) if (Tcl_EvalFile(interp, scriptfile.c_str()) != TCL_OK) log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp())); #else - log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n"); + log_error("Can't execute TCL script: this version of yosys is not built with TCL support enabled.\n"); +#endif + } else if (scriptfile_python) { +#ifdef WITH_PYTHON + PyObject *sys = PyImport_ImportModule("sys"); + PyObject *new_argv = PyList_New(argc - optind + 1); + PyList_SetItem(new_argv, 0, PyUnicode_FromString(scriptfile.c_str())); + for (int i = optind; i < argc; ++i) + PyList_SetItem(new_argv, i - optind + 1, PyUnicode_FromString(argv[i])); + + PyObject *old_argv = PyObject_GetAttrString(sys, "argv"); + PyObject_SetAttrString(sys, "argv", new_argv); + Py_DECREF(old_argv); + + FILE *scriptfp = fopen(scriptfile.c_str(), "r"); + if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) { + log_error("Python interpreter encountered an error:\n"); + log_flush(); + PyErr_Print(); + } +#else + log_error("Can't execute Python script: this version of yosys is not built with Python support enabled.\n"); #endif } else run_frontend(scriptfile, "script"); From 738b5eef0bcbf04eff1a263fd8177c9e24ed02da Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Tue, 10 Sep 2024 13:41:04 +0300 Subject: [PATCH 02/58] Add dirname of script file to sys.path This matches the behavior of running a Python interpreter, where the first element of sys.path is the dirname of the script being run. This allows importing of files and modules in the same directory without messing with PYTHONPATH or similar. --- kernel/driver.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index bd32872a2..0e0a8fa7a 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -660,11 +660,13 @@ int main(int argc, char **argv) PyList_SetItem(new_argv, 0, PyUnicode_FromString(scriptfile.c_str())); for (int i = optind; i < argc; ++i) PyList_SetItem(new_argv, i - optind + 1, PyUnicode_FromString(argv[i])); - + PyObject *old_argv = PyObject_GetAttrString(sys, "argv"); PyObject_SetAttrString(sys, "argv", new_argv); Py_DECREF(old_argv); - + + PyRun_SimpleString(("import os;sys.path.insert(0, os.path.dirname(os.path.abspath(\""+scriptfile+"\")))").c_str()); + FILE *scriptfp = fopen(scriptfile.c_str(), "r"); if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) { log_error("Python interpreter encountered an error:\n"); From 8dac27108efdb17313fbeb0a96b1eaae0ecaf2b7 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Wed, 11 Sep 2024 21:39:37 +0300 Subject: [PATCH 03/58] Typos --- kernel/driver.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index 0e0a8fa7a..d30b19c96 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -305,12 +305,12 @@ int main(int argc, char **argv) printf(" execute the commands in the tcl script file (see 'help tcl' for details)\n"); printf("\n"); printf(" -C\n"); - printf(" enters TCL interatcive shell mode\n"); + printf(" enters TCL interactive shell mode\n"); #endif #ifdef WITH_PYTHON printf("\n"); printf(" -y python_scriptfile\n"); - printf(" execute the python script"); + printf(" execute a python script with libyosys available as a built-in module\n"); #endif printf("\n"); printf(" -p command\n"); From 4cfdb7ab5031c40c5804612e5c84613c060b0977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 10 Sep 2024 20:42:55 +0200 Subject: [PATCH 04/58] Adjust operation naming in aigmap test --- tests/techmap/aigmap.ys | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/techmap/aigmap.ys b/tests/techmap/aigmap.ys index 6f6cdd1f2..deec440e2 100644 --- a/tests/techmap/aigmap.ys +++ b/tests/techmap/aigmap.ys @@ -84,14 +84,14 @@ assign name``_y2 = op name``_a2; `BIOP(xnor, ~^, 3, 3, 3) `BIOP(logic_and, &&, 3, 3, 1) `BIOP(logic_or, ||, 3, 3, 1) -`BIOP(logic_eq, ==, 3, 3, 1) -`BIOP(logic_ne, !=, 3, 3, 1) -`BIOP(logic_lt, <, 3, 3, 1) -`BIOP(logic_le, <=, 3, 3, 1) -`BIOP(logic_gt, >, 3, 3, 1) -`BIOP(logic_ge, >=, 3, 3, 1) +`BIOP(eq, ==, 3, 3, 1) +`BIOP(ne, !=, 3, 3, 1) +`BIOP(lt, <, 3, 3, 1) +`BIOP(le, <=, 3, 3, 1) +`BIOP(gt, >, 3, 3, 1) +`BIOP(ge, >=, 3, 3, 1) `UNOP(pos, +, 3) -`UNOP(neg, ~, 3) +`UNOP(not, ~, 3) `UNOP_REDUCE(logic_not, !, 3) `UNOP_REDUCE(reduce_and, &, 3) `UNOP_REDUCE(reduce_or, |, 3) From fb26945a20a5d20585567f90a9d1eaad279f78be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 10 Sep 2024 20:53:23 +0200 Subject: [PATCH 05/58] Start an 'aiger2' backend --- backends/aiger2/Makefile.inc | 1 + backends/aiger2/aiger.cc | 593 +++++++++++++++++++++++++++++++++++ tests/various/aiger2.ys | 156 +++++++++ 3 files changed, 750 insertions(+) create mode 100644 backends/aiger2/Makefile.inc create mode 100644 backends/aiger2/aiger.cc create mode 100644 tests/various/aiger2.ys diff --git a/backends/aiger2/Makefile.inc b/backends/aiger2/Makefile.inc new file mode 100644 index 000000000..494b8d6c6 --- /dev/null +++ b/backends/aiger2/Makefile.inc @@ -0,0 +1 @@ +OBJS += backends/aiger2/aiger.o diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc new file mode 100644 index 000000000..64cde2de4 --- /dev/null +++ b/backends/aiger2/aiger.cc @@ -0,0 +1,593 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) Martin Povišer + * + * 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. + * + */ + +// TODOs: +// - gracefully handling inout ports (an error message probably) + +#include "kernel/register.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +#define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa) + +#define REDUCE_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool) + +#define LOGIC_OPS ID($logic_and), ID($logic_or), ID($logic_not) + +#define GATE_OPS ID($_BUF_), ID($_NOT_), ID($_AND_), ID($_NAND_), ID($_OR_), ID($_NOR_), \ + ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_), \ + ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_) + +// TODO +//#define ARITH_OPS ID($add), ID($sub), ID($lt), ID($le), ID($ge), ID($gt), ID($neg) + +#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS /*, ARITH_OPS*/ + +template +struct Index { + struct HierCursor; + struct ModuleInfo { + Module *module; + int len; + dict windices; + dict suboffsets; + + bool indexing = false; + bool indexed = false; + }; + + dict modules; + + int index_wires(ModuleInfo &info, RTLIL::Module *m) + { + int sum = 0; + for (auto w : m->wires()) { + info.windices[w] = sum; + sum += w->width; + } + return sum; + } + + int index_module(RTLIL::Module *m) + { + ModuleInfo &info = modules[m]; + + if (info.indexed) + return info.len; + + if (info.indexing && !info.indexed) + log_error("Hierarchy error\n"); + + info.module = m; + int pos = index_wires(info, m); + + for (auto cell : m->cells()) { + if (cell->type.in(KNOWN_OPS)) + continue; + + Module *submodule = m->design->module(cell->type); + if (!submodule || submodule->get_blackbox_attribute()) + log_error("Unsupported cell type: %s (%s in %s)\n", + log_id(cell->type), log_id(cell), log_id(m)); + info.suboffsets[cell] = pos; + pos += index_module(submodule); + } + + info.len = pos; + return info.len; + } + + Design *design; + Module *top; + ModuleInfo *top_minfo; + std::vector lits; + + Index(RTLIL::Module *top) + : design(top->design), top(top) + { + modules.reserve(top->design->modules().size()); + int nlits = index_module(top); + log_debug("allocating for %d literals\n", nlits); + lits.resize(nlits, Writer::empty_lit()); + top_minfo = &modules.at(top); + } + + bool const_folding = false; + + Lit AND(Lit a, Lit b) + { + if (const_folding) { + if (a == Writer::CONST_FALSE || b == Writer::CONST_FALSE) + return Writer::CONST_FALSE; + if (a == Writer::CONST_TRUE) + return b; + if (b == Writer::CONST_TRUE) + return a; + } + + return (static_cast(this))->emit_gate(a, b); + } + + Lit NOT(Lit lit) + { + return Writer::negate(lit); + } + + Lit OR(Lit a, Lit b) + { + return NOT(AND(NOT(a), NOT(b))); + } + + Lit MUX(Lit a, Lit b, Lit s) + { + if (const_folding) { + if (a == b) + return a; + if (s == Writer::CONST_FALSE) + return a; + if (s == Writer::CONST_TRUE) + return b; + } + + return OR(AND(a, NOT(s)), AND(b, s)); + } + + Lit XOR(Lit a, Lit b) + { + return OR(AND(a, NOT(b)), AND(NOT(a), b)); + } + + Lit impl_op(HierCursor &cursor, Cell *cell, IdString oport, int obit) + { + if (cell->type.in(REDUCE_OPS, LOGIC_OPS) && obit != 0) { + return Writer::CONST_FALSE; + } else if (cell->type.in(REDUCE_OPS, ID($logic_not))) { + SigSpec inport = cell->getPort(ID::A); + + log_assert(inport.size() > 0); // TODO + + Lit acc = visit(cursor, inport[0]); + for (int i = 1; i < inport.size(); i++) { + Lit l = visit(cursor, inport[i]); + if (cell->type == ID($reduce_and)) { + acc = AND(acc, l); + } else if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not))) { + acc = OR(acc, l); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + acc = XOR(acc, l); + } + } + + if (!cell->type.in(ID($reduce_xnor), ID($logic_not))) + return acc; + else + return NOT(acc); + } else if (cell->type.in(ID($logic_and), ID($logic_or))) { + SigSpec aport = cell->getPort(ID::A); + SigSpec bport = cell->getPort(ID::B); + + log_assert(aport.size() > 0 && bport.size() > 0); // TODO + + Lit a = visit(cursor, aport[0]); + for (int i = 1; i < aport.size(); i++) { + Lit l = visit(cursor, aport[i]); + a = OR(a, l); + } + + Lit b = visit(cursor, bport[0]); + for (int i = 1; i < bport.size(); i++) { + Lit l = visit(cursor, bport[i]); + b = OR(b, l); + } + + if (cell->type == ID($logic_and)) + return AND(a, b); + else if (cell->type == ID($logic_or)) + return OR(a, b); + else + log_abort(); + } else if (cell->type.in(BITWISE_OPS, GATE_OPS)) { + SigSpec aport = cell->getPort(ID::A); + Lit a; + if (obit < aport.size()) { + a = visit(cursor, aport[obit]); + } else { + if (cell->getParam(ID::A_SIGNED).as_bool()) + a = visit(cursor, aport.msb()); + else + a = Writer::CONST_FALSE; + } + + if (cell->type.in(ID($buf), ID($_BUF_))) { + return a; + } else if (cell->type.in(ID($not), ID($_NOT_))) { + return NOT(a); + } else { + SigSpec bport = cell->getPort(ID::B); + Lit b; + if (obit < bport.size()) { + b = visit(cursor, bport[obit]); + } else { + if (cell->getParam(ID::B_SIGNED).as_bool()) + b = visit(cursor, bport.msb()); + else + b = Writer::CONST_FALSE; + } + + if (cell->type.in(ID($and), ID($_AND_))) { + return AND(a, b); + } else if (cell->type.in(ID($_NAND_))) { + return NOT(AND(a, b)); + } else if (cell->type.in(ID($or), ID($_OR_))) { + return OR(a, b); + } else if (cell->type.in(ID($_NOR_))) { + return NOT(OR(a, b)); + } else if (cell->type.in(ID($xor), ID($_XOR_))) { + return XOR(a, b); + } else if (cell->type.in(ID($xnor), ID($_XNOR_))) { + return NOT(XOR(a, b)); + } else if (cell->type.in(ID($_ANDNOT_))) { + return AND(a, NOT(b)); + } else if (cell->type.in(ID($_ORNOT_))) { + return OR(a, NOT(b)); + } else if (cell->type.in(ID($mux), ID($_MUX_))) { + Lit s = visit(cursor, cell->getPort(ID::S)); + return MUX(a, b, s); + } else if (cell->type.in(ID($_NMUX_))) { + Lit s = visit(cursor, cell->getPort(ID::S)[obit]); + return NOT(MUX(a, b, s)); + } else if (cell->type.in(ID($fa))) { + Lit c = visit(cursor, cell->getPort(ID::C)[obit]); + Lit ab = XOR(a, b); + if (oport == ID::Y) { + return XOR(ab, c); + } else /* oport == ID::X */ { + return OR(AND(a, b), AND(c, ab)); + } + } else if (cell->type.in(ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_))) { + Lit c, d; + + c = visit(cursor, cell->getPort(ID::C)[obit]); + if (/* 4 input types */ cell->type.in(ID($_AOI4_), ID($_OAI4_))) + d = visit(cursor, cell->getPort(ID::D)[obit]); + else + d = cell->type == ID($_AOI3_) ? 1 : 0; + + if (/* aoi */ cell->type.in(ID($_AOI3_), ID($_AOI4_))) + return NOT(OR(AND(a, b), AND(c, d))); + else + return NOT(AND(OR(a, b), OR(c, d))); + } else { + log_abort(); + } + } + } else { + log_abort(); + } + } + + struct HierCursor { + typedef std::pair Level; + std::vector levels; + int instance_offset = 0; + + HierCursor(ModuleInfo &top) + { + levels.push_back(Level(top, nullptr)); + } + + ModuleInfo ¤t_minfo() + { + log_assert(!levels.empty()); + return levels.back().first; + } + + int bitwire_index(SigBit bit) + { + log_assert(bit.wire != nullptr); + return instance_offset + current_minfo().windices[bit.wire] + bit.offset; + } + + Cell *exit() + { + log_assert(levels.size() > 1); + Cell *instance = levels.back().second; + + levels.pop_back(); + instance_offset -= current_minfo().suboffsets.at(instance); + + // return the instance we just exited + return instance; + } + + Module *enter(Index &index, Cell *cell) + { + Design *design = index.design; + auto &minfo = current_minfo(); + log_assert(minfo.suboffsets.count(cell)); + Module *def = design->module(cell->type); + log_assert(def); + levels.push_back(Level(index.modules.at(def), cell)); + instance_offset += minfo.suboffsets.at(cell); + + // return the module definition we just entered + return def; + } + }; + + int visit(HierCursor &cursor, SigBit bit) + { + if (!bit.wire) { + if (bit == State::S1) + return Writer::CONST_TRUE; + else if (bit == State::S0) + return Writer::CONST_FALSE; + else + log_error("Unhandled state %s\n", log_signal(bit)); + } + + int idx = cursor.bitwire_index(bit); + if (lits[idx] != Writer::empty_lit()) { + // literal already assigned + return lits[idx]; + } + + Lit ret; + if (!bit.wire->port_input) { + // an output of a cell + Cell *driver = bit.wire->driverCell(); + + if (driver->type.in(KNOWN_OPS)) { + ret = impl_op(cursor, driver, bit.wire->driverPort(), bit.offset); + } else { + Module *def = cursor.enter(*this, driver); + { + IdString portname = bit.wire->driverPort(); + Wire *w = def->wire(portname); + if (!w) + log_error("Output port %s on instance %s of %s doesn't exist\n", + log_id(portname), log_id(driver), log_id(def)); + if (bit.offset >= w->width) + log_error("Bit position %d of output port %s on instance %s of %s is out of range (port has width %d)\n", + bit.offset, log_id(portname), log_id(driver), log_id(def), w->width); + ret = visit(cursor, SigBit(w, bit.offset)); + } + cursor.exit(); + } + + } else { + // a module input: we cannot be the top module, otherwise + // the branch for pre-existing literals would have been taken + log_assert(cursor.levels.size() > 1); + + // step into the upper module + Cell *instance = cursor.exit(); + { + IdString portname = bit.wire->name; + if (!instance->hasPort(portname)) + log_error("Input port %s on instance %s of %s unconnected\n", + log_id(portname), log_id(instance), log_id(instance->type)); + auto &port = instance->getPort(portname); + if (bit.offset >= port.size()) + log_error("Bit %d of input port %s on instance %s of %s unconnected\n", + bit.offset, log_id(portname), log_id(instance), log_id(instance->type)); + ret = visit(cursor, port[bit.offset]); + } + cursor.enter(*this, instance); + } + + lits[idx] = ret; + return ret; + } + + void set_top_port(SigBit bit, Lit lit) + { + log_assert(bit.wire); + log_assert(bit.wire->module == top); + log_assert(bit.wire->port_input); + + lits[top_minfo->windices[bit.wire] + bit.offset] = lit; + } + + Lit get_top_port(SigBit bit) + { + HierCursor cursor(*top_minfo); + Lit ret = visit(cursor, bit); + log_assert(cursor.levels.size() == 1); + log_assert(cursor.instance_offset == 0); + return ret; + } +}; + +struct AigerWriter : Index { + typedef unsigned int Lit; + + const static Lit CONST_FALSE = 0; + const static Lit CONST_TRUE = 1; + + // for some reason having a 'const static int EMPTY_LIT' + // led to linkage errors + static Lit empty_lit() + { + return 0x80000000; + } + + static Lit negate(Lit lit) { + return lit ^ 1; + } + + std::ostream *f; + Lit lit_counter; + int ninputs, nlatches, noutputs, nands; + + void encode(int delta) + { + log_assert(delta >= 0); + unsigned int x = delta; + while (x & ~0x7f) { + f->put((x & 0x7f) | 0x80); + x = x >> 7; + } + f->put(x); + } + + Lit emit_gate(Lit a, Lit b) + { + Lit out = lit_counter; + nands++; + lit_counter += 2; + + if (a < b) std::swap(a, b); + encode(out - a); + encode(a - b); + return out; + } + + void reset_counters() + { + lit_counter = 2; + ninputs = nlatches = noutputs = nands = 0; + } + + void write_header() { + log_assert(lit_counter == (ninputs + nlatches + nands) * 2 + 2); + + char buf[128]; + snprintf(buf, sizeof(buf) - 1, "aig %08d %08d %08d %08d %08d\n", + ninputs + nlatches + nands, ninputs, nlatches, noutputs, nands); + f->seekp(0); + f->write(buf, strlen(buf)); + } + + void write(std::ostream *f) { + reset_counters(); + + // populate inputs + std::vector inputs; + for (auto w : top->wires()) + if (w->port_input) + for (int i = 0; i < w->width; i++) { + set_top_port(SigBit(w, i), lit_counter); + inputs.push_back(SigBit(w, i)); + lit_counter += 2; + ninputs++; + } + + this->f = f; + // start with the header + write_header(); + // insert padding where output literals will go (once known) + for (auto w : top->wires()) + if (w->port_output) { + for (auto bit : SigSpec(w)) { + (void) bit; + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", 0); + f->write(buf, strlen(buf)); + noutputs++; + } + } + auto data_start = f->tellp(); + + // now the guts + std::vector> outputs; + for (auto w : top->wires()) + if (w->port_output) { + for (auto bit : SigSpec(w)) + outputs.push_back({bit, get_top_port(bit)}); + } + auto data_end = f->tellp(); + + // revisit header and the list of outputs + write_header(); + for (auto pair : outputs) { + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", pair.second); + f->write(buf, strlen(buf)); + } + // double check we arrived at the same offset for the + // main data section + log_assert(data_start == f->tellp()); + + f->seekp(data_end); + int i = 0; + for (auto pair : outputs) { + if (pair.first.is_wire()) { + char buf[32]; + snprintf(buf, sizeof(buf) - 1, "o%d ", i); + f->write(buf, strlen(buf)); + std::string name = RTLIL::unescape_id(pair.first.wire->name); + f->write(name.data(), name.size()); + f->put('\n'); + } + i++; + } + i = 0; + for (auto bit : inputs) { + if (bit.is_wire()) { + char buf[32]; + snprintf(buf, sizeof(buf) - 1, "i%d ", i); + f->write(buf, strlen(buf)); + std::string name = RTLIL::unescape_id(bit.wire->name); + f->write(name.data(), name.size()); + f->put('\n'); + } + i++; + } + } + + AigerWriter(RTLIL::Module *top) + : Index(top) {} +}; + +struct Aiger2Backend : Backend { + Aiger2Backend() : Backend("aiger2", "write design to AIGER file (new)") + { + experimental(); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing AIGER2 backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + break; + } + extra_args(f, filename, args, argidx); + + Module *top = design->top_module(); + + if (!top || !design->selected_whole_module(top)) + log_cmd_error("No top module selected\n"); + + design->bufNormalize(true); + AigerWriter writer(top); + writer.write(f); + + // we are leaving the sacred land, un-bufnormalize + // (if not, this will lead to bugs: the buf-normalized + // flag must not be kept on past the code that can work + // with it) + design->bufNormalize(false); + } +} Aiger2Backend; + +PRIVATE_NAMESPACE_END diff --git a/tests/various/aiger2.ys b/tests/various/aiger2.ys new file mode 100644 index 000000000..61e6d275a --- /dev/null +++ b/tests/various/aiger2.ys @@ -0,0 +1,156 @@ +read_verilog -icells < Date: Wed, 11 Sep 2024 11:01:38 +0200 Subject: [PATCH 06/58] aiger2: Support `$pos` --- backends/aiger2/aiger.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 64cde2de4..89a23ff3f 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -38,7 +38,7 @@ PRIVATE_NAMESPACE_BEGIN // TODO //#define ARITH_OPS ID($add), ID($sub), ID($lt), ID($le), ID($ge), ID($gt), ID($neg) -#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS /*, ARITH_OPS*/ +#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos) /*, ARITH_OPS*/ template struct Index { @@ -203,7 +203,7 @@ struct Index { return OR(a, b); else log_abort(); - } else if (cell->type.in(BITWISE_OPS, GATE_OPS)) { + } else if (cell->type.in(BITWISE_OPS, GATE_OPS, ID($pos))) { SigSpec aport = cell->getPort(ID::A); Lit a; if (obit < aport.size()) { @@ -215,7 +215,7 @@ struct Index { a = Writer::CONST_FALSE; } - if (cell->type.in(ID($buf), ID($_BUF_))) { + if (cell->type.in(ID($buf), ID($pos), ID($_BUF_))) { return a; } else if (cell->type.in(ID($not), ID($_NOT_))) { return NOT(a); From 5671c101731c58c5285d2493527911fde67ec25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 11:02:40 +0200 Subject: [PATCH 07/58] aiger2: Add strashing option --- backends/aiger2/aiger.cc | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 89a23ff3f..533abc004 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -99,9 +99,15 @@ struct Index { ModuleInfo *top_minfo; std::vector lits; - Index(RTLIL::Module *top) - : design(top->design), top(top) + Index() { + } + + void setup(RTLIL::Module *top) + { + design = top->design; + this->top = top; + modules.reserve(top->design->modules().size()); int nlits = index_module(top); log_debug("allocating for %d literals\n", nlits); @@ -109,7 +115,9 @@ struct Index { top_minfo = &modules.at(top); } - bool const_folding = false; + bool const_folding = true; + bool strashing = false; + dict, int> cache; Lit AND(Lit a, Lit b) { @@ -122,7 +130,20 @@ struct Index { return a; } - return (static_cast(this))->emit_gate(a, b); + if (!strashing) { + return (static_cast(this))->emit_gate(a, b); + } else { + if (a < b) std::swap(a, b); + auto pair = std::make_pair(a, b); + + if (!cache.count(pair)) { + Lit nl = (static_cast(this))->emit_gate(a, b); + cache[pair] = nl; + return nl; + } else { + return cache.at(pair); + } + } } Lit NOT(Lit lit) @@ -552,9 +573,6 @@ struct AigerWriter : Index { i++; } } - - AigerWriter(RTLIL::Module *top) - : Index(top) {} }; struct Aiger2Backend : Backend { @@ -568,8 +586,13 @@ struct Aiger2Backend : Backend { log_header(design, "Executing AIGER2 backend.\n"); size_t argidx; + AigerWriter writer; + writer.const_folding = true; for (argidx = 1; argidx < args.size(); argidx++) { - break; + if (args[argidx] == "-strash") + writer.strashing = true; + else + break; } extra_args(f, filename, args, argidx); @@ -579,7 +602,7 @@ struct Aiger2Backend : Backend { log_cmd_error("No top module selected\n"); design->bufNormalize(true); - AigerWriter writer(top); + writer.setup(top); writer.write(f); // we are leaving the sacred land, un-bufnormalize From de8a2fb936e25eb54074595b129f02fc14144fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 11:03:14 +0200 Subject: [PATCH 08/58] aiger2: Fix duplicate symbols on multibit ports --- backends/aiger2/aiger.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 533abc004..a49b5bf1e 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -550,7 +550,7 @@ struct AigerWriter : Index { f->seekp(data_end); int i = 0; for (auto pair : outputs) { - if (pair.first.is_wire()) { + if (SigSpec(pair.first).is_wire()) { char buf[32]; snprintf(buf, sizeof(buf) - 1, "o%d ", i); f->write(buf, strlen(buf)); @@ -562,7 +562,7 @@ struct AigerWriter : Index { } i = 0; for (auto bit : inputs) { - if (bit.is_wire()) { + if (SigSpec(bit).is_wire()) { char buf[32]; snprintf(buf, sizeof(buf) - 1, "i%d ", i); f->write(buf, strlen(buf)); From e59387e5a9538ee32fdd3f8554e350d506b7b48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 11:10:43 +0200 Subject: [PATCH 09/58] aiger2: Add `aigsize` as a second user of index --- backends/aiger2/aiger.cc | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index a49b5bf1e..075bc129d 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -613,4 +613,68 @@ struct Aiger2Backend : Backend { } } Aiger2Backend; +struct AIGCounter : Index { + typedef int Lit; + const static Lit CONST_FALSE = -1; + const static Lit CONST_TRUE = 1; + static Lit empty_lit() { return 0; } + static Lit negate(Lit lit) { return -lit; } + int nvars = 1; + int ngates = 0; + + Lit emit_gate([[maybe_unused]] Lit a, [[maybe_unused]] Lit b) + { + ngates++; + return ++nvars; + } + + void count() { + // populate inputs + for (auto w : top->wires()) + if (w->port_input) + for (int i = 0; i < w->width; i++) + set_top_port(SigBit(w, i), ++nvars); + + for (auto w : top->wires()) + if (w->port_output) { + for (auto bit : SigSpec(w)) + (void) get_top_port(bit); + } + } +}; + +struct AigsizePass : Pass { + AigsizePass() : Pass("aigsize", "estimate AIG size for design") {} + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing AIGSIZE pass. (size design AIG)\n"); + + size_t argidx; + AIGCounter counter; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-strash") + counter.strashing = true; + else + break; + } + extra_args(args, argidx, design); + + Module *top = design->top_module(); + + if (!top || !design->selected_whole_module(top)) + log_cmd_error("No top module selected\n"); + + design->bufNormalize(true); + counter.setup(top); + counter.count(); + log("Counted %d gates\n", counter.ngates); + + // we are leaving the sacred land, un-bufnormalize + // (if not, this will lead to bugs: the buf-normalized + // flag must not be kept on past the code that can work + // with it) + design->bufNormalize(false); + } +} AigsizePass; + PRIVATE_NAMESPACE_END From d7128cb78787a6a5299c1c1c126ec673e8d440e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 21:08:03 +0200 Subject: [PATCH 10/58] aiger2: Use shorthands --- backends/aiger2/aiger.cc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 075bc129d..873a44129 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -42,6 +42,9 @@ PRIVATE_NAMESPACE_BEGIN template struct Index { + static constexpr Lit CFALSE = Writer::CONST_FALSE; + static constexpr Lit CTRUE = Writer::CONST_TRUE; + struct HierCursor; struct ModuleInfo { Module *module; @@ -122,11 +125,11 @@ struct Index { Lit AND(Lit a, Lit b) { if (const_folding) { - if (a == Writer::CONST_FALSE || b == Writer::CONST_FALSE) - return Writer::CONST_FALSE; - if (a == Writer::CONST_TRUE) + if (a == CFALSE || b == CFALSE) + return CFALSE; + if (a == CTRUE) return b; - if (b == Writer::CONST_TRUE) + if (b == CTRUE) return a; } @@ -161,9 +164,9 @@ struct Index { if (const_folding) { if (a == b) return a; - if (s == Writer::CONST_FALSE) + if (s == CFALSE) return a; - if (s == Writer::CONST_TRUE) + if (s == CTRUE) return b; } @@ -233,7 +236,7 @@ struct Index { if (cell->getParam(ID::A_SIGNED).as_bool()) a = visit(cursor, aport.msb()); else - a = Writer::CONST_FALSE; + a = CFALSE; } if (cell->type.in(ID($buf), ID($pos), ID($_BUF_))) { @@ -249,7 +252,7 @@ struct Index { if (cell->getParam(ID::B_SIGNED).as_bool()) b = visit(cursor, bport.msb()); else - b = Writer::CONST_FALSE; + b = CFALSE; } if (cell->type.in(ID($and), ID($_AND_))) { @@ -357,9 +360,9 @@ struct Index { { if (!bit.wire) { if (bit == State::S1) - return Writer::CONST_TRUE; + return CTRUE; else if (bit == State::S0) - return Writer::CONST_FALSE; + return CFALSE; else log_error("Unhandled state %s\n", log_signal(bit)); } From 8e29675a23539c958b2024fbc00021708a3dcdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 21:08:41 +0200 Subject: [PATCH 11/58] aiger2: Support `$bwmux`, comparison operators --- backends/aiger2/aiger.cc | 69 ++++++++++++++++++++++++++++++++++++---- tests/various/aiger2.ys | 15 +++++++-- 2 files changed, 76 insertions(+), 8 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 873a44129..bc22dac94 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -25,7 +25,8 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa) +#define BITWISE_OPS ID($buf), ID($not), ID($mux), ID($and), ID($or), ID($xor), ID($xnor), ID($fa), \ + ID($bwmux) #define REDUCE_OPS ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool) @@ -35,10 +36,12 @@ PRIVATE_NAMESPACE_BEGIN ID($_XOR_), ID($_XNOR_), ID($_ANDNOT_), ID($_ORNOT_), ID($_MUX_), ID($_NMUX_), \ ID($_AOI3_), ID($_OAI3_), ID($_AOI4_), ID($_OAI4_) -// TODO -//#define ARITH_OPS ID($add), ID($sub), ID($lt), ID($le), ID($ge), ID($gt), ID($neg) +#define CMP_OPS ID($eq), ID($ne), ID($lt), ID($le), ID($ge), ID($gt) -#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos) /*, ARITH_OPS*/ +// TODO +//#define ARITH_OPS ID($add), ID($sub), ID($neg) + +#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS /*, ARITH_OPS*/ template struct Index { @@ -178,10 +181,61 @@ struct Index { return OR(AND(a, NOT(b)), AND(NOT(a), b)); } + Lit XNOR(Lit a, Lit b) + { + return NOT(OR(AND(a, NOT(b)), AND(NOT(a), b))); + } + + Lit CARRY(Lit a, Lit b, Lit c) + { + if (const_folding) { + if (c == CTRUE) { + return OR(a, b); + } else if (c == CFALSE) { + return AND(a, b); + } + } + return OR(AND(a, b), AND(c, OR(a, b))); + } + Lit impl_op(HierCursor &cursor, Cell *cell, IdString oport, int obit) { - if (cell->type.in(REDUCE_OPS, LOGIC_OPS) && obit != 0) { - return Writer::CONST_FALSE; + if (cell->type.in(REDUCE_OPS, LOGIC_OPS, CMP_OPS) && obit != 0) { + return CFALSE; + } else if (cell->type.in(CMP_OPS)) { + SigSpec aport = cell->getPort(ID::A); + bool asigned = cell->getParam(ID::A_SIGNED).as_bool(); + SigSpec bport = cell->getPort(ID::B); + bool bsigned = cell->getParam(ID::B_SIGNED).as_bool(); + + int width = std::max(aport.size(), bport.size()) + 1; + aport.extend_u0(width, asigned); + bport.extend_u0(width, bsigned); + + if (cell->type.in(ID($eq), ID($ne))) { + int carry = CTRUE; + for (int i = 0; i < width; i++) { + Lit a = visit(cursor, aport[i]); + Lit b = visit(cursor, bport[i]); + carry = AND(carry, XNOR(a, b)); + } + return (cell->type == ID($eq)) ? carry : /* $ne */ NOT(carry); + } else if (cell->type.in(ID($lt), ID($le), ID($gt), ID($ge))) { + if (cell->type.in(ID($gt), ID($ge))) + std::swap(aport, bport); + int carry = cell->type.in(ID($le), ID($ge)) ? CFALSE : CTRUE; + Lit a, b; + // TODO: this might not be the most economic structure; revisit at a later date + for (int i = 0; i < width; i++) { + a = visit(cursor, aport[i]); + b = visit(cursor, bport[i]); + if (i != width - 1) + carry = CARRY(a, NOT(b), carry); + } + return XOR(carry, XNOR(a, b)); + } else { + log_abort(); + } } else if (cell->type.in(REDUCE_OPS, ID($logic_not))) { SigSpec inport = cell->getPort(ID::A); @@ -274,6 +328,9 @@ struct Index { } else if (cell->type.in(ID($mux), ID($_MUX_))) { Lit s = visit(cursor, cell->getPort(ID::S)); return MUX(a, b, s); + } else if (cell->type.in(ID($bwmux))) { + Lit s = visit(cursor, cell->getPort(ID::S)[obit]); + return MUX(a, b, s); } else if (cell->type.in(ID($_NMUX_))) { Lit s = visit(cursor, cell->getPort(ID::S)[obit]); return NOT(MUX(a, b, s)); diff --git a/tests/various/aiger2.ys b/tests/various/aiger2.ys index 61e6d275a..5d8c6e848 100644 --- a/tests/various/aiger2.ys +++ b/tests/various/aiger2.ys @@ -83,6 +83,12 @@ assign name``_y2 = op name``_a2; `BIOP(xnor, ~^, 3, 3, 3) `BIOP(logic_and, &&, 4, 3, 1) `BIOP(logic_or, ||, 3, 3, 2) +`BIOP(eq, ==, 3, 3, 1) +`BIOP(ne, !=, 3, 3, 1) +`BIOP(lt, <, 3, 3, 1) +`BIOP(le, <=, 3, 3, 1) +`BIOP(gt, >, 3, 3, 1) +`BIOP(ge, >=, 3, 3, 1) `UNOP(not, ~, 3) `UNOP_REDUCE(logic_not, !, 3) `UNOP_REDUCE(reduce_and, &, 3) @@ -97,6 +103,11 @@ wire [1:0] fa_a, fa_b, fa_c, fa_x, fa_y; \$fa #( .WIDTH(2) ) fa(.A(fa_a), .B(fa_b), .C(fa_c), .X(fa_x), .Y(fa_y)); + +wire [1:0] bwmux_a, bwmux_b, bwmux_s, bwmux_y; +\$bwmux #( + .WIDTH(2) +) bwmux(.A(bwmux_a), .B(bwmux_b), .S(bwmux_s), .Y(bwmux_y)); endmodule EOF @@ -110,8 +121,8 @@ select -clear delete test read_aiger -module_name test aiger2_ops.aig select -assert-none test/t:$_AND_ test/t:$_NOT_ %% test/c:* %D -miter -equiv -flatten gold test miter -sat -verify -prove trigger 0 miter +miter -equiv -make_outcmp -flatten gold test miter +sat -verify -show-ports -prove trigger 0 miter design -reset read_verilog -icells < Date: Wed, 11 Sep 2024 21:08:57 +0200 Subject: [PATCH 12/58] aiger2: Fix literal typing --- backends/aiger2/aiger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index bc22dac94..0c787f11d 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -413,7 +413,7 @@ struct Index { } }; - int visit(HierCursor &cursor, SigBit bit) + Lit visit(HierCursor &cursor, SigBit bit) { if (!bit.wire) { if (bit == State::S1) From dbc937b2a7482d243b16c5de1f508eee71b19a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 21:09:28 +0200 Subject: [PATCH 13/58] aiger2: Describe supported cells in help --- backends/aiger2/aiger.cc | 47 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 0c787f11d..34b9eb324 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -21,6 +21,7 @@ // - gracefully handling inout ports (an error message probably) #include "kernel/register.h" +#include "kernel/celltypes.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -641,7 +642,51 @@ struct Aiger2Backend : Backend { experimental(); } - void execute(std::ostream *&f, std::string filename, std::vector args, RTLIL::Design *design) override + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_aiger2 [options] [filename]\n"); + log("\n"); + log("Write the current design to an AIGER file.\n"); + log("\n"); + log("This command is able to ingest all combinational cells except for:\n"); + log("\n"); + pool supported = {KNOWN_OPS}; + CellTypes ct; + ct.setup_internals_eval(); + log(" "); + int col = 0; + for (auto pair : ct.cell_types) + if (!supported.count(pair.first)) { + if (col + pair.first.size() + 2 > 72) { + log("\n "); + col = 0; + } + col += pair.first.size() + 2; + log("%s, ", log_id(pair.first)); + } + log("\n"); + log("\n"); + log("And all combinational gates except for:\n"); + log("\n"); + CellTypes ct2; + ct2.setup_stdcells(); + log(" "); + col = 0; + for (auto pair : ct2.cell_types) + if (!supported.count(pair.first)) { + if (col + pair.first.size() + 2 > 72) { + log("\n "); + col = 0; + } + col += pair.first.size() + 2; + log("%s, ", log_id(pair.first)); + } + log("\n"); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override { log_header(design, "Executing AIGER2 backend.\n"); From 9db1ca83fc80a6ffc6bab52cc70924304ed54c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 11 Sep 2024 21:15:17 +0200 Subject: [PATCH 14/58] aiger2: Drop `empty_lit()` as a function --- backends/aiger2/aiger.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 34b9eb324..d300827d3 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -118,7 +118,7 @@ struct Index { modules.reserve(top->design->modules().size()); int nlits = index_module(top); log_debug("allocating for %d literals\n", nlits); - lits.resize(nlits, Writer::empty_lit()); + lits.resize(nlits, Writer::EMPTY_LIT); top_minfo = &modules.at(top); } @@ -426,7 +426,7 @@ struct Index { } int idx = cursor.bitwire_index(bit); - if (lits[idx] != Writer::empty_lit()) { + if (lits[idx] != Writer::EMPTY_LIT) { // literal already assigned return lits[idx]; } @@ -503,13 +503,7 @@ struct AigerWriter : Index { const static Lit CONST_FALSE = 0; const static Lit CONST_TRUE = 1; - - // for some reason having a 'const static int EMPTY_LIT' - // led to linkage errors - static Lit empty_lit() - { - return 0x80000000; - } + const static constexpr Lit EMPTY_LIT = std::numeric_limits::max(); static Lit negate(Lit lit) { return lit ^ 1; @@ -722,7 +716,7 @@ struct AIGCounter : Index { typedef int Lit; const static Lit CONST_FALSE = -1; const static Lit CONST_TRUE = 1; - static Lit empty_lit() { return 0; } + const static constexpr Lit EMPTY_LIT = 0; static Lit negate(Lit lit) { return -lit; } int nvars = 1; int ngates = 0; From 4976abb867c5e1dc0474975c46c966b238c5ce4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:16:15 +0200 Subject: [PATCH 15/58] read_liberty: Optionally import unit delay arcs --- frontends/liberty/liberty.cc | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/frontends/liberty/liberty.cc b/frontends/liberty/liberty.cc index 16fed54f2..e183cbf10 100644 --- a/frontends/liberty/liberty.cc +++ b/frontends/liberty/liberty.cc @@ -465,6 +465,9 @@ struct LibertyFrontend : public Frontend { log(" -setattr \n"); log(" set the specified attribute (to the value 1) on all loaded modules\n"); log("\n"); + log(" -unit_delay\n"); + log(" import combinational timing arcs under the unit delay model\n"); + log("\n"); } void execute(std::istream *&f, std::string filename, std::vector args, RTLIL::Design *design) override { @@ -475,6 +478,7 @@ struct LibertyFrontend : public Frontend { bool flag_ignore_miss_func = false; bool flag_ignore_miss_dir = false; bool flag_ignore_miss_data_latch = false; + bool flag_unit_delay = false; std::vector attributes; size_t argidx; @@ -514,6 +518,10 @@ struct LibertyFrontend : public Frontend { attributes.push_back(RTLIL::escape_id(args[++argidx])); continue; } + if (arg == "-unit_delay") { + flag_unit_delay = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -652,6 +660,7 @@ struct LibertyFrontend : public Frontend { continue; RTLIL::Wire *wire = module->wires_.at(RTLIL::escape_id(node->args.at(0))); + log_assert(wire); if (dir && dir->value == "inout") { wire->port_input = true; @@ -690,6 +699,43 @@ struct LibertyFrontend : public Frontend { } module->connect(RTLIL::SigSig(wire, out_sig)); } + + if (flag_unit_delay) { + pool done; + + for (auto timing : node->children) + if (timing->id == "timing" && timing->args.empty()) { + auto type = timing->find("timing_type"); + auto related_pin = timing->find("related_pin"); + if (!type || type->value != "combinational" || !related_pin) + continue; + + Wire *related = module->wire(RTLIL::escape_id(related_pin->value)); + if (!related) + log_error("Failed to find related pin %s for timing of pin %s on %s\n", + related_pin->value.c_str(), log_id(wire), log_id(module)); + + if (done.count(related)) + continue; + + RTLIL::Cell *spec = module->addCell(NEW_ID, ID($specify2)); + spec->setParam(ID::SRC_WIDTH, 1); + spec->setParam(ID::DST_WIDTH, 1); + spec->setParam(ID::T_FALL_MAX, 1000); + spec->setParam(ID::T_FALL_TYP, 1000); + spec->setParam(ID::T_FALL_MIN, 1000); + spec->setParam(ID::T_RISE_MAX, 1000); + spec->setParam(ID::T_RISE_TYP, 1000); + spec->setParam(ID::T_RISE_MIN, 1000); + spec->setParam(ID::SRC_DST_POL, false); + spec->setParam(ID::SRC_DST_PEN, false); + spec->setParam(ID::FULL, false); + spec->setPort(ID::EN, Const(1, 1)); + spec->setPort(ID::SRC, related); + spec->setPort(ID::DST, wire); + done.insert(related); + } + } } } From 31476e89b6524069b5ebd75e9e80700afdc58f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:09:47 +0200 Subject: [PATCH 16/58] tests: Avoid temporary script file --- tests/liberty/.gitignore | 1 - tests/liberty/run-test.sh | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/liberty/.gitignore b/tests/liberty/.gitignore index bf5ba57dc..8763aaac5 100644 --- a/tests/liberty/.gitignore +++ b/tests/liberty/.gitignore @@ -1,4 +1,3 @@ *.log -test.ys *.filtered *.verilogsim diff --git a/tests/liberty/run-test.sh b/tests/liberty/run-test.sh index 07d2cce1c..771bb69f7 100755 --- a/tests/liberty/run-test.sh +++ b/tests/liberty/run-test.sh @@ -3,10 +3,7 @@ set -e for x in *.lib; do echo "Testing on $x.." - echo "read_verilog small.v" > test.ys - echo "synth -top small" >> test.ys - echo "dfflibmap -info -liberty ${x}" >> test.ys - ../../yosys -ql ${x%.lib}.log -s test.ys + ../../yosys -p "read_verilog small.v; synth -top small; dfflibmap -info -liberty ${x}" -ql ${x%.lib}.log ../../yosys-filterlib - $x 2>/dev/null > $x.filtered ../../yosys-filterlib -verilogsim $x > $x.verilogsim diff $x.filtered $x.filtered.ok && diff $x.verilogsim $x.verilogsim.ok From d5756eb9be6de45bc108fe5c1c5557491f4e96e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:14:52 +0200 Subject: [PATCH 17/58] tests: Add trivial liberty -unit_delay test --- tests/liberty/run-test.sh | 5 +++++ tests/liberty/unit_delay.ys | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 tests/liberty/unit_delay.ys diff --git a/tests/liberty/run-test.sh b/tests/liberty/run-test.sh index 771bb69f7..ff5b20d74 100755 --- a/tests/liberty/run-test.sh +++ b/tests/liberty/run-test.sh @@ -8,3 +8,8 @@ for x in *.lib; do ../../yosys-filterlib -verilogsim $x > $x.verilogsim diff $x.filtered $x.filtered.ok && diff $x.verilogsim $x.verilogsim.ok done + +for x in *.ys; do + echo "Running $x.." + ../../yosys -q -s $x -l ${x%.ys}.log +done diff --git a/tests/liberty/unit_delay.ys b/tests/liberty/unit_delay.ys new file mode 100644 index 000000000..8dd409183 --- /dev/null +++ b/tests/liberty/unit_delay.ys @@ -0,0 +1,3 @@ +# Nothing gets imported: the file lacks timing data +read_liberty -wb -unit_delay normal.lib +select -assert-none =*/t:$specify* From 6c1fa45995f690ba1c8f8412ab09d938f83d9130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 16 Sep 2024 17:20:34 +0200 Subject: [PATCH 18/58] aiger2: Ingest `$pmux` --- backends/aiger2/aiger.cc | 41 +++++++++++++++++++++++++++++++++++++++- tests/various/aiger2.ys | 28 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index d300827d3..c0b9a0d7a 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -42,7 +42,7 @@ PRIVATE_NAMESPACE_BEGIN // TODO //#define ARITH_OPS ID($add), ID($sub), ID($neg) -#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS /*, ARITH_OPS*/ +#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, ID($pmux) /*, ARITH_OPS*/ template struct Index { @@ -199,6 +199,28 @@ struct Index { return OR(AND(a, b), AND(c, OR(a, b))); } + Lit REDUCE(std::vector lits, bool op_xor=false) + { + std::vector next; + while (lits.size() > 1) { + next.clear(); + for (int i = 0; i < lits.size(); i += 2) { + if (i + 1 >= lits.size()) { + next.push_back(lits[i]); + } else { + Lit a = lits[i], b = lits[i + 1]; + next.push_back(op_xor ? XOR(a, b) : AND(a, b)); + } + } + next.swap(lits); + } + + if (lits.empty()) + return op_xor ? CFALSE : CTRUE; + else + return lits.front(); + } + Lit impl_op(HierCursor &cursor, Cell *cell, IdString oport, int obit) { if (cell->type.in(REDUCE_OPS, LOGIC_OPS, CMP_OPS) && obit != 0) { @@ -360,6 +382,23 @@ struct Index { log_abort(); } } + } else if (cell->type == ID($pmux)) { + SigSpec aport = cell->getPort(ID::A); + SigSpec bport = cell->getPort(ID::B); + SigSpec sport = cell->getPort(ID::S); + int width = aport.size(); + + Lit a = visit(cursor, aport[obit]); + + std::vector bar, sels; + for (int i = 0; i < sport.size(); i++) { + Lit s = visit(cursor, sport[i]); + Lit b = visit(cursor, bport[width * i + obit]); + bar.push_back(NOT(AND(s, b))); + sels.push_back(NOT(s)); + } + + return OR(AND(REDUCE(sels), a), NOT(REDUCE(bar))); } else { log_abort(); } diff --git a/tests/various/aiger2.ys b/tests/various/aiger2.ys index 5d8c6e848..0efb5dd76 100644 --- a/tests/various/aiger2.ys +++ b/tests/various/aiger2.ys @@ -165,3 +165,31 @@ read_aiger -module_name test aiger2_ops.aig select -assert-none test/t:$_AND_ test/t:$_NOT_ %% test/c:* %D miter -equiv -flatten gold test miter sat -verify -prove trigger 0 miter + +design -reset +read_verilog -icells < Date: Mon, 16 Sep 2024 17:25:17 +0200 Subject: [PATCH 19/58] aiger2: Use `REDUCE` for reduction ops --- backends/aiger2/aiger.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index c0b9a0d7a..0a949be48 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -262,21 +262,21 @@ struct Index { } else if (cell->type.in(REDUCE_OPS, ID($logic_not))) { SigSpec inport = cell->getPort(ID::A); - log_assert(inport.size() > 0); // TODO - - Lit acc = visit(cursor, inport[0]); - for (int i = 1; i < inport.size(); i++) { - Lit l = visit(cursor, inport[i]); - if (cell->type == ID($reduce_and)) { - acc = AND(acc, l); + std::vector lits; + for (int i = 0; i < inport.size(); i++) { + Lit lit = visit(cursor, inport[i]); + if (cell->type.in(ID($reduce_and), ID($reduce_xor), ID($reduce_xnor))) { + lits.push_back(lit); } else if (cell->type.in(ID($reduce_or), ID($reduce_bool), ID($logic_not))) { - acc = OR(acc, l); - } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { - acc = XOR(acc, l); + lits.push_back(NOT(lit)); + } else { + log_abort(); } } - if (!cell->type.in(ID($reduce_xnor), ID($logic_not))) + Lit acc = REDUCE(lits, cell->type.in(ID($reduce_xor), ID($reduce_xnor))); + + if (!cell->type.in(ID($reduce_xnor), ID($reduce_or), ID($reduce_bool))) return acc; else return NOT(acc); From 6cecf19ff4929ef04ce6915b4360f4d9b1782075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 16 Sep 2024 18:26:02 +0200 Subject: [PATCH 20/58] aiger2: Ingest `$bmux` --- backends/aiger2/aiger.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 0a949be48..5521a8133 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -42,7 +42,8 @@ PRIVATE_NAMESPACE_BEGIN // TODO //#define ARITH_OPS ID($add), ID($sub), ID($neg) -#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, ID($pmux) /*, ARITH_OPS*/ +#define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, \ + ID($pmux), ID($bmux) /*, ARITH_OPS*/ template struct Index { @@ -399,6 +400,25 @@ struct Index { } return OR(AND(REDUCE(sels), a), NOT(REDUCE(bar))); + } else if (cell->type == ID($bmux)) { + SigSpec aport = cell->getPort(ID::A); + SigSpec sport = cell->getPort(ID::S); + int width = cell->getParam(ID::WIDTH).as_int(); + + std::vector data; + for (int i = obit; i < aport.size(); i += width) + data.push_back(visit(cursor, aport[i])); + + std::vector next; + for (int i = 0; i < sport.size(); i++) { + Lit s = visit(cursor, sport[i]); + next.clear(); + for (int j = 0; j < data.size(); j += 2) + next.push_back(MUX(data[j], data[j + 1], s)); + data.swap(next); + } + log_assert(data.size() == 1); + return data[0]; } else { log_abort(); } From 1ab7f2993300e5444655b3e7aa8807f2f3bfa180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 11:55:04 +0200 Subject: [PATCH 21/58] Start read_xaiger2 -sc_mapping --- frontends/aiger2/Makefile.inc | 2 + frontends/aiger2/xaiger.cc | 275 ++++++++++++++++++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 frontends/aiger2/Makefile.inc create mode 100644 frontends/aiger2/xaiger.cc diff --git a/frontends/aiger2/Makefile.inc b/frontends/aiger2/Makefile.inc new file mode 100644 index 000000000..b4a9f6b89 --- /dev/null +++ b/frontends/aiger2/Makefile.inc @@ -0,0 +1,2 @@ + +OBJS += frontends/aiger2/xaiger.o diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc new file mode 100644 index 000000000..d2c2aa4b2 --- /dev/null +++ b/frontends/aiger2/xaiger.cc @@ -0,0 +1,275 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) Martin Povišer + * + * 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/register.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +uint32_t read_be32(std::istream &f) { + return ((uint32_t) f.get() << 24) | + ((uint32_t) f.get() << 16) | + ((uint32_t) f.get() << 8) | (uint32_t) f.get(); +} + +IdString read_idstring(std::istream &f) +{ + std::string str; + std::getline(f, str, '\0'); + if (!f.good()) + log_error("failed to read string\n"); + return RTLIL::escape_id(str); +} + +struct Xaiger2Frontend : public Frontend { + Xaiger2Frontend() : Frontend("xaiger2", "read XAIGER file (new)") {} + + void read_sc_mapping(std::istream *&f, std::string filename, std::vector args, Design *design) + { + IdString module_name; + std::string map_filename; + + size_t argidx; + for (argidx = 2; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-module_name" && argidx + 1 < args.size()) { + module_name = RTLIL::escape_id(args[++argidx]); + continue; + } + if (arg == "-map2" && argidx + 1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + break; + } + extra_args(f, filename, args, argidx, true); + + if (map_filename.empty()) + log_error("A '-map2' argument required\n"); + if (module_name.empty()) + log_error("A '-module_name' argument required\n"); + + Module *module = design->module(module_name); + if (!module) + log_error("Module '%s' not found\n", log_id(module_name)); + + std::ifstream map_file; + map_file.open(map_filename); + if (!map_file) + log_error("Failed to open map file '%s'\n", map_filename.c_str()); + + unsigned int M, I, L, O, A; + std::string header; + if (!(*f >> header >> M >> I >> L >> O >> A) || header != "aig") + log_error("Bad header\n"); + std::string line; + std::getline(*f, line); + log_debug("M=%u I=%u L=%u O=%u A=%u\n", M, I, L, O, A); + + if (L != 0) + log_error("Latches unsupported\n"); + if (I + L + A != M) + log_error("Inconsistent header\n"); + + std::vector outputs; + for (int i = 0; i < O; i++) { + int po; + *f >> po; + log_assert(f->get() == '\n'); + outputs.push_back(po); + } + + std::vector bits(2 + 2*M, RTLIL::Sm); + bits[0] = RTLIL::S0; + bits[1] = RTLIL::S1; + + std::string type; + while (map_file >> type) { + if (type == "pi") { + int pi_idx; + int woffset; + std::string name; + if (!(map_file >> pi_idx >> woffset >> name)) + log_error("Bad map file (1)\n"); + int lit = (2 * pi_idx) + 2; + if (lit < 0 || lit >= bits.size()) + log_error("Bad map file (2)\n"); + Wire *w = module->wire(name); + if (!w || woffset < 0 || woffset >= w->width) + log_error("Map file references non-existent signal bit %s[%d]\n", + name.c_str(), woffset); + bits[lit] = SigBit(w, woffset); + } else { + std::string scratch; + std::getline(map_file, scratch); + } + } + + for (int i = 0; i < A; i++) { + while (f->get() & 0x80 && !f->eof()); + while (f->get() & 0x80 && !f->eof()); + } + + if (f->get() != 'c') + log_error("Missing 'c' ahead of extensions\n"); + if (f->peek() == '\n') + f->get(); + + bool read_mapping = false; + uint32_t no_cells, no_instances; + for (int c = f->get(); c != EOF; c = f->get()) { + if (c == 'M') { + uint32_t len = read_be32(*f); + read_mapping = true; + + no_cells = read_be32(*f); + no_instances = read_be32(*f); + + log_debug("M: len=%u no_cells=%u no_instances=%u\n", len, no_cells, no_instances); + + struct MappingCell { + RTLIL::IdString type; + RTLIL::IdString out; + std::vector ins; + }; + std::vector cells; + cells.resize(no_cells); + + for (unsigned i = 0; i < no_cells; ++i) { + auto &cell = cells[i]; + cell.type = read_idstring(*f); + cell.out = read_idstring(*f); + uint32_t nins = read_be32(*f); + for (uint32_t j = 0; j < nins; j++) + cell.ins.push_back(read_idstring(*f)); + log_debug("M: Cell %s (out %s, ins", log_id(cell.type), log_id(cell.out)); + for (auto in : cell.ins) + log_debug(" %s", log_id(in)); + log_debug(")\n"); + } + + for (unsigned i = 0; i < no_instances; ++i) { + uint32_t cell_id = read_be32(*f); + uint32_t out_lit = read_be32(*f); + + log_assert(out_lit < bits.size()); + log_assert(bits[out_lit] == RTLIL::Sm); + log_assert(cell_id < cells.size()); + auto &cell = cells[cell_id]; + Cell *instance = module->addCell(NEW_ID, cell.type); + auto out_w = module->addWire(NEW_ID); + instance->setPort(cell.out, out_w); + bits[out_lit] = out_w; + for (auto in : cell.ins) { + uint32_t in_lit = read_be32(*f); + log_assert(out_lit < bits.size()); + log_assert(bits[in_lit] != RTLIL::Sm); + instance->setPort(in, bits[in_lit]); + } + } + } else if (c == '\n') { + break; + } else { + uint32_t len = read_be32(*f); + f->ignore(len); + log_debug("section '%c' (%d): ignoring %d bytes\n", c, c, len); + } + } + + if (!read_mapping) + log_error("Missing mapping (no 'M' section)\n"); + + while (true) { + std::string scratch; + std::getline(*f, scratch); + if (f->eof()) + break; + log_assert(!f->fail()); + log("input file: %s\n", scratch.c_str()); + } + + log("Read %d instances with cell library of size %d.\n", + no_instances, no_cells); + + // TODO + map_file.close(); + map_file.open(map_filename); + while (map_file >> type) { + if (type == "po") { + int po_idx; + int woffset; + std::string name; + if (!(map_file >> po_idx >> woffset >> name)) + log_error("Bad map file (3)\n"); + if (po_idx < 0 || po_idx >= outputs.size()) + log_error("Bad map file (4)\n"); + int lit = outputs[po_idx]; + if (lit < 0 || lit >= bits.size()) + log_error("Bad map file (5)\n"); + if (bits[lit] == RTLIL::Sm) + log_error("Bad map file (6)\n"); + Wire *w = module->wire(name); + if (!w || woffset < 0 || woffset >= w->width) + log_error("Map file references non-existent signal bit %s[%d]\n", + name.c_str(), woffset); + module->connect(SigBit(w, woffset), bits[lit]); + } else if (type == "pseudopo") { + int po_idx; + int poffset; + std::string box_name; + std::string box_port; + if (!(map_file >> po_idx >> poffset >> box_name >> box_port)) + log_error("Bad map file (7)\n"); + if (po_idx < 0 || po_idx >= outputs.size()) + log_error("Bad map file (8)\n"); + int lit = outputs[po_idx]; + if (lit < 0 || lit >= bits.size()) + log_error("Bad map file (9)\n"); + if (bits[lit] == RTLIL::Sm) + log_error("Bad map file (10)\n"); + Cell *cell = module->cell(box_name); + if (!cell || !cell->hasPort(box_port)) + log_error("Map file references non-existent box port %s/%s\n", + box_name.c_str(), box_port.c_str()); + SigSpec &port = cell->connections_[box_port]; + if (poffset < 0 || poffset >= port.size()) + log_error("Map file references non-existent box port bit %s/%s[%d]\n", + box_name.c_str(), box_port.c_str(), poffset); + port[poffset] = bits[lit]; + } else { + std::string scratch; + std::getline(map_file, scratch); + } + } + } + + void execute(std::istream *&f, std::string filename, std::vector args, Design *design) override + { + log_header(design, "Executing XAIGER2 frontend.\n"); + + if (args.size() > 1 && args[1] == "-sc_mapping") { + read_sc_mapping(f, filename, args, design); + return; + } + + log_cmd_error("Mode '-sc_mapping' must be selected\n"); + } +} Xaiger2Frontend; + +PRIVATE_NAMESPACE_END From 72d65063c32adce9eb2e33bee41736e8339fbea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:38:05 +0200 Subject: [PATCH 22/58] aiger2: Ignore benign cells --- backends/aiger2/aiger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 5521a8133..c6386b783 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -87,7 +87,7 @@ struct Index { int pos = index_wires(info, m); for (auto cell : m->cells()) { - if (cell->type.in(KNOWN_OPS)) + if (cell->type.in(KNOWN_OPS) || cell->type.in(ID($scopeinfo), ID($specify2), ID($specify3))) continue; Module *submodule = m->design->module(cell->type); From 2a3e907da847342da56fd7a067e4f288213c36db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:39:05 +0200 Subject: [PATCH 23/58] aiger2: Adjust typing --- backends/aiger2/aiger.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index c6386b783..d205f8508 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -125,7 +125,7 @@ struct Index { bool const_folding = true; bool strashing = false; - dict, int> cache; + dict, Lit> cache; Lit AND(Lit a, Lit b) { @@ -602,7 +602,7 @@ struct AigerWriter : Index { } void write_header() { - log_assert(lit_counter == (ninputs + nlatches + nands) * 2 + 2); + log_assert(lit_counter == (Lit) (ninputs + nlatches + nands) * 2 + 2); char buf[128]; snprintf(buf, sizeof(buf) - 1, "aig %08d %08d %08d %08d %08d\n", From ea765686b69c889f0cef7a5f4e0e670a4ab0b6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:41:05 +0200 Subject: [PATCH 24/58] aiger2: Adjust hierarchy/port handling --- backends/aiger2/aiger.cc | 185 ++++++++++++++++++++++++++++++--------- tests/various/aiger2.ys | 2 +- 2 files changed, 145 insertions(+), 42 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index d205f8508..5e58a9ebc 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -19,6 +19,7 @@ // TODOs: // - gracefully handling inout ports (an error message probably) +// - undriven wires #include "kernel/register.h" #include "kernel/celltypes.h" @@ -56,6 +57,7 @@ struct Index { int len; dict windices; dict suboffsets; + pool found_blackboxes; bool indexing = false; bool indexed = false; @@ -73,6 +75,10 @@ struct Index { return sum; } + bool flatten = false; + bool inline_whiteboxes = false; + bool allow_blackboxes = false; + int index_module(RTLIL::Module *m) { ModuleInfo &info = modules[m]; @@ -91,11 +97,21 @@ struct Index { continue; Module *submodule = m->design->module(cell->type); - if (!submodule || submodule->get_blackbox_attribute()) - log_error("Unsupported cell type: %s (%s in %s)\n", - log_id(cell->type), log_id(cell), log_id(m)); - info.suboffsets[cell] = pos; - pos += index_module(submodule); + + if (submodule && flatten && + !submodule->get_bool_attribute(ID::keep_hierarchy) && + !submodule->get_blackbox_attribute(inline_whiteboxes)) { + info.suboffsets[cell] = pos; + pos += index_module(submodule); + } else { + if (allow_blackboxes) { + info.found_blackboxes.insert(cell); + } else { + if (!submodule || submodule->get_blackbox_attribute()) + log_error("Unsupported cell type: %s (%s in %s)\n", + log_id(cell->type), log_id(cell), log_id(m)); + } + } } info.len = pos; @@ -429,30 +445,36 @@ struct Index { std::vector levels; int instance_offset = 0; - HierCursor(ModuleInfo &top) + HierCursor() { - levels.push_back(Level(top, nullptr)); } - ModuleInfo ¤t_minfo() + ModuleInfo &leaf_minfo(Index &index) { - log_assert(!levels.empty()); - return levels.back().first; + if (levels.empty()) + return *index.top_minfo; + else + return levels.back().first; } - int bitwire_index(SigBit bit) + Module *leaf_module(Index &index) + { + return leaf_minfo(index).module; + } + + int bitwire_index(Index &index, SigBit bit) { log_assert(bit.wire != nullptr); - return instance_offset + current_minfo().windices[bit.wire] + bit.offset; + return instance_offset + leaf_minfo(index).windices[bit.wire] + bit.offset; } - Cell *exit() + Cell *exit(Index &index) { - log_assert(levels.size() > 1); + log_assert(!levels.empty()); Cell *instance = levels.back().second; levels.pop_back(); - instance_offset -= current_minfo().suboffsets.at(instance); + instance_offset -= leaf_minfo(index).suboffsets.at(instance); // return the instance we just exited return instance; @@ -461,7 +483,7 @@ struct Index { Module *enter(Index &index, Cell *cell) { Design *design = index.design; - auto &minfo = current_minfo(); + auto &minfo = leaf_minfo(index); log_assert(minfo.suboffsets.count(cell)); Module *def = design->module(cell->type); log_assert(def); @@ -471,6 +493,47 @@ struct Index { // return the module definition we just entered return def; } + + bool is_top() + { + return levels.empty(); + } + + std::string path() + { + std::string ret; + bool first = true; + for (auto pair : levels) { + if (!first) + ret += "."; + if (!pair.second) + ret += RTLIL::unescape_id(pair.first.module->name); + else + ret += RTLIL::unescape_id(pair.second->name); + first = false; + } + return ret; + } + + int hash() const + { + int hash = 0; + for (auto pair : levels) + hash += (uintptr_t) pair.second; + return hash; + } + + bool operator==(const HierCursor &other) const + { + if (levels.size() != other.levels.size()) + return false; + + for (int i = 0; i < levels.size(); i++) + if (levels[i].second != other.levels[i].second) + return false; + + return true; + } }; Lit visit(HierCursor &cursor, SigBit bit) @@ -484,7 +547,7 @@ struct Index { log_error("Unhandled state %s\n", log_signal(bit)); } - int idx = cursor.bitwire_index(bit); + int idx = cursor.bitwire_index(*this, bit); if (lits[idx] != Writer::EMPTY_LIT) { // literal already assigned return lits[idx]; @@ -510,16 +573,15 @@ struct Index { bit.offset, log_id(portname), log_id(driver), log_id(def), w->width); ret = visit(cursor, SigBit(w, bit.offset)); } - cursor.exit(); + cursor.exit(*this); } - } else { // a module input: we cannot be the top module, otherwise // the branch for pre-existing literals would have been taken - log_assert(cursor.levels.size() > 1); + log_assert(!cursor.is_top()); // step into the upper module - Cell *instance = cursor.exit(); + Cell *instance = cursor.exit(*this); { IdString portname = bit.wire->name; if (!instance->hasPort(portname)) @@ -538,23 +600,54 @@ struct Index { return ret; } - void set_top_port(SigBit bit, Lit lit) + Lit &pi_literal(SigBit bit, HierCursor *cursor=nullptr) { log_assert(bit.wire); - log_assert(bit.wire->module == top); - log_assert(bit.wire->port_input); - lits[top_minfo->windices[bit.wire] + bit.offset] = lit; + if (!cursor) { + log_assert(bit.wire->module == top); + log_assert(bit.wire->port_input); + return lits[top_minfo->windices[bit.wire] + bit.offset]; + } else { + log_assert(bit.wire->module == cursor->leaf_module(*this)); + return lits[cursor->bitwire_index(*this, bit)]; + } } - Lit get_top_port(SigBit bit) + Lit eval_po(SigBit bit, HierCursor *cursor=nullptr) { - HierCursor cursor(*top_minfo); - Lit ret = visit(cursor, bit); - log_assert(cursor.levels.size() == 1); - log_assert(cursor.instance_offset == 0); + Lit ret; + if (!cursor) { + HierCursor cursor_; + ret = visit(cursor_, bit); + log_assert(cursor_.is_top()); + log_assert(cursor_.instance_offset == 0); + } else { + ret = visit(*cursor, bit); + } return ret; } + + void visit_hierarchy(std::function f, + HierCursor &cursor) + { + f(cursor); + + ModuleInfo &minfo = cursor.leaf_minfo(*this); + for (auto cell : minfo.module->cells()) { + if (minfo.suboffsets.count(cell)) { + cursor.enter(*this, cell); + visit_hierarchy(f, cursor); + cursor.exit(*this); + } + } + } + + void visit_hierarchy(std::function f) + { + HierCursor cursor; + visit_hierarchy(f, cursor); + } }; struct AigerWriter : Index { @@ -616,20 +709,25 @@ struct AigerWriter : Index { // populate inputs std::vector inputs; - for (auto w : top->wires()) + for (auto id : top->ports) { + Wire *w = top->wire(id); + log_assert(w); if (w->port_input) for (int i = 0; i < w->width; i++) { - set_top_port(SigBit(w, i), lit_counter); + pi_literal(SigBit(w, i)) = lit_counter; inputs.push_back(SigBit(w, i)); lit_counter += 2; ninputs++; } + } this->f = f; // start with the header write_header(); // insert padding where output literals will go (once known) - for (auto w : top->wires()) + for (auto id : top->ports) { + Wire *w = top->wire(id); + log_assert(w); if (w->port_output) { for (auto bit : SigSpec(w)) { (void) bit; @@ -639,6 +737,7 @@ struct AigerWriter : Index { noutputs++; } } + } auto data_start = f->tellp(); // now the guts @@ -646,7 +745,7 @@ struct AigerWriter : Index { for (auto w : top->wires()) if (w->port_output) { for (auto bit : SigSpec(w)) - outputs.push_back({bit, get_top_port(bit)}); + outputs.push_back({bit, eval_po(bit)}); } auto data_end = f->tellp(); @@ -749,6 +848,8 @@ struct Aiger2Backend : Backend { for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-strash") writer.strashing = true; + else if (args[argidx] == "-flatten") + writer.flatten = true; else break; } @@ -791,12 +892,12 @@ struct AIGCounter : Index { for (auto w : top->wires()) if (w->port_input) for (int i = 0; i < w->width; i++) - set_top_port(SigBit(w, i), ++nvars); + pi_literal(SigBit(w, i)) = ++nvars; for (auto w : top->wires()) if (w->port_output) { for (auto bit : SigSpec(w)) - (void) get_top_port(bit); + (void) eval_po(bit); } } }; @@ -808,10 +909,12 @@ struct AigsizePass : Pass { log_header(design, "Executing AIGSIZE pass. (size design AIG)\n"); size_t argidx; - AIGCounter counter; + AIGCounter writer; for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-strash") - counter.strashing = true; + writer.strashing = true; + else if (args[argidx] == "-flatten") + writer.flatten = true; else break; } @@ -823,9 +926,9 @@ struct AigsizePass : Pass { log_cmd_error("No top module selected\n"); design->bufNormalize(true); - counter.setup(top); - counter.count(); - log("Counted %d gates\n", counter.ngates); + writer.setup(top); + writer.count(); + log("Counted %d gates\n", writer.ngates); // we are leaving the sacred land, un-bufnormalize // (if not, this will lead to bugs: the buf-normalized diff --git a/tests/various/aiger2.ys b/tests/various/aiger2.ys index 0efb5dd76..e008cdfaf 100644 --- a/tests/various/aiger2.ys +++ b/tests/various/aiger2.ys @@ -158,7 +158,7 @@ copy test gold flatten gold techmap submodule1 select test -write_aiger2 aiger2_ops.aig +write_aiger2 -flatten aiger2_ops.aig select -clear delete test read_aiger -module_name test aiger2_ops.aig From 5f8d7ff1702277d380bc90462ec6006222e2eab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:53:31 +0200 Subject: [PATCH 25/58] Start new write_xaiger2 backend for export w/ boxes --- backends/aiger2/aiger.cc | 439 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 438 insertions(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 5e58a9ebc..6af7b0948 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -700,13 +700,14 @@ struct AigerWriter : Index { char buf[128]; snprintf(buf, sizeof(buf) - 1, "aig %08d %08d %08d %08d %08d\n", ninputs + nlatches + nands, ninputs, nlatches, noutputs, nands); - f->seekp(0); f->write(buf, strlen(buf)); } void write(std::ostream *f) { reset_counters(); + auto file_start = f->tellp(); + // populate inputs std::vector inputs; for (auto id : top->ports) { @@ -750,6 +751,7 @@ struct AigerWriter : Index { auto data_end = f->tellp(); // revisit header and the list of outputs + f->seekp(file_start); write_header(); for (auto pair : outputs) { char buf[16]; @@ -788,6 +790,390 @@ struct AigerWriter : Index { } }; +struct XAigerWriter : AigerWriter { + XAigerWriter() + { + allow_blackboxes = true; + } + + bool mapping_prep = false; + pool keep_wires; + std::ofstream map_file; + + typedef std::pair HierBit; + std::vector pos; + std::vector pis; + int proper_pos_counter = 0; + + pool driven_by_opaque_box; + + void ensure_pi(SigBit bit, HierCursor cursor={}, + bool box_port=false) + { + Lit &lit = pi_literal(bit, &cursor); + if (lit == EMPTY_LIT) { + lit = lit_counter; + pis.push_back(std::make_pair(bit, cursor)); + lit_counter += 2; + if (map_file.is_open() && !box_port) { + log_assert(cursor.is_top()); // TODO + driven_by_opaque_box.insert(bit); + map_file << "pi " << pis.size() - 1 << " " << bit.offset + << " " << bit.wire->name.c_str() << "\n"; + } + } else { + log_assert(!box_port); + } + } + + bool is_pi(SigBit bit, HierCursor cursor={}) + { + return pi_literal(bit, &cursor) != EMPTY_LIT; + } + + void pad_pi() + { + pis.push_back(std::make_pair(RTLIL::Sx, HierCursor{})); + lit_counter += 2; + } + + void append_box_ports(Cell *box, HierCursor &cursor, bool inputs) + { + for (auto &conn : box->connections_) { + bool is_input = box->input(conn.first); + bool is_output = box->output(conn.first); + + if (!(is_input || is_output) || (is_input && is_output)) + log_error("Ambiguous port direction on %s/%s\n", + log_id(box->type), log_id(conn.first)); + + if (is_input && inputs) { + int bitp = 0; + for (auto bit : conn.second) { + if (!bit.wire) { + bitp++; + continue; + } + + if (map_file.is_open()) { + log_assert(cursor.is_top()); + map_file << "pseudopo " << proper_pos_counter++ << " " << bitp + << " " << box->name.c_str() + << " " << conn.first.c_str() << "\n"; + } + + pos.push_back(std::make_pair(bit, cursor)); + + if (mapping_prep) + conn.second[bitp] = RTLIL::Sx; + + bitp++; + } + } else if (is_output && !inputs) { + for (auto &bit : conn.second) { + if (!bit.wire || bit.wire->port_input) + log_error("Bad connection"); + + + ensure_pi(bit, cursor); + keep_wires.insert(bit.wire); + } + } + } + } + + RTLIL::Module *holes_module; + + std::stringstream h_buffer; + static void write_be32(std::ostream &buffer, uint32_t u32) + { + typedef unsigned char uchar; + unsigned char u32_be[4] = { + (uchar) (u32 >> 24), (uchar) (u32 >> 16), (uchar) (u32 >> 8), (uchar) u32 + }; + buffer.write((char *) u32_be, sizeof(u32_be)); + } + + void prep_boxes(int pending_pos_num) + { + // boxes which have timing data, maybe a whitebox model + std::vector> nonopaque_boxes; + // boxes which are fully opaque + std::vector> opaque_boxes; + + log_debug("found boxes:\n"); + visit_hierarchy([&](HierCursor &cursor) { + auto &minfo = cursor.leaf_minfo(*this); + + for (auto box : minfo.found_blackboxes) { + log_debug(" - %s.%s (type %s): ", cursor.path().c_str(), + RTLIL::unescape_id(box->name).c_str(), + log_id(box->type)); + + Module *box_module = design->module(box->type), *box_derived; + + if (box_module && !box->parameters.empty()) { + // TODO: This is potentially costly even if a cached derivation exists + box_derived = design->module(box_module->derive(design, box->parameters)); + log_assert(box_derived); + } else { + box_derived = box_module; + } + + if (box_derived && box_derived->has_attribute(ID::abc9_box_id)) { + // This is an ABC9 box, we have timing data, maybe even a whitebox model + // These need to go last in the AIGER port list. + nonopaque_boxes.push_back(std::make_tuple(cursor, box, box_derived)); + log_debug("non-opaque\n"); + } else { + opaque_boxes.push_back(std::make_tuple(cursor, box, box_derived)); + log_debug("opaque\n"); + } + } + }); + + for (auto [cursor, box, def] : opaque_boxes) + append_box_ports(box, cursor, true); + + holes_module = design->addModule(NEW_ID); + std::vector holes_pis; + int boxes_ci_num = 0, boxes_co_num = 0; + + int box_seq = 0; + + for (auto [cursor, box, def] : nonopaque_boxes) { + // use `def->name` not `box->type` as we want the derived type + Cell *holes_wb = holes_module->addCell(NEW_ID, def->name); + int holes_pi_idx = 0; + + if (map_file.is_open()) { + log_assert(cursor.is_top()); + map_file << "box " << box_seq << " " << box->name.c_str() << "\n"; + } + box_seq++; + + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + log_assert(port); + + SigSpec conn = box->hasPort(port_id) ? box->getPort(port_id) : SigSpec{}; + + if (port->port_input && !port->port_output) { + // primary + for (int i = 0; i < port->width; i++) { + SigBit bit; + if (i < conn.size()) { + bit = conn[i]; + } else { + // FIXME: hierarchical path + log_warning("connection on port %s[%d] of instance %s (type %s) missing, using 1'bx\n", + log_id(port_id), i, log_id(box), log_id(box->type)); + bit = RTLIL::Sx; + } + + pos.push_back(std::make_pair(bit, cursor)); + } + boxes_co_num += port->width; + + if (mapping_prep && !conn.empty()) + box->setPort(port_id, SigSpec(RTLIL::Sx, conn.size())); + + // holes + SigSpec in_conn; + for (int i = 0; i < port->width; i++) { + while (holes_pi_idx >= (int) holes_pis.size()) { + Wire *w = holes_module->addWire(NEW_ID, 1); + w->port_input = true; + holes_module->ports.push_back(w->name); + holes_pis.push_back(w); + } + in_conn.append(holes_pis[i]); + holes_pi_idx++; + } + holes_wb->setPort(port_id, in_conn); + } else if (port->port_output && !port->port_input) { + // primary + for (int i = 0; i < port->width; i++) { + SigBit bit; + if (i < conn.size() && conn[i].is_wire()) { + bit = conn[i]; + } else { + // FIXME: hierarchical path + log_warning("connection on port %s[%d] of instance %s (type %s) missing\n", + log_id(port_id), i, log_id(box), log_id(box->type)); + pad_pi(); + continue; + } + + ensure_pi(bit, cursor, true); + keep_wires.insert(bit.wire); + } + boxes_ci_num += port->width; + + // holes + Wire *w = holes_module->addWire(NEW_ID, port->width); + w->port_output = true; + holes_module->ports.push_back(w->name); + holes_wb->setPort(port_id, w); + } else { + log_error("Ambiguous port direction on %s/%s\n", + log_id(box->type), log_id(port_id)); + } + } + } + + for (auto [cursor, box, def] : opaque_boxes) + append_box_ports(box, cursor, false); + + write_be32(h_buffer, 1); + write_be32(h_buffer, pis.size()); + log_debug("ciNum = %zu\n", pis.size()); + write_be32(h_buffer, pending_pos_num + pos.size()); + log_debug("coNum = %zu\n", boxes_co_num + pos.size()); + write_be32(h_buffer, pis.size() - boxes_ci_num); + log_debug("piNum = %zu\n", pis.size() - boxes_ci_num); + write_be32(h_buffer, pending_pos_num + pos.size() - boxes_co_num); + log_debug("poNum = %zu\n", pending_pos_num + pos.size() - boxes_co_num); + write_be32(h_buffer, nonopaque_boxes.size()); + + box_seq = 0; + for (auto [cursor, box, def] : nonopaque_boxes) { + int box_ci_num = 0, box_co_num = 0; + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + log_assert(port); + if (port->port_input && !port->port_output) { + box_co_num += port->width; + } else if (port->port_output && !port->port_input) { + box_ci_num += port->width; + } else { + log_abort(); + } + } + + write_be32(h_buffer, box_co_num); + write_be32(h_buffer, box_ci_num); + write_be32(h_buffer, def->attributes.at(ID::abc9_box_id).as_int()); + write_be32(h_buffer, box_seq++); + } + } + + void clear_boxes() + { + design->remove(holes_module); + } + + void write(std::ostream *f) { + reset_counters(); + + for (auto w : top->wires()) + if (w->port_input) + for (int i = 0; i < w->width; i++) + ensure_pi(SigBit(w, i)); + + int proper_po_num = 0; + for (auto w : top->wires()) + if (w->port_output) + proper_po_num += w->width; + + prep_boxes(proper_po_num); + for (auto w : top->wires()) + if (w->port_output) + for (int i = 0; i < w->width; i++) { + if (map_file.is_open() && !driven_by_opaque_box.count(SigBit(w, i))) { + map_file << "po " << proper_pos_counter++ << " " << i + << " " << w->name.c_str() << "\n"; + } + pos.push_back(std::make_pair(SigBit(w, i), HierCursor{})); + } + + this->f = f; + // start with the header + ninputs = pis.size(); + noutputs = pos.size(); + write_header(); + // insert padding where output literals will go (once known) + for (auto _ : pos) { + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", 0); + f->write(buf, strlen(buf)); + } + auto data_start = f->tellp(); + + // now the guts + std::vector outlits; + for (auto &pair : pos) + outlits.push_back(eval_po(pair.first, &pair.second)); + + // revisit header and the list of outputs + f->seekp(0); + ninputs = pis.size(); + noutputs = pos.size(); + write_header(); + for (auto lit : outlits) { + char buf[16]; + snprintf(buf, sizeof(buf) - 1, "%08d\n", lit); + f->write(buf, strlen(buf)); + } + // double check we arrived at the same offset for the + // main data section + log_assert(data_start == f->tellp()); + + // extensions + f->seekp(0, std::ios::end); + + f->put('c'); + + // insert empty 'r' and 's' sections (abc crashes if we provide 'a' without those) + f->put('r'); + write_be32(*f, 4); + write_be32(*f, 0); + f->put('s'); + write_be32(*f, 4); + write_be32(*f, 0); + + f->put('h'); + // TODO: get rid of std::string copy + std::string h_buffer_str = h_buffer.str(); + write_be32(*f, h_buffer_str.size()); + f->write(h_buffer_str.data(), h_buffer_str.size()); + +#if 1 + f->put('a'); + write_be32(*f, 0); // size to be filled later + auto holes_aiger_start = f->tellp(); + { + AigerWriter holes_writer; + holes_writer.flatten = true; + holes_writer.inline_whiteboxes = true; + holes_writer.setup(holes_module); + holes_writer.write(f); + } + auto holes_aiger_size = f->tellp() - holes_aiger_start; + f->seekp(holes_aiger_start, std::ios::beg); + f->seekp(-4, std::ios::cur); + write_be32(*f, holes_aiger_size); +#endif + f->seekp(0, std::ios::end); + + if (mapping_prep) { + std::vector to_remove_cells; + for (auto cell : top->cells()) + if (!top_minfo->found_blackboxes.count(cell)) + to_remove_cells.push_back(cell); + for (auto cell : to_remove_cells) + top->remove(cell); + pool to_remove; + for (auto wire : top->wires()) + if (!wire->port_input && !wire->port_output && !keep_wires.count(wire)) + to_remove.insert(wire); + top->remove(to_remove); + } + + clear_boxes(); + } +}; + struct Aiger2Backend : Backend { Aiger2Backend() : Backend("aiger2", "write design to AIGER file (new)") { @@ -872,6 +1258,57 @@ struct Aiger2Backend : Backend { } } Aiger2Backend; +struct XAiger2Backend : Backend { + XAiger2Backend() : Backend("xaiger2", "write design to XAIGER file (new)") + { + experimental(); + } + + void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override + { + log_header(design, "Executing XAIGER2 backend.\n"); + + size_t argidx; + XAigerWriter writer; + std::string map_filename; + writer.const_folding = true; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-strash") + writer.strashing = true; + else if (args[argidx] == "-flatten") + writer.flatten = true; + else if (args[argidx] == "-mapping_prep") + writer.mapping_prep = true; + else if (args[argidx] == "-map2" && argidx + 1 < args.size()) + map_filename = args[++argidx]; + else + break; + } + extra_args(f, filename, args, argidx); + + Module *top = design->top_module(); + + if (!top || !design->selected_whole_module(top)) + log_cmd_error("No top module selected\n"); + + if (!map_filename.empty()) { + writer.map_file.open(map_filename); + if (!writer.map_file) + log_cmd_error("Failed to open '%s' for writing\n", map_filename.c_str()); + } + + design->bufNormalize(true); + writer.setup(top); + writer.write(f); + + // we are leaving the sacred land, un-bufnormalize + // (if not, this will lead to bugs: the buf-normalized + // flag must not be kept on past the code that can work + // with it) + design->bufNormalize(false); + } +} XAiger2Backend; + struct AIGCounter : Index { typedef int Lit; const static Lit CONST_FALSE = -1; From 3a1b003cc34481a98bcfe61199004cebe6835c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:53:56 +0200 Subject: [PATCH 26/58] celltypes: Fix `$buf` eval --- kernel/celltypes.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 9868827e6..b6d89a805 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -381,11 +381,10 @@ struct CellTypes HANDLE_CELL_TYPE(modfloor) HANDLE_CELL_TYPE(pow) HANDLE_CELL_TYPE(pos) - HANDLE_CELL_TYPE(buf) HANDLE_CELL_TYPE(neg) #undef HANDLE_CELL_TYPE - if (type == ID($_BUF_)) + if (type.in(ID($_BUF_), ID($buf))) return arg1; if (type == ID($_NOT_)) return eval_not(arg1); From f168b2f4b18abd4ae6a397a1a7fc4642b218c033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 18 Sep 2024 16:54:38 +0200 Subject: [PATCH 27/58] read_xaiger2: Update box handling --- frontends/aiger2/xaiger.cc | 183 ++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 5 deletions(-) diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index d2c2aa4b2..b60cd0c88 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -95,6 +95,8 @@ struct Xaiger2Frontend : public Frontend { outputs.push_back(po); } + std::vector> boxes; + std::vector retained_boxes; std::vector bits(2 + 2*M, RTLIL::Sm); bits[0] = RTLIL::S0; bits[1] = RTLIL::S1; @@ -115,6 +117,34 @@ struct Xaiger2Frontend : public Frontend { log_error("Map file references non-existent signal bit %s[%d]\n", name.c_str(), woffset); bits[lit] = SigBit(w, woffset); + } else if (type == "box") { + int box_seq; + std::string name; + if (!(map_file >> box_seq >> name)) + log_error("Bad map file (20)\n"); + if (box_seq < 0) + log_error("Bad map file (21)\n"); + + Cell *box = module->cell(RTLIL::escape_id(name)); + if (!box) + log_error("Map file references non-existent box %s\n", + name.c_str()); + + Module *def = design->module(box->type); + if (def && !box->parameters.empty()) { + // TODO: This is potentially costly even if a cached derivation exists + def = design->module(def->derive(design, box->parameters)); + log_assert(def); + } + + if (!def) + log_error("Bad map file (22)\n"); + + if (box_seq >= boxes.size()) { + boxes.resize(box_seq + 1); + retained_boxes.resize(box_seq + 1); + } + boxes[box_seq] = std::make_pair(box, def); } else { std::string scratch; std::getline(map_file, scratch); @@ -130,7 +160,73 @@ struct Xaiger2Frontend : public Frontend { log_error("Missing 'c' ahead of extensions\n"); if (f->peek() == '\n') f->get(); + auto extensions_start = f->tellg(); + log_debug("reading 'h' (first pass)\n"); + for (int c = f->get(); c != EOF; c = f->get()) { + if (c == 'h') { + uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes; + len = read_be32(*f); + read_be32(*f); + ci_num = read_be32(*f); + co_num = read_be32(*f); + pi_num = read_be32(*f); + po_num = read_be32(*f); + no_boxes = read_be32(*f); + + log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n", + len, ci_num, co_num, pi_num, po_num, no_boxes); + + int ci_counter = 0; + for (uint32_t i = 0; i < no_boxes; i++) { + uint32_t box_inputs, box_outputs, box_id, box_seq; + box_inputs = read_be32(*f); + box_outputs = read_be32(*f); + box_id = read_be32(*f); + box_seq = read_be32(*f); + + log("box_seq=%d boxes.size=%d\n", box_seq, boxes.size()); + log_assert(box_seq < boxes.size()); + + auto [cell, def] = boxes[box_seq]; + log_assert(cell && def); + retained_boxes[box_seq] = true; + + int box_ci_idx = 0; + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + if (port->port_output) { + if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width) + log_error("Malformed design (1)\n"); + + SigSpec &conn = cell->connections_[port_id]; + for (int j = 0; j < port->width; j++) { + if (conn[j].wire && conn[j].wire->port_output) + conn[j] = module->addWire(module->uniquify( + stringf("$box$%s$%s$%d", + cell->name.isPublic() ? cell->name.c_str() + 1 : cell->name.c_str(), + port_id.isPublic() ? port_id.c_str() + 1 : port_id.c_str(), + j))); + + bits[2*(pi_num + ci_counter + box_ci_idx++) + 2] = conn[j]; + } + } + } + + log_assert(box_ci_idx == box_outputs); + ci_counter += box_ci_idx; + } + log_assert(pi_num + ci_counter == ci_num); + } else if (c == '\n') { + break; + } else { + uint32_t len = read_be32(*f); + f->ignore(len); + log_debug("section '%c' (%d): ignoring %d bytes\n", c, c, len); + } + } + + f->seekg(extensions_start); bool read_mapping = false; uint32_t no_cells, no_instances; for (int c = f->get(); c != EOF; c = f->get()) { @@ -172,10 +268,11 @@ struct Xaiger2Frontend : public Frontend { log_assert(bits[out_lit] == RTLIL::Sm); log_assert(cell_id < cells.size()); auto &cell = cells[cell_id]; - Cell *instance = module->addCell(NEW_ID, cell.type); - auto out_w = module->addWire(NEW_ID); + Cell *instance = module->addCell(module->uniquify(stringf("$sc%d", out_lit)), cell.type); + auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit))); instance->setPort(cell.out, out_w); bits[out_lit] = out_w; + log_debug("setting %d (driven by %s)\n", out_lit, log_id(cell.type)); for (auto in : cell.ins) { uint32_t in_lit = read_be32(*f); log_assert(out_lit < bits.size()); @@ -195,6 +292,74 @@ struct Xaiger2Frontend : public Frontend { if (!read_mapping) log_error("Missing mapping (no 'M' section)\n"); + log("Read %d instances with cell library of size %d.\n", + no_instances, no_cells); + + f->seekg(extensions_start); + log_debug("reading 'h' (second pass)\n"); + int co_counter = 0; + for (int c = f->get(); c != EOF; c = f->get()) { + if (c == 'h') { + uint32_t len, ci_num, co_num, pi_num, po_num, no_boxes; + len = read_be32(*f); + read_be32(*f); + ci_num = read_be32(*f); + co_num = read_be32(*f); + pi_num = read_be32(*f); + po_num = read_be32(*f); + no_boxes = read_be32(*f); + + log_debug("len=%u ci_num=%u co_num=%u pi_num=%u po_nun=%u no_boxes=%u\n", + len, ci_num, co_num, pi_num, po_num, no_boxes); + + for (uint32_t i = 0; i < no_boxes; i++) { + uint32_t box_inputs, box_outputs, box_id, box_seq; + box_inputs = read_be32(*f); + box_outputs = read_be32(*f); + box_id = read_be32(*f); + box_seq = read_be32(*f); + + log("box_seq=%d boxes.size=%d\n", box_seq, boxes.size()); + log_assert(box_seq < boxes.size()); + + auto [cell, def] = boxes[box_seq]; + log_assert(cell && def); + + int box_co_idx = 0; + for (auto port_id : def->ports) { + Wire *port = def->wire(port_id); + SigSpec conn; + if (port->port_input) { + if (!cell->hasPort(port_id) || cell->getPort(port_id).size() != port->width) + log_error("Malformed design (2)\n"); + + SigSpec conn; + for (int j = 0; j < port->width; j++) { + log_assert(co_counter + box_co_idx < outputs.size()); + int lit = outputs[co_counter + box_co_idx++]; + log_assert(lit >= 0 && lit < bits.size()); + SigBit bit = bits[lit]; + if (bit == RTLIL::Sm) + log_error("Malformed mapping (1)\n"); + conn.append(bit); + } + cell->setPort(port_id, conn); + } + } + + log_assert(box_co_idx == box_inputs); + co_counter += box_co_idx; + } + log_assert(po_num + co_counter == co_num); + } else if (c == '\n') { + break; + } else { + uint32_t len = read_be32(*f); + f->ignore(len); + log_debug("section '%c' (%d): ignoring %d bytes\n", c, c, len); + } + } + while (true) { std::string scratch; std::getline(*f, scratch); @@ -204,10 +369,9 @@ struct Xaiger2Frontend : public Frontend { log("input file: %s\n", scratch.c_str()); } - log("Read %d instances with cell library of size %d.\n", - no_instances, no_cells); + log_debug("co_counter=%d\n", co_counter); - // TODO + // TODO: seek without close/open map_file.close(); map_file.open(map_filename); while (map_file >> type) { @@ -217,11 +381,13 @@ struct Xaiger2Frontend : public Frontend { std::string name; if (!(map_file >> po_idx >> woffset >> name)) log_error("Bad map file (3)\n"); + po_idx += co_counter; if (po_idx < 0 || po_idx >= outputs.size()) log_error("Bad map file (4)\n"); int lit = outputs[po_idx]; if (lit < 0 || lit >= bits.size()) log_error("Bad map file (5)\n"); + log("output=%d lit=%d\n", po_idx, lit); if (bits[lit] == RTLIL::Sm) log_error("Bad map file (6)\n"); Wire *w = module->wire(name); @@ -236,6 +402,7 @@ struct Xaiger2Frontend : public Frontend { std::string box_port; if (!(map_file >> po_idx >> poffset >> box_name >> box_port)) log_error("Bad map file (7)\n"); + po_idx += co_counter; if (po_idx < 0 || po_idx >= outputs.size()) log_error("Bad map file (8)\n"); int lit = outputs[po_idx]; @@ -257,6 +424,12 @@ struct Xaiger2Frontend : public Frontend { std::getline(map_file, scratch); } } + + int box_seq = 0; + for (auto [cell, def] : boxes) { + if (!retained_boxes[box_seq++]) + module->remove(cell); + } } void execute(std::istream *&f, std::string filename, std::vector args, Design *design) override From 9018d06a33d2d66f57ad825053c31f85417339a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Thu, 19 Sep 2024 11:21:09 +0200 Subject: [PATCH 28/58] quicklogic: Avoid carry chains in division mapping The default mapping rules for division-like operations (div/divfloor/ mod/modfloor) invoke subtractions which can get mapped to carry chains in FPGA flows. Optimizations across carry chains are weak, so in practice this ends up too costly compared to implementing the division purely in soft logic. For this reason arrange for `techmap.v` ignoring division operations under `-D NODIV`, and use this mode in `synth_quicklogic` to avoid carry chains for divisions. --- techlibs/common/techmap.v | 3 ++- techlibs/quicklogic/synth_quicklogic.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v index 68b276588..119296147 100644 --- a/techlibs/common/techmap.v +++ b/techlibs/common/techmap.v @@ -304,6 +304,7 @@ endmodule // Divide and Modulo // -------------------------------------------------------- +`ifndef NODIV module \$__div_mod_u (A, B, Y, R); parameter WIDTH = 1; @@ -531,7 +532,7 @@ module _90_modfloor (A, B, Y); .R(Y) ); endmodule - +`endif // -------------------------------------------------------- // Power diff --git a/techlibs/quicklogic/synth_quicklogic.cc b/techlibs/quicklogic/synth_quicklogic.cc index 0e7aaa752..76ef44570 100644 --- a/techlibs/quicklogic/synth_quicklogic.cc +++ b/techlibs/quicklogic/synth_quicklogic.cc @@ -266,7 +266,8 @@ struct SynthQuickLogicPass : public ScriptPass { if (check_label("map_gates")) { if (inferAdder && family == "qlf_k6n10f") { - run("techmap -map +/techmap.v -map " + lib_path + family + "/arith_map.v", "(unless -no_adder)"); + run("techmap -map +/techmap.v -map " + lib_path + family + "/arith_map.v -D NODIV", "(unless -no_adder)"); + run("techmap", "(unless -no_adder)"); } else { run("techmap"); } From 3e3515e7d9a0f74503245ccd1b2ff4456fa619f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 24 Sep 2024 17:46:50 +0200 Subject: [PATCH 29/58] log: Never silence `log_cmd_error` Add extra handling to arrange for `log_cmd_error` never being silenced by the command line `-v N` option. Similar path for `log_error` exists already. --- kernel/log.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/log.cc b/kernel/log.cc index 55895da06..fabbe09fd 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -459,8 +459,21 @@ void log_cmd_error(const char *format, ...) if (log_cmd_error_throw) { log_last_error = vstringf(format, ap); + + // Make sure the error message gets through any selective silencing + // of log output + bool pop_errfile = false; + if (log_errfile != NULL) { + log_files.push_back(log_errfile); + pop_errfile = true; + } + log("ERROR: %s", log_last_error.c_str()); log_flush(); + + if (pop_errfile) + log_files.pop_back(); + throw log_cmd_error_exception(); } From 35c8ad61ac9baaacf7919359b63b930e37d21b09 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 30 Sep 2024 17:38:43 +0300 Subject: [PATCH 30/58] cli/python: error-checking, python interpreter bugfix * Less brittle method of adding script dirname to sys.path * Check if scriptfp successfully opens before using it * Move `log_error` to after `PyErr_Print()` is called --- kernel/driver.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/driver.cc b/kernel/driver.cc index d30b19c96..53608c260 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -665,13 +665,19 @@ int main(int argc, char **argv) PyObject_SetAttrString(sys, "argv", new_argv); Py_DECREF(old_argv); - PyRun_SimpleString(("import os;sys.path.insert(0, os.path.dirname(os.path.abspath(\""+scriptfile+"\")))").c_str()); + PyObject *py_path = PyUnicode_FromString(scriptfile.c_str()); + PyObject_SetAttrString(sys, "_yosys_script_path", py_path); + Py_DECREF(py_path); + PyRun_SimpleString("import os, sys; sys.path.insert(0, os.path.dirname(os.path.abspath(sys._yosys_script_path)))"); FILE *scriptfp = fopen(scriptfile.c_str(), "r"); + if (scriptfp == nullptr) { + log_error("Failed to open file '%s' for reading.\n", scriptfile.c_str()); + } if (PyRun_SimpleFile(scriptfp, scriptfile.c_str()) != 0) { - log_error("Python interpreter encountered an error:\n"); log_flush(); PyErr_Print(); + log_error("Python interpreter encountered an exception."); } #else log_error("Can't execute Python script: this version of yosys is not built with Python support enabled.\n"); From 13ecbd5c76b37b8dc14b945a63d1a54b1fcfeced Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 3 Oct 2024 20:05:28 +0100 Subject: [PATCH 31/58] quicklogic: test that dividing by a constant does not infer carry chains --- tests/arch/quicklogic/qlf_k6n10f/div.ys | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/arch/quicklogic/qlf_k6n10f/div.ys diff --git a/tests/arch/quicklogic/qlf_k6n10f/div.ys b/tests/arch/quicklogic/qlf_k6n10f/div.ys new file mode 100644 index 000000000..5ca5b3051 --- /dev/null +++ b/tests/arch/quicklogic/qlf_k6n10f/div.ys @@ -0,0 +1,14 @@ +# division by constants should not infer carry chains. +read_verilog < Date: Sun, 6 Oct 2024 08:38:16 +0200 Subject: [PATCH 32/58] docs: Simplify images generation to allow parallel build - remove the tidy target from the main target. * aux/log file are already excluded in a .gititgnore file * allow parallel generation as the tidy target imposes sequential build --- docs/source/_images/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/_images/Makefile b/docs/source/_images/Makefile index 26cc47284..cc00050f1 100644 --- a/docs/source/_images/Makefile +++ b/docs/source/_images/Makefile @@ -1,4 +1,4 @@ -all: examples all_tex tidy +all: examples all_tex # set a fake time in pdf generation to prevent unnecessary differences in output FAKETIME := TZ='Z' faketime -f '2022-01-01 00:00:00 x0,001' From d8038c11d1f1f4adf7cc5d00c632752e402cd4e2 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:07:17 +1300 Subject: [PATCH 33/58] Add -j flag to make docs CI --- .github/workflows/prepare-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare-docs.yml b/.github/workflows/prepare-docs.yml index 9233e6295..19680e992 100644 --- a/.github/workflows/prepare-docs.yml +++ b/.github/workflows/prepare-docs.yml @@ -32,7 +32,7 @@ jobs: - name: Prepare docs shell: bash run: - make docs/prep TARGETS= EXTRA_TARGETS= + make docs/prep -j${{ env.procs }} TARGETS= EXTRA_TARGETS= - name: Upload artifact uses: actions/upload-artifact@v4 From 571d181fb4bcb7fe63f2b886e5b41f02d2519884 Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:26:29 +1300 Subject: [PATCH 34/58] Fix top-level make docs prerequisites Add `$(TARGETS)` for gen_examples and gen_images since they need the `yosys` executable. Add guidelines source files as a prerequisite to docs/source/generated while we're at it. --- Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index b3b8269c5..71e7891d9 100644 --- a/Makefile +++ b/Makefile @@ -979,16 +979,17 @@ docs/source/cmd/abc.rst: $(TARGETS) $(EXTRA_TARGETS) ./$(PROGRAM_PREFIX)yosys -p 'help -write-rst-command-reference-manual' PHONY: docs/gen_examples docs/gen_images docs/guidelines docs/usage docs/reqs -docs/gen_examples: +docs/gen_examples: $(TARGETS) $(Q) $(MAKE) -C docs examples -docs/gen_images: +docs/gen_images: $(TARGETS) $(Q) $(MAKE) -C docs images DOCS_GUIDELINE_FILES := GettingStarted CodingStyle -docs/guidelines docs/source/generated: +DOCS_GUIDELINE_SOURCE := $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) +docs/guidelines docs/source/generated: $(DOCS_GUIDELINE_SOURCE) $(Q) mkdir -p docs/source/generated - $(Q) cp -f $(addprefix guidelines/,$(DOCS_GUIDELINE_FILES)) docs/source/generated + $(Q) cp -f $(DOCS_GUIDELINE_SOURCE) docs/source/generated # some commands return an error and print the usage text to stderr define DOC_USAGE_STDERR From 6155c59d00125c58b4eb66f419ef6485439c0de2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 00:21:37 +0000 Subject: [PATCH 35/58] Bump version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 71e7891d9..5b6fc3ff2 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ ifeq ($(OS), Haiku) CXXFLAGS += -D_DEFAULT_SOURCE endif -YOSYS_VER := 0.45+148 +YOSYS_VER := 0.45+153 # Note: We arrange for .gitcommit to contain the (short) commit hash in # tarballs generated with git-archive(1) using .gitattributes. The git repo From 3e6e8c892e9f44c933c2ffc4c660cc38370ea69d Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 7 Oct 2024 11:09:02 +0200 Subject: [PATCH 36/58] Bump abc submodule --- abc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abc b/abc index 2188bc712..cac8f99ea 160000 --- a/abc +++ b/abc @@ -1 +1 @@ -Subproject commit 2188bc71228b0788569d83ad2b7e7b91ca5dcc09 +Subproject commit cac8f99eaa220a5e3db5caeb87cef0a975c953a2 From d4e009fc2f7152cd3c26f7776c711745a5a90257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Wed, 2 Oct 2024 11:28:19 +0200 Subject: [PATCH 37/58] aiger2: Add TODO --- backends/aiger2/aiger.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 6af7b0948..89ef06a37 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -20,6 +20,7 @@ // TODOs: // - gracefully handling inout ports (an error message probably) // - undriven wires +// - zero-width operands #include "kernel/register.h" #include "kernel/celltypes.h" From e58a9b6ab6cd990a5f2cbe396972a1bdcd7ccb7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Tue, 9 Apr 2024 18:30:51 +0200 Subject: [PATCH 38/58] abc9: Understand ASIC options similar to `abc` --- passes/techmap/abc9.cc | 3 ++- passes/techmap/abc9_exe.cc | 43 +++++++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/passes/techmap/abc9.cc b/passes/techmap/abc9.cc index 876917e56..a96a82659 100644 --- a/passes/techmap/abc9.cc +++ b/passes/techmap/abc9.cc @@ -220,7 +220,8 @@ struct Abc9Pass : public ScriptPass std::string arg = args[argidx]; if ((arg == "-exe" || arg == "-script" || arg == "-D" || /*arg == "-S" ||*/ arg == "-lut" || arg == "-luts" || - /*arg == "-box" ||*/ arg == "-W") && + /*arg == "-box" ||*/ arg == "-W" || arg == "-genlib" || + arg == "-constr" || arg == "-dont_use" || arg == "-liberty") && argidx+1 < args.size()) { if (arg == "-lut" || arg == "-luts") lut_mode = true; diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index 8e02e25a4..e62850aa4 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -166,9 +166,9 @@ struct abc9_output_filter void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file, vector lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, - bool show_tempdir, std::string box_file, std::string lut_file, - std::string wire_delay, std::string tempdir_name -) + bool show_tempdir, std::string box_file, std::string lut_file, std::vector genlib_files, + std::vector liberty_files, std::string wire_delay, std::string tempdir_name, + std::string constr_file, std::vector dont_use_cells) { std::string abc9_script; @@ -176,8 +176,19 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else if (!lut_file.empty()) abc9_script += stringf("read_lut \"%s\"; ", lut_file.c_str()); - else - log_abort(); + else if (!liberty_files.empty() || !genlib_files.empty()) { + std::string dont_use_args; + for (std::string dont_use_cell : dont_use_cells) { + dont_use_args += stringf("-X \"%s\" ", dont_use_cell.c_str()); + } + for (std::string liberty_file : liberty_files) { + abc9_script += stringf("read_lib %s -w \"%s\" ; ", dont_use_args.c_str(), liberty_file.c_str()); + } + for (std::string liberty_file : genlib_files) + abc9_script += stringf("read_library \"%s\"; ", liberty_file.c_str()); + if (!constr_file.empty()) + abc9_script += stringf("read_constr -v \"%s\"; ", constr_file.c_str()); + } log_assert(!box_file.empty()); abc9_script += stringf("read_box \"%s\"; ", box_file.c_str()); @@ -411,7 +422,8 @@ struct Abc9ExePass : public Pass { log_header(design, "Executing ABC9_EXE pass (technology mapping using ABC9).\n"); std::string exe_file = yosys_abc_executable; - std::string script_file, clk_str, box_file, lut_file; + std::string script_file, clk_str, box_file, lut_file, constr_file; + std::vector genlib_files, liberty_files, dont_use_cells; std::string delay_target, lutin_shared = "-S 1", wire_delay; std::string tempdir_name; bool fast_mode = false, dff_mode = false; @@ -499,6 +511,22 @@ struct Abc9ExePass : public Pass { tempdir_name = args[++argidx]; continue; } + if (arg == "-genlib" && argidx+1 < args.size()) { + genlib_files.push_back(args[++argidx]); + continue; + } + if (arg == "-liberty" && argidx+1 < args.size()) { + liberty_files.push_back(args[++argidx]); + continue; + } + if (arg == "-dont_use" && argidx+1 < args.size()) { + dont_use_cells.push_back(args[++argidx]); + continue; + } + if (arg == "-constr" && argidx+1 < args.size()) { + constr_file = args[++argidx]; + continue; + } break; } extra_args(args, argidx, design); @@ -562,7 +590,8 @@ struct Abc9ExePass : public Pass { abc9_module(design, script_file, exe_file, lut_costs, dff_mode, delay_target, lutin_shared, fast_mode, show_tempdir, - box_file, lut_file, wire_delay, tempdir_name); + box_file, lut_file, genlib_files, liberty_files, wire_delay, tempdir_name, + constr_file, dont_use_cells); } } Abc9ExePass; From 2b1b5652f1106466dfc538d52bffbe336013de85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 4 Oct 2024 10:02:53 +0200 Subject: [PATCH 39/58] Adjust `read_xaiger2` prints --- frontends/aiger2/xaiger.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index b60cd0c88..56fa5a2a6 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -222,10 +222,12 @@ struct Xaiger2Frontend : public Frontend { } else { uint32_t len = read_be32(*f); f->ignore(len); - log_debug("section '%c' (%d): ignoring %d bytes\n", c, c, len); + log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); } } + log_debug("reading 'M' (second pass)\n"); + f->seekg(extensions_start); bool read_mapping = false; uint32_t no_cells, no_instances; @@ -272,7 +274,6 @@ struct Xaiger2Frontend : public Frontend { auto out_w = module->addWire(module->uniquify(stringf("$lit%d", out_lit))); instance->setPort(cell.out, out_w); bits[out_lit] = out_w; - log_debug("setting %d (driven by %s)\n", out_lit, log_id(cell.type)); for (auto in : cell.ins) { uint32_t in_lit = read_be32(*f); log_assert(out_lit < bits.size()); @@ -285,7 +286,7 @@ struct Xaiger2Frontend : public Frontend { } else { uint32_t len = read_be32(*f); f->ignore(len); - log_debug("section '%c' (%d): ignoring %d bytes\n", c, c, len); + log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); } } @@ -356,7 +357,7 @@ struct Xaiger2Frontend : public Frontend { } else { uint32_t len = read_be32(*f); f->ignore(len); - log_debug("section '%c' (%d): ignoring %d bytes\n", c, c, len); + log_debug(" section '%c' (%d): ignoring %d bytes\n", c, c, len); } } @@ -387,7 +388,6 @@ struct Xaiger2Frontend : public Frontend { int lit = outputs[po_idx]; if (lit < 0 || lit >= bits.size()) log_error("Bad map file (5)\n"); - log("output=%d lit=%d\n", po_idx, lit); if (bits[lit] == RTLIL::Sm) log_error("Bad map file (6)\n"); Wire *w = module->wire(name); From 8d12492610e0fdfff84b12fb0fc337daf4e74055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Fri, 4 Oct 2024 10:03:30 +0200 Subject: [PATCH 40/58] read_xaiger2: Fix detecting the end of extensions --- frontends/aiger2/xaiger.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index 56fa5a2a6..4c7305876 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -219,6 +219,8 @@ struct Xaiger2Frontend : public Frontend { log_assert(pi_num + ci_counter == ci_num); } else if (c == '\n') { break; + } else if (c == 'c') { + break; } else { uint32_t len = read_be32(*f); f->ignore(len); @@ -283,6 +285,8 @@ struct Xaiger2Frontend : public Frontend { } } else if (c == '\n') { break; + } else if (c == 'c') { + break; } else { uint32_t len = read_be32(*f); f->ignore(len); @@ -354,6 +358,8 @@ struct Xaiger2Frontend : public Frontend { log_assert(po_num + co_counter == co_num); } else if (c == '\n') { break; + } else if (c == 'c') { + break; } else { uint32_t len = read_be32(*f); f->ignore(len); From f7c7371ea9157ef1199c4a76d20364b3699313b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:03:52 +0200 Subject: [PATCH 41/58] aiger2: Fix relative ordering of PI/POs and box I/Os --- backends/aiger2/aiger.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 89ef06a37..9df9c9b20 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -934,7 +934,7 @@ struct XAigerWriter : AigerWriter { }); for (auto [cursor, box, def] : opaque_boxes) - append_box_ports(box, cursor, true); + append_box_ports(box, cursor, false); holes_module = design->addModule(NEW_ID); std::vector holes_pis; @@ -1024,7 +1024,7 @@ struct XAigerWriter : AigerWriter { } for (auto [cursor, box, def] : opaque_boxes) - append_box_ports(box, cursor, false); + append_box_ports(box, cursor, true); write_be32(h_buffer, 1); write_be32(h_buffer, pis.size()); From 4c0a8a132614ee354913815f3b651f8735a3a730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:04:45 +0200 Subject: [PATCH 42/58] aiger2: Add analysis step to order boxes --- backends/aiger2/aiger.cc | 122 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 9df9c9b20..b6994c221 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -537,6 +537,11 @@ struct Index { } }; + bool visit_hook(int idx, HierCursor &cursor, SigBit bit) + { + return false; + } + Lit visit(HierCursor &cursor, SigBit bit) { if (!bit.wire) { @@ -554,6 +559,12 @@ struct Index { return lits[idx]; } + // provide means for the derived class to override + // the visit behavior + if ((static_cast(this))->visit_hook(idx, cursor, bit)) { + return lits[idx]; + } + Lit ret; if (!bit.wire->port_input) { // an output of a cell @@ -791,6 +802,102 @@ struct AigerWriter : Index { } }; +struct XAigerAnalysis : Index { + const static int CONST_FALSE = 0; + const static int CONST_TRUE = 0; + const static constexpr int EMPTY_LIT = -1; + + XAigerAnalysis() + { + allow_blackboxes = true; + + // Disable const folding and strashing as literal values are not unique + const_folding = false; + strashing = false; + } + + static int negate(int lit) + { + return lit; + } + + int emit_gate(int a, int b) + { + return max(a, b) + 1; + } + + pool seen; + + bool visit_hook(int idx, HierCursor &cursor, SigBit bit) + { + log_assert(cursor.is_top()); // TOOD: fix analyzer to work with hierarchy + + if (bit.wire->port_input) + return false; + + Cell *driver = bit.wire->driverCell(); + if (!driver->type.isPublic()) + return false; + + Module *mod = design->module(driver->type); + log_assert(mod); + if (!mod->has_attribute(ID::abc9_box_id)) + return false; + + int max = 1; + for (auto wire : mod->wires()) + if (wire->port_input) + for (int i = 0; i < wire->width; i++) { + int ilevel = visit(cursor, driver->getPort(wire->name)[i]); + max = std::max(max, ilevel + 1); + } + lits[idx] = max; + + if (!seen.count(driver)) + seen.insert(driver); + + return true; + } + + void analyze(Module *top) + { + setup(top); + + for (auto id : top->ports) { + Wire *w = top->wire(id); + log_assert(w); + if (w->port_input) + for (int i = 0; i < w->width; i++) + pi_literal(SigBit(w, i)) = 0; + } + + HierCursor cursor; + for (auto box : top_minfo->found_blackboxes) { + Module *def = design->module(box->type); + if (!box->type.isPublic() || (def && !def->has_attribute(ID::abc9_box_id))) + for (auto &conn : box->connections_) + if (box->output(conn.first)) + for (auto bit : conn.second) + pi_literal(bit, &cursor) = 0; + } + + for (auto w : top->wires()) + if (w->port_output) { + for (auto bit : SigSpec(w)) + (void) eval_po(bit); + } + + for (auto box : top_minfo->found_blackboxes) { + Module *def = design->module(box->type); + if (!box->type.isPublic() || (def && !def->has_attribute(ID::abc9_box_id))) + for (auto &conn : box->connections_) + if (box->input(conn.first)) + for (auto bit : conn.second) + (void) eval_po(bit); + } + } +}; + struct XAigerWriter : AigerWriter { XAigerWriter() { @@ -897,6 +1004,11 @@ struct XAigerWriter : AigerWriter { void prep_boxes(int pending_pos_num) { + XAigerAnalysis analysis; + log_debug("preforming analysis on '%s'\n", log_id(top)); + analysis.analyze(top); + log_debug("analysis on '%s' done\n", log_id(top)); + // boxes which have timing data, maybe a whitebox model std::vector> nonopaque_boxes; // boxes which are fully opaque @@ -942,6 +1054,16 @@ struct XAigerWriter : AigerWriter { int box_seq = 0; + std::vector boxes_order(analysis.seen.begin(), analysis.seen.end()); + std::reverse(boxes_order.begin(), boxes_order.end()); + + nonopaque_boxes.clear(); + for (auto box : boxes_order) { + HierCursor cursor; + Module *def = design->module(box->type); + nonopaque_boxes.push_back(std::make_tuple(cursor, box, def)); + } + for (auto [cursor, box, def] : nonopaque_boxes) { // use `def->name` not `box->type` as we want the derived type Cell *holes_wb = holes_module->addCell(NEW_ID, def->name); From b8f389370b0fb3ecb520a9a02d0cda744315aea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:05:44 +0200 Subject: [PATCH 43/58] aiger2: Convert x-states to zeroes --- backends/aiger2/aiger.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index b6994c221..97b506178 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -549,6 +549,8 @@ struct Index { return CTRUE; else if (bit == State::S0) return CFALSE; + else if (bit == State::Sx) + return CFALSE; else log_error("Unhandled state %s\n", log_signal(bit)); } From e0a86d5483804f4c4977c353232e64e70c539582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:07:25 +0200 Subject: [PATCH 44/58] abc_new: Start new command for aiger2-based round trip --- passes/techmap/Makefile.inc | 2 + passes/techmap/abc_new.cc | 153 ++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 passes/techmap/abc_new.cc diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 6bf40434d..4e1d16744 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -12,10 +12,12 @@ OBJS += passes/techmap/abc.o OBJS += passes/techmap/abc9.o OBJS += passes/techmap/abc9_exe.o OBJS += passes/techmap/abc9_ops.o +OBJS += passes/techmap/abc_new.o ifneq ($(ABCEXTERNAL),) passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +passes/techmap/abc_new.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif endif diff --git a/passes/techmap/abc_new.cc b/passes/techmap/abc_new.cc new file mode 100644 index 000000000..eefe34f84 --- /dev/null +++ b/passes/techmap/abc_new.cc @@ -0,0 +1,153 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2024 Martin Povišer + * + * 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/register.h" +#include "kernel/rtlil.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct AbcNewPass : public ScriptPass { + AbcNewPass() : ScriptPass("abc_new", "(experimental) use ABC for SC technology mapping (new)") + { + experimental(); + } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" abc_new [options] [selection]\n"); + log("\n"); + log("This command uses the ABC tool [1] to optimize the current design and map it to\n"); + log("the target standard cell library.\n"); + log("\n"); + log(" -run :\n"); + log(" only run the commands between the labels (see below). an empty\n"); + 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(" -exe \n"); + log(" -script \n"); + log(" -D \n"); + log(" -constr \n"); + log(" -dont_use \n"); + log(" -liberty \n"); + log(" these options are passed on to the 'abc9_exe' command which invokes\n"); + log(" the ABC tool on individual modules of the design. please see\n"); + log(" 'help abc9_exe' for more details\n"); + log("\n"); + log("[1] http://www.eecs.berkeley.edu/~alanmi/abc/\n"); + log("\n"); + help_script(); + log("\n"); + } + + bool cleanup; + std::string abc_exe_options; + + void execute(std::vector args, RTLIL::Design *d) override + { + std::string run_from, run_to; + cleanup = true; + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-exe" || args[argidx] == "-script" || + args[argidx] == "-D" || + args[argidx] == "-constr" || args[argidx] == "-dont_use" || + args[argidx] == "-liberty") { + abc_exe_options += " " + args[argidx] + " " + args[argidx + 1]; + argidx++; + } else if (args[argidx] == "-run" && argidx + 1 < args.size()) { + size_t pos = args[++argidx].find(':'); + if (pos == std::string::npos) + break; + run_from = args[argidx].substr(0, pos); + run_to = args[argidx].substr(pos + 1); + } else if (args[argidx] == "-nocleanup") { + cleanup = false; + } else { + break; + } + } + extra_args(args, argidx, d); + + log_header(d, "Executing ABC_NEW pass.\n"); + log_push(); + run_script(d, run_from, run_to); + log_pop(); + } + + void script() override + { + if (check_label("check")) { + run("abc9_ops -check"); + } + + if (check_label("prep_boxes")) { + run("box_derive"); + run("abc9_ops -prep_box"); + } + + if (check_label("map")) { + std::vector selected_modules; + + if (!help_mode) { + selected_modules = active_design->selected_whole_modules_warn(); + active_design->selection_stack.emplace_back(false); + } else { + selected_modules = {nullptr}; + run("foreach module in selection"); + } + + for (auto mod : selected_modules) { + std::string tmpdir = ""; + std::string modname = ""; + std::string exe_options = "[options]"; + if (!help_mode) { + tmpdir = cleanup ? (get_base_tmpdir() + "/") : "_tmp_"; + tmpdir += proc_program_prefix() + "yosys-abc-XXXXXX"; + tmpdir = make_temp_dir(tmpdir); + modname = mod->name.str(); + exe_options = abc_exe_options; + log_header(active_design, "Mapping module '%s'.\n", log_id(mod)); + log_push(); + active_design->selection().select(mod); + } + + run(stringf(" abc9_ops -write_box %s/input.box", tmpdir.c_str())); + run(stringf(" write_xaiger2 -mapping_prep -map2 %s/input.map2 %s/input.xaig", tmpdir.c_str(), tmpdir.c_str())); + run(stringf(" abc9_exe %s -cwd %s -box %s/input.box", exe_options.c_str(), tmpdir.c_str(), tmpdir.c_str())); + run(stringf(" read_xaiger2 -sc_mapping -module_name %s -map2 %s/input.map2 %s/output.aig", + modname.c_str(), tmpdir.c_str(), tmpdir.c_str())); + + if (!help_mode) { + active_design->selection().selected_modules.clear(); + log_pop(); + } + } + + if (!help_mode) { + active_design->selection_stack.pop_back(); + } + } + } +} AbcNewPass; + +PRIVATE_NAMESPACE_END From 81688e3ba2fb4a1dba403541512825264281df76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:12:16 +0200 Subject: [PATCH 45/58] aigsize: Remove --- backends/aiger2/aiger.cc | 66 ---------------------------------------- 1 file changed, 66 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 97b506178..336dad7a7 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -1434,70 +1434,4 @@ struct XAiger2Backend : Backend { } } XAiger2Backend; -struct AIGCounter : Index { - typedef int Lit; - const static Lit CONST_FALSE = -1; - const static Lit CONST_TRUE = 1; - const static constexpr Lit EMPTY_LIT = 0; - static Lit negate(Lit lit) { return -lit; } - int nvars = 1; - int ngates = 0; - - Lit emit_gate([[maybe_unused]] Lit a, [[maybe_unused]] Lit b) - { - ngates++; - return ++nvars; - } - - void count() { - // populate inputs - for (auto w : top->wires()) - if (w->port_input) - for (int i = 0; i < w->width; i++) - pi_literal(SigBit(w, i)) = ++nvars; - - for (auto w : top->wires()) - if (w->port_output) { - for (auto bit : SigSpec(w)) - (void) eval_po(bit); - } - } -}; - -struct AigsizePass : Pass { - AigsizePass() : Pass("aigsize", "estimate AIG size for design") {} - void execute(std::vector args, RTLIL::Design *design) override - { - log_header(design, "Executing AIGSIZE pass. (size design AIG)\n"); - - size_t argidx; - AIGCounter writer; - for (argidx = 1; argidx < args.size(); argidx++) { - if (args[argidx] == "-strash") - writer.strashing = true; - else if (args[argidx] == "-flatten") - writer.flatten = true; - else - break; - } - extra_args(args, argidx, design); - - Module *top = design->top_module(); - - if (!top || !design->selected_whole_module(top)) - log_cmd_error("No top module selected\n"); - - design->bufNormalize(true); - writer.setup(top); - writer.count(); - log("Counted %d gates\n", writer.ngates); - - // we are leaving the sacred land, un-bufnormalize - // (if not, this will lead to bugs: the buf-normalized - // flag must not be kept on past the code that can work - // with it) - design->bufNormalize(false); - } -} AigsizePass; - PRIVATE_NAMESPACE_END From ac79a052ba9013580eadc26f4c9032819f162cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:12:29 +0200 Subject: [PATCH 46/58] aiger2: Adjust help --- backends/aiger2/aiger.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 336dad7a7..a6a4b841a 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -1300,7 +1300,7 @@ struct XAigerWriter : AigerWriter { }; struct Aiger2Backend : Backend { - Aiger2Backend() : Backend("aiger2", "write design to AIGER file (new)") + Aiger2Backend() : Backend("aiger2", "(experimental) write design to AIGER file") { experimental(); } @@ -1384,7 +1384,7 @@ struct Aiger2Backend : Backend { } Aiger2Backend; struct XAiger2Backend : Backend { - XAiger2Backend() : Backend("xaiger2", "write design to XAIGER file (new)") + XAiger2Backend() : Backend("xaiger2", "(experimental) write design to XAIGER file") { experimental(); } From 3b6dcc7bd0370bff5fde16cfa0406bc60356eace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:30:22 +0200 Subject: [PATCH 47/58] abc9_exe: Remove `-genlib` option --- passes/techmap/abc9_exe.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index e62850aa4..ccfc7a7f8 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -166,7 +166,7 @@ struct abc9_output_filter void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe_file, vector lut_costs, bool dff_mode, std::string delay_target, std::string /*lutin_shared*/, bool fast_mode, - bool show_tempdir, std::string box_file, std::string lut_file, std::vector genlib_files, + bool show_tempdir, std::string box_file, std::string lut_file, std::vector liberty_files, std::string wire_delay, std::string tempdir_name, std::string constr_file, std::vector dont_use_cells) { @@ -176,7 +176,7 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe abc9_script += stringf("read_lut %s/lutdefs.txt; ", tempdir_name.c_str()); else if (!lut_file.empty()) abc9_script += stringf("read_lut \"%s\"; ", lut_file.c_str()); - else if (!liberty_files.empty() || !genlib_files.empty()) { + else if (!liberty_files.empty()) { std::string dont_use_args; for (std::string dont_use_cell : dont_use_cells) { dont_use_args += stringf("-X \"%s\" ", dont_use_cell.c_str()); @@ -184,8 +184,6 @@ void abc9_module(RTLIL::Design *design, std::string script_file, std::string exe for (std::string liberty_file : liberty_files) { abc9_script += stringf("read_lib %s -w \"%s\" ; ", dont_use_args.c_str(), liberty_file.c_str()); } - for (std::string liberty_file : genlib_files) - abc9_script += stringf("read_library \"%s\"; ", liberty_file.c_str()); if (!constr_file.empty()) abc9_script += stringf("read_constr -v \"%s\"; ", constr_file.c_str()); } @@ -423,7 +421,7 @@ struct Abc9ExePass : public Pass { std::string exe_file = yosys_abc_executable; std::string script_file, clk_str, box_file, lut_file, constr_file; - std::vector genlib_files, liberty_files, dont_use_cells; + std::vector liberty_files, dont_use_cells; std::string delay_target, lutin_shared = "-S 1", wire_delay; std::string tempdir_name; bool fast_mode = false, dff_mode = false; @@ -511,10 +509,6 @@ struct Abc9ExePass : public Pass { tempdir_name = args[++argidx]; continue; } - if (arg == "-genlib" && argidx+1 < args.size()) { - genlib_files.push_back(args[++argidx]); - continue; - } if (arg == "-liberty" && argidx+1 < args.size()) { liberty_files.push_back(args[++argidx]); continue; @@ -590,7 +584,7 @@ struct Abc9ExePass : public Pass { abc9_module(design, script_file, exe_file, lut_costs, dff_mode, delay_target, lutin_shared, fast_mode, show_tempdir, - box_file, lut_file, genlib_files, liberty_files, wire_delay, tempdir_name, + box_file, lut_file, liberty_files, wire_delay, tempdir_name, constr_file, dont_use_cells); } } Abc9ExePass; From 2e587c835fd95eac7ac5114f26f07f9f3f5982e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:30:37 +0200 Subject: [PATCH 48/58] abc9_exe: Document SC mapping options --- passes/techmap/abc9_exe.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/passes/techmap/abc9_exe.cc b/passes/techmap/abc9_exe.cc index ccfc7a7f8..651a8375c 100644 --- a/passes/techmap/abc9_exe.cc +++ b/passes/techmap/abc9_exe.cc @@ -368,6 +368,26 @@ struct Abc9ExePass : public Pass { log(" of output quality):\n"); log("%s\n", fold_abc9_cmd(RTLIL::constpad.at("abc9.script.default.fast").substr(1,std::string::npos)).c_str()); log("\n"); + log(" -constr \n"); + log(" pass this file with timing constraints to ABC.\n"); + log(" use with -liberty.\n"); + log("\n"); + log(" a constr file contains two lines:\n"); + log(" set_driving_cell \n"); + log(" set_load \n"); + log("\n"); + log(" the set_driving_cell statement defines which cell type is assumed to\n"); + log(" drive the primary inputs and the set_load statement sets the load in\n"); + log(" femtofarads for each primary output.\n"); + log("\n"); + log(" -liberty \n"); + log(" read the given Liberty file as a description of the target cell library.\n"); + log(" this option can be used multiple times.\n"); + log("\n"); + log(" -dont_use \n"); + log(" avoid usage of the technology cell when mapping the design.\n"); + log(" this option can be used multiple times.\n"); + log("\n"); log(" -D \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"); From ebe51e206ea036090d6068a6fa9e26d7b2c769b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 18:37:53 +0200 Subject: [PATCH 49/58] aiger2: Fix warnings --- backends/aiger2/aiger.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index a6a4b841a..0406ca0ac 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -222,8 +222,8 @@ struct Index { std::vector next; while (lits.size() > 1) { next.clear(); - for (int i = 0; i < lits.size(); i += 2) { - if (i + 1 >= lits.size()) { + for (int i = 0; i < (int) lits.size(); i += 2) { + if (i + 1 >= (int) lits.size()) { next.push_back(lits[i]); } else { Lit a = lits[i], b = lits[i + 1]; @@ -430,7 +430,7 @@ struct Index { for (int i = 0; i < sport.size(); i++) { Lit s = visit(cursor, sport[i]); next.clear(); - for (int j = 0; j < data.size(); j += 2) + for (int j = 0; j < (int) data.size(); j += 2) next.push_back(MUX(data[j], data[j + 1], s)); data.swap(next); } @@ -537,7 +537,7 @@ struct Index { } }; - bool visit_hook(int idx, HierCursor &cursor, SigBit bit) + bool visit_hook(int, HierCursor&, SigBit) { return false; } From 373e7a1485a1e8942429dd6df9fecc4fe687eb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 19:23:11 +0200 Subject: [PATCH 50/58] aiger2: Fix print --- backends/aiger2/aiger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 0406ca0ac..8de482bc0 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -1154,7 +1154,7 @@ struct XAigerWriter : AigerWriter { write_be32(h_buffer, pis.size()); log_debug("ciNum = %zu\n", pis.size()); write_be32(h_buffer, pending_pos_num + pos.size()); - log_debug("coNum = %zu\n", boxes_co_num + pos.size()); + log_debug("coNum = %zu\n", pending_pos_num + pos.size()); write_be32(h_buffer, pis.size() - boxes_ci_num); log_debug("piNum = %zu\n", pis.size() - boxes_ci_num); write_be32(h_buffer, pending_pos_num + pos.size() - boxes_co_num); From 47fd2b9debadb877837c81f37bad1fd8da8c0476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Sat, 5 Oct 2024 19:23:30 +0200 Subject: [PATCH 51/58] aiger2: Update help --- backends/aiger2/aiger.cc | 41 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 8de482bc0..29b8ee96b 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -1311,8 +1311,15 @@ struct Aiger2Backend : Backend { log("\n"); log(" write_aiger2 [options] [filename]\n"); log("\n"); - log("Write the current design to an AIGER file.\n"); + log("Write the selected module to an AIGER file.\n"); log("\n"); + log(" -strash\n"); + log(" perform structural hashing while writing\n"); + log("\n"); + log(" -flatten\n"); + log(" allow descending into submodules and write a flattened view of the design\n"); + log(" hierarchy starting at the selected top\n"); + log("\n"); log("This command is able to ingest all combinational cells except for:\n"); log("\n"); pool supported = {KNOWN_OPS}; @@ -1384,11 +1391,41 @@ struct Aiger2Backend : Backend { } Aiger2Backend; struct XAiger2Backend : Backend { - XAiger2Backend() : Backend("xaiger2", "(experimental) write design to XAIGER file") + XAiger2Backend() : Backend("xaiger2", "(experimental) write module to XAIGER file") { experimental(); } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_xaiger2 [options] [filename]\n"); + log("\n"); + log("Write the selected module to a XAIGER file including the 'h' and 'a' extensions\n"); + log("with box information for ABC.\n"); + log("\n"); + log(" -strash\n"); + log(" perform structural hashing while writing\n"); + log("\n"); + log(" -flatten\n"); + log(" allow descending into submodules and write a flattened view of the design\n"); + log(" hierarchy starting at the selected top\n"); + log("\n"); + log(" -mapping_prep\n"); + log(" after the file is written, prepare the module for reintegration of\n"); + log(" a mapping in a subsequent command. all cells which are not blackboxed nor\n"); + log(" whiteboxed are removed from the design as well as all wires which only\n"); + log(" connect to removed cells\n"); + log(" (conflicts with -flatten)\n"); + log("\n"); + log(" -map2 \n"); + log(" write a map2 file which 'read_xaiger2 -sc_mapping' can read to\n"); + log(" reintegrate a mapping\n"); + log(" (conflicts with -flatten)\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector args, Design *design) override { log_header(design, "Executing XAIGER2 backend.\n"); From d0a11e26f391a670b73b822671e7b974e112e59a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 7 Oct 2024 11:59:52 +0200 Subject: [PATCH 52/58] aiger2: Add test of writing a flattened view --- tests/various/aiger2.ys | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/various/aiger2.ys b/tests/various/aiger2.ys index e008cdfaf..d2b024d2f 100644 --- a/tests/various/aiger2.ys +++ b/tests/various/aiger2.ys @@ -193,3 +193,32 @@ select -assert-none test/t:$_AND_ test/t:$_NOT_ %% test/c:* %D equiv_make gold test equiv equiv_induct -undef equiv equiv_status -assert equiv + +design -reset +read_verilog -icells < Date: Mon, 7 Oct 2024 12:27:37 +0200 Subject: [PATCH 53/58] aiger2: Try to fix VS build --- backends/aiger2/aiger.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/backends/aiger2/aiger.cc b/backends/aiger2/aiger.cc index 29b8ee96b..3fc6ccdba 100644 --- a/backends/aiger2/aiger.cc +++ b/backends/aiger2/aiger.cc @@ -47,11 +47,8 @@ PRIVATE_NAMESPACE_BEGIN #define KNOWN_OPS BITWISE_OPS, REDUCE_OPS, LOGIC_OPS, GATE_OPS, ID($pos), CMP_OPS, \ ID($pmux), ID($bmux) /*, ARITH_OPS*/ -template +template struct Index { - static constexpr Lit CFALSE = Writer::CONST_FALSE; - static constexpr Lit CTRUE = Writer::CONST_TRUE; - struct HierCursor; struct ModuleInfo { Module *module; @@ -664,7 +661,7 @@ struct Index { } }; -struct AigerWriter : Index { +struct AigerWriter : Index { typedef unsigned int Lit; const static Lit CONST_FALSE = 0; @@ -804,7 +801,7 @@ struct AigerWriter : Index { } }; -struct XAigerAnalysis : Index { +struct XAigerAnalysis : Index { const static int CONST_FALSE = 0; const static int CONST_TRUE = 0; const static constexpr int EMPTY_LIT = -1; From f44a418212c61ae032d1aecbdc3de588ba3a1f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 7 Oct 2024 12:27:54 +0200 Subject: [PATCH 54/58] read_xaiger2: Add casts to silence warnings --- frontends/aiger2/xaiger.cc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index 4c7305876..18cfe94dd 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -88,7 +88,7 @@ struct Xaiger2Frontend : public Frontend { log_error("Inconsistent header\n"); std::vector outputs; - for (int i = 0; i < O; i++) { + for (int i = 0; i < (int) O; i++) { int po; *f >> po; log_assert(f->get() == '\n'); @@ -110,7 +110,7 @@ struct Xaiger2Frontend : public Frontend { if (!(map_file >> pi_idx >> woffset >> name)) log_error("Bad map file (1)\n"); int lit = (2 * pi_idx) + 2; - if (lit < 0 || lit >= bits.size()) + if (lit < 0 || lit >= (int) bits.size()) log_error("Bad map file (2)\n"); Wire *w = module->wire(name); if (!w || woffset < 0 || woffset >= w->width) @@ -140,7 +140,7 @@ struct Xaiger2Frontend : public Frontend { if (!def) log_error("Bad map file (22)\n"); - if (box_seq >= boxes.size()) { + if (box_seq >= (int) boxes.size()) { boxes.resize(box_seq + 1); retained_boxes.resize(box_seq + 1); } @@ -151,7 +151,7 @@ struct Xaiger2Frontend : public Frontend { } } - for (int i = 0; i < A; i++) { + for (int i = 0; i < (int) A; i++) { while (f->get() & 0x80 && !f->eof()); while (f->get() & 0x80 && !f->eof()); } @@ -185,7 +185,7 @@ struct Xaiger2Frontend : public Frontend { box_id = read_be32(*f); box_seq = read_be32(*f); - log("box_seq=%d boxes.size=%d\n", box_seq, boxes.size()); + log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size()); log_assert(box_seq < boxes.size()); auto [cell, def] = boxes[box_seq]; @@ -213,7 +213,7 @@ struct Xaiger2Frontend : public Frontend { } } - log_assert(box_ci_idx == box_outputs); + log_assert(box_ci_idx == (int) box_outputs); ci_counter += box_ci_idx; } log_assert(pi_num + ci_counter == ci_num); @@ -324,7 +324,7 @@ struct Xaiger2Frontend : public Frontend { box_id = read_be32(*f); box_seq = read_be32(*f); - log("box_seq=%d boxes.size=%d\n", box_seq, boxes.size()); + log("box_seq=%d boxes.size=%d\n", box_seq, (int) boxes.size()); log_assert(box_seq < boxes.size()); auto [cell, def] = boxes[box_seq]; @@ -340,9 +340,9 @@ struct Xaiger2Frontend : public Frontend { SigSpec conn; for (int j = 0; j < port->width; j++) { - log_assert(co_counter + box_co_idx < outputs.size()); + log_assert(co_counter + box_co_idx < (int) outputs.size()); int lit = outputs[co_counter + box_co_idx++]; - log_assert(lit >= 0 && lit < bits.size()); + log_assert(lit >= 0 && lit < (int) bits.size()); SigBit bit = bits[lit]; if (bit == RTLIL::Sm) log_error("Malformed mapping (1)\n"); @@ -352,7 +352,7 @@ struct Xaiger2Frontend : public Frontend { } } - log_assert(box_co_idx == box_inputs); + log_assert(box_co_idx == (int) box_inputs); co_counter += box_co_idx; } log_assert(po_num + co_counter == co_num); @@ -389,10 +389,10 @@ struct Xaiger2Frontend : public Frontend { if (!(map_file >> po_idx >> woffset >> name)) log_error("Bad map file (3)\n"); po_idx += co_counter; - if (po_idx < 0 || po_idx >= outputs.size()) + if (po_idx < 0 || po_idx >= (int) outputs.size()) log_error("Bad map file (4)\n"); int lit = outputs[po_idx]; - if (lit < 0 || lit >= bits.size()) + if (lit < 0 || lit >= (int) bits.size()) log_error("Bad map file (5)\n"); if (bits[lit] == RTLIL::Sm) log_error("Bad map file (6)\n"); @@ -409,10 +409,10 @@ struct Xaiger2Frontend : public Frontend { if (!(map_file >> po_idx >> poffset >> box_name >> box_port)) log_error("Bad map file (7)\n"); po_idx += co_counter; - if (po_idx < 0 || po_idx >= outputs.size()) + if (po_idx < 0 || po_idx >= (int) outputs.size()) log_error("Bad map file (8)\n"); int lit = outputs[po_idx]; - if (lit < 0 || lit >= bits.size()) + if (lit < 0 || lit >= (int) bits.size()) log_error("Bad map file (9)\n"); if (bits[lit] == RTLIL::Sm) log_error("Bad map file (10)\n"); From 7989d53c58d793109dee49404bf17b0871277cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 7 Oct 2024 14:19:49 +0200 Subject: [PATCH 55/58] read_xaiger2: Add help --- frontends/aiger2/xaiger.cc | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontends/aiger2/xaiger.cc b/frontends/aiger2/xaiger.cc index 18cfe94dd..04beee396 100644 --- a/frontends/aiger2/xaiger.cc +++ b/frontends/aiger2/xaiger.cc @@ -38,7 +38,26 @@ IdString read_idstring(std::istream &f) } struct Xaiger2Frontend : public Frontend { - Xaiger2Frontend() : Frontend("xaiger2", "read XAIGER file (new)") {} + Xaiger2Frontend() : Frontend("xaiger2", "(experimental) read XAIGER file") + { + experimental(); + } + + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" read_xaiger2 -sc_mapping [options] \n"); + log("\n"); + log("Read a standard cell mapping from a XAIGER file into an existing module.\n"); + log("\n"); + log(" -module_name \n"); + log(" name of the target module\n"); + log("\n"); + log(" -map2 \n"); + log(" read file with symbol information\n"); + log("\n"); + } void read_sc_mapping(std::istream *&f, std::string filename, std::vector args, Design *design) { From b01b17689e919ed09ec80918b6aa23d093f355e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 7 Oct 2024 14:49:17 +0200 Subject: [PATCH 56/58] Add test of error not getting silenced --- tests/various/logger_cmd_error.sh | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100755 tests/various/logger_cmd_error.sh diff --git a/tests/various/logger_cmd_error.sh b/tests/various/logger_cmd_error.sh new file mode 100755 index 000000000..dd0585965 --- /dev/null +++ b/tests/various/logger_cmd_error.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +trap 'echo "ERROR in logger_cmd_error.sh" >&2; exit 1' ERR + +(../../yosys -v 3 -C <&1 | grep -F "ERROR: Module \`nonexistent' not found!" > /dev/null From ca5c2fdff1d2e1e5bc1fbfb1764e75e934f015a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Povi=C5=A1er?= Date: Mon, 7 Oct 2024 15:26:48 +0200 Subject: [PATCH 57/58] quicklogic: Relax the LUT number test --- tests/arch/quicklogic/qlf_k6n10f/div.ys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/arch/quicklogic/qlf_k6n10f/div.ys b/tests/arch/quicklogic/qlf_k6n10f/div.ys index 5ca5b3051..dd5de9d3a 100644 --- a/tests/arch/quicklogic/qlf_k6n10f/div.ys +++ b/tests/arch/quicklogic/qlf_k6n10f/div.ys @@ -10,5 +10,5 @@ EOF equiv_opt -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module -select -assert-count 26 t:$lut +select -assert-max 100 t:$lut select -assert-none t:$lut %% t:* %D From a76bcdc58fd22ba9441cdcc2b80c565966034c6e Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 7 Oct 2024 17:58:48 +0200 Subject: [PATCH 58/58] bufnorm: avoid remove warning. NFC --- passes/techmap/bufnorm.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/passes/techmap/bufnorm.cc b/passes/techmap/bufnorm.cc index 75060673e..3c2cd9230 100644 --- a/passes/techmap/bufnorm.cc +++ b/passes/techmap/bufnorm.cc @@ -70,9 +70,9 @@ struct BufnormPass : public Pass { log(" to chain 'keep' wires first, then ports in declaration order,\n"); log(" and then the other wires in alphanumeric sort order.)\n"); log("\n"); - log(" -noinit\n"); - log(" Do not move 'init' attributes to the wires on FF output ports.\n"); - log("\n"); + // log(" -noinit\n"); + // log(" Do not move 'init' attributes to the wires on FF output ports.\n"); + // log("\n"); log("Run 'bufnorm' with -pos, -bits, or -conn on the whole design to remove all\n"); log("$buf buffer cells and exit 'buffered-normalized mode' again.\n"); log("\n"); @@ -108,7 +108,7 @@ struct BufnormPass : public Pass { bool nokeep_mode = false; bool nosticky_mode = false; bool alphasort_mode = false; - bool noinit_mode = false; // FIXME: Actually move init attributes + // bool noinit_mode = false; // FIXME: Actually move init attributes bool nomode_mode = false; bool pos_mode = false; @@ -169,11 +169,11 @@ struct BufnormPass : public Pass { got_non_update_reset_opt = true; continue; } - if (arg == "-noinit") { - noinit_mode = true; - got_non_update_reset_opt = true; - continue; - } + // if (arg == "-noinit") { + // noinit_mode = true; + // got_non_update_reset_opt = true; + // continue; + // } if (arg == "-pos") { pos_mode = true; got_non_update_reset_opt = true;