aboutsummaryrefslogtreecommitdiffstats
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/aiger/Makefile.inc1
-rw-r--r--backends/aiger/aiger.cc120
-rw-r--r--backends/aiger/xaiger.cc806
-rw-r--r--backends/blif/blif.cc113
-rw-r--r--backends/btor/README23
-rw-r--r--backends/btor/btor.cc1999
-rw-r--r--backends/btor/test_cells.sh30
-rwxr-xr-xbackends/btor/verilog2btor.sh37
-rw-r--r--backends/edif/edif.cc142
-rw-r--r--backends/firrtl/firrtl.cc856
-rw-r--r--backends/ilang/ilang_backend.cc31
-rw-r--r--backends/intersynth/intersynth.cc10
-rw-r--r--backends/json/json.cc65
-rw-r--r--backends/protobuf/.gitignore2
-rw-r--r--backends/protobuf/Makefile.inc8
-rw-r--r--backends/protobuf/protobuf.cc371
-rw-r--r--backends/simplec/simplec.cc12
-rw-r--r--backends/simplec/test00_uut.v6
-rw-r--r--backends/smt2/Makefile.inc20
-rw-r--r--backends/smt2/smt2.cc283
-rw-r--r--backends/smt2/smtbmc.py467
-rw-r--r--backends/smt2/smtio.py365
-rw-r--r--backends/smv/smv.cc98
-rw-r--r--backends/spice/spice.cc6
-rw-r--r--backends/table/table.cc8
-rw-r--r--backends/verilog/verilog_backend.cc551
26 files changed, 4842 insertions, 1588 deletions
diff --git a/backends/aiger/Makefile.inc b/backends/aiger/Makefile.inc
index 0fc37e95c..4a4cf30bd 100644
--- a/backends/aiger/Makefile.inc
+++ b/backends/aiger/Makefile.inc
@@ -1,3 +1,4 @@
OBJS += backends/aiger/aiger.o
+OBJS += backends/aiger/xaiger.o
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index 526e50a49..a51e3648c 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -70,37 +70,41 @@ struct AigerWriter
int bit2aig(SigBit bit)
{
- if (aig_map.count(bit) == 0)
- {
- aig_map[bit] = -1;
-
- if (initstate_bits.count(bit)) {
- log_assert(initstate_ff > 0);
- aig_map[bit] = initstate_ff;
- } else
- if (not_map.count(bit)) {
- int a = bit2aig(not_map.at(bit)) ^ 1;
- aig_map[bit] = a;
- } else
- if (and_map.count(bit)) {
- auto args = and_map.at(bit);
- int a0 = bit2aig(args.first);
- int a1 = bit2aig(args.second);
- aig_map[bit] = mkgate(a0, a1);
- } else
- if (alias_map.count(bit)) {
- aig_map[bit] = bit2aig(alias_map.at(bit));
- }
+ auto it = aig_map.find(bit);
+ if (it != aig_map.end()) {
+ log_assert(it->second >= 0);
+ return it->second;
+ }
- if (bit == State::Sx || bit == State::Sz)
- log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
+ // NB: Cannot use iterator returned from aig_map.insert()
+ // since this function is called recursively
+
+ int a = -1;
+ if (not_map.count(bit)) {
+ a = bit2aig(not_map.at(bit)) ^ 1;
+ } else
+ if (and_map.count(bit)) {
+ auto args = and_map.at(bit);
+ int a0 = bit2aig(args.first);
+ int a1 = bit2aig(args.second);
+ a = mkgate(a0, a1);
+ } else
+ if (alias_map.count(bit)) {
+ a = bit2aig(alias_map.at(bit));
+ } else
+ if (initstate_bits.count(bit)) {
+ a = initstate_ff;
}
- log_assert(aig_map.at(bit) >= 0);
- return aig_map.at(bit);
+ if (bit == State::Sx || bit == State::Sz)
+ log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
+
+ log_assert(a >= 0);
+ aig_map[bit] = a;
+ return a;
}
- AigerWriter(Module *module, bool zinit_mode) : module(module), zinit_mode(zinit_mode), sigmap(module)
+ AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module)
{
pool<SigBit> undriven_bits;
pool<SigBit> unused_bits;
@@ -293,6 +297,10 @@ struct AigerWriter
aig_map[bit] = 2*aig_m;
}
+ if (imode && input_bits.empty()) {
+ aig_m++, aig_i++;
+ }
+
if (zinit_mode)
{
for (auto it : ff_map) {
@@ -362,6 +370,12 @@ struct AigerWriter
aig_latchin.push_back(a);
}
+ if (lmode && aig_l == 0) {
+ aig_m++, aig_l++;
+ aig_latchinit.push_back(0);
+ aig_latchin.push_back(0);
+ }
+
if (!initstate_bits.empty() || !init_inputs.empty())
aig_latchin.push_back(1);
@@ -371,6 +385,11 @@ struct AigerWriter
aig_outputs.push_back(bit2aig(bit));
}
+ if (omode && output_bits.empty()) {
+ aig_o++;
+ aig_outputs.push_back(0);
+ }
+
for (auto it : asserts) {
aig_b++;
int bit_a = bit2aig(it.first);
@@ -378,6 +397,11 @@ struct AigerWriter
aig_outputs.push_back(mkgate(bit_a^1, bit_en));
}
+ if (bmode && asserts.empty()) {
+ aig_b++;
+ aig_outputs.push_back(0);
+ }
+
for (auto it : assumes) {
aig_c++;
int bit_a = bit2aig(it.first);
@@ -657,7 +681,7 @@ struct AigerWriter
struct AigerBackend : public Backend {
AigerBackend() : Backend("aiger", "write design to AIGER file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -671,7 +695,7 @@ struct AigerBackend : public Backend {
log("invariant constraints.\n");
log("\n");
log(" -ascii\n");
- log(" write ASCII version of AGIER format\n");
+ log(" write ASCII version of AIGER format\n");
log("\n");
log(" -zinit\n");
log(" convert FFs to zero-initialized FFs, adding additional inputs for\n");
@@ -689,14 +713,23 @@ struct AigerBackend : public Backend {
log(" -vmap <filename>\n");
log(" like -map, but more verbose\n");
log("\n");
+ log(" -I, -O, -B, -L\n");
+ log(" If the design contains no input/output/assert/flip-flop then create one\n");
+ log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n");
+ log(" AIGER file happy.\n");
+ log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool ascii_mode = false;
bool zinit_mode = false;
bool miter_mode = false;
bool symbols_mode = false;
bool verbose_map = false;
+ bool imode = false;
+ bool omode = false;
+ bool bmode = false;
+ bool lmode = false;
std::string map_filename;
log_header(design, "Executing AIGER backend.\n");
@@ -729,19 +762,44 @@ struct AigerBackend : public Backend {
verbose_map = true;
continue;
}
+ if (args[argidx] == "-I") {
+ imode = true;
+ continue;
+ }
+ if (args[argidx] == "-O") {
+ omode = true;
+ continue;
+ }
+ if (args[argidx] == "-B") {
+ bmode = true;
+ continue;
+ }
+ if (args[argidx] == "-L") {
+ lmode = true;
+ continue;
+ }
break;
}
- extra_args(f, filename, args, argidx);
+ extra_args(f, filename, args, argidx, !ascii_mode);
Module *top_module = design->top_module();
if (top_module == nullptr)
log_error("Can't find top module in current design!\n");
- AigerWriter writer(top_module, zinit_mode);
+ if (!design->selected_whole_module(top_module))
+ log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
+
+ if (!top_module->processes.empty())
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module));
+ if (!top_module->memories.empty())
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module));
+
+ AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode);
writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
if (!map_filename.empty()) {
+ rewrite_filename(filename);
std::ofstream mapf;
mapf.open(map_filename.c_str(), std::ofstream::trunc);
if (mapf.fail())
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
new file mode 100644
index 000000000..b72dd6890
--- /dev/null
+++ b/backends/aiger/xaiger.cc
@@ -0,0 +1,806 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * 2019 Eddie Hung <eddie@fpgeh.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// https://stackoverflow.com/a/46137633
+#ifdef _MSC_VER
+#include <stdlib.h>
+#define bswap32 _byteswap_ulong
+#elif defined(__APPLE__)
+#include <libkern/OSByteOrder.h>
+#define bswap32 OSSwapInt32
+#elif defined(__GNUC__)
+#define bswap32 __builtin_bswap32
+#else
+#include <cstdint>
+inline static uint32_t bswap32(uint32_t x)
+{
+ // https://stackoverflow.com/a/27796212
+ register uint32_t value = number_to_be_reversed;
+ uint8_t lolo = (value >> 0) & 0xFF;
+ uint8_t lohi = (value >> 8) & 0xFF;
+ uint8_t hilo = (value >> 16) & 0xFF;
+ uint8_t hihi = (value >> 24) & 0xFF;
+ return (hihi << 24)
+ | (hilo << 16)
+ | (lohi << 8)
+ | (lolo << 0);
+}
+#endif
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+inline int32_t to_big_endian(int32_t i32) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ return bswap32(i32);
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ return i32;
+#else
+#error "Unknown endianness"
+#endif
+}
+
+void aiger_encode(std::ostream &f, int x)
+{
+ log_assert(x >= 0);
+
+ while (x & ~0x7f) {
+ f.put((x & 0x7f) | 0x80);
+ x = x >> 7;
+ }
+
+ f.put(x);
+}
+
+struct XAigerWriter
+{
+ Module *module;
+ SigMap sigmap;
+
+ pool<SigBit> input_bits, output_bits;
+ dict<SigBit, SigBit> not_map, alias_map;
+ dict<SigBit, pair<SigBit, SigBit>> and_map;
+ vector<SigBit> ci_bits, co_bits;
+ dict<SigBit, Cell*> ff_bits;
+ dict<SigBit, float> arrival_times;
+
+ vector<pair<int, int>> aig_gates;
+ vector<int> aig_outputs;
+ int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
+
+ dict<SigBit, int> aig_map;
+ dict<SigBit, int> ordered_outputs;
+
+ vector<Cell*> box_list;
+
+ int mkgate(int a0, int a1)
+ {
+ aig_m++, aig_a++;
+ aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0));
+ return 2*aig_m;
+ }
+
+ int bit2aig(SigBit bit)
+ {
+ auto it = aig_map.find(bit);
+ if (it != aig_map.end()) {
+ log_assert(it->second >= 0);
+ return it->second;
+ }
+
+ // NB: Cannot use iterator returned from aig_map.insert()
+ // since this function is called recursively
+
+ int a = -1;
+ if (not_map.count(bit)) {
+ a = bit2aig(not_map.at(bit)) ^ 1;
+ } else
+ if (and_map.count(bit)) {
+ auto args = and_map.at(bit);
+ int a0 = bit2aig(args.first);
+ int a1 = bit2aig(args.second);
+ a = mkgate(a0, a1);
+ } else
+ if (alias_map.count(bit)) {
+ a = bit2aig(alias_map.at(bit));
+ }
+
+ if (bit == State::Sx || bit == State::Sz) {
+ log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n");
+ a = aig_map.at(State::S0);
+ }
+
+ log_assert(a >= 0);
+ aig_map[bit] = a;
+ return a;
+ }
+
+ XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module)
+ {
+ pool<SigBit> undriven_bits;
+ pool<SigBit> unused_bits;
+
+ // promote public wires
+ for (auto wire : module->wires())
+ if (wire->name[0] == '\\')
+ sigmap.add(wire);
+
+ // promote input wires
+ for (auto wire : module->wires())
+ if (wire->port_input)
+ sigmap.add(wire);
+
+ // promote keep wires
+ for (auto wire : module->wires())
+ if (wire->get_bool_attribute(ID::keep))
+ sigmap.add(wire);
+
+ for (auto wire : module->wires())
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wirebit(wire, i);
+ SigBit bit = sigmap(wirebit);
+
+ if (bit.wire == nullptr) {
+ if (wire->port_output) {
+ aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
+ output_bits.insert(wirebit);
+ }
+ continue;
+ }
+
+ undriven_bits.insert(bit);
+ unused_bits.insert(bit);
+
+ bool keep = wire->get_bool_attribute(ID::keep);
+ if (wire->port_input || keep)
+ input_bits.insert(bit);
+
+ if (wire->port_output || keep) {
+ if (bit != wirebit)
+ alias_map[wirebit] = bit;
+ output_bits.insert(wirebit);
+ }
+ }
+
+ dict<IdString,dict<IdString,int>> arrival_cache;
+ for (auto cell : module->cells()) {
+ RTLIL::Module* inst_module = module->design->module(cell->type);
+ if (!cell->has_keep_attr()) {
+ if (cell->type == "$_NOT_")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
+ unused_bits.erase(A);
+ undriven_bits.erase(Y);
+ not_map[Y] = A;
+ continue;
+ }
+
+ if (cell->type == "$_AND_")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit B = sigmap(cell->getPort("\\B").as_bit());
+ SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
+ unused_bits.erase(A);
+ unused_bits.erase(B);
+ undriven_bits.erase(Y);
+ and_map[Y] = make_pair(A, B);
+ continue;
+ }
+
+ if (cell->type == "$__ABC9_FF_" &&
+ // The presence of an abc9_mergeability attribute indicates
+ // that we do want to pass this flop to ABC
+ cell->attributes.count("\\abc9_mergeability"))
+ {
+ SigBit D = sigmap(cell->getPort("\\D").as_bit());
+ SigBit Q = sigmap(cell->getPort("\\Q").as_bit());
+ unused_bits.erase(D);
+ undriven_bits.erase(Q);
+ alias_map[Q] = D;
+ auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell));
+ log_assert(r.second);
+ if (input_bits.erase(Q))
+ log_assert(Q.wire->attributes.count(ID::keep));
+ continue;
+ }
+
+ if (inst_module) {
+ bool abc9_flop = false;
+ auto it = cell->attributes.find("\\abc9_box_seq");
+ if (it != cell->attributes.end()) {
+ int abc9_box_seq = it->second.as_int();
+ if (GetSize(box_list) <= abc9_box_seq)
+ box_list.resize(abc9_box_seq+1);
+ box_list[abc9_box_seq] = cell;
+ // Only flop boxes may have arrival times
+ abc9_flop = inst_module->get_bool_attribute("\\abc9_flop");
+ if (!abc9_flop)
+ continue;
+ }
+
+ auto &cell_arrivals = arrival_cache[cell->type];
+ for (const auto &conn : cell->connections()) {
+ auto r = cell_arrivals.insert(conn.first);
+ auto &arrival = r.first->second;
+ if (r.second) {
+ auto port_wire = inst_module->wire(conn.first);
+ if (port_wire->port_output) {
+ auto it = port_wire->attributes.find("\\abc9_arrival");
+ if (it != port_wire->attributes.end()) {
+ if (it->second.flags != 0)
+ log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type));
+ arrival = it->second.as_int();
+ }
+ }
+ }
+ if (arrival)
+ for (auto bit : sigmap(conn.second))
+ arrival_times[bit] = arrival;
+ }
+
+ if (abc9_flop)
+ continue;
+ }
+ }
+
+ bool cell_known = inst_module || cell->known();
+ for (const auto &c : cell->connections()) {
+ if (c.second.is_fully_const()) continue;
+ auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr;
+ auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first);
+ auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first);
+ if (!is_input && !is_output)
+ log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type));
+
+ if (is_input)
+ for (auto b : c.second) {
+ Wire *w = b.wire;
+ if (!w) continue;
+ // Do not add as PO if bit is already a PI
+ if (input_bits.count(b))
+ continue;
+ if (!w->port_output || !cell_known) {
+ SigBit I = sigmap(b);
+ if (I != b)
+ alias_map[b] = I;
+ output_bits.insert(b);
+ }
+ }
+ }
+
+ //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
+ }
+
+ dict<IdString, std::vector<IdString>> box_ports;
+ for (auto cell : box_list) {
+ log_assert(cell);
+
+ RTLIL::Module* box_module = module->design->module(cell->type);
+ log_assert(box_module);
+ log_assert(box_module->attributes.count("\\abc9_box_id"));
+
+ auto r = box_ports.insert(cell->type);
+ if (r.second) {
+ // Make carry in the last PI, and carry out the last PO
+ // since ABC requires it this way
+ IdString carry_in, carry_out;
+ for (const auto &port_name : box_module->ports) {
+ auto w = box_module->wire(port_name);
+ log_assert(w);
+ if (w->get_bool_attribute("\\abc9_carry")) {
+ if (w->port_input) {
+ if (carry_in != IdString())
+ log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module));
+ carry_in = port_name;
+ }
+ if (w->port_output) {
+ if (carry_out != IdString())
+ log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module));
+ carry_out = port_name;
+ }
+ }
+ else
+ r.first->second.push_back(port_name);
+ }
+
+ if (carry_in != IdString() && carry_out == IdString())
+ log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module));
+ if (carry_in == IdString() && carry_out != IdString())
+ log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module));
+ if (carry_in != IdString()) {
+ r.first->second.push_back(carry_in);
+ r.first->second.push_back(carry_out);
+ }
+ }
+
+ for (auto port_name : r.first->second) {
+ auto w = box_module->wire(port_name);
+ log_assert(w);
+ auto rhs = cell->connections_.at(port_name, SigSpec());
+ rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs)));
+ if (w->port_input)
+ for (auto b : rhs) {
+ SigBit I = sigmap(b);
+ if (b == RTLIL::Sx)
+ b = State::S0;
+ else if (I != b) {
+ if (I == RTLIL::Sx)
+ alias_map[b] = State::S0;
+ else
+ alias_map[b] = I;
+ }
+ co_bits.emplace_back(b);
+ unused_bits.erase(I);
+ }
+ if (w->port_output)
+ for (const auto &b : rhs) {
+ SigBit O = sigmap(b);
+ if (O != b)
+ alias_map[O] = b;
+ ci_bits.emplace_back(b);
+ undriven_bits.erase(O);
+ // If PI and CI, then must be a (* keep *) wire
+ if (input_bits.erase(O)) {
+ log_assert(output_bits.count(O));
+ log_assert(O.wire->get_bool_attribute(ID::keep));
+ }
+ }
+ }
+
+ // Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box
+ if (box_module->get_bool_attribute("\\abc9_flop")) {
+ SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str()));
+ if (rhs.empty())
+ log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module));
+
+ for (auto b : rhs) {
+ SigBit I = sigmap(b);
+ if (b == RTLIL::Sx)
+ b = State::S0;
+ else if (I != b) {
+ if (I == RTLIL::Sx)
+ alias_map[b] = State::S0;
+ else
+ alias_map[b] = I;
+ }
+ co_bits.emplace_back(b);
+ unused_bits.erase(I);
+ }
+ }
+ }
+
+ for (auto bit : input_bits)
+ undriven_bits.erase(bit);
+ for (auto bit : output_bits)
+ unused_bits.erase(sigmap(bit));
+ for (auto bit : unused_bits)
+ undriven_bits.erase(bit);
+
+ // Make all undriven bits a primary input
+ for (auto bit : undriven_bits) {
+ input_bits.insert(bit);
+ undriven_bits.erase(bit);
+ }
+
+ if (holes_mode) {
+ struct sort_by_port_id {
+ bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const {
+ return a.wire->port_id < b.wire->port_id ||
+ (a.wire->port_id == b.wire->port_id && a.offset < b.offset);
+ }
+ };
+ input_bits.sort(sort_by_port_id());
+ output_bits.sort(sort_by_port_id());
+ }
+
+ aig_map[State::S0] = 0;
+ aig_map[State::S1] = 1;
+
+ for (const auto &bit : input_bits) {
+ aig_m++, aig_i++;
+ log_assert(!aig_map.count(bit));
+ aig_map[bit] = 2*aig_m;
+ }
+
+ for (const auto &i : ff_bits) {
+ const Cell *cell = i.second;
+ const SigBit &q = sigmap(cell->getPort("\\Q"));
+ aig_m++, aig_i++;
+ log_assert(!aig_map.count(q));
+ aig_map[q] = 2*aig_m;
+ }
+
+ for (auto &bit : ci_bits) {
+ aig_m++, aig_i++;
+ // 1'bx may exist here due to a box output
+ // that has been padded to its full width
+ if (bit == State::Sx)
+ continue;
+ log_assert(!aig_map.count(bit));
+ aig_map[bit] = 2*aig_m;
+ }
+
+ for (auto bit : co_bits) {
+ ordered_outputs[bit] = aig_o++;
+ aig_outputs.push_back(bit2aig(bit));
+ }
+
+ for (const auto &bit : output_bits) {
+ ordered_outputs[bit] = aig_o++;
+ int aig;
+ // Unlike bit2aig() which checks aig_map first, for
+ // inout/keep bits, since aig_map will point to
+ // the PI, first attempt to find the NOT/AND driver
+ // before resorting to an aig_map lookup (which
+ // could be another PO)
+ if (input_bits.count(bit)) {
+ if (not_map.count(bit)) {
+ aig = bit2aig(not_map.at(bit)) ^ 1;
+ } else if (and_map.count(bit)) {
+ auto args = and_map.at(bit);
+ int a0 = bit2aig(args.first);
+ int a1 = bit2aig(args.second);
+ aig = mkgate(a0, a1);
+ }
+ else
+ aig = aig_map.at(bit);
+ }
+ else
+ aig = bit2aig(bit);
+ aig_outputs.push_back(aig);
+ }
+
+ for (auto &i : ff_bits) {
+ const SigBit &d = i.first;
+ aig_o++;
+ aig_outputs.push_back(aig_map.at(d));
+ }
+ }
+
+ void write_aiger(std::ostream &f, bool ascii_mode)
+ {
+ int aig_obc = aig_o;
+ int aig_obcj = aig_obc;
+ int aig_obcjf = aig_obcj;
+
+ log_assert(aig_m == aig_i + aig_l + aig_a);
+ log_assert(aig_obcjf == GetSize(aig_outputs));
+
+ f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a);
+ f << stringf("\n");
+
+ if (ascii_mode)
+ {
+ for (int i = 0; i < aig_i; i++)
+ f << stringf("%d\n", 2*i+2);
+
+ for (int i = 0; i < aig_obc; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("1\n");
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obcj; i < aig_obcjf; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = 0; i < aig_a; i++)
+ f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second);
+ }
+ else
+ {
+ for (int i = 0; i < aig_obc; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("1\n");
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obcj; i < aig_obcjf; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = 0; i < aig_a; i++) {
+ int lhs = 2*(aig_i+aig_l+i)+2;
+ int rhs0 = aig_gates.at(i).first;
+ int rhs1 = aig_gates.at(i).second;
+ int delta0 = lhs - rhs0;
+ int delta1 = rhs0 - rhs1;
+ aiger_encode(f, delta0);
+ aiger_encode(f, delta1);
+ }
+ }
+
+ f << "c";
+
+ auto write_buffer = [](std::stringstream &buffer, int i32) {
+ int32_t i32_be = to_big_endian(i32);
+ buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be));
+ };
+ std::stringstream h_buffer;
+ auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
+ write_h_buffer(1);
+ log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits));
+ write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
+ log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits));
+ write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits));
+ log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits));
+ write_h_buffer(input_bits.size() + ff_bits.size());
+ log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits));
+ write_h_buffer(output_bits.size() + ff_bits.size());
+ log_debug("boxNum = %d\n", GetSize(box_list));
+ write_h_buffer(box_list.size());
+
+ auto write_buffer_float = [](std::stringstream &buffer, float f32) {
+ buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32));
+ };
+ std::stringstream i_buffer;
+ auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1);
+ for (auto bit : input_bits)
+ write_i_buffer(arrival_times.at(bit, 0));
+ //std::stringstream o_buffer;
+ //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1);
+ //for (auto bit : output_bits)
+ // write_o_buffer(0);
+
+ if (!box_list.empty() || !ff_bits.empty()) {
+ dict<IdString, std::tuple<int,int,int>> cell_cache;
+
+ int box_count = 0;
+ for (auto cell : box_list) {
+ log_assert(cell);
+
+ RTLIL::Module* box_module = module->design->module(cell->type);
+ log_assert(box_module);
+
+ auto r = cell_cache.insert(cell->type);
+ auto &v = r.first->second;
+ if (r.second) {
+ int box_inputs = 0, box_outputs = 0;
+ for (auto port_name : box_module->ports) {
+ RTLIL::Wire *w = box_module->wire(port_name);
+ log_assert(w);
+ if (w->port_input)
+ box_inputs += GetSize(w);
+ if (w->port_output)
+ box_outputs += GetSize(w);
+ }
+
+ // For flops only, create an extra 1-bit input that drives a new wire
+ // called "<cell>.abc9_ff.Q" that is used below
+ if (box_module->get_bool_attribute("\\abc9_flop"))
+ box_inputs++;
+
+ std::get<0>(v) = box_inputs;
+ std::get<1>(v) = box_outputs;
+ std::get<2>(v) = box_module->attributes.at("\\abc9_box_id").as_int();
+ }
+
+ write_h_buffer(std::get<0>(v));
+ write_h_buffer(std::get<1>(v));
+ write_h_buffer(std::get<2>(v));
+ write_h_buffer(box_count++);
+ }
+
+ std::stringstream r_buffer;
+ auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
+ log_debug("flopNum = %d\n", GetSize(ff_bits));
+ write_r_buffer(ff_bits.size());
+
+ std::stringstream s_buffer;
+ auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);
+ write_s_buffer(ff_bits.size());
+
+ for (const auto &i : ff_bits) {
+ const SigBit &d = i.first;
+ const Cell *cell = i.second;
+
+ int mergeability = cell->attributes.at(ID(abc9_mergeability)).as_int();
+ log_assert(mergeability > 0);
+ write_r_buffer(mergeability);
+
+ Const init = cell->attributes.at(ID(abc9_init));
+ log_assert(GetSize(init) == 1);
+ if (init == State::S1)
+ write_s_buffer(1);
+ else if (init == State::S0)
+ write_s_buffer(0);
+ else {
+ log_assert(init == State::Sx);
+ write_s_buffer(0);
+ }
+
+ write_i_buffer(arrival_times.at(d, 0));
+ //write_o_buffer(0);
+ }
+
+ f << "r";
+ std::string buffer_str = r_buffer.str();
+ int32_t buffer_size_be = to_big_endian(buffer_str.size());
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+
+ f << "s";
+ buffer_str = s_buffer.str();
+ buffer_size_be = to_big_endian(buffer_str.size());
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+
+ RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str()));
+ if (holes_module) {
+ std::stringstream a_buffer;
+ XAigerWriter writer(holes_module, true /* holes_mode */);
+ writer.write_aiger(a_buffer, false /*ascii_mode*/);
+
+ f << "a";
+ std::string buffer_str = a_buffer.str();
+ int32_t buffer_size_be = to_big_endian(buffer_str.size());
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+ }
+ }
+
+ f << "h";
+ std::string buffer_str = h_buffer.str();
+ int32_t buffer_size_be = to_big_endian(buffer_str.size());
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+
+ f << "i";
+ buffer_str = i_buffer.str();
+ buffer_size_be = to_big_endian(buffer_str.size());
+ f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ f.write(buffer_str.data(), buffer_str.size());
+ //f << "o";
+ //buffer_str = o_buffer.str();
+ //buffer_size_be = to_big_endian(buffer_str.size());
+ //f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be));
+ //f.write(buffer_str.data(), buffer_str.size());
+
+ f << stringf("Generated by %s\n", yosys_version_str);
+
+ module->design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
+ module->design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size());
+ module->design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size());
+ module->design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size());
+ }
+
+ void write_map(std::ostream &f)
+ {
+ dict<int, string> input_lines;
+ dict<int, string> output_lines;
+
+ for (auto wire : module->wires())
+ {
+ SigSpec sig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ RTLIL::SigBit b(wire, i);
+ if (input_bits.count(b)) {
+ int a = aig_map.at(b);
+ log_assert((a & 1) == 0);
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire));
+ }
+
+ if (output_bits.count(b)) {
+ int o = ordered_outputs.at(b);
+ int init = 2;
+ output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init);
+ continue;
+ }
+ }
+ }
+
+ input_lines.sort();
+ for (auto &it : input_lines)
+ f << it.second;
+ log_assert(input_lines.size() == input_bits.size());
+
+ int box_count = 0;
+ for (auto cell : box_list)
+ f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name));
+
+ output_lines.sort();
+ for (auto &it : output_lines)
+ f << it.second;
+ log_assert(output_lines.size() == output_bits.size());
+ }
+};
+
+struct XAigerBackend : public Backend {
+ XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_xaiger [options] [filename]\n");
+ log("\n");
+ log("Write the top module (according to the (* top *) attribute or if only one module\n");
+ log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or");
+ log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n");
+ log("pseudo-outputs. Whitebox contents will be taken from the '<module-name>$holes'\n");
+ log("module, if it exists.\n");
+ log("\n");
+ log(" -ascii\n");
+ log(" write ASCII version of AIGER format\n");
+ log("\n");
+ log(" -map <filename>\n");
+ log(" write an extra file with port and box symbols\n");
+ log("\n");
+ }
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool ascii_mode = false;
+ std::string map_filename;
+
+ log_header(design, "Executing XAIGER backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-ascii") {
+ ascii_mode = true;
+ continue;
+ }
+ if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) {
+ map_filename = args[++argidx];
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx, !ascii_mode);
+
+ Module *top_module = design->top_module();
+
+ if (top_module == nullptr)
+ log_error("Can't find top module in current design!\n");
+
+ if (!design->selected_whole_module(top_module))
+ log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module));
+
+ if (!top_module->processes.empty())
+ log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module));
+ if (!top_module->memories.empty())
+ log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module));
+
+ XAigerWriter writer(top_module);
+ writer.write_aiger(*f, ascii_mode);
+
+ if (!map_filename.empty()) {
+ std::ofstream mapf;
+ mapf.open(map_filename.c_str(), std::ofstream::trunc);
+ if (mapf.fail())
+ log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
+ writer.write_map(mapf);
+ }
+ }
+} XAigerBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
index f9230a1e6..b6e38c16c 100644
--- a/backends/blif/blif.cc
+++ b/backends/blif/blif.cc
@@ -38,8 +38,10 @@ struct BlifDumperConfig
bool impltf_mode;
bool gates_mode;
bool cname_mode;
+ bool iname_mode;
bool param_mode;
bool attr_mode;
+ bool iattr_mode;
bool blackbox_mode;
bool noalias_mode;
@@ -48,7 +50,8 @@ struct BlifDumperConfig
std::string true_type, true_out, false_type, false_out, undef_type, undef_out;
BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false),
- cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false), noalias_mode(false) { }
+ cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false),
+ blackbox_mode(false), noalias_mode(false) { }
};
struct BlifDumper
@@ -137,7 +140,7 @@ struct BlifDumper
return "subckt";
if (!design->modules_.count(RTLIL::escape_id(cell_type)))
return "gate";
- if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox"))
+ if (design->modules_.at(RTLIL::escape_id(cell_type))->get_blackbox_attribute())
return "gate";
return "subckt";
}
@@ -193,7 +196,7 @@ struct BlifDumper
}
f << stringf("\n");
- if (module->get_bool_attribute("\\blackbox")) {
+ if (module->get_blackbox_attribute()) {
f << stringf(".blackbox\n");
f << stringf(".end\n");
return;
@@ -240,118 +243,125 @@ struct BlifDumper
if (!config->icells_mode && cell->type == "$_NOT_") {
f << stringf(".names %s %s\n0 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_AND_") {
f << stringf(".names %s %s %s\n11 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_OR_") {
f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_XOR_") {
f << stringf(".names %s %s %s\n10 1\n01 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_NAND_") {
f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_NOR_") {
f << stringf(".names %s %s %s\n00 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_XNOR_") {
f << stringf(".names %s %s %s\n11 1\n00 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_ANDNOT_") {
f << stringf(".names %s %s %s\n10 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_ORNOT_") {
f << stringf(".names %s %s %s\n1- 1\n-0 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_AOI3_") {
f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_OAI3_") {
f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_AOI4_") {
f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_OAI4_") {
f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_MUX_") {
f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
- continue;
+ goto internal_cell;
+ }
+
+ if (!config->icells_mode && cell->type == "$_NMUX_") {
+ f << stringf(".names %s %s %s %s\n0-0 1\n-01 1\n",
+ cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")),
+ cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y")));
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_FF_") {
f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
cstr_init(cell->getPort("\\Q")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_DFF_N_") {
f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_DFF_P_") {
f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_DLATCH_N_") {
f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$_DLATCH_P_") {
f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")),
cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q")));
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$lut") {
@@ -367,13 +377,13 @@ struct BlifDumper
f << stringf("\n");
RTLIL::SigSpec mask = cell->parameters.at("\\LUT");
for (int i = 0; i < (1 << width); i++)
- if (mask[i] == RTLIL::S1) {
+ if (mask[i] == State::S1) {
for (int j = width-1; j >= 0; j--) {
f << ((i>>j)&1 ? '1' : '0');
}
f << " 1\n";
}
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$sop") {
@@ -401,17 +411,31 @@ struct BlifDumper
}
f << " 1\n";
}
- continue;
+ goto internal_cell;
}
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
for (auto &conn : cell->connections())
- for (int i = 0; i < conn.second.size(); i++) {
- if (conn.second.size() == 1)
- f << stringf(" %s", cstr(conn.first));
- else
- f << stringf(" %s[%d]", cstr(conn.first), i);
- f << stringf("=%s", cstr(conn.second.extract(i, 1)));
+ {
+ if (conn.second.size() == 1) {
+ f << stringf(" %s=%s", cstr(conn.first), cstr(conn.second[0]));
+ continue;
+ }
+
+ Module *m = design->module(cell->type);
+ Wire *w = m ? m->wire(conn.first) : nullptr;
+
+ if (w == nullptr) {
+ for (int i = 0; i < GetSize(conn.second); i++)
+ f << stringf(" %s[%d]=%s", cstr(conn.first), i, cstr(conn.second[i]));
+ } else {
+ for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) {
+ SigBit sig(w, i);
+ f << stringf(" %s[%d]=%s", cstr(conn.first), sig.wire->upto ?
+ sig.wire->start_offset+sig.wire->width-sig.offset-1 :
+ sig.wire->start_offset+sig.offset, cstr(conn.second[i]));
+ }
+ }
}
f << stringf("\n");
@@ -421,6 +445,14 @@ struct BlifDumper
dump_params(".attr", cell->attributes);
if (config->param_mode)
dump_params(".param", cell->parameters);
+
+ if (0) {
+ internal_cell:
+ if (config->iname_mode)
+ f << stringf(".cname %s\n", cstr(cell->name));
+ if (config->iattr_mode)
+ dump_params(".attr", cell->attributes);
+ }
}
for (auto &conn : module->connections())
@@ -453,7 +485,7 @@ struct BlifDumper
struct BlifBackend : public Backend {
BlifBackend() : Backend("blif", "write design to BLIF file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -511,6 +543,11 @@ struct BlifBackend : public Backend {
log(" -cname\n");
log(" use the non-standard .cname statement to write cell names\n");
log("\n");
+ log(" -iname, -iattr\n");
+ log(" enable -cname and -attr functionality for .names statements\n");
+ log(" (the .cname and .attr statements will be included in the BLIF\n");
+ log(" output after the truth table for the .names statement)\n");
+ log("\n");
log(" -blackbox\n");
log(" write blackbox cells with .blackbox statement.\n");
log("\n");
@@ -518,7 +555,7 @@ struct BlifBackend : public Backend {
log(" do not write definitions for the $true, $false and $undef wires.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string top_module_name;
std::string buf_type, buf_in, buf_out;
@@ -587,6 +624,14 @@ struct BlifBackend : public Backend {
config.attr_mode = true;
continue;
}
+ if (args[argidx] == "-iname") {
+ config.iname_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-iattr") {
+ config.iattr_mode = true;
+ continue;
+ }
if (args[argidx] == "-blackbox") {
config.blackbox_mode = true;
continue;
@@ -616,7 +661,7 @@ struct BlifBackend : public Backend {
for (auto module_it : design->modules_)
{
RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode)
+ if (module->get_blackbox_attribute() && !config.blackbox_mode)
continue;
if (module->processes.size() != 0)
diff --git a/backends/btor/README b/backends/btor/README
deleted file mode 100644
index efcf0d8f5..000000000
--- a/backends/btor/README
+++ /dev/null
@@ -1,23 +0,0 @@
-
-This is the Yosys BTOR backend.
-It is developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy
-
-Master git repository for the BTOR backend:
-https://github.com/ahmedirfan1983/yosys
-
-
-[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking
-Johannes Kepler University, Linz, Austria
-http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf
-
-
-Todos:
-------
-
-- Add checks for unsupported stuff
- - unsupported cell types
- - async resets
- - etc..
-
-- Add support for $lut cells
-
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index bbe90e85f..c1da4b127 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -2,7 +2,6 @@
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
- * Copyright (C) 2014 Ahmed Irfan <irfan@fbk.eu>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -18,9 +17,10 @@
*
*/
-// [[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking
-// Johannes Kepler University, Linz, Austria
-// http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf
+// [[CITE]] Btor2 , BtorMC and Boolector 3.0
+// Aina Niemetz, Mathias Preiner, Clifford Wolf, Armin Biere
+// Computer Aided Verification - 30th International Conference, CAV 2018
+// https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/
#include "kernel/rtlil.h"
#include "kernel/register.h"
@@ -28,1083 +28,1240 @@
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include <string>
-#include <math.h>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-struct BtorDumperConfig
+struct BtorWorker
{
- bool subckt_mode;
- bool conn_mode;
- bool impltf_mode;
+ std::ostream &f;
+ SigMap sigmap;
+ RTLIL::Module *module;
+ bool verbose;
+ bool single_bad;
- std::string buf_type, buf_in, buf_out;
- std::string true_type, true_out, false_type, false_out;
+ int next_nid = 1;
+ int initstate_nid = -1;
- BtorDumperConfig() : subckt_mode(false), conn_mode(false), impltf_mode(false) { }
-};
+ // <width> => <sid>
+ dict<int, int> sorts_bv;
-struct WireInfo
-{
- RTLIL::IdString cell_name;
- const RTLIL::SigChunk *chunk;
+ // (<address-width>, <data-width>) => <sid>
+ dict<pair<int, int>, int> sorts_mem;
- WireInfo(RTLIL::IdString c, const RTLIL::SigChunk* ch) : cell_name(c), chunk(ch) { }
-};
+ // SigBit => (<nid>, <bitidx>)
+ dict<SigBit, pair<int, int>> bit_nid;
-struct WireInfoOrder
-{
- bool operator() (const WireInfo& x, const WireInfo& y)
+ // <nid> => <bvwidth>
+ dict<int, int> nid_width;
+
+ // SigSpec => <nid>
+ dict<SigSpec, int> sig_nid;
+
+ // bit to driving cell
+ dict<SigBit, Cell*> bit_cell;
+
+ // nids for constants
+ dict<Const, int> consts;
+
+ // ff inputs that need to be evaluated (<nid>, <ff_cell>)
+ vector<pair<int, Cell*>> ff_todo;
+
+ pool<Cell*> cell_recursion_guard;
+ vector<int> bad_properties;
+ dict<SigBit, bool> initbits;
+ pool<Wire*> statewires;
+ string indent;
+
+ void btorf(const char *fmt, ...)
{
- return x.chunk < y.chunk;
+ va_list ap;
+ va_start(ap, fmt);
+ f << indent << vstringf(fmt, ap);
+ va_end(ap);
}
-};
-struct BtorDumper
-{
- std::ostream &f;
- RTLIL::Module *module;
- RTLIL::Design *design;
- BtorDumperConfig *config;
- CellTypes ct;
+ void btorf_push(const string &id)
+ {
+ if (verbose) {
+ f << indent << stringf(" ; begin %s\n", id.c_str());
+ indent += " ";
+ }
+ }
- SigMap sigmap;
- std::map<RTLIL::IdString, std::set<WireInfo,WireInfoOrder>> inter_wire_map;//<wire, dependency list> for mapping the intermediate wires that are output of some cell
- std::map<RTLIL::IdString, int> line_ref;//mapping of ids to line_num of the btor file
- std::map<RTLIL::SigSpec, int> sig_ref;//mapping of sigspec to the line_num of the btor file
- int line_num;//last line number of btor file
- std::string str;//temp string for writing file
- std::map<RTLIL::IdString, bool> basic_wires;//input wires and registers
- RTLIL::IdString curr_cell; //current cell being dumped
- std::map<std::string, std::string> cell_type_translation, s_cell_type_translation; //RTLIL to BTOR translation
- std::map<int, std::set<std::pair<int,int>>> mem_next; // memory (line_number)'s set of condition and write
- BtorDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig *config) :
- f(f), module(module), design(design), config(config), ct(design), sigmap(module)
+ void btorf_pop(const string &id)
{
- line_num=0;
- str.clear();
- for(auto it=module->wires_.begin(); it!=module->wires_.end(); ++it)
- {
- if(it->second->port_input)
- {
- basic_wires[it->first]=true;
- }
- else
- {
- basic_wires[it->first]=false;
- }
- inter_wire_map[it->first].clear();
+ if (verbose) {
+ indent = indent.substr(4);
+ f << indent << stringf(" ; end %s\n", id.c_str());
}
- curr_cell.clear();
- //assert
- cell_type_translation["$assert"] = "root";
- //unary
- cell_type_translation["$not"] = "not";
- cell_type_translation["$neg"] = "neg";
- cell_type_translation["$reduce_and"] = "redand";
- cell_type_translation["$reduce_or"] = "redor";
- cell_type_translation["$reduce_xor"] = "redxor";
- cell_type_translation["$reduce_bool"] = "redor";
- //binary
- cell_type_translation["$and"] = "and";
- cell_type_translation["$or"] = "or";
- cell_type_translation["$xor"] = "xor";
- cell_type_translation["$xnor"] = "xnor";
- cell_type_translation["$shr"] = "srl";
- cell_type_translation["$shl"] = "sll";
- cell_type_translation["$sshr"] = "sra";
- cell_type_translation["$sshl"] = "sll";
- cell_type_translation["$shift"] = "srl";
- cell_type_translation["$shiftx"] = "srl";
- cell_type_translation["$lt"] = "ult";
- cell_type_translation["$le"] = "ulte";
- cell_type_translation["$gt"] = "ugt";
- cell_type_translation["$ge"] = "ugte";
- cell_type_translation["$eq"] = "eq";
- cell_type_translation["$eqx"] = "eq";
- cell_type_translation["$ne"] = "ne";
- cell_type_translation["$nex"] = "ne";
- cell_type_translation["$add"] = "add";
- cell_type_translation["$sub"] = "sub";
- cell_type_translation["$mul"] = "mul";
- cell_type_translation["$mod"] = "urem";
- cell_type_translation["$div"] = "udiv";
- //mux
- cell_type_translation["$mux"] = "cond";
- //reg
- cell_type_translation["$dff"] = "next";
- cell_type_translation["$adff"] = "next";
- cell_type_translation["$dffsr"] = "next";
- //memories
- //nothing here
- //slice
- cell_type_translation["$slice"] = "slice";
- //concat
- cell_type_translation["$concat"] = "concat";
-
- //signed cell type translation
- //binary
- s_cell_type_translation["$modx"] = "srem";
- s_cell_type_translation["$mody"] = "smod";
- s_cell_type_translation["$div"] = "sdiv";
- s_cell_type_translation["$lt"] = "slt";
- s_cell_type_translation["$le"] = "slte";
- s_cell_type_translation["$gt"] = "sgt";
- s_cell_type_translation["$ge"] = "sgte";
+ }
+ int get_bv_sid(int width)
+ {
+ if (sorts_bv.count(width) == 0) {
+ int nid = next_nid++;
+ btorf("%d sort bitvec %d\n", nid, width);
+ sorts_bv[width] = nid;
+ }
+ return sorts_bv.at(width);
}
- vector<shared_str> cstr_buf;
+ int get_mem_sid(int abits, int dbits)
+ {
+ pair<int, int> key(abits, dbits);
+ if (sorts_mem.count(key) == 0) {
+ int addr_sid = get_bv_sid(abits);
+ int data_sid = get_bv_sid(dbits);
+ int nid = next_nid++;
+ btorf("%d sort array %d %d\n", nid, addr_sid, data_sid);
+ sorts_mem[key] = nid;
+ }
+ return sorts_mem.at(key);
+ }
- const char *cstr(const RTLIL::IdString id)
+ void add_nid_sig(int nid, const SigSpec &sig)
{
- str = RTLIL::unescape_id(id);
- for (size_t i = 0; i < str.size(); ++i)
- if (str[i] == '#' || str[i] == '=')
- str[i] = '?';
- cstr_buf.push_back(str);
- return cstr_buf.back().c_str();
+ if (verbose)
+ f << indent << stringf("; %d %s\n", nid, log_signal(sig));
+
+ for (int i = 0; i < GetSize(sig); i++)
+ bit_nid[sig[i]] = make_pair(nid, i);
+
+ sig_nid[sig] = nid;
+ nid_width[nid] = GetSize(sig);
}
- int dump_wire(RTLIL::Wire* wire)
+ void export_cell(Cell *cell)
{
- if(basic_wires[wire->name])
- {
- log("writing wire %s\n", cstr(wire->name));
- auto it = line_ref.find(wire->name);
- if(it==std::end(line_ref))
- {
- ++line_num;
- line_ref[wire->name]=line_num;
- str = stringf("%d var %d %s", line_num, wire->width, cstr(wire->name));
- f << stringf("%s\n", str.c_str());
- return line_num;
- }
- else return it->second;
+ if (cell_recursion_guard.count(cell)) {
+ string cell_list;
+ for (auto c : cell_recursion_guard)
+ cell_list += stringf("\n %s", log_id(c));
+ log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list.c_str());
}
- else // case when the wire is not basic wire
+
+ cell_recursion_guard.insert(cell);
+ btorf_push(log_id(cell));
+
+ if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx",
+ "$concat", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
{
- log("case of non-basic wire - %s\n", cstr(wire->name));
- auto it = line_ref.find(wire->name);
- if(it==std::end(line_ref))
+ string btor_op;
+ if (cell->type == "$add") btor_op = "add";
+ if (cell->type == "$sub") btor_op = "sub";
+ if (cell->type == "$mul") btor_op = "mul";
+ if (cell->type.in("$shl", "$sshl")) btor_op = "sll";
+ if (cell->type == "$shr") btor_op = "srl";
+ if (cell->type == "$sshr") btor_op = "sra";
+ if (cell->type.in("$shift", "$shiftx")) btor_op = "shift";
+ if (cell->type.in("$and", "$_AND_")) btor_op = "and";
+ if (cell->type.in("$or", "$_OR_")) btor_op = "or";
+ if (cell->type.in("$xor", "$_XOR_")) btor_op = "xor";
+ if (cell->type == "$concat") btor_op = "concat";
+ if (cell->type == "$_NAND_") btor_op = "nand";
+ if (cell->type == "$_NOR_") btor_op = "nor";
+ if (cell->type.in("$xnor", "$_XNOR_")) btor_op = "xnor";
+ log_assert(!btor_op.empty());
+
+ int width = GetSize(cell->getPort("\\Y"));
+ width = std::max(width, GetSize(cell->getPort("\\A")));
+ width = std::max(width, GetSize(cell->getPort("\\B")));
+
+ bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false;
+ bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false;
+
+ if (btor_op == "shift" && !b_signed)
+ btor_op = "srl";
+
+ if (cell->type.in("$shl", "$sshl", "$shr", "$sshr"))
+ b_signed = false;
+
+ if (cell->type == "$sshr" && !a_signed)
+ btor_op = "srl";
+
+ int sid = get_bv_sid(width);
+ int nid;
+
+ if (btor_op == "shift")
{
- std::set<WireInfo, WireInfoOrder>& dep_set = inter_wire_map.at(wire->name);
- int wire_line = 0;
- int wire_width = 0;
- for(auto dep_set_it=dep_set.begin(); dep_set_it!=dep_set.end(); ++dep_set_it)
- {
- RTLIL::IdString cell_id = dep_set_it->cell_name;
- if(cell_id == curr_cell)
- break;
- log(" -- found cell %s\n", cstr(cell_id));
- RTLIL::Cell* cell = module->cells_.at(cell_id);
- const RTLIL::SigSpec* cell_output = get_cell_output(cell);
- int cell_line = dump_cell(cell);
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, false);
+ int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed);
- if(dep_set.size()==1 && wire->width == cell_output->size())
- {
- wire_line = cell_line;
- break;
- }
- else
- {
- int prev_wire_line=0; //previously dumped wire line
- int start_bit=0;
- for(unsigned j=0; j<cell_output->chunks().size(); ++j)
- {
- start_bit+=cell_output->chunks().at(j).width;
- if(cell_output->chunks().at(j).wire->name == wire->name)
- {
- prev_wire_line = wire_line;
- wire_line = ++line_num;
- str = stringf("%d slice %d %d %d %d;1", line_num, cell_output->chunks().at(j).width,
- cell_line, start_bit-1, start_bit-cell_output->chunks().at(j).width);
- f << stringf("%s\n", str.c_str());
- wire_width += cell_output->chunks().at(j).width;
- if(prev_wire_line!=0)
- {
- ++line_num;
- str = stringf("%d concat %d %d %d", line_num, wire_width, wire_line, prev_wire_line);
- f << stringf("%s\n", str.c_str());
- wire_line = line_num;
- }
- }
- }
- }
- }
- if(dep_set.size()==0)
- {
- log(" - checking sigmap\n");
- RTLIL::SigSpec s = RTLIL::SigSpec(wire);
- wire_line = dump_sigspec(&s, s.size());
- line_ref[wire->name]=wire_line;
- }
- line_ref[wire->name]=wire_line;
- return wire_line;
+ int nid_r = next_nid++;
+ btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b);
+
+ int nid_b_neg = next_nid++;
+ btorf("%d neg %d %d\n", nid_b_neg, sid, nid_b);
+
+ int nid_l = next_nid++;
+ btorf("%d sll %d %d %d\n", nid_l, sid, nid_a, nid_b_neg);
+
+ int sid_bit = get_bv_sid(1);
+ int nid_zero = get_sig_nid(Const(0, width));
+ int nid_b_ltz = next_nid++;
+ btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero);
+
+ nid = next_nid++;
+ btorf("%d ite %d %d %d %d\n", nid, sid, nid_b_ltz, nid_l, nid_r);
}
else
{
- log(" -- already processed wire\n");
- return it->second;
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed);
+ int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed);
+
+ nid = next_nid++;
+ btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b);
+ }
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) < width) {
+ int sid = get_bv_sid(GetSize(sig));
+ int nid2 = next_nid++;
+ btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1);
+ nid = nid2;
}
+
+ add_nid_sig(nid, sig);
+ goto okay;
}
- log_abort();
- return -1;
- }
- int dump_memory(const RTLIL::Memory* memory)
- {
- log("writing memory %s\n", cstr(memory->name));
- auto it = line_ref.find(memory->name);
- if(it==std::end(line_ref))
+ if (cell->type.in("$div", "$mod"))
{
- ++line_num;
- int address_bits = ceil_log2(memory->size);
- str = stringf("%d array %d %d", line_num, memory->width, address_bits);
- line_ref[memory->name]=line_num;
- f << stringf("%s\n", str.c_str());
- return line_num;
+ string btor_op;
+ if (cell->type == "$div") btor_op = "div";
+ if (cell->type == "$mod") btor_op = "rem";
+ log_assert(!btor_op.empty());
+
+ int width = GetSize(cell->getPort("\\Y"));
+ width = std::max(width, GetSize(cell->getPort("\\A")));
+ width = std::max(width, GetSize(cell->getPort("\\B")));
+
+ bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false;
+ bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false;
+
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed);
+ int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed);
+
+ int sid = get_bv_sid(width);
+ int nid = next_nid++;
+ btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b);
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) < width) {
+ int sid = get_bv_sid(GetSize(sig));
+ int nid2 = next_nid++;
+ btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1);
+ nid = nid2;
+ }
+
+ add_nid_sig(nid, sig);
+ goto okay;
}
- else return it->second;
- }
- int dump_memory_next(const RTLIL::Memory* memory)
- {
- auto mem_it = line_ref.find(memory->name);
- int address_bits = ceil_log2(memory->size);
- if(mem_it==std::end(line_ref))
- {
- log("can not write next of a memory that is not dumped yet\n");
- log_abort();
- }
- else
- {
- auto acond_list_it = mem_next.find(mem_it->second);
- if(acond_list_it!=std::end(mem_next))
+ if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
{
- std::set<std::pair<int,int>>& cond_list = acond_list_it->second;
- if(cond_list.empty())
- {
- return 0;
- }
- auto it=cond_list.begin();
- ++line_num;
- str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, mem_it->second);
- f << stringf("%s\n", str.c_str());
- ++it;
- for(; it!=cond_list.end(); ++it)
- {
- ++line_num;
- str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, line_num-1);
- f << stringf("%s\n", str.c_str());
- }
- ++line_num;
- str = stringf("%d anext %d %d %d %d", line_num, memory->width, address_bits, mem_it->second, line_num-1);
- f << stringf("%s\n", str.c_str());
- return 1;
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+ int nid_b = get_sig_nid(cell->getPort("\\B"));
+
+ int nid1 = next_nid++;
+ int nid2 = next_nid++;
+
+ if (cell->type == "$_ANDNOT_") {
+ btorf("%d not %d %d\n", nid1, sid, nid_b);
+ btorf("%d and %d %d %d\n", nid2, sid, nid_a, nid1);
+ }
+
+ if (cell->type == "$_ORNOT_") {
+ btorf("%d not %d %d\n", nid1, sid, nid_b);
+ btorf("%d or %d %d %d\n", nid2, sid, nid_a, nid1);
+ }
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+ add_nid_sig(nid2, sig);
+ goto okay;
}
- return 0;
- }
- }
- int dump_const(const RTLIL::Const* data, int width, int offset)
- {
- log("writing const \n");
- if((data->flags & RTLIL::CONST_FLAG_STRING) == 0)
+ if (cell->type.in("$_OAI3_", "$_AOI3_"))
{
- if(width<0)
- width = data->bits.size() - offset;
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+ int nid_b = get_sig_nid(cell->getPort("\\B"));
+ int nid_c = get_sig_nid(cell->getPort("\\C"));
+
+ int nid1 = next_nid++;
+ int nid2 = next_nid++;
+ int nid3 = next_nid++;
+
+ if (cell->type == "$_OAI3_") {
+ btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b);
+ btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c);
+ btorf("%d not %d %d\n", nid3, sid, nid2);
+ }
- std::string data_str = data->as_string();
- //if(offset > 0)
- data_str = data_str.substr(offset, width);
+ if (cell->type == "$_AOI3_") {
+ btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b);
+ btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c);
+ btorf("%d not %d %d\n", nid3, sid, nid2);
+ }
- ++line_num;
- str = stringf("%d const %d %s", line_num, width, data_str.c_str());
- f << stringf("%s\n", str.c_str());
- return line_num;
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+ add_nid_sig(nid3, sig);
+ goto okay;
}
- else
- log("writing const error\n");
- log_abort();
- return -1;
- }
- int dump_sigchunk(const RTLIL::SigChunk* chunk)
- {
- log("writing sigchunk\n");
- int l=-1;
- if(chunk->wire == NULL)
+ if (cell->type.in("$_OAI4_", "$_AOI4_"))
{
- RTLIL::Const data_const(chunk->data);
- l=dump_const(&data_const, chunk->width, chunk->offset);
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+ int nid_b = get_sig_nid(cell->getPort("\\B"));
+ int nid_c = get_sig_nid(cell->getPort("\\C"));
+ int nid_d = get_sig_nid(cell->getPort("\\D"));
+
+ int nid1 = next_nid++;
+ int nid2 = next_nid++;
+ int nid3 = next_nid++;
+ int nid4 = next_nid++;
+
+ if (cell->type == "$_OAI4_") {
+ btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b);
+ btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d);
+ btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2);
+ btorf("%d not %d %d\n", nid4, sid, nid3);
+ }
+
+ if (cell->type == "$_AOI4_") {
+ btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b);
+ btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d);
+ btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2);
+ btorf("%d not %d %d\n", nid4, sid, nid3);
+ }
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+ add_nid_sig(nid4, sig);
+ goto okay;
}
- else
+
+ if (cell->type.in("$lt", "$le", "$eq", "$eqx", "$ne", "$nex", "$ge", "$gt"))
{
- if (chunk->width == chunk->wire->width && chunk->offset == 0)
- l = dump_wire(chunk->wire);
- else
- {
- int wire_line_num = dump_wire(chunk->wire);
- log_assert(wire_line_num>0);
- ++line_num;
- str = stringf("%d slice %d %d %d %d;2", line_num, chunk->width, wire_line_num,
- chunk->width + chunk->offset - 1, chunk->offset);
- f << stringf("%s\n", str.c_str());
- l = line_num;
+ string btor_op;
+ if (cell->type == "$lt") btor_op = "lt";
+ if (cell->type == "$le") btor_op = "lte";
+ if (cell->type.in("$eq", "$eqx")) btor_op = "eq";
+ if (cell->type.in("$ne", "$nex")) btor_op = "neq";
+ if (cell->type == "$ge") btor_op = "gte";
+ if (cell->type == "$gt") btor_op = "gt";
+ log_assert(!btor_op.empty());
+
+ int width = 1;
+ width = std::max(width, GetSize(cell->getPort("\\A")));
+ width = std::max(width, GetSize(cell->getPort("\\B")));
+
+ bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false;
+ bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false;
+
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed);
+ int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed);
+
+ int nid = next_nid++;
+ if (cell->type.in("$lt", "$le", "$ge", "$gt")) {
+ btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b);
+ } else {
+ btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b);
+ }
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) > 1) {
+ int sid = get_bv_sid(GetSize(sig));
+ int nid2 = next_nid++;
+ btorf("%d uext %d %d %d\n", nid2, sid, nid, GetSize(sig) - 1);
+ nid = nid2;
}
+
+ add_nid_sig(nid, sig);
+ goto okay;
}
- return l;
- }
- int dump_sigspec(const RTLIL::SigSpec* sig, int expected_width)
- {
- log("writing sigspec\n");
- RTLIL::SigSpec s = sigmap(*sig);
- int l = -1;
- auto it = sig_ref.find(s);
- if(it == std::end(sig_ref))
+ if (cell->type.in("$not", "$neg", "$_NOT_"))
{
- if (s.is_chunk())
- {
- l = dump_sigchunk(&s.chunks().front());
+ string btor_op;
+ if (cell->type.in("$not", "$_NOT_")) btor_op = "not";
+ if (cell->type == "$neg") btor_op = "neg";
+ log_assert(!btor_op.empty());
+
+ int width = GetSize(cell->getPort("\\Y"));
+ width = std::max(width, GetSize(cell->getPort("\\A")));
+
+ bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false;
+
+ int sid = get_bv_sid(width);
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed);
+
+ int nid = next_nid++;
+ btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a);
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) < width) {
+ int sid = get_bv_sid(GetSize(sig));
+ int nid2 = next_nid++;
+ btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1);
+ nid = nid2;
+ }
+
+ add_nid_sig(nid, sig);
+ goto okay;
+ }
+
+ if (cell->type.in("$logic_and", "$logic_or", "$logic_not"))
+ {
+ string btor_op;
+ if (cell->type == "$logic_and") btor_op = "and";
+ if (cell->type == "$logic_or") btor_op = "or";
+ if (cell->type == "$logic_not") btor_op = "not";
+ log_assert(!btor_op.empty());
+
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+ int nid_b = btor_op != "not" ? get_sig_nid(cell->getPort("\\B")) : 0;
+
+ if (GetSize(cell->getPort("\\A")) > 1) {
+ int nid_red_a = next_nid++;
+ btorf("%d redor %d %d\n", nid_red_a, sid, nid_a);
+ nid_a = nid_red_a;
+ }
+
+ if (btor_op != "not" && GetSize(cell->getPort("\\B")) > 1) {
+ int nid_red_b = next_nid++;
+ btorf("%d redor %d %d\n", nid_red_b, sid, nid_b);
+ nid_b = nid_red_b;
}
+
+ int nid = next_nid++;
+ if (btor_op != "not")
+ btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b);
else
- {
- int l1, l2, w1, w2;
- l1 = dump_sigchunk(&s.chunks().front());
- log_assert(l1>0);
- w1 = s.chunks().front().width;
- for (unsigned i=1; i < s.chunks().size(); ++i)
- {
- l2 = dump_sigchunk(&s.chunks().at(i));
- log_assert(l2>0);
- w2 = s.chunks().at(i).width;
- ++line_num;
- str = stringf("%d concat %d %d %d", line_num, w1+w2, l2, l1);
- f << stringf("%s\n", str.c_str());
- l1=line_num;
- w1+=w2;
- }
- l = line_num;
+ btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a);
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) > 1) {
+ int sid = get_bv_sid(GetSize(sig));
+ int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1));
+ int nid2 = next_nid++;
+ btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid);
+ nid = nid2;
}
- sig_ref[s] = l;
+
+ add_nid_sig(nid, sig);
+ goto okay;
}
- else
+
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor"))
{
- l = it->second;
+ string btor_op;
+ if (cell->type == "$reduce_and") btor_op = "redand";
+ if (cell->type.in("$reduce_or", "$reduce_bool")) btor_op = "redor";
+ if (cell->type.in("$reduce_xor", "$reduce_xnor")) btor_op = "redxor";
+ log_assert(!btor_op.empty());
+
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+
+ int nid = next_nid++;
+ btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a);
+
+ if (cell->type == "$reduce_xnor") {
+ int nid2 = next_nid++;
+ btorf("%d not %d %d %d\n", nid2, sid, nid);
+ nid = nid2;
+ }
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+
+ if (GetSize(sig) > 1) {
+ int sid = get_bv_sid(GetSize(sig));
+ int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1));
+ int nid2 = next_nid++;
+ btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid);
+ nid = nid2;
+ }
+
+ add_nid_sig(nid, sig);
+ goto okay;
}
- if (expected_width != s.size())
+ if (cell->type.in("$mux", "$_MUX_", "$_NMUX_"))
{
- log(" - changing width of sigspec\n");
- //TODO: this block may not be needed anymore, due to explicit type conversion by "splice" command
- if(expected_width > s.size())
- {
- //TODO: case the signal is signed
- ++line_num;
- str = stringf ("%d zero %d", line_num, expected_width - s.size());
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf ("%d concat %d %d %d", line_num, expected_width, line_num-1, l);
- f << stringf("%s\n", str.c_str());
- l = line_num;
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = sigmap(cell->getPort("\\S"));
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ int nid_a = get_sig_nid(sig_a);
+ int nid_b = get_sig_nid(sig_b);
+ int nid_s = get_sig_nid(sig_s);
+
+ int sid = get_bv_sid(GetSize(sig_y));
+ int nid = next_nid++;
+ btorf("%d ite %d %d %d %d\n", nid, sid, nid_s, nid_b, nid_a);
+
+ if (cell->type == "$_NMUX_") {
+ int tmp = nid;
+ nid = next_nid++;
+ btorf("%d not %d %d\n", nid, sid, tmp);
}
- else if(expected_width < s.size())
- {
- ++line_num;
- str = stringf ("%d slice %d %d %d %d;3", line_num, expected_width, l, expected_width-1, 0);
- f << stringf("%s\n", str.c_str());
- l = line_num;
+
+ add_nid_sig(nid, sig_y);
+ goto okay;
+ }
+
+ if (cell->type == "$pmux")
+ {
+ SigSpec sig_a = sigmap(cell->getPort("\\A"));
+ SigSpec sig_b = sigmap(cell->getPort("\\B"));
+ SigSpec sig_s = sigmap(cell->getPort("\\S"));
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ int width = GetSize(sig_a);
+ int sid = get_bv_sid(width);
+ int nid = get_sig_nid(sig_a);
+
+ for (int i = 0; i < GetSize(sig_s); i++) {
+ int nid_b = get_sig_nid(sig_b.extract(i*width, width));
+ int nid_s = get_sig_nid(sig_s.extract(i));
+ int nid2 = next_nid++;
+ btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid);
+ nid = nid2;
}
+
+ add_nid_sig(nid, sig_y);
+ goto okay;
}
- log_assert(l>0);
- return l;
- }
- int dump_cell(const RTLIL::Cell* cell)
- {
- auto it = line_ref.find(cell->name);
- if(it==std::end(line_ref))
+ if (cell->type.in("$dff", "$ff", "$_DFF_P_", "$_DFF_N", "$_FF_"))
{
- curr_cell = cell->name;
- //assert cell
- if(cell->type == "$assert")
- {
- log("writing assert cell - %s\n", cstr(cell->type));
- const RTLIL::SigSpec* expr = &cell->getPort(RTLIL::IdString("\\A"));
- const RTLIL::SigSpec* en = &cell->getPort(RTLIL::IdString("\\EN"));
- log_assert(expr->size() == 1);
- log_assert(en->size() == 1);
- int expr_line = dump_sigspec(expr, 1);
- int en_line = dump_sigspec(en, 1);
- int one_line = ++line_num;
- str = stringf("%d one 1", line_num);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at("$eq").c_str(), 1, en_line, one_line);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at("$mux").c_str(), 1, line_num-1,
- expr_line, one_line);
- f << stringf("%s\n", str.c_str());
- int cell_line = ++line_num;
- str = stringf("%d %s %d %d", line_num, cell_type_translation.at("$assert").c_str(), 1, -1*(line_num-1));
- //multiplying the line number with -1, which means logical negation
- //the reason for negative sign is that the properties in btor are given as "negation of the original property"
- //bug identified by bobosoft
- //http://www.reddit.com/r/yosys/comments/1w3xig/btor_backend_bug/
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=cell_line;
+ SigSpec sig_d = sigmap(cell->getPort("\\D"));
+ SigSpec sig_q = sigmap(cell->getPort("\\Q"));
+
+ IdString symbol;
+
+ if (sig_q.is_wire()) {
+ Wire *w = sig_q.as_wire();
+ if (w->port_id == 0) {
+ statewires.insert(w);
+ symbol = w->name;
+ }
}
- //unary cells
- else if(cell->type == "$not" || cell->type == "$neg" || cell->type == "$pos" || cell->type == "$reduce_and" ||
- cell->type == "$reduce_or" || cell->type == "$reduce_xor" || cell->type == "$reduce_bool")
+
+ Const initval;
+ for (int i = 0; i < GetSize(sig_q); i++)
+ if (initbits.count(sig_q[i]))
+ initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0);
+ else
+ initval.bits.push_back(State::Sx);
+
+ int nid_init_val = -1;
+
+ if (!initval.is_fully_undef())
+ nid_init_val = get_sig_nid(initval, -1, false, true);
+
+ int sid = get_bv_sid(GetSize(sig_q));
+ int nid = next_nid++;
+
+ if (symbol.empty())
+ btorf("%d state %d\n", nid, sid);
+ else
+ btorf("%d state %d %s\n", nid, sid, log_id(symbol));
+
+ if (nid_init_val >= 0) {
+ int nid_init = next_nid++;
+ if (verbose)
+ btorf("; initval = %s\n", log_signal(initval));
+ btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val);
+ }
+
+ ff_todo.push_back(make_pair(nid, cell));
+ add_nid_sig(nid, sig_q);
+ goto okay;
+ }
+
+ if (cell->type.in("$anyconst", "$anyseq"))
+ {
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ int sid = get_bv_sid(GetSize(sig_y));
+ int nid = next_nid++;
+
+ btorf("%d state %d\n", nid, sid);
+
+ if (cell->type == "$anyconst") {
+ int nid2 = next_nid++;
+ btorf("%d next %d %d %d\n", nid2, sid, nid, nid);
+ }
+
+ add_nid_sig(nid, sig_y);
+ goto okay;
+ }
+
+ if (cell->type == "$initstate")
+ {
+ SigSpec sig_y = sigmap(cell->getPort("\\Y"));
+
+ if (initstate_nid < 0)
{
- log("writing unary cell - %s\n", cstr(cell->type));
- int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- w = w>output_width ? w:output_width; //padding of w
- int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w);
- int cell_line = l;
- if(cell->type != "$pos")
- {
- cell_line = ++line_num;
- bool reduced = (cell->type == "$not" || cell->type == "$neg") ? false : true;
- str = stringf ("%d %s %d %d", cell_line, cell_type_translation.at(cell->type.str()).c_str(), reduced?output_width:w, l);
- f << stringf("%s\n", str.c_str());
- }
- if(output_width < w && (cell->type == "$not" || cell->type == "$neg" || cell->type == "$pos"))
- {
- ++line_num;
- str = stringf ("%d slice %d %d %d %d;4", line_num, output_width, cell_line, output_width-1, 0);
- f << stringf("%s\n", str.c_str());
- cell_line = line_num;
- }
- line_ref[cell->name]=cell_line;
+ int sid = get_bv_sid(1);
+ int one_nid = get_sig_nid(State::S1);
+ int zero_nid = get_sig_nid(State::S0);
+ initstate_nid = next_nid++;
+ btorf("%d state %d\n", initstate_nid, sid);
+ btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid);
+ btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid);
}
- else if(cell->type == "$reduce_xnor" || cell->type == "$logic_not")//no direct translation in btor
+
+ add_nid_sig(initstate_nid, sig_y);
+ goto okay;
+ }
+
+ if (cell->type == "$mem")
+ {
+ int abits = cell->getParam("\\ABITS").as_int();
+ int width = cell->getParam("\\WIDTH").as_int();
+ int nwords = cell->getParam("\\SIZE").as_int();
+ int rdports = cell->getParam("\\RD_PORTS").as_int();
+ int wrports = cell->getParam("\\WR_PORTS").as_int();
+
+ Const wr_clk_en = cell->getParam("\\WR_CLK_ENABLE");
+ Const rd_clk_en = cell->getParam("\\RD_CLK_ENABLE");
+
+ bool asyncwr = wr_clk_en.is_fully_zero();
+
+ if (!asyncwr && !wr_clk_en.is_fully_ones())
+ log_error("Memory %s.%s has mixed async/sync write ports.\n",
+ log_id(module), log_id(cell));
+
+ if (!rd_clk_en.is_fully_zero())
+ log_error("Memory %s.%s has sync read ports.\n",
+ log_id(module), log_id(cell));
+
+ SigSpec sig_rd_addr = sigmap(cell->getPort("\\RD_ADDR"));
+ SigSpec sig_rd_data = sigmap(cell->getPort("\\RD_DATA"));
+
+ SigSpec sig_wr_addr = sigmap(cell->getPort("\\WR_ADDR"));
+ SigSpec sig_wr_data = sigmap(cell->getPort("\\WR_DATA"));
+ SigSpec sig_wr_en = sigmap(cell->getPort("\\WR_EN"));
+
+ int data_sid = get_bv_sid(width);
+ int bool_sid = get_bv_sid(1);
+ int sid = get_mem_sid(abits, width);
+
+ Const initdata = cell->getParam("\\INIT");
+ initdata.exts(nwords*width);
+ int nid_init_val = -1;
+
+ if (!initdata.is_fully_undef())
{
- log("writing unary cell - %s\n", cstr(cell->type));
- int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- log_assert(output_width == 1);
- int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w);
- if(cell->type == "$logic_not" && w > 1)
- {
- ++line_num;
- str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l);
- f << stringf("%s\n", str.c_str());
+ bool constword = true;
+ Const firstword = initdata.extract(0, width);
+
+ for (int i = 1; i < nwords; i++) {
+ Const thisword = initdata.extract(i*width, width);
+ if (thisword != firstword) {
+ constword = false;
+ break;
+ }
}
- else if(cell->type == "$reduce_xnor")
+
+ if (constword)
{
- ++line_num;
- str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_xor").c_str(), output_width, l);
- f << stringf("%s\n", str.c_str());
+ if (verbose)
+ btorf("; initval = %s\n", log_signal(firstword));
+ nid_init_val = get_sig_nid(firstword, -1, false, true);
}
- ++line_num;
- str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$not").c_str(), output_width, l);
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=line_num;
- }
- //binary cells
- else if(cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" ||
- cell->type == "$lt" || cell->type == "$le" || cell->type == "$eq" || cell->type == "$ne" ||
- cell->type == "$eqx" || cell->type == "$nex" || cell->type == "$ge" || cell->type == "$gt" )
- {
- log("writing binary cell - %s\n", cstr(cell->type));
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- log_assert(!(cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" ||
- cell->type == "$ge" || cell->type == "$gt") || output_width == 1);
- bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool();
- bool l2_signed YS_ATTRIBUTE(unused) = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool();
- int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
-
- log_assert(l1_signed == l2_signed);
- l1_width = l1_width > output_width ? l1_width : output_width;
- l1_width = l1_width > l2_width ? l1_width : l2_width;
- l2_width = l2_width > l1_width ? l2_width : l1_width;
-
- int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width);
- int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width);
-
- ++line_num;
- std::string op = cell_type_translation.at(cell->type.str());
- if(cell->type == "$lt" || cell->type == "$le" ||
- cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" ||
- cell->type == "$ge" || cell->type == "$gt")
+ else
{
- if(l1_signed)
- op = s_cell_type_translation.at(cell->type.str());
+ nid_init_val = next_nid++;
+ btorf("%d state %d\n", nid_init_val, sid);
+
+ for (int i = 0; i < nwords; i++) {
+ Const thisword = initdata.extract(i*width, width);
+ if (thisword.is_fully_undef())
+ continue;
+ Const thisaddr(i, abits);
+ int nid_thisword = get_sig_nid(thisword, -1, false, true);
+ int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true);
+ int last_nid_init_val = nid_init_val;
+ nid_init_val = next_nid++;
+ if (verbose)
+ btorf("; initval[%d] = %s\n", i, log_signal(thisword));
+ btorf("%d write %d %d %d %d\n", nid_init_val, sid, last_nid_init_val, nid_thisaddr, nid_thisword);
+ }
}
+ }
+
- str = stringf ("%d %s %d %d %d", line_num, op.c_str(), output_width, l1, l2);
- f << stringf("%s\n", str.c_str());
+ int nid = next_nid++;
+ int nid_head = nid;
- line_ref[cell->name]=line_num;
+ if (cell->name[0] == '$')
+ btorf("%d state %d\n", nid, sid);
+ else
+ btorf("%d state %d %s\n", nid, sid, log_id(cell));
+
+ if (nid_init_val >= 0)
+ {
+ int nid_init = next_nid++;
+ btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val);
}
- else if(cell->type == "$add" || cell->type == "$sub" || cell->type == "$mul" || cell->type == "$div" ||
- cell->type == "$mod" )
+
+ if (asyncwr)
{
- //TODO: division by zero case
- log("writing binary cell - %s\n", cstr(cell->type));
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool();
- bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool();
- int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
-
- log_assert(l1_signed == l2_signed);
- l1_width = l1_width > output_width ? l1_width : output_width;
- l1_width = l1_width > l2_width ? l1_width : l2_width;
- l2_width = l2_width > l1_width ? l2_width : l1_width;
-
- int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width);
- int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width);
-
- ++line_num;
- std::string op = cell_type_translation.at(cell->type.str());
- if(cell->type == "$div" && l1_signed)
- op = s_cell_type_translation.at(cell->type.str());
- else if(cell->type == "$mod")
+ for (int port = 0; port < wrports; port++)
{
- if(l1_signed)
- op = s_cell_type_translation.at("$modx");
- else if(l2_signed)
- op = s_cell_type_translation.at("$mody");
- }
- str = stringf ("%d %s %d %d %d", line_num, op.c_str(), l1_width, l1, l2);
- f << stringf("%s\n", str.c_str());
+ SigSpec wa = sig_wr_addr.extract(port*abits, abits);
+ SigSpec wd = sig_wr_data.extract(port*width, width);
+ SigSpec we = sig_wr_en.extract(port*width, width);
- if(output_width < l1_width)
- {
- ++line_num;
- str = stringf ("%d slice %d %d %d %d;5", line_num, output_width, line_num-1, output_width-1, 0);
- f << stringf("%s\n", str.c_str());
+ int wa_nid = get_sig_nid(wa);
+ int wd_nid = get_sig_nid(wd);
+ int we_nid = get_sig_nid(we);
+
+ int nid2 = next_nid++;
+ btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid);
+
+ int nid3 = next_nid++;
+ btorf("%d not %d %d\n", nid3, data_sid, we_nid);
+
+ int nid4 = next_nid++;
+ btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3);
+
+ int nid5 = next_nid++;
+ btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid);
+
+ int nid6 = next_nid++;
+ btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4);
+
+ int nid7 = next_nid++;
+ btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6);
+
+ int nid8 = next_nid++;
+ btorf("%d redor %d %d\n", nid8, bool_sid, we_nid);
+
+ int nid9 = next_nid++;
+ btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head);
+
+ nid_head = nid9;
}
- line_ref[cell->name]=line_num;
}
- else if(cell->type == "$shr" || cell->type == "$shl" || cell->type == "$sshr" || cell->type == "$sshl" || cell->type == "$shift" || cell->type == "$shiftx")
+
+ for (int port = 0; port < rdports; port++)
{
- log("writing binary cell - %s\n", cstr(cell->type));
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool();
- //bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool();
- int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- l1_width = 1 << ceil_log2(l1_width);
- int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
- //log_assert(l2_width <= ceil_log2(l1_width)) );
- int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width);
- int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), ceil_log2(l1_width));
- int cell_output = ++line_num;
- str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), l1_width, l1, l2);
- f << stringf("%s\n", str.c_str());
-
- if(l2_width > ceil_log2(l1_width))
- {
- int extra_width = l2_width - ceil_log2(l1_width);
- l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width);
- ++line_num;
- str = stringf ("%d slice %d %d %d %d;6", line_num, extra_width, l2, l2_width-1, l2_width-extra_width);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf ("%d one %d", line_num, extra_width);
- f << stringf("%s\n", str.c_str());
- int mux = ++line_num;
- str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$gt").c_str(), 1, line_num-2, line_num-1);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d %s %d", line_num, l1_signed && cell->type == "$sshr" ? "ones":"zero", l1_width);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf ("%d %s %d %d %d %d", line_num, cell_type_translation.at("$mux").c_str(), l1_width, mux, line_num-1, cell_output);
- f << stringf("%s\n", str.c_str());
- cell_output = line_num;
- }
+ SigSpec ra = sig_rd_addr.extract(port*abits, abits);
+ SigSpec rd = sig_rd_data.extract(port*width, width);
- if(output_width < l1_width)
- {
- ++line_num;
- str = stringf ("%d slice %d %d %d %d;5", line_num, output_width, cell_output, output_width-1, 0);
- f << stringf("%s\n", str.c_str());
- cell_output = line_num;
- }
- line_ref[cell->name] = cell_output;
+ int ra_nid = get_sig_nid(ra);
+ int rd_nid = next_nid++;
+
+ btorf("%d read %d %d %d\n", rd_nid, data_sid, nid_head, ra_nid);
+
+ add_nid_sig(rd_nid, rd);
}
- else if(cell->type == "$logic_and" || cell->type == "$logic_or")//no direct translation in btor
+
+ if (!asyncwr)
{
- log("writing binary cell - %s\n", cstr(cell->type));
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- log_assert(output_width == 1);
- int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width);
- int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width);
- int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
- if(l1_width >1)
- {
- ++line_num;
- str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l1);
- f << stringf("%s\n", str.c_str());
- l1 = line_num;
- }
- if(l2_width > 1)
- {
- ++line_num;
- str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l2);
- f << stringf("%s\n", str.c_str());
- l2 = line_num;
+ ff_todo.push_back(make_pair(nid, cell));
+ }
+ else
+ {
+ int nid2 = next_nid++;
+ btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head);
+ }
+
+ goto okay;
+ }
+
+ log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
+
+ okay:
+ btorf_pop(log_id(cell));
+ cell_recursion_guard.erase(cell);
+ }
+
+ int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false, bool is_init = false)
+ {
+ int nid = -1;
+ sigmap.apply(sig);
+
+ for (auto bit : sig)
+ if (bit == State::Sx)
+ goto has_undef_bits;
+
+ if (0)
+ {
+ has_undef_bits:
+ SigSpec sig_mask_undef, sig_noundef;
+ int first_undef = -1;
+
+ for (int i = 0; i < GetSize(sig); i++)
+ if (sig[i] == State::Sx) {
+ if (first_undef < 0)
+ first_undef = i;
+ sig_mask_undef.append(State::S1);
+ sig_noundef.append(State::S0);
+ } else {
+ sig_mask_undef.append(State::S0);
+ sig_noundef.append(sig[i]);
}
- if(cell->type == "$logic_and")
- {
- ++line_num;
- str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$and").c_str(), output_width, l1, l2);
+
+ if (to_width < 0 || first_undef < to_width)
+ {
+ int sid = get_bv_sid(GetSize(sig));
+
+ int nid_input = next_nid++;
+ if (is_init)
+ btorf("%d state %d\n", nid_input, sid);
+ else
+ btorf("%d input %d\n", nid_input, sid);
+
+ int nid_masked_input;
+ if (sig_mask_undef.is_fully_ones()) {
+ nid_masked_input = nid_input;
+ } else {
+ int nid_mask_undef = get_sig_nid(sig_mask_undef);
+ nid_masked_input = next_nid++;
+ btorf("%d and %d %d %d\n", nid_masked_input, sid, nid_input, nid_mask_undef);
}
- else if(cell->type == "$logic_or")
- {
- ++line_num;
- str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$or").c_str(), output_width, l1, l2);
+
+ if (sig_noundef.is_fully_zero()) {
+ nid = nid_masked_input;
+ } else {
+ int nid_noundef = get_sig_nid(sig_noundef);
+ nid = next_nid++;
+ btorf("%d or %d %d %d\n", nid, sid, nid_masked_input, nid_noundef);
}
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=line_num;
- }
- //multiplexers
- else if(cell->type == "$mux")
- {
- log("writing mux cell\n");
- int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
- int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width);
- int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width);
- int s = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), 1);
- ++line_num;
- str = stringf ("%d %s %d %d %d %d",
- line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, s, l2, l1);
- //if s is 0 then l1, if s is 1 then l2 //according to the implementation of mux cell
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=line_num;
+
+ goto extend_or_trim;
}
- else if(cell->type == "$pmux")
- {
- log("writing pmux cell\n");
- int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
- int select_width = cell->parameters.at(RTLIL::IdString("\\S_WIDTH")).as_int();
- int default_case = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width);
- int cases = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width*select_width);
- int select = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), select_width);
- int *c = new int[select_width];
-
- for (int i=0; i<select_width; ++i)
- {
- ++line_num;
- str = stringf ("%d slice 1 %d %d %d", line_num, select, i, i);
- f << stringf("%s\n", str.c_str());
- c[i] = line_num;
- ++line_num;
- str = stringf ("%d slice %d %d %d %d", line_num, output_width, cases, i*output_width+output_width-1,
- i*output_width);
- f << stringf("%s\n", str.c_str());
- }
-
- ++line_num;
- str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[select_width-1], c[select_width-1]+1, default_case);
- f << stringf("%s\n", str.c_str());
-
- for (int i=select_width-2; i>=0; --i)
- {
- ++line_num;
- str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[i], c[i]+1, line_num-1);
- f << stringf("%s\n", str.c_str());
- }
-
- line_ref[cell->name]=line_num;
- }
- //registers
- else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
+
+ sig = sig_noundef;
+ }
+
+ if (sig_nid.count(sig) == 0)
+ {
+ // <nid>, <bitidx>
+ vector<pair<int, int>> nidbits;
+
+ // collect all bits
+ for (int i = 0; i < GetSize(sig); i++)
{
- //TODO: remodelling of adff cells
- log("writing cell - %s\n", cstr(cell->type));
- int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
- log(" - width is %d\n", output_width);
- int cond = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLK")), 1);
- bool polarity = cell->parameters.at(RTLIL::IdString("\\CLK_POLARITY")).as_bool();
- const RTLIL::SigSpec* cell_output = &cell->getPort(RTLIL::IdString("\\Q"));
- int value = dump_sigspec(&cell->getPort(RTLIL::IdString("\\D")), output_width);
- unsigned start_bit = 0;
- for(unsigned i=0; i<cell_output->chunks().size(); ++i)
+ SigBit bit = sig[i];
+
+ if (bit_nid.count(bit) == 0)
{
- output_width = cell_output->chunks().at(i).width;
- log_assert( output_width == cell_output->chunks().at(i).wire->width);//full reg is given the next value
- int reg = dump_wire(cell_output->chunks().at(i).wire);//register
- int slice = value;
- if(cell_output->chunks().size()>1)
+ if (bit.wire == nullptr)
{
- start_bit+=output_width;
- slice = ++line_num;
- str = stringf ("%d slice %d %d %d %d;", line_num, output_width, value, start_bit-1,
- start_bit-output_width);
- f << stringf("%s\n", str.c_str());
+ Const c(bit.data);
+
+ while (i+GetSize(c) < GetSize(sig) && sig[i+GetSize(c)].wire == nullptr)
+ c.bits.push_back(sig[i+GetSize(c)].data);
+
+ if (consts.count(c) == 0) {
+ int sid = get_bv_sid(GetSize(c));
+ int nid = next_nid++;
+ btorf("%d const %d %s\n", nid, sid, c.as_string().c_str());
+ consts[c] = nid;
+ nid_width[nid] = GetSize(c);
+ }
+
+ int nid = consts.at(c);
+
+ for (int j = 0; j < GetSize(c); j++)
+ nidbits.push_back(make_pair(nid, j));
+
+ i += GetSize(c)-1;
+ continue;
}
- if(cell->type == "$dffsr")
+ else
{
- int sync_reset = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLR")), 1);
- bool sync_reset_pol = cell->parameters.at(RTLIL::IdString("\\CLR_POLARITY")).as_bool();
- int sync_reset_value = dump_sigspec(&cell->getPort(RTLIL::IdString("\\SET")),
- output_width);
- bool sync_reset_value_pol = cell->parameters.at(RTLIL::IdString("\\SET_POLARITY")).as_bool();
- ++line_num;
- str = stringf ("%d %s %d %s%d %s%d %d", line_num, cell_type_translation.at("$mux").c_str(),
- output_width, sync_reset_pol ? "":"-", sync_reset, sync_reset_value_pol? "":"-",
- sync_reset_value, slice);
- f << stringf("%s\n", str.c_str());
- slice = line_num;
- }
- ++line_num;
- str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(),
- output_width, polarity?"":"-", cond, slice, reg);
+ if (bit_cell.count(bit) == 0)
+ {
+ SigSpec s = bit;
- f << stringf("%s\n", str.c_str());
- int next = line_num;
- if(cell->type == "$adff")
- {
- int async_reset = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ARST")), 1);
- bool async_reset_pol = cell->parameters.at(RTLIL::IdString("\\ARST_POLARITY")).as_bool();
- int async_reset_value = dump_const(&cell->parameters.at(RTLIL::IdString("\\ARST_VALUE")),
- output_width, 0);
- ++line_num;
- str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(),
- output_width, async_reset_pol ? "":"-", async_reset, async_reset_value, next);
- f << stringf("%s\n", str.c_str());
+ while (i+GetSize(s) < GetSize(sig) && sig[i+GetSize(s)].wire != nullptr &&
+ bit_cell.count(sig[i+GetSize(s)]) == 0)
+ s.append(sig[i+GetSize(s)]);
+
+ log_warning("No driver for signal %s.\n", log_signal(s));
+
+ int sid = get_bv_sid(GetSize(s));
+ int nid = next_nid++;
+ btorf("%d input %d\n", nid, sid);
+ nid_width[nid] = GetSize(s);
+
+ for (int j = 0; j < GetSize(s); j++)
+ nidbits.push_back(make_pair(nid, j));
+
+ i += GetSize(s)-1;
+ continue;
+ }
+ else
+ {
+ export_cell(bit_cell.at(bit));
+ log_assert(bit_nid.count(bit));
+ }
}
- ++line_num;
- str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(),
- output_width, reg, next);
- f << stringf("%s\n", str.c_str());
}
- line_ref[cell->name]=line_num;
- }
- //memories
- else if(cell->type == "$memrd")
- {
- log("writing memrd cell\n");
- if (cell->parameters.at("\\CLK_ENABLE").as_bool() == true)
- log_error("The btor backen does not support $memrd cells with built-in registers. Run memory_dff with -wr_only.\n");
- str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string();
- int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str())));
- int address_width = cell->parameters.at(RTLIL::IdString("\\ABITS")).as_int();
- int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width);
- int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
- ++line_num;
- str = stringf("%d read %d %d %d", line_num, data_width, mem, address);
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=line_num;
+
+ nidbits.push_back(bit_nid.at(bit));
}
- else if(cell->type == "$memwr")
+
+ int width = 0;
+ int nid = -1;
+
+ // group bits and emit slice-concat chain
+ for (int i = 0; i < GetSize(nidbits); i++)
{
- log("writing memwr cell\n");
- if (cell->parameters.at("\\CLK_ENABLE").as_bool() == false)
- log_error("The btor backen does not support $memwr cells without built-in registers. Run memory_dff (but with -wr_only).\n");
- int clk = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLK")), 1);
- bool polarity = cell->parameters.at(RTLIL::IdString("\\CLK_POLARITY")).as_bool();
- int enable = dump_sigspec(&cell->getPort(RTLIL::IdString("\\EN")), 1);
- int address_width = cell->parameters.at(RTLIL::IdString("\\ABITS")).as_int();
- int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width);
- int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int();
- int data = dump_sigspec(&cell->getPort(RTLIL::IdString("\\DATA")), data_width);
- str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string();
- int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str())));
- //check if the memory has already next
- /*
- auto it = mem_next.find(mem);
- if(it != std::end(mem_next))
- {
- ++line_num;
- str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string();
- RTLIL::Memory *memory = module->memories.at(RTLIL::IdString(str.c_str()));
- int address_bits = ceil_log2(memory->size);
- str = stringf("%d array %d %d", line_num, memory->width, address_bits);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d eq 1 %d %d; mem invar", line_num, mem, line_num - 1);
- f << stringf("%s\n", str.c_str());
- mem = line_num - 1;
+ int nid2 = nidbits[i].first;
+ int lower = nidbits[i].second;
+ int upper = lower;
+
+ while (i+1 < GetSize(nidbits) && nidbits[i+1].first == nidbits[i].first &&
+ nidbits[i+1].second == nidbits[i].second+1)
+ upper++, i++;
+
+ int nid3 = nid2;
+
+ if (lower != 0 || upper+1 != nid_width.at(nid2)) {
+ int sid = get_bv_sid(upper-lower+1);
+ nid3 = next_nid++;
+ btorf("%d slice %d %d %d %d\n", nid3, sid, nid2, upper, lower);
}
- */
- ++line_num;
- if(polarity)
- str = stringf("%d one 1", line_num);
- else
- str = stringf("%d zero 1", line_num);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d eq 1 %d %d", line_num, clk, line_num-1);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d and 1 %d %d", line_num, line_num-1, enable);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d write %d %d %d %d %d", line_num, data_width, address_width, mem, address, data);
- f << stringf("%s\n", str.c_str());
- /*
- ++line_num;
- str = stringf("%d acond %d %d %d %d %d", line_num, data_width, address_width, line_num-2, line_num-1, mem);
- f << stringf("%s\n", str.c_str());
- ++line_num;
- str = stringf("%d anext %d %d %d %d", line_num, data_width, address_width, mem, line_num-1);
- f << stringf("%s\n", str.c_str());
- */
- mem_next[mem].insert(std::make_pair(line_num-1, line_num));
+
+ int nid4 = nid3;
+
+ if (nid >= 0) {
+ int sid = get_bv_sid(width+upper-lower+1);
+ nid4 = next_nid++;
+ btorf("%d concat %d %d %d\n", nid4, sid, nid3, nid);
+ }
+
+ width += upper-lower+1;
+ nid = nid4;
}
- else if(cell->type == "$slice")
+
+ sig_nid[sig] = nid;
+ nid_width[nid] = width;
+ }
+
+ nid = sig_nid.at(sig);
+
+ extend_or_trim:
+ if (to_width >= 0 && to_width != GetSize(sig))
+ {
+ if (to_width < GetSize(sig))
{
- log("writing slice cell\n");
- const RTLIL::SigSpec* input = &cell->getPort(RTLIL::IdString("\\A"));
- int input_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- log_assert(input->size() == input_width);
- int input_line = dump_sigspec(input, input_width);
- const RTLIL::SigSpec* output YS_ATTRIBUTE(unused) = &cell->getPort(RTLIL::IdString("\\Y"));
- int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int();
- log_assert(output->size() == output_width);
- int offset = cell->parameters.at(RTLIL::IdString("\\OFFSET")).as_int();
- ++line_num;
- str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, input_line, output_width+offset-1, offset);
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=line_num;
+ int sid = get_bv_sid(to_width);
+ int nid2 = next_nid++;
+ btorf("%d slice %d %d %d 0\n", nid2, sid, nid, to_width-1);
+ nid = nid2;
}
- else if(cell->type == "$concat")
+ else
{
- log("writing concat cell\n");
- const RTLIL::SigSpec* input_a = &cell->getPort(RTLIL::IdString("\\A"));
- int input_a_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int();
- log_assert(input_a->size() == input_a_width);
- int input_a_line = dump_sigspec(input_a, input_a_width);
- const RTLIL::SigSpec* input_b = &cell->getPort(RTLIL::IdString("\\B"));
- int input_b_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int();
- log_assert(input_b->size() == input_b_width);
- int input_b_line = dump_sigspec(input_b, input_b_width);
- ++line_num;
- str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), input_a_width+input_b_width,
- input_a_line, input_b_line);
- f << stringf("%s\n", str.c_str());
- line_ref[cell->name]=line_num;
+ int sid = get_bv_sid(to_width);
+ int nid2 = next_nid++;
+ btorf("%d %s %d %d %d\n", nid2, is_signed ? "sext" : "uext",
+ sid, nid, to_width - GetSize(sig));
+ nid = nid2;
}
- curr_cell.clear();
- return line_num;
- }
- else
- {
- return it->second;
}
+
+ return nid;
}
- const RTLIL::SigSpec* get_cell_output(RTLIL::Cell* cell)
+ BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad) :
+ f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad)
{
- const RTLIL::SigSpec *output_sig = nullptr;
- if (cell->type == "$memrd")
- {
- output_sig = &cell->getPort(RTLIL::IdString("\\DATA"));
- }
- else if(cell->type == "$memwr" || cell->type == "$assert")
+ btorf_push("inputs");
+
+ for (auto wire : module->wires())
{
- //no output
+ if (wire->attributes.count("\\init")) {
+ Const attrval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(wire) && i < GetSize(attrval); i++)
+ if (attrval[i] == State::S0 || attrval[i] == State::S1)
+ initbits[sigmap(SigBit(wire, i))] = (attrval[i] == State::S1);
+ }
+
+ if (!wire->port_id || !wire->port_input)
+ continue;
+
+ SigSpec sig = sigmap(wire);
+ int sid = get_bv_sid(GetSize(sig));
+ int nid = next_nid++;
+
+ btorf("%d input %d %s\n", nid, sid, log_id(wire));
+ add_nid_sig(nid, sig);
}
- else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
+
+ btorf_pop("inputs");
+
+ for (auto cell : module->cells())
+ for (auto &conn : cell->connections())
{
- output_sig = &cell->getPort(RTLIL::IdString("\\Q"));
+ if (!cell->output(conn.first))
+ continue;
+
+ for (auto bit : sigmap(conn.second))
+ bit_cell[bit] = cell;
}
- else
+
+ for (auto wire : module->wires())
{
- output_sig = &cell->getPort(RTLIL::IdString("\\Y"));
- }
- return output_sig;
- }
+ if (!wire->port_id || !wire->port_output)
+ continue;
- void dump_property(RTLIL::Wire *wire)
- {
- int l = dump_wire(wire);
- ++line_num;
- str = stringf("%d root 1 %d", line_num, l);
- f << stringf("%s\n", str.c_str());
- }
+ btorf_push(stringf("output %s", log_id(wire)));
- void dump()
- {
- f << stringf(";module %s\n", cstr(module->name));
+ int nid = get_sig_nid(wire);
+ btorf("%d output %d %s\n", next_nid++, nid, log_id(wire));
- log("creating intermediate wires map\n");
- //creating map of intermediate wires as output of some cell
- for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it)
+ btorf_pop(stringf("output %s", log_id(wire)));
+ }
+
+ for (auto cell : module->cells())
{
- RTLIL::Cell *cell = it->second;
- const RTLIL::SigSpec* output_sig = get_cell_output(cell);
- if(output_sig==nullptr)
- continue;
- RTLIL::SigSpec s = sigmap(*output_sig);
- output_sig = &s;
- log(" - %s\n", cstr(it->second->type));
- if (cell->type == "$memrd")
- {
- for(unsigned i=0; i<output_sig->chunks().size(); ++i)
- {
- RTLIL::Wire *w = output_sig->chunks().at(i).wire;
- RTLIL::IdString wire_id = w->name;
- inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i)));
- }
- }
- else if(cell->type == "$memwr")
- {
- continue;//nothing to do
- }
- else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
+ if (cell->type == "$assume")
{
- RTLIL::IdString wire_id = output_sig->chunks().front().wire->name;
- for(unsigned i=0; i<output_sig->chunks().size(); ++i)
- {
- RTLIL::Wire *w = output_sig->chunks().at(i).wire;
- RTLIL::IdString wire_id = w->name;
- inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i)));
- basic_wires[wire_id] = true;
- }
+ btorf_push(log_id(cell));
+
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+ int nid_en = get_sig_nid(cell->getPort("\\EN"));
+ int nid_not_en = next_nid++;
+ int nid_a_or_not_en = next_nid++;
+ int nid = next_nid++;
+
+ btorf("%d not %d %d\n", nid_not_en, sid, nid_en);
+ btorf("%d or %d %d %d\n", nid_a_or_not_en, sid, nid_a, nid_not_en);
+ btorf("%d constraint %d\n", nid, nid_a_or_not_en);
+
+ btorf_pop(log_id(cell));
}
- else
+
+ if (cell->type == "$assert")
{
- for(unsigned i=0; i<output_sig->chunks().size(); ++i)
- {
- RTLIL::Wire *w = output_sig->chunks().at(i).wire;
- RTLIL::IdString wire_id = w->name;
- inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i)));
+ btorf_push(log_id(cell));
+
+ int sid = get_bv_sid(1);
+ int nid_a = get_sig_nid(cell->getPort("\\A"));
+ int nid_en = get_sig_nid(cell->getPort("\\EN"));
+ int nid_not_a = next_nid++;
+ int nid_en_and_not_a = next_nid++;
+
+ btorf("%d not %d %d\n", nid_not_a, sid, nid_a);
+ btorf("%d and %d %d %d\n", nid_en_and_not_a, sid, nid_en, nid_not_a);
+
+ if (single_bad) {
+ bad_properties.push_back(nid_en_and_not_a);
+ } else {
+ int nid = next_nid++;
+ string infostr = log_id(cell);
+ if (infostr[0] == '$' && cell->attributes.count("\\src")) {
+ infostr = cell->attributes.at("\\src").decode_string().c_str();
+ std::replace(infostr.begin(), infostr.end(), ' ', '_');
+ }
+ btorf("%d bad %d %s\n", nid, nid_en_and_not_a, infostr.c_str());
}
- }
- }
- log("writing input\n");
- std::map<int, RTLIL::Wire*> inputs, outputs;
- std::vector<RTLIL::Wire*> safety;
-
- for (auto &wire_it : module->wires_) {
- RTLIL::Wire *wire = wire_it.second;
- if (wire->port_input)
- inputs[wire->port_id] = wire;
- if (wire->port_output) {
- outputs[wire->port_id] = wire;
- if (wire->name.str().find("safety") != std::string::npos )
- safety.push_back(wire);
+ btorf_pop(log_id(cell));
}
}
- f << stringf(";inputs\n");
- for (auto &it : inputs) {
- RTLIL::Wire *wire = it.second;
- dump_wire(wire);
+ for (auto wire : module->wires())
+ {
+ if (wire->port_id || wire->name[0] == '$')
+ continue;
+
+ btorf_push(stringf("wire %s", log_id(wire)));
+
+ int sid = get_bv_sid(GetSize(wire));
+ int nid = get_sig_nid(sigmap(wire));
+
+ if (statewires.count(wire))
+ continue;
+
+ int this_nid = next_nid++;
+ btorf("%d uext %d %d %d %s\n", this_nid, sid, nid, 0, log_id(wire));
+
+ btorf_pop(stringf("wire %s", log_id(wire)));
+ continue;
}
- f << stringf("\n");
- log("writing memories\n");
- for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it)
+ while (!ff_todo.empty())
{
- dump_memory(mem_it->second);
- }
+ vector<pair<int, Cell*>> todo;
+ todo.swap(ff_todo);
+
+ for (auto &it : todo)
+ {
+ int nid = it.first;
+ Cell *cell = it.second;
+
+ btorf_push(stringf("next %s", log_id(cell)));
- log("writing output wires\n");
- for (auto &it : outputs) {
- RTLIL::Wire *wire = it.second;
- dump_wire(wire);
+ if (cell->type == "$mem")
+ {
+ int abits = cell->getParam("\\ABITS").as_int();
+ int width = cell->getParam("\\WIDTH").as_int();
+ int wrports = cell->getParam("\\WR_PORTS").as_int();
+
+ SigSpec sig_wr_addr = sigmap(cell->getPort("\\WR_ADDR"));
+ SigSpec sig_wr_data = sigmap(cell->getPort("\\WR_DATA"));
+ SigSpec sig_wr_en = sigmap(cell->getPort("\\WR_EN"));
+
+ int data_sid = get_bv_sid(width);
+ int bool_sid = get_bv_sid(1);
+ int sid = get_mem_sid(abits, width);
+ int nid_head = nid;
+
+ for (int port = 0; port < wrports; port++)
+ {
+ SigSpec wa = sig_wr_addr.extract(port*abits, abits);
+ SigSpec wd = sig_wr_data.extract(port*width, width);
+ SigSpec we = sig_wr_en.extract(port*width, width);
+
+ int wa_nid = get_sig_nid(wa);
+ int wd_nid = get_sig_nid(wd);
+ int we_nid = get_sig_nid(we);
+
+ int nid2 = next_nid++;
+ btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid);
+
+ int nid3 = next_nid++;
+ btorf("%d not %d %d\n", nid3, data_sid, we_nid);
+
+ int nid4 = next_nid++;
+ btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3);
+
+ int nid5 = next_nid++;
+ btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid);
+
+ int nid6 = next_nid++;
+ btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4);
+
+ int nid7 = next_nid++;
+ btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6);
+
+ int nid8 = next_nid++;
+ btorf("%d redor %d %d\n", nid8, bool_sid, we_nid);
+
+ int nid9 = next_nid++;
+ btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head);
+
+ nid_head = nid9;
+ }
+
+ int nid2 = next_nid++;
+ btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head);
+ }
+ else
+ {
+ SigSpec sig = sigmap(cell->getPort("\\D"));
+ int nid_q = get_sig_nid(sig);
+ int sid = get_bv_sid(GetSize(sig));
+ btorf("%d next %d %d %d\n", next_nid++, sid, nid, nid_q);
+ }
+
+ btorf_pop(stringf("next %s", log_id(cell)));
+ }
}
- log("writing cells\n");
- for(auto cell_it = module->cells_.begin(); cell_it != module->cells_.end(); ++cell_it)
+ while (!bad_properties.empty())
{
- dump_cell(cell_it->second);
- }
+ vector<int> todo;
+ bad_properties.swap(todo);
- log("writing memory next");
- for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it)
- {
- dump_memory_next(mem_it->second);
- }
+ int sid = get_bv_sid(1);
+ int cursor = 0;
- for(auto it: safety)
- dump_property(it);
+ while (cursor+1 < GetSize(todo))
+ {
+ int nid_a = todo[cursor++];
+ int nid_b = todo[cursor++];
+ int nid = next_nid++;
- f << stringf("\n");
+ bad_properties.push_back(nid);
+ btorf("%d or %d %d %d\n", nid, sid, nid_a, nid_b);
+ }
- log("writing outputs info\n");
- f << stringf(";outputs\n");
- for (auto &it : outputs) {
- RTLIL::Wire *wire = it.second;
- int l = dump_wire(wire);
- f << stringf(";%d %s", l, cstr(wire->name));
+ if (!bad_properties.empty()) {
+ if (cursor < GetSize(todo))
+ bad_properties.push_back(todo[cursor++]);
+ log_assert(cursor == GetSize(todo));
+ } else {
+ int nid = next_nid++;
+ log_assert(cursor == 0);
+ log_assert(GetSize(todo) == 1);
+ btorf("%d bad %d\n", nid, todo[cursor]);
+ }
}
- f << stringf("\n");
- }
-
- static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig &config)
- {
- BtorDumper dumper(f, module, design, &config);
- dumper.dump();
}
};
struct BtorBackend : public Backend {
BtorBackend() : Backend("btor", "write design to BTOR file") { }
-
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" write_btor [filename]\n");
+ log(" write_btor [options] [filename]\n");
+ log("\n");
+ log("Write a BTOR description of the current design.\n");
+ log("\n");
+ log(" -v\n");
+ log(" Add comments and indentation to BTOR output file\n");
+ log("\n");
+ log(" -s\n");
+ log(" Output only a single bad property for all asserts\n");
log("\n");
- log("Write the current design to an BTOR file.\n");
}
-
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- std::string top_module_name;
- std::string buf_type, buf_in, buf_out;
- std::string true_type, true_out;
- std::string false_type, false_out;
- BtorDumperConfig config;
+ bool verbose = false, single_bad = false;
log_header(design, "Executing BTOR backend.\n");
- size_t argidx=1;
- extra_args(f, filename, args, argidx);
-
- if (top_module_name.empty())
- for (auto & mod_it:design->modules_)
- if (mod_it.second->get_bool_attribute("\\top"))
- top_module_name = mod_it.first.str();
-
- *f << stringf("; Generated by %s\n", yosys_version_str);
- *f << stringf("; %s developed and maintained by Clifford Wolf <clifford@clifford.at>\n", yosys_version_str);
- *f << stringf("; BTOR Backend developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy\n");
- *f << stringf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n");
-
- std::vector<RTLIL::Module*> mod_list;
-
- for (auto module_it : design->modules_)
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
{
- RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox"))
+ if (args[argidx] == "-v") {
+ verbose = true;
continue;
-
- if (module->processes.size() != 0)
- log_error("Found unmapped processes in module %s: unmapped processes are not supported in BTOR backend!\n", RTLIL::id2cstr(module->name));
-
- if (module->name == RTLIL::escape_id(top_module_name)) {
- BtorDumper::dump(*f, module, design, config);
- top_module_name.clear();
+ }
+ if (args[argidx] == "-s") {
+ single_bad = true;
continue;
}
-
- mod_list.push_back(module);
+ break;
}
+ extra_args(f, filename, args, argidx);
+
+ RTLIL::Module *topmod = design->top_module();
+
+ if (topmod == nullptr)
+ log_cmd_error("No top module found.\n");
+
+ *f << stringf("; BTOR description generated by %s for module %s.\n",
+ yosys_version_str, log_id(topmod));
- if (!top_module_name.empty())
- log_error("Can't find top module `%s'!\n", top_module_name.c_str());
+ BtorWorker(*f, topmod, verbose, single_bad);
- for (auto module : mod_list)
- BtorDumper::dump(*f, module, design, config);
+ *f << stringf("; end of yosys output\n");
}
} BtorBackend;
diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh
new file mode 100644
index 000000000..e0f1a0514
--- /dev/null
+++ b/backends/btor/test_cells.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+set -ex
+
+rm -rf test_cells.tmp
+mkdir -p test_cells.tmp
+cd test_cells.tmp
+
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod'
+
+for fn in test_*.il; do
+ ../../../yosys -p "
+ read_ilang $fn
+ rename gold gate
+ synth
+
+ read_ilang $fn
+ miter -equiv -make_assert -flatten gold gate main
+ hierarchy -top main
+ write_btor ${fn%.il}.btor
+ "
+ boolectormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out
+ if grep " SATISFIABLE" ${fn%.il}.out; then
+ echo "Check failed for ${fn%.il}."
+ exit 1
+ fi
+done
+
+echo "OK."
+
diff --git a/backends/btor/verilog2btor.sh b/backends/btor/verilog2btor.sh
deleted file mode 100755
index dfd7f1a85..000000000
--- a/backends/btor/verilog2btor.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-
-#
-# Script to write BTOR from Verilog design
-#
-
-if [ "$#" -ne 3 ]; then
- echo "Usage: $0 input.v output.btor top-module-name" >&2
- exit 1
-fi
-if ! [ -e "$1" ]; then
- echo "$1 not found" >&2
- exit 1
-fi
-
-FULL_PATH=$(readlink -f $1)
-DIR=$(dirname $FULL_PATH)
-
-./yosys -q -p "
-read_verilog -sv $1;
-hierarchy -top $3;
-hierarchy -libdir $DIR;
-hierarchy -check;
-proc;
-opt; opt_expr -mux_undef; opt;
-rename -hide;;;
-#techmap -map +/pmux2mux.v;;
-splice; opt;
-memory_dff -wr_only;
-memory_collect;;
-flatten;;
-memory_unpack;
-splitnets -driver;
-setundef -zero -undriven;
-opt;;;
-write_btor $2;"
-
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index a6534b911..616b754ce 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -90,7 +90,7 @@ struct EdifNames
struct EdifBackend : public Backend {
EdifBackend() : Backend("edif", "write design to EDIF netlist file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -106,6 +106,13 @@ struct EdifBackend : public Backend {
log(" if the design contains constant nets. use \"hilomap\" to map to custom\n");
log(" constant drivers first)\n");
log("\n");
+ log(" -gndvccy\n");
+ log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is \"G\"\n");
+ log(" for \"GND\" and \"P\" for \"VCC\".)\n");
+ log("\n");
+ log(" -attrprop\n");
+ log(" create EDIF properties for cell attributes\n");
+ log("\n");
log(" -pvector {par|bra|ang}\n");
log(" sets the delimiting character for module port rename clauses to\n");
log(" parentheses, square brackets, or angle brackets.\n");
@@ -116,13 +123,14 @@ struct EdifBackend : public Backend {
log("is targeted.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing EDIF backend.\n");
std::string top_module_name;
bool port_rename = false;
+ bool attr_properties = false;
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
- bool nogndvcc = false;
+ bool nogndvcc = false, gndvccy = false;
CellTypes ct(design);
EdifNames edif_names;
@@ -137,6 +145,14 @@ struct EdifBackend : public Backend {
nogndvcc = true;
continue;
}
+ if (args[argidx] == "-gndvccy") {
+ gndvccy = true;
+ continue;
+ }
+ if (args[argidx] == "-attrprop") {
+ attr_properties = true;
+ continue;
+ }
if (args[argidx] == "-pvector" && argidx+1 < args.size()) {
std::string parray;
port_rename = true;
@@ -162,7 +178,7 @@ struct EdifBackend : public Backend {
for (auto module_it : design->modules_)
{
RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
if (top_module_name.empty())
@@ -176,7 +192,7 @@ struct EdifBackend : public Backend {
for (auto cell_it : module->cells_)
{
RTLIL::Cell *cell = cell_it.second;
- if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) {
+ if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_blackbox_attribute()) {
lib_cell_ports[cell->type];
for (auto p : cell->connections())
lib_cell_ports[cell->type][p.first] = GetSize(p.second);
@@ -203,7 +219,7 @@ struct EdifBackend : public Backend {
*f << stringf(" (cellType GENERIC)\n");
*f << stringf(" (view VIEW_NETLIST\n");
*f << stringf(" (viewType NETLIST)\n");
- *f << stringf(" (interface (port G (direction OUTPUT)))\n");
+ *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G');
*f << stringf(" )\n");
*f << stringf(" )\n");
@@ -211,7 +227,7 @@ struct EdifBackend : public Backend {
*f << stringf(" (cellType GENERIC)\n");
*f << stringf(" (view VIEW_NETLIST\n");
*f << stringf(" (viewType NETLIST)\n");
- *f << stringf(" (interface (port P (direction OUTPUT)))\n");
+ *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P');
*f << stringf(" )\n");
*f << stringf(" )\n");
}
@@ -284,13 +300,33 @@ struct EdifBackend : public Backend {
*f << stringf(" (library DESIGN\n");
*f << stringf(" (edifLevel 0)\n");
*f << stringf(" (technology (numberDefinition))\n");
+
+ auto add_prop = [&](IdString name, Const val) {
+ if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0)
+ *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str());
+ else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def())
+ *f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int());
+ else {
+ std::string hex_string = "";
+ for (size_t i = 0; i < val.bits.size(); i += 4) {
+ int digit_value = 0;
+ if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
+ if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
+ if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
+ if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
+ char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
+ hex_string = std::string(digit_str) + hex_string;
+ }
+ *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str());
+ }
+ };
for (auto module : sorted_modules)
{
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
SigMap sigmap(module);
- std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db;
+ std::map<RTLIL::SigSpec, std::set<std::pair<std::string, bool>>> net_join_db;
*f << stringf(" (cell %s\n", EDIF_DEF(module->name));
*f << stringf(" (cellType GENERIC)\n");
@@ -307,17 +343,26 @@ struct EdifBackend : public Backend {
else if (!wire->port_input)
dir = "OUTPUT";
if (wire->width == 1) {
- *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(wire->name), dir);
+ *f << stringf(" (port %s (direction %s)", EDIF_DEF(wire->name), dir);
+ if (attr_properties)
+ for (auto &p : wire->attributes)
+ add_prop(p.first, p.second);
+ *f << ")\n";
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
- net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
+ net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input));
} else {
int b[2];
b[wire->upto ? 0 : 1] = wire->start_offset;
b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1;
- *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
+ *f << stringf(" (port (array %s %d) (direction %s)", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir);
+ if (attr_properties)
+ for (auto &p : wire->attributes)
+ add_prop(p.first, p.second);
+
+ *f << ")\n";
for (int i = 0; i < wire->width; i++) {
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i));
- net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1));
+ net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input));
}
}
}
@@ -333,23 +378,11 @@ struct EdifBackend : public Backend {
*f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type),
lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : "");
for (auto &p : cell->parameters)
- if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0)
- *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str());
- else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def())
- *f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int());
- else {
- std::string hex_string = "";
- for (size_t i = 0; i < p.second.bits.size(); i += 4) {
- int digit_value = 0;
- if (i+0 < p.second.bits.size() && p.second.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1;
- if (i+1 < p.second.bits.size() && p.second.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2;
- if (i+2 < p.second.bits.size() && p.second.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4;
- if (i+3 < p.second.bits.size() && p.second.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8;
- char digit_str[2] = { "0123456789abcdef"[digit_value], 0 };
- hex_string = std::string(digit_str) + hex_string;
- }
- *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str());
- }
+ add_prop(p.first, p.second);
+ if (attr_properties)
+ for (auto &p : cell->attributes)
+ add_prop(p.first, p.second);
+
*f << stringf(")\n");
for (auto &p : cell->connections()) {
RTLIL::SigSpec sig = sigmap(p.second);
@@ -358,7 +391,7 @@ struct EdifBackend : public Backend {
log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n",
i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i]));
else if (sig.size() == 1)
- net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)));
+ net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first)));
else {
int member_idx = GetSize(sig)-i-1;
auto m = design->module(cell->type);
@@ -367,8 +400,8 @@ struct EdifBackend : public Backend {
if (w)
member_idx = GetSize(w)-i-1;
}
- net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))",
- EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)));
+ net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))",
+ EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first)));
}
}
}
@@ -377,11 +410,13 @@ struct EdifBackend : public Backend {
if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) {
if (sig == RTLIL::State::Sx) {
for (auto &ref : it.second)
- log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str());
+ log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str());
sig = RTLIL::State::S0;
+ } else if (sig == RTLIL::State::Sz) {
+ continue;
} else {
for (auto &ref : it.second)
- log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str());
+ log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str());
log_abort();
}
}
@@ -398,16 +433,45 @@ struct EdifBackend : public Backend {
}
*f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
for (auto &ref : it.second)
- *f << stringf(" %s\n", ref.c_str());
+ *f << stringf(" %s\n", ref.first.c_str());
if (sig.wire == NULL) {
if (nogndvcc)
log_error("Design contains constant nodes (map with \"hilomap\" first).\n");
if (sig == RTLIL::State::S0)
- *f << stringf(" (portRef G (instanceRef GND))\n");
+ *f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
if (sig == RTLIL::State::S1)
- *f << stringf(" (portRef P (instanceRef VCC))\n");
+ *f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
+ }
+ *f << stringf(" )");
+ if (attr_properties && sig.wire != NULL)
+ for (auto &p : sig.wire->attributes)
+ add_prop(p.first, p.second);
+ *f << stringf("\n )\n");
+ }
+ for (auto &wire_it : module->wires_) {
+ RTLIL::Wire *wire = wire_it.second;
+ if (!wire->get_bool_attribute(ID::keep))
+ continue;
+ for(int i = 0; i < wire->width; i++) {
+ SigBit raw_sig = RTLIL::SigSpec(wire, i);
+ SigBit mapped_sig = sigmap(raw_sig);
+ if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0)
+ continue;
+ std::string netname = log_signal(raw_sig);
+ for (size_t i = 0; i < netname.size(); i++)
+ if (netname[i] == ' ' || netname[i] == '\\')
+ netname.erase(netname.begin() + i--);
+ *f << stringf(" (net %s (joined\n", EDIF_DEF(netname));
+ auto &refs = net_join_db.at(mapped_sig);
+ for (auto &ref : refs)
+ if (ref.second)
+ *f << stringf(" %s\n", ref.first.c_str());
+ *f << stringf(" )");
+ if (attr_properties && raw_sig.wire != NULL)
+ for (auto &p : raw_sig.wire->attributes)
+ add_prop(p.first, p.second);
+ *f << stringf("\n )\n");
}
- *f << stringf(" ))\n");
}
*f << stringf(" )\n");
*f << stringf(" )\n");
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index 06cbc9b2b..87db0edf7 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -23,7 +23,11 @@
#include "kernel/celltypes.h"
#include "kernel/cellaigs.h"
#include "kernel/log.h"
+#include <algorithm>
#include <string>
+#include <regex>
+#include <vector>
+#include <cmath>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
@@ -32,6 +36,28 @@ pool<string> used_names;
dict<IdString, string> namecache;
int autoid_counter;
+typedef unsigned FDirection;
+static const FDirection FD_NODIRECTION = 0x0;
+static const FDirection FD_IN = 0x1;
+static const FDirection FD_OUT = 0x2;
+static const FDirection FD_INOUT = 0x3;
+static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width
+
+// Get a port direction with respect to a specific module.
+FDirection getPortFDirection(IdString id, Module *module)
+{
+ Wire *wire = module->wires_.at(id);
+ FDirection direction = FD_NODIRECTION;
+ if (wire && wire->port_id)
+ {
+ if (wire->port_input)
+ direction |= FD_IN;
+ if (wire->port_output)
+ direction |= FD_OUT;
+ }
+ return direction;
+}
+
string next_id()
{
string new_id;
@@ -77,6 +103,127 @@ struct FirrtlWorker
dict<SigBit, pair<string, int>> reverse_wire_map;
string unconn_id;
+ RTLIL::Design *design;
+ std::string indent;
+
+ // Define read/write ports and memories.
+ // We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction.
+ // For the moment, we don't handle $readmemh or $readmemb.
+ // These will be part of a subsequent PR.
+ struct read_port {
+ string name;
+ bool clk_enable;
+ bool clk_parity;
+ bool transparent;
+ RTLIL::SigSpec clk;
+ RTLIL::SigSpec ena;
+ RTLIL::SigSpec addr;
+ read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) {
+ // Current (3/13/2019) conventions:
+ // generate a constant 0 for clock and a constant 1 for enable if they are undefined.
+ if (!clk.is_fully_def())
+ this->clk = SigSpec(State::S0);
+ if (!ena.is_fully_def())
+ this->ena = SigSpec(State::S1);
+ }
+ string gen_read(const char * indent) {
+ string addr_expr = make_expr(addr);
+ string ena_expr = make_expr(ena);
+ string clk_expr = make_expr(clk);
+ string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
+ string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
+ string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
+ return addr_str + ena_str + clk_str;
+ }
+ };
+ struct write_port : read_port {
+ RTLIL::SigSpec mask;
+ write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) {
+ if (!clk.is_fully_def())
+ this->clk = SigSpec(RTLIL::Const(0));
+ if (!ena.is_fully_def())
+ this->ena = SigSpec(RTLIL::Const(0));
+ if (!mask.is_fully_def())
+ this->ena = SigSpec(RTLIL::Const(1));
+ }
+ string gen_read(const char * /* indent */) {
+ log_error("gen_read called on write_port: %s\n", name.c_str());
+ return stringf("gen_read called on write_port: %s\n", name.c_str());
+ }
+ string gen_write(const char * indent) {
+ string addr_expr = make_expr(addr);
+ string ena_expr = make_expr(ena);
+ string clk_expr = make_expr(clk);
+ string mask_expr = make_expr(mask);
+ string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str());
+ string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str());
+ string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str());
+ string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str());
+ return addr_str + ena_str + clk_str + mask_str;
+ }
+ };
+ /* Memories defined within this module. */
+ struct memory {
+ Cell *pCell; // for error reporting
+ string name; // memory name
+ int abits; // number of address bits
+ int size; // size (in units) of the memory
+ int width; // size (in bits) of each element
+ int read_latency;
+ int write_latency;
+ vector<read_port> read_ports;
+ vector<write_port> write_ports;
+ std::string init_file;
+ std::string init_file_srcFileSpec;
+ string srcLine;
+ memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") {
+ // Provide defaults for abits or size if one (but not the other) is specified.
+ if (this->abits == 0 && this->size != 0) {
+ this->abits = ceil_log2(this->size);
+ } else if (this->abits != 0 && this->size == 0) {
+ this->size = 1 << this->abits;
+ }
+ // Sanity-check this construction.
+ if (this->name == "") {
+ log_error("Nameless memory%s\n", this->atLine());
+ }
+ if (this->abits == 0 && this->size == 0) {
+ log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine());
+ }
+ if (this->width == 0) {
+ log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine());
+ }
+ }
+ // We need a default constructor for the dict insert.
+ memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){}
+
+ const char *atLine() {
+ if (srcLine == "") {
+ if (pCell) {
+ auto p = pCell->attributes.find("\\src");
+ srcLine = " at " + p->second.decode_string();
+ }
+ }
+ return srcLine.c_str();
+ }
+ void add_memory_read_port(read_port &rp) {
+ read_ports.push_back(rp);
+ }
+ void add_memory_write_port(write_port &wp) {
+ write_ports.push_back(wp);
+ }
+ void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) {
+ this->init_file = init_file;
+ this->init_file_srcFileSpec = init_file_srcFileSpec;
+ }
+
+ };
+ dict<string, memory> memories;
+
+ void register_memory(memory &m)
+ {
+ memories[m.name] = m;
+ }
void register_reverse_wire_map(string id, SigSpec sig)
{
@@ -84,11 +231,11 @@ struct FirrtlWorker
reverse_wire_map[sig[i]] = make_pair(id, i);
}
- FirrtlWorker(Module *module, std::ostream &f) : module(module), f(f)
+ FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ")
{
}
- string make_expr(SigSpec sig)
+ static string make_expr(const SigSpec &sig)
{
string expr;
@@ -135,6 +282,117 @@ struct FirrtlWorker
return expr;
}
+ std::string fid(RTLIL::IdString internal_id)
+ {
+ return make_id(internal_id);
+ }
+
+ std::string cellname(RTLIL::Cell *cell)
+ {
+ return fid(cell->name).c_str();
+ }
+
+ void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs)
+ {
+ std::string cell_type = fid(cell->type);
+ std::string instanceOf;
+ // If this is a parameterized module, its parent module is encoded in the cell type
+ if (cell->type.begins_with("$paramod"))
+ {
+ std::string::iterator it;
+ for (it = cell_type.begin(); it < cell_type.end(); it++)
+ {
+ switch (*it) {
+ case '\\': /* FALL_THROUGH */
+ case '=': /* FALL_THROUGH */
+ case '\'': /* FALL_THROUGH */
+ case '$': instanceOf.append("_"); break;
+ default: instanceOf.append(1, *it); break;
+ }
+ }
+ }
+ else
+ {
+ instanceOf = cell_type;
+ }
+
+ std::string cell_name = cellname(cell);
+ std::string cell_name_comment;
+ if (cell_name != fid(cell->name))
+ cell_name_comment = " /* " + fid(cell->name) + " */ ";
+ else
+ cell_name_comment = "";
+ // Find the module corresponding to this instance.
+ auto instModule = design->module(cell->type);
+ // If there is no instance for this, just return.
+ if (instModule == NULL)
+ {
+ log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
+ return;
+ }
+ wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str()));
+
+ for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
+ if (it->second.size() > 0) {
+ const SigSpec &secondSig = it->second;
+ const std::string firstName = cell_name + "." + make_id(it->first);
+ const std::string secondExpr = make_expr(secondSig);
+ // Find the direction for this port.
+ FDirection dir = getPortFDirection(it->first, instModule);
+ std::string sourceExpr, sinkExpr;
+ const SigSpec *sinkSig = nullptr;
+ switch (dir) {
+ case FD_INOUT:
+ log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second));
+ /* FALLTHRU */
+ case FD_OUT:
+ sourceExpr = firstName;
+ sinkExpr = secondExpr;
+ sinkSig = &secondSig;
+ break;
+ case FD_NODIRECTION:
+ log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second));
+ /* FALLTHRU */
+ case FD_IN:
+ sourceExpr = secondExpr;
+ sinkExpr = firstName;
+ break;
+ default:
+ log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir);
+ break;
+ }
+ // Check for subfield assignment.
+ std::string bitsString = "bits(";
+ if (sinkExpr.compare(0, bitsString.length(), bitsString) == 0) {
+ if (sinkSig == nullptr)
+ log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str());
+ // Don't generate the assignment here.
+ // Add the source and sink to the "reverse_wire_map" and we'll output the assignment
+ // as part of the coalesced subfield assignments for this wire.
+ register_reverse_wire_map(sourceExpr, *sinkSig);
+ } else {
+ wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str()));
+ }
+ }
+ }
+ wire_exprs.push_back(stringf("\n"));
+
+ }
+
+ // Given an expression for a shift amount, and a maximum width,
+ // generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics.
+ std::string gen_dshl(const string b_expr, const int b_width)
+ {
+ string result = b_expr;
+ if (b_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) {
+ int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1;
+ string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1);
+ // Deal with the difference in semantics between FIRRTL and verilog
+ result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1);
+ }
+ return result;
+ }
+
void run()
{
f << stringf(" module %s:\n", make_id(module->name));
@@ -142,58 +400,90 @@ struct FirrtlWorker
for (auto wire : module->wires())
{
+ const auto wireName = make_id(wire->name);
+ // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it.
+ if (wire->attributes.count("\\init")) {
+ log_warning("Initial value (%s) for (%s.%s) not supported\n",
+ wire->attributes.at("\\init").as_string().c_str(),
+ log_id(module), log_id(wire));
+ }
if (wire->port_id)
{
if (wire->port_input && wire->port_output)
log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire));
port_decls.push_back(stringf(" %s %s: UInt<%d>\n", wire->port_input ? "input" : "output",
- make_id(wire->name), wire->width));
+ wireName, wire->width));
}
else
{
- wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", make_id(wire->name), wire->width));
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width));
}
}
for (auto cell : module->cells())
{
+ static Const ndef(0, 0);
+
+ // Is this cell is a module instance?
+ if (cell->type[0] != '$')
+ {
+ process_instance(cell, wire_exprs);
+ continue;
+ }
+ // Not a module instance. Set up cell properties
+ bool extract_y_bits = false; // Assume no extraction of final bits will be required.
+ int a_width = cell->parameters.at("\\A_WIDTH", ndef).as_int(); // The width of "A"
+ int b_width = cell->parameters.at("\\B_WIDTH", ndef).as_int(); // The width of "A"
+ const int y_width = cell->parameters.at("\\Y_WIDTH", ndef).as_int(); // The width of the result
+ const bool a_signed = cell->parameters.at("\\A_SIGNED", ndef).as_bool();
+ const bool b_signed = cell->parameters.at("\\B_SIGNED", ndef).as_bool();
+ bool firrtl_is_signed = a_signed; // The result is signed (subsequent code may change this).
+ int firrtl_width = 0;
+ string primop;
+ bool always_uint = false;
+ string y_id = make_id(cell->name);
+
if (cell->type.in("$not", "$logic_not", "$neg", "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_bool", "$reduce_xnor"))
{
- string y_id = make_id(cell->name);
- bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
- int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
string a_expr = make_expr(cell->getPort("\\A"));
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
- if (cell->parameters.at("\\A_SIGNED").as_bool()) {
+ if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
}
- a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+ // Don't use the results of logical operations (a single bit) to control padding
+ if (!(cell->type.in("$eq", "$eqx", "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$reduce_bool", "$logic_not") && y_width == 1) ) {
+ a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+ }
- string primop;
- bool always_uint = false;
+ // Assume the FIRRTL width is a single bit.
+ firrtl_width = 1;
if (cell->type == "$not") primop = "not";
- if (cell->type == "$neg") primop = "neg";
- if (cell->type == "$logic_not") {
+ else if (cell->type == "$neg") {
+ primop = "neg";
+ firrtl_is_signed = true; // Result of "neg" is signed (an SInt).
+ firrtl_width = a_width;
+ } else if (cell->type == "$logic_not") {
primop = "eq";
a_expr = stringf("%s, UInt(0)", a_expr.c_str());
}
- if (cell->type == "$reduce_and") primop = "andr";
- if (cell->type == "$reduce_or") primop = "orr";
- if (cell->type == "$reduce_xor") primop = "xorr";
- if (cell->type == "$reduce_xnor") {
+ else if (cell->type == "$reduce_and") primop = "andr";
+ else if (cell->type == "$reduce_or") primop = "orr";
+ else if (cell->type == "$reduce_xor") primop = "xorr";
+ else if (cell->type == "$reduce_xnor") {
primop = "not";
a_expr = stringf("xorr(%s)", a_expr.c_str());
}
- if (cell->type == "$reduce_bool") {
- primop = "neq";
- a_expr = stringf("%s, UInt(0)", a_expr.c_str());
- }
+ else if (cell->type == "$reduce_bool") {
+ primop = "neq";
+ // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand.
+ a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width);
+ }
string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str());
- if ((is_signed && !always_uint))
+ if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
@@ -201,99 +491,226 @@ struct FirrtlWorker
continue;
}
- if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx",
- "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
- "$logic_and", "$logic_or"))
+ if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$xnor", "$and", "$or", "$eq", "$eqx",
+ "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl",
+ "$logic_and", "$logic_or", "$pow"))
{
- string y_id = make_id(cell->name);
- bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool();
- int y_width = cell->parameters.at("\\Y_WIDTH").as_int();
string a_expr = make_expr(cell->getPort("\\A"));
string b_expr = make_expr(cell->getPort("\\B"));
wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
- if (cell->parameters.at("\\A_SIGNED").as_bool()) {
+ if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
+ // Expand the "A" operand to the result width
+ if (a_width < y_width) {
+ a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+ a_width = y_width;
+ }
}
- if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type != "$shr")) {
+ // Shift amount is always unsigned, and needn't be padded to result width,
+ // otherwise, we need to cast the b_expr appropriately
+ if (b_signed && !cell->type.in("$shr", "$sshr", "$shl", "$sshl", "$pow")) {
b_expr = "asSInt(" + b_expr + ")";
+ // Expand the "B" operand to the result width
+ if (b_width < y_width) {
+ b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
+ b_width = y_width;
+ }
}
- a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
-
- if ((cell->type != "$shl") && (cell->type != "$sshl")) {
- b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
- }
-
- if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) {
- a_expr = "asUInt(" + a_expr + ")";
+ // For the arithmetic ops, expand operand widths to result widths befor performing the operation.
+ // This corresponds (according to iverilog) to what verilog compilers implement.
+ if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$xnor", "$and", "$or"))
+ {
+ if (a_width < y_width) {
+ a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+ a_width = y_width;
+ }
+ if (b_width < y_width) {
+ b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
+ b_width = y_width;
+ }
+ }
+ // Assume the FIRRTL width is the width of "A"
+ firrtl_width = a_width;
+ auto a_sig = cell->getPort("\\A");
+
+ if (cell->type == "$add") {
+ primop = "add";
+ firrtl_is_signed = a_signed | b_signed;
+ firrtl_width = max(a_width, b_width);
+ } else if (cell->type == "$sub") {
+ primop = "sub";
+ firrtl_is_signed = true;
+ int a_widthInc = (!a_signed && b_signed) ? 2 : (a_signed && !b_signed) ? 1 : 0;
+ int b_widthInc = (a_signed && !b_signed) ? 2 : (!a_signed && b_signed) ? 1 : 0;
+ firrtl_width = max(a_width + a_widthInc, b_width + b_widthInc);
+ } else if (cell->type == "$mul") {
+ primop = "mul";
+ firrtl_is_signed = a_signed | b_signed;
+ firrtl_width = a_width + b_width;
+ } else if (cell->type == "$div") {
+ primop = "div";
+ firrtl_is_signed = a_signed | b_signed;
+ firrtl_width = a_width;
+ } else if (cell->type == "$mod") {
+ primop = "rem";
+ firrtl_width = min(a_width, b_width);
+ } else if (cell->type == "$and") {
+ primop = "and";
+ always_uint = true;
+ firrtl_width = max(a_width, b_width);
+ }
+ else if (cell->type == "$or" ) {
+ primop = "or";
+ always_uint = true;
+ firrtl_width = max(a_width, b_width);
+ }
+ else if (cell->type == "$xor") {
+ primop = "xor";
+ always_uint = true;
+ firrtl_width = max(a_width, b_width);
+ }
+ else if (cell->type == "$xnor") {
+ primop = "xnor";
+ always_uint = true;
+ firrtl_width = max(a_width, b_width);
+ }
+ else if ((cell->type == "$eq") | (cell->type == "$eqx")) {
+ primop = "eq";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if ((cell->type == "$ne") | (cell->type == "$nex")) {
+ primop = "neq";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if (cell->type == "$gt") {
+ primop = "gt";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if (cell->type == "$ge") {
+ primop = "geq";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if (cell->type == "$lt") {
+ primop = "lt";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if (cell->type == "$le") {
+ primop = "leq";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if ((cell->type == "$shl") | (cell->type == "$sshl")) {
+ // FIRRTL will widen the result (y) by the amount of the shift.
+ // We'll need to offset this by extracting the un-widened portion as Verilog would do.
+ extract_y_bits = true;
+ // Is the shift amount constant?
+ auto b_sig = cell->getPort("\\B");
+ if (b_sig.is_fully_const()) {
+ primop = "shl";
+ int shift_amount = b_sig.as_int();
+ b_expr = std::to_string(shift_amount);
+ firrtl_width = a_width + shift_amount;
+ } else {
+ primop = "dshl";
+ // Convert from FIRRTL left shift semantics.
+ b_expr = gen_dshl(b_expr, b_width);
+ firrtl_width = a_width + (1 << b_width) - 1;
+ }
+ }
+ else if ((cell->type == "$shr") | (cell->type == "$sshr")) {
+ // We don't need to extract a specific range of bits.
+ extract_y_bits = false;
+ // Is the shift amount constant?
+ auto b_sig = cell->getPort("\\B");
+ if (b_sig.is_fully_const()) {
+ primop = "shr";
+ int shift_amount = b_sig.as_int();
+ b_expr = std::to_string(shift_amount);
+ firrtl_width = max(1, a_width - shift_amount);
+ } else {
+ primop = "dshr";
+ firrtl_width = a_width;
+ }
+ // We'll need to do some special fixups if the source (and thus result) is signed.
+ if (firrtl_is_signed) {
+ // If this is a "logical" shift right, pretend the source is unsigned.
+ if (cell->type == "$shr") {
+ a_expr = "asUInt(" + a_expr + ")";
+ }
+ }
+ }
+ else if ((cell->type == "$logic_and")) {
+ primop = "and";
+ a_expr = "neq(" + a_expr + ", UInt(0))";
+ b_expr = "neq(" + b_expr + ", UInt(0))";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if ((cell->type == "$logic_or")) {
+ primop = "or";
+ a_expr = "neq(" + a_expr + ", UInt(0))";
+ b_expr = "neq(" + b_expr + ", UInt(0))";
+ always_uint = true;
+ firrtl_width = 1;
+ }
+ else if ((cell->type == "$pow")) {
+ if (a_sig.is_fully_const() && a_sig.as_int() == 2) {
+ // We'll convert this to a shift. To simplify things, change the a_expr to "1"
+ // so we can use b_expr directly as a shift amount.
+ // Only support 2 ** N (i.e., shift left)
+ // FIRRTL will widen the result (y) by the amount of the shift.
+ // We'll need to offset this by extracting the un-widened portion as Verilog would do.
+ a_expr = firrtl_is_signed ? "SInt(1)" : "UInt(1)";
+ extract_y_bits = true;
+ // Is the shift amount constant?
+ auto b_sig = cell->getPort("\\B");
+ if (b_sig.is_fully_const()) {
+ primop = "shl";
+ int shiftAmount = b_sig.as_int();
+ if (shiftAmount < 0) {
+ log_error("Negative power exponent - %d: %s.%s\n", shiftAmount, log_id(module), log_id(cell));
+ }
+ b_expr = std::to_string(shiftAmount);
+ firrtl_width = a_width + shiftAmount;
+ } else {
+ primop = "dshl";
+ // Convert from FIRRTL left shift semantics.
+ b_expr = gen_dshl(b_expr, b_width);
+ firrtl_width = a_width + (1 << b_width) - 1;
+ }
+ } else {
+ log_error("Non power 2: %s.%s\n", log_id(module), log_id(cell));
+ }
}
-
- string primop;
- bool always_uint = false;
- if (cell->type == "$add") primop = "add";
- if (cell->type == "$sub") primop = "sub";
- if (cell->type == "$mul") primop = "mul";
- if (cell->type == "$div") primop = "div";
- if (cell->type == "$mod") primop = "rem";
- if (cell->type == "$and") {
- primop = "and";
- always_uint = true;
- }
- if (cell->type == "$or" ) {
- primop = "or";
- always_uint = true;
- }
- if (cell->type == "$xor") {
- primop = "xor";
- always_uint = true;
- }
- if ((cell->type == "$eq") | (cell->type == "$eqx")) {
- primop = "eq";
- always_uint = true;
- }
- if ((cell->type == "$ne") | (cell->type == "$nex")) {
- primop = "neq";
- always_uint = true;
- }
- if (cell->type == "$gt") {
- primop = "gt";
- always_uint = true;
- }
- if (cell->type == "$ge") {
- primop = "geq";
- always_uint = true;
- }
- if (cell->type == "$lt") {
- primop = "lt";
- always_uint = true;
- }
- if (cell->type == "$le") {
- primop = "leq";
- always_uint = true;
- }
- if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl";
- if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr";
- if ((cell->type == "$logic_and")) {
- primop = "and";
- a_expr = "neq(" + a_expr + ", UInt(0))";
- b_expr = "neq(" + b_expr + ", UInt(0))";
- always_uint = true;
- }
- if ((cell->type == "$logic_or")) {
- primop = "or";
- a_expr = "neq(" + a_expr + ", UInt(0))";
- b_expr = "neq(" + b_expr + ", UInt(0))";
- always_uint = true;
- }
if (!cell->parameters.at("\\B_SIGNED").as_bool()) {
b_expr = "asUInt(" + b_expr + ")";
}
- string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
+ string expr;
+ // Deal with $xnor == ~^ (not xor)
+ if (primop == "xnor") {
+ expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str());
+ } else {
+ expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str());
+ }
+
+ // Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result.
+ // If the operation is signed, the FIRRTL width will be 1 one bit larger.
+ if (extract_y_bits) {
+ expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1);
+ } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) {
+ expr = stringf("pad(%s, %d)", expr.c_str(), y_width);
+ }
- if ((is_signed && !always_uint) || cell->type.in("$sub"))
+ if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
@@ -304,7 +721,6 @@ struct FirrtlWorker
if (cell->type.in("$mux"))
{
- string y_id = make_id(cell->name);
int width = cell->parameters.at("\\WIDTH").as_int();
string a_expr = make_expr(cell->getPort("\\A"));
string b_expr = make_expr(cell->getPort("\\B"));
@@ -325,6 +741,7 @@ struct FirrtlWorker
int abits = cell->parameters.at("\\ABITS").as_int();
int width = cell->parameters.at("\\WIDTH").as_int();
int size = cell->parameters.at("\\SIZE").as_int();
+ memory m(cell, mem_id, abits, size, width);
int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
@@ -341,33 +758,24 @@ struct FirrtlWorker
if (offset != 0)
log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell));
- cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str()));
- cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width));
- cell_exprs.push_back(stringf(" depth => %d\n", size));
-
- for (int i = 0; i < rd_ports; i++)
- cell_exprs.push_back(stringf(" reader => r%d\n", i));
-
- for (int i = 0; i < wr_ports; i++)
- cell_exprs.push_back(stringf(" writer => w%d\n", i));
-
- cell_exprs.push_back(stringf(" read-latency => 0\n"));
- cell_exprs.push_back(stringf(" write-latency => 1\n"));
- cell_exprs.push_back(stringf(" read-under-write => undefined\n"));
-
for (int i = 0; i < rd_ports; i++)
{
if (rd_clk_enable[i] != State::S0)
log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
+ SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(i*abits, abits);
SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width);
- string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits));
-
- cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
- cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i));
- cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i));
-
- register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig);
+ string addr_expr = make_expr(addr_sig);
+ string name(stringf("%s.r%d", m.name.c_str(), i));
+ bool clk_enable = false;
+ bool clk_parity = true;
+ bool transparency = false;
+ SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
+ SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1);
+ read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig);
+ m.add_memory_read_port(rp);
+ cell_exprs.push_back(rp.gen_read(indent.c_str()));
+ register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig);
}
for (int i = 0; i < wr_ports; i++)
@@ -378,9 +786,16 @@ struct FirrtlWorker
if (wr_clk_polarity[i] != State::S1)
log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
- string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits));
- string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width));
- string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i));
+ string name(stringf("%s.w%d", m.name.c_str(), i));
+ bool clk_enable = true;
+ bool clk_parity = true;
+ bool transparency = false;
+ SigSpec addr_sig =cell->getPort("\\WR_ADDR").extract(i*abits, abits);
+ string addr_expr = make_expr(addr_sig);
+ SigSpec data_sig =cell->getPort("\\WR_DATA").extract(i*width, width);
+ string data_expr = make_expr(data_sig);
+ SigSpec clk_sig = cell->getPort("\\WR_CLK").extract(i);
+ string clk_expr = make_expr(clk_sig);
SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width);
string wen_expr = make_expr(wen_sig[0]);
@@ -389,13 +804,57 @@ struct FirrtlWorker
if (wen_sig[0] != wen_sig[i])
log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
- cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str()));
- cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str()));
- cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str()));
- cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i));
- cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str()));
+ SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1);
+ write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig);
+ m.add_memory_write_port(wp);
+ cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str()));
+ cell_exprs.push_back(wp.gen_write(indent.c_str()));
}
+ register_memory(m);
+ continue;
+ }
+ if (cell->type.in("$memwr", "$memrd", "$meminit"))
+ {
+ std::string cell_type = fid(cell->type);
+ std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string());
+ int abits = cell->parameters.at("\\ABITS").as_int();
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ memory *mp = nullptr;
+ if (cell->type == "$meminit" ) {
+ log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str());
+ } else {
+ // It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition.
+ auto addrSig = cell->getPort("\\ADDR");
+ auto dataSig = cell->getPort("\\DATA");
+ auto enableSig = cell->getPort("\\EN");
+ auto clockSig = cell->getPort("\\CLK");
+ Const clk_enable = cell->parameters.at("\\CLK_ENABLE");
+ Const clk_polarity = cell->parameters.at("\\CLK_POLARITY");
+
+ // Do we already have an entry for this memory?
+ if (memories.count(mem_id) == 0) {
+ memory m(cell, mem_id, abits, 0, width);
+ register_memory(m);
+ }
+ mp = &memories.at(mem_id);
+ int portNum = 0;
+ bool transparency = false;
+ string data_expr = make_expr(dataSig);
+ if (cell->type.in("$memwr")) {
+ portNum = (int) mp->write_ports.size();
+ write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig);
+ mp->add_memory_write_port(wp);
+ cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str()));
+ cell_exprs.push_back(wp.gen_write(indent.c_str()));
+ } else if (cell->type.in("$memrd")) {
+ portNum = (int) mp->read_ports.size();
+ read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig);
+ mp->add_memory_read_port(rp);
+ cell_exprs.push_back(rp.gen_read(indent.c_str()));
+ register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig);
+ }
+ }
continue;
}
@@ -405,19 +864,85 @@ struct FirrtlWorker
if (clkpol == false)
log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell));
- string q_id = make_id(cell->name);
int width = cell->parameters.at("\\WIDTH").as_int();
string expr = make_expr(cell->getPort("\\D"));
string clk_expr = "asClock(" + make_expr(cell->getPort("\\CLK")) + ")";
- wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", q_id.c_str(), width, clk_expr.c_str()));
+ wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", y_id.c_str(), width, clk_expr.c_str()));
- cell_exprs.push_back(stringf(" %s <= %s\n", q_id.c_str(), expr.c_str()));
- register_reverse_wire_map(q_id, cell->getPort("\\Q"));
+ cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
+ register_reverse_wire_map(y_id, cell->getPort("\\Q"));
continue;
}
+ // This may be a parameterized module - paramod.
+ if (cell->type.begins_with("$paramod"))
+ {
+ process_instance(cell, wire_exprs);
+ continue;
+ }
+ if (cell->type == "$shiftx") {
+ // assign y = a[b +: y_width];
+ // We'll extract the correct bits as part of the primop.
+
+ string a_expr = make_expr(cell->getPort("\\A"));
+ // Get the initial bit selector
+ string b_expr = make_expr(cell->getPort("\\B"));
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+
+ if (cell->getParam("\\B_SIGNED").as_bool()) {
+ // Use validif to constrain the selection (test the sign bit)
+ auto b_string = b_expr.c_str();
+ int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1;
+ b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string);
+ }
+ string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str());
+
+ cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
+ register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+ continue;
+ }
+ if (cell->type == "$shift") {
+ // assign y = a >> b;
+ // where b may be negative
+
+ string a_expr = make_expr(cell->getPort("\\A"));
+ string b_expr = make_expr(cell->getPort("\\B"));
+ auto b_string = b_expr.c_str();
+ string expr;
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+
+ if (cell->getParam("\\B_SIGNED").as_bool()) {
+ // We generate a left or right shift based on the sign of b.
+ std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_width).c_str(), y_width);
+ std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
+ expr = stringf("mux(%s < 0, %s, %s)",
+ b_string,
+ dshl.c_str(),
+ dshr.c_str()
+ );
+ } else {
+ expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string);
+ }
+ cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
+ register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+ continue;
+ }
+ if (cell->type == "$pos") {
+ // assign y = a;
+// printCell(cell);
+ string a_expr = make_expr(cell->getPort("\\A"));
+ // Verilog appears to treat the result as signed, so if the result is wider than "A",
+ // we need to pad.
+ if (a_width < y_width) {
+ a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+ }
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+ cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), a_expr.c_str()));
+ register_reverse_wire_map(y_id, cell->getPort("\\Y"));
+ continue;
+ }
log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
}
@@ -494,14 +1019,14 @@ struct FirrtlWorker
if (is_valid) {
if (make_unconn_id) {
wire_decls.push_back(stringf(" wire %s: UInt<1>\n", unconn_id.c_str()));
- cell_exprs.push_back(stringf(" %s is invalid\n", unconn_id.c_str()));
+ wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str()));
}
wire_exprs.push_back(stringf(" %s <= %s\n", make_id(wire->name), expr.c_str()));
} else {
if (make_unconn_id) {
unconn_id.clear();
}
- wire_exprs.push_back(stringf(" %s is invalid\n", make_id(wire->name)));
+ wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name)));
}
}
@@ -515,6 +1040,24 @@ struct FirrtlWorker
f << stringf("\n");
+ // If we have any memory definitions, output them.
+ for (auto kv : memories) {
+ memory &m = kv.second;
+ f << stringf(" mem %s:\n", m.name.c_str());
+ f << stringf(" data-type => UInt<%d>\n", m.width);
+ f << stringf(" depth => %d\n", m.size);
+ for (int i = 0; i < (int) m.read_ports.size(); i += 1) {
+ f << stringf(" reader => r%d\n", i);
+ }
+ for (int i = 0; i < (int) m.write_ports.size(); i += 1) {
+ f << stringf(" writer => w%d\n", i);
+ }
+ f << stringf(" read-latency => %d\n", m.read_latency);
+ f << stringf(" write-latency => %d\n", m.write_latency);
+ f << stringf(" read-under-write => undefined\n");
+ }
+ f << stringf("\n");
+
for (auto str : cell_exprs)
f << str;
@@ -527,50 +1070,65 @@ struct FirrtlWorker
struct FirrtlBackend : public Backend {
FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" write_firrtl [options] [filename]\n");
log("\n");
log("Write a FIRRTL netlist of the current design.\n");
+ log("The following commands are executed by this command:\n");
+ log(" pmuxtree\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++)
- {
- // if (args[argidx] == "-aig") {
- // aig_mode = true;
- // continue;
- // }
- break;
+ size_t argidx = args.size(); // We aren't expecting any arguments.
+
+ // If we weren't explicitly passed a filename, use the last argument (if it isn't a flag).
+ if (filename == "") {
+ if (argidx > 0 && args[argidx - 1][0] != '-') {
+ // extra_args and friends need to see this argument.
+ argidx -= 1;
+ filename = args[argidx];
+ }
}
extra_args(f, filename, args, argidx);
- log_header(design, "Executing FIRRTL backend.\n");
+ if (!design->full_selection())
+ log_cmd_error("This command only operates on fully selected designs!\n");
- Module *top = design->top_module();
+ log_header(design, "Executing FIRRTL backend.\n");
+ log_push();
- if (top == nullptr)
- log_error("No top module found!\n");
+ Pass::call(design, stringf("pmuxtree"));
namecache.clear();
autoid_counter = 0;
+ // Get the top module, or a reasonable facsimile - we need something for the circuit name.
+ Module *top = design->top_module();
+ Module *last = nullptr;
+ // Generate module and wire names.
for (auto module : design->modules()) {
make_id(module->name);
+ last = module;
+ if (top == nullptr && module->get_bool_attribute("\\top")) {
+ top = module;
+ }
for (auto wire : module->wires())
if (wire->port_id)
make_id(wire->name);
}
+ if (top == nullptr)
+ top = last;
+
*f << stringf("circuit %s:\n", make_id(top->name));
for (auto module : design->modules())
{
- FirrtlWorker worker(module, *f);
+ FirrtlWorker worker(module, *f, design);
worker.run();
}
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
index 16d1a97f5..e06786220 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/ilang/ilang_backend.cc
@@ -40,8 +40,8 @@ void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
for (int i = 0; i < width; i++) {
log_assert(offset+i < (int)data.bits.size());
switch (data.bits[offset+i]) {
- case RTLIL::S0: break;
- case RTLIL::S1: val |= 1 << i; break;
+ case State::S0: break;
+ case State::S1: val |= 1 << i; break;
default: val = -1; break;
}
}
@@ -54,8 +54,8 @@ void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
for (int i = offset+width-1; i >= offset; i--) {
log_assert(i < (int)data.bits.size());
switch (data.bits[i]) {
- case RTLIL::S0: f << stringf("0"); break;
- case RTLIL::S1: f << stringf("1"); break;
+ case State::S0: f << stringf("0"); break;
+ case State::S1: f << stringf("1"); break;
case RTLIL::Sx: f << stringf("x"); break;
case RTLIL::Sz: f << stringf("z"); break;
case RTLIL::Sa: f << stringf("-"); break;
@@ -160,7 +160,10 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::
}
f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str());
for (auto &it : cell->parameters) {
- f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str());
+ f << stringf("%s parameter%s%s %s ", indent.c_str(),
+ (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "",
+ (it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "",
+ it.first.c_str());
dump_const(f, it.second);
f << stringf("\n");
}
@@ -201,10 +204,15 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it)
{
+ for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) {
+ f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str());
+ dump_const(f, ait->second);
+ f << stringf("\n");
+ }
f << stringf("%s case ", indent.c_str());
for (size_t i = 0; i < (*it)->compare.size(); i++) {
if (i > 0)
- f << stringf(", ");
+ f << stringf(" , ");
dump_sigspec(f, (*it)->compare[i]);
}
f << stringf("\n");
@@ -219,7 +227,7 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
{
f << stringf("%s" "sync ", indent.c_str());
switch (sy->type) {
- if (0) case RTLIL::ST0: f << stringf("low ");
+ case RTLIL::ST0: f << stringf("low ");
if (0) case RTLIL::ST1: f << stringf("high ");
if (0) case RTLIL::STp: f << stringf("posedge ");
if (0) case RTLIL::STn: f << stringf("negedge ");
@@ -382,7 +390,7 @@ PRIVATE_NAMESPACE_BEGIN
struct IlangBackend : public Backend {
IlangBackend() : Backend("ilang", "write design to ilang file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -395,7 +403,7 @@ struct IlangBackend : public Backend {
log(" only write selected parts of the design.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool selected = false;
@@ -422,7 +430,7 @@ struct IlangBackend : public Backend {
struct DumpPass : public Pass {
DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -445,7 +453,7 @@ struct DumpPass : public Pass {
log(" like -outfile but append instead of overwrite\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string filename;
bool flag_m = false, flag_n = false, append = false;
@@ -480,6 +488,7 @@ struct DumpPass : public Pass {
std::stringstream buf;
if (!filename.empty()) {
+ rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc);
if (ff->fail()) {
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
index 34cb52fb4..809a0fa09 100644
--- a/backends/intersynth/intersynth.cc
+++ b/backends/intersynth/intersynth.cc
@@ -46,7 +46,7 @@ static std::string netname(std::set<std::string> &conntypes_code, std::set<std::
struct IntersynthBackend : public Backend {
IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -71,7 +71,7 @@ struct IntersynthBackend : public Backend {
log("http://www.clifford.at/intersynth/\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing INTERSYNTH backend.\n");
log_push();
@@ -108,7 +108,7 @@ struct IntersynthBackend : public Backend {
if (f.fail())
log_error("Can't open lib file `%s'.\n", filename.c_str());
RTLIL::Design *lib = new RTLIL::Design;
- Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog");
+ Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog"));
libs.push_back(lib);
}
@@ -127,7 +127,7 @@ struct IntersynthBackend : public Backend {
RTLIL::Module *module = module_it.second;
SigMap sigmap(module);
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0)
continue;
@@ -183,7 +183,7 @@ struct IntersynthBackend : public Backend {
if (param.second.bits.size() != 32) {
node_code += stringf(" %s '", RTLIL::id2cstr(param.first));
for (int i = param.second.bits.size()-1; i >= 0; i--)
- node_code += param.second.bits[i] == RTLIL::S1 ? "1" : "0";
+ node_code += param.second.bits[i] == State::S1 ? "1" : "0";
} else
node_code += stringf(" %s 0x%x", RTLIL::id2cstr(param.first), param.second.as_int());
}
diff --git a/backends/json/json.cc b/backends/json/json.cc
index d3b7077a2..107009ee4 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -83,18 +83,43 @@ struct JsonWriter
return str + " ]";
}
+ void write_parameter_value(const Const &value)
+ {
+ if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) {
+ string str = value.decode_string();
+ int state = 0;
+ for (char c : str) {
+ if (state == 0) {
+ if (c == '0' || c == '1' || c == 'x' || c == 'z')
+ state = 0;
+ else if (c == ' ')
+ state = 1;
+ else
+ state = 2;
+ } else if (state == 1 && c != ' ')
+ state = 2;
+ }
+ if (state < 2)
+ str += " ";
+ f << get_string(str);
+ } else
+ if (GetSize(value) == 32 && value.is_fully_def()) {
+ if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0)
+ f << stringf("%d", value.as_int());
+ else
+ f << stringf("%u", value.as_int());
+ } else {
+ f << get_string(value.as_string());
+ }
+ }
+
void write_parameters(const dict<IdString, Const> &parameters, bool for_module=false)
{
bool first = true;
for (auto &param : parameters) {
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str());
- if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0)
- f << get_string(param.second.decode_string());
- else if (GetSize(param.second.bits) > 32)
- f << get_string(param.second.as_string());
- else
- f << stringf("%d", param.second.as_int());
+ write_parameter_value(param.second);
first = false;
}
}
@@ -124,6 +149,10 @@ struct JsonWriter
f << stringf("%s\n", first ? "" : ",");
f << stringf(" %s: {\n", get_name(n).c_str());
f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output");
+ if (w->start_offset)
+ f << stringf(" \"offset\": %d,\n", w->start_offset);
+ if (w->upto)
+ f << stringf(" \"upto\": 1,\n");
f << stringf(" \"bits\": %s\n", get_bits(w).c_str());
f << stringf(" }");
first = false;
@@ -187,6 +216,10 @@ struct JsonWriter
f << stringf(" %s: {\n", get_name(w->name).c_str());
f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0");
f << stringf(" \"bits\": %s,\n", get_bits(w).c_str());
+ if (w->start_offset)
+ f << stringf(" \"offset\": %d,\n", w->start_offset);
+ if (w->upto)
+ f << stringf(" \"upto\": 1,\n");
f << stringf(" \"attributes\": {");
write_parameters(w->attributes);
f << stringf("\n }\n");
@@ -250,7 +283,7 @@ struct JsonWriter
struct JsonBackend : public Backend {
JsonBackend() : Backend("json", "write design to a JSON file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -332,12 +365,13 @@ struct JsonBackend : public Backend {
log("Module and cell ports and nets can be single bit wide or vectors of multiple\n");
log("bits. Each individual signal bit is assigned a unique integer. The <bit_vector>\n");
log("values referenced above are vectors of this integers. Signal bits that are\n");
- log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n");
- log("a number.\n");
+ log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n");
+ log("\"z\" instead of a number.\n");
log("\n");
- log("Numeric parameter and attribute values up to 32 bits are written as decimal\n");
- log("values. Numbers larger than that are written as string holding the binary\n");
- log("representation of the value.\n");
+ log("Numeric 32-bit parameter and attribute values are written as decimal values.\n");
+ log("Bit verctors of different sizes, or ones containing 'x' or 'z' bits, are written\n");
+ log("as string holding the binary representation of the value. Strings are written\n");
+ log("as strings, with an appended blank in cases of strings of the form /[01xz]* */.\n");
log("\n");
log("For example the following Verilog code:\n");
log("\n");
@@ -458,7 +492,7 @@ struct JsonBackend : public Backend {
log("format. A program processing this format must ignore all unknown fields.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool aig_mode = false;
@@ -482,7 +516,7 @@ struct JsonBackend : public Backend {
struct JsonPass : public Pass {
JsonPass() : Pass("json", "write design in JSON format") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -499,7 +533,7 @@ struct JsonPass : public Pass {
log("See 'help write_json' for a description of the JSON format used.\n");
log("\n");
}
- virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string filename;
bool aig_mode = false;
@@ -523,6 +557,7 @@ struct JsonPass : public Pass {
std::stringstream buf;
if (!filename.empty()) {
+ rewrite_filename(filename);
std::ofstream *ff = new std::ofstream;
ff->open(filename.c_str(), std::ofstream::trunc);
if (ff->fail()) {
diff --git a/backends/protobuf/.gitignore b/backends/protobuf/.gitignore
new file mode 100644
index 000000000..849b38d45
--- /dev/null
+++ b/backends/protobuf/.gitignore
@@ -0,0 +1,2 @@
+yosys.pb.cc
+yosys.pb.h
diff --git a/backends/protobuf/Makefile.inc b/backends/protobuf/Makefile.inc
new file mode 100644
index 000000000..834cad42c
--- /dev/null
+++ b/backends/protobuf/Makefile.inc
@@ -0,0 +1,8 @@
+ifeq ($(ENABLE_PROTOBUF),1)
+
+backends/protobuf/yosys.pb.cc backends/protobuf/yosys.pb.h: misc/yosys.proto
+ $(Q) cd misc && protoc --cpp_out "../backends/protobuf" yosys.proto
+
+OBJS += backends/protobuf/protobuf.o backends/protobuf/yosys.pb.o
+
+endif
diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc
new file mode 100644
index 000000000..671686173
--- /dev/null
+++ b/backends/protobuf/protobuf.cc
@@ -0,0 +1,371 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <google/protobuf/text_format.h>
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/cellaigs.h"
+#include "kernel/log.h"
+#include "yosys.pb.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct ProtobufDesignSerializer
+{
+ bool aig_mode_;
+ bool use_selection_;
+ yosys::pb::Design *pb_;
+
+ Design *design_;
+ Module *module_;
+
+ SigMap sigmap_;
+ int sigidcounter_;
+ dict<SigBit, uint64_t> sigids_;
+ pool<Aig> aig_models_;
+
+
+ ProtobufDesignSerializer(bool use_selection, bool aig_mode) :
+ aig_mode_(aig_mode), use_selection_(use_selection) { }
+
+ string get_name(IdString name)
+ {
+ return RTLIL::unescape_id(name);
+ }
+
+
+ void serialize_parameters(google::protobuf::Map<std::string, yosys::pb::Parameter> *out,
+ const dict<IdString, Const> &parameters)
+ {
+ for (auto &param : parameters) {
+ std::string key = get_name(param.first);
+
+
+ yosys::pb::Parameter pb_param;
+
+ if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) {
+ pb_param.set_str(param.second.decode_string());
+ } else if (GetSize(param.second.bits) > 64) {
+ pb_param.set_str(param.second.as_string());
+ } else {
+ pb_param.set_int_(param.second.as_int());
+ }
+
+ (*out)[key] = pb_param;
+ }
+ }
+
+ void get_bits(yosys::pb::BitVector *out, SigSpec sig)
+ {
+ for (auto bit : sigmap_(sig)) {
+ auto sig = out->add_signal();
+
+ // Constant driver.
+ if (bit.wire == nullptr) {
+ if (bit == State::S0) sig->set_constant(sig->CONSTANT_DRIVER_LOW);
+ else if (bit == State::S1) sig->set_constant(sig->CONSTANT_DRIVER_HIGH);
+ else if (bit == State::Sz) sig->set_constant(sig->CONSTANT_DRIVER_Z);
+ else sig->set_constant(sig->CONSTANT_DRIVER_X);
+ continue;
+ }
+
+ // Signal - give it a unique identifier.
+ if (sigids_.count(bit) == 0) {
+ sigids_[bit] = sigidcounter_++;
+ }
+ sig->set_id(sigids_[bit]);
+ }
+ }
+
+ void serialize_module(yosys::pb::Module* out, Module *module)
+ {
+ module_ = module;
+ log_assert(module_->design == design_);
+ sigmap_.set(module_);
+ sigids_.clear();
+ sigidcounter_ = 0;
+
+ serialize_parameters(out->mutable_attribute(), module_->attributes);
+
+ for (auto n : module_->ports) {
+ Wire *w = module->wire(n);
+ if (use_selection_ && !module_->selected(w))
+ continue;
+
+ yosys::pb::Module::Port pb_port;
+ pb_port.set_direction(w->port_input ? w->port_output ?
+ yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT : yosys::pb::DIRECTION_OUTPUT);
+ get_bits(pb_port.mutable_bits(), w);
+ (*out->mutable_port())[get_name(n)] = pb_port;
+ }
+
+ for (auto c : module_->cells()) {
+ if (use_selection_ && !module_->selected(c))
+ continue;
+
+ yosys::pb::Module::Cell pb_cell;
+ pb_cell.set_hide_name(c->name[0] == '$');
+ pb_cell.set_type(get_name(c->type));
+
+ if (aig_mode_) {
+ Aig aig(c);
+ if (aig.name.empty())
+ continue;
+ pb_cell.set_model(aig.name);
+ aig_models_.insert(aig);
+ }
+ serialize_parameters(pb_cell.mutable_parameter(), c->parameters);
+ serialize_parameters(pb_cell.mutable_attribute(), c->attributes);
+
+ if (c->known()) {
+ for (auto &conn : c->connections()) {
+ yosys::pb::Direction direction = yosys::pb::DIRECTION_OUTPUT;
+ if (c->input(conn.first))
+ direction = c->output(conn.first) ? yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT;
+ (*pb_cell.mutable_port_direction())[get_name(conn.first)] = direction;
+ }
+ }
+ for (auto &conn : c->connections()) {
+ yosys::pb::BitVector vec;
+ get_bits(&vec, conn.second);
+ (*pb_cell.mutable_connection())[get_name(conn.first)] = vec;
+ }
+
+ (*out->mutable_cell())[get_name(c->name)] = pb_cell;
+ }
+
+ for (auto w : module_->wires()) {
+ if (use_selection_ && !module_->selected(w))
+ continue;
+
+ auto netname = out->add_netname();
+ netname->set_hide_name(w->name[0] == '$');
+ get_bits(netname->mutable_bits(), w);
+ serialize_parameters(netname->mutable_attributes(), w->attributes);
+ }
+ }
+
+
+ void serialize_models(google::protobuf::Map<string, yosys::pb::Model> *models)
+ {
+ for (auto &aig : aig_models_) {
+ yosys::pb::Model pb_model;
+ for (auto &node : aig.nodes) {
+ auto pb_node = pb_model.add_node();
+ if (node.portbit >= 0) {
+ if (node.inverter) {
+ pb_node->set_type(pb_node->TYPE_NPORT);
+ } else {
+ pb_node->set_type(pb_node->TYPE_PORT);
+ }
+ auto port = pb_node->mutable_port();
+ port->set_portname(log_id(node.portname));
+ port->set_bitindex(node.portbit);
+ } else if (node.left_parent < 0 && node.right_parent < 0) {
+ if (node.inverter) {
+ pb_node->set_type(pb_node->TYPE_TRUE);
+ } else {
+ pb_node->set_type(pb_node->TYPE_FALSE);
+ }
+ } else {
+ if (node.inverter) {
+ pb_node->set_type(pb_node->TYPE_NAND);
+ } else {
+ pb_node->set_type(pb_node->TYPE_AND);
+ }
+ auto gate = pb_node->mutable_gate();
+ gate->set_left(node.left_parent);
+ gate->set_right(node.right_parent);
+ }
+ for (auto &op : node.outports) {
+ auto pb_op = pb_node->add_out_port();
+ pb_op->set_name(log_id(op.first));
+ pb_op->set_bit_index(op.second);
+ }
+ }
+ (*models)[aig.name] = pb_model;
+ }
+ }
+
+ void serialize_design(yosys::pb::Design *pb, Design *design)
+ {
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+ pb_ = pb;
+ pb_->Clear();
+ pb_->set_creator(yosys_version_str);
+
+ design_ = design;
+ design_->sort();
+
+ auto modules = use_selection_ ? design_->selected_modules() : design_->modules();
+ for (auto mod : modules) {
+ yosys::pb::Module pb_mod;
+ serialize_module(&pb_mod, mod);
+ (*pb->mutable_modules())[mod->name.str()] = pb_mod;
+ }
+
+ serialize_models(pb_->mutable_models());
+ }
+};
+
+struct ProtobufBackend : public Backend {
+ ProtobufBackend(): Backend("protobuf", "write design to a Protocol Buffer file") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_protobuf [options] [filename]\n");
+ log("\n");
+ log("Write a JSON netlist of the current design.\n");
+ log("\n");
+ log(" -aig\n");
+ log(" include AIG models for the different gate types\n");
+ log("\n");
+ log(" -text\n");
+ log(" output protobuf in Text/ASCII representation\n");
+ log("\n");
+ log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n");
+ log("Yosys source code distribution.\n");
+ log("\n");
+ }
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ bool aig_mode = false;
+ bool text_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-aig") {
+ aig_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-text") {
+ text_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx, !text_mode);
+
+ log_header(design, "Executing Protobuf backend.\n");
+
+ yosys::pb::Design pb;
+ ProtobufDesignSerializer serializer(false, aig_mode);
+ serializer.serialize_design(&pb, design);
+
+ if (text_mode) {
+ string out;
+ google::protobuf::TextFormat::PrintToString(pb, &out);
+ *f << out;
+ } else {
+ pb.SerializeToOstream(f);
+ }
+ }
+} ProtobufBackend;
+
+struct ProtobufPass : public Pass {
+ ProtobufPass() : Pass("protobuf", "write design in Protobuf format") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" protobuf [options] [selection]\n");
+ log("\n");
+ log("Write a JSON netlist of all selected objects.\n");
+ log("\n");
+ log(" -o <filename>\n");
+ log(" write to the specified file.\n");
+ log("\n");
+ log(" -aig\n");
+ log(" include AIG models for the different gate types\n");
+ log("\n");
+ log(" -text\n");
+ log(" output protobuf in Text/ASCII representation\n");
+ log("\n");
+ log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n");
+ log("Yosys source code distribution.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ std::string filename;
+ bool aig_mode = false;
+ bool text_mode = false;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-o" && argidx+1 < args.size()) {
+ filename = args[++argidx];
+ continue;
+ }
+ if (args[argidx] == "-aig") {
+ aig_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-text") {
+ text_mode = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ std::ostream *f;
+ std::stringstream buf;
+
+ if (!filename.empty()) {
+ rewrite_filename(filename);
+ std::ofstream *ff = new std::ofstream;
+ ff->open(filename.c_str(), text_mode ? std::ofstream::trunc : (std::ofstream::trunc | std::ofstream::binary));
+ if (ff->fail()) {
+ delete ff;
+ log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno));
+ }
+ f = ff;
+ } else {
+ f = &buf;
+ }
+
+ yosys::pb::Design pb;
+ ProtobufDesignSerializer serializer(true, aig_mode);
+ serializer.serialize_design(&pb, design);
+
+ if (text_mode) {
+ string out;
+ google::protobuf::TextFormat::PrintToString(pb, &out);
+ *f << out;
+ } else {
+ pb.SerializeToOstream(f);
+ }
+
+ if (!filename.empty()) {
+ delete f;
+ } else {
+ log("%s", buf.str().c_str());
+ }
+ }
+} ProtobufPass;
+
+PRIVATE_NAMESPACE_END;
diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc
index c9656caff..54dbb84af 100644
--- a/backends/simplec/simplec.cc
+++ b/backends/simplec/simplec.cc
@@ -472,7 +472,7 @@ struct SimplecWorker
return;
}
- if (cell->type == "$_MUX_")
+ if (cell->type.in("$_MUX_", "$_NMUX_"))
{
SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
@@ -484,7 +484,9 @@ struct SimplecWorker
string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0";
// casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933)
- string expr = stringf("%s ? (bool)%s : (bool)%s", s_expr.c_str(), b_expr.c_str(), a_expr.c_str());
+ string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(),
+ cell->type == "$_NMUX_" ? "!" : "", b_expr.c_str(),
+ cell->type == "$_NMUX_" ? "!" : "", a_expr.c_str());
log_assert(y.wire);
funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
@@ -742,13 +744,13 @@ struct SimplecWorker
struct SimplecBackend : public Backend {
SimplecBackend() : Backend("simplec", "convert design to simple C code") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" write_simplec [options] [filename]\n");
log("\n");
- log("Write simple C code for simulating the design. The C code writen can be used to\n");
+ log("Write simple C code for simulating the design. The C code written can be used to\n");
log("simulate the design in a C environment, but the purpose of this command is to\n");
log("generate code that works well with C-based formal verification.\n");
log("\n");
@@ -761,7 +763,7 @@ struct SimplecBackend : public Backend {
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
reserved_cids.clear();
id2cid.clear();
diff --git a/backends/simplec/test00_uut.v b/backends/simplec/test00_uut.v
index 744dbe9e3..92329a6f9 100644
--- a/backends/simplec/test00_uut.v
+++ b/backends/simplec/test00_uut.v
@@ -3,12 +3,12 @@ module test(input [31:0] a, b, c, output [31:0] x, y, z, w);
unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y));
assign z = a ^ b ^ c, w = z;
endmodule
-
+
module unit_x(input [31:0] a, b, c, output [31:0] x);
assign x = (a & b) | c;
endmodule
-
+
module unit_y(input [31:0] a, b, c, output [31:0] y);
assign y = a & (b | c);
endmodule
-
+
diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc
index eacda2734..68394a909 100644
--- a/backends/smt2/Makefile.inc
+++ b/backends/smt2/Makefile.inc
@@ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o
ifneq ($(CONFIG),mxe)
ifneq ($(CONFIG),emcc)
+
+# MSYS targets support yosys-smtbmc, but require a launcher script
+ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64))
+TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py
+# Needed to find the Python interpreter for yosys-smtbmc scripts.
+# Override if necessary, it is only used for msys2 targets.
+PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3)
+
+yosys-smtbmc-script.py: backends/smt2/smtbmc.py
+ $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \
+ -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@
+
+yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py
+ $(P) $(CXX) -DGUI=0 -O -s -o $@ $<
+# Other targets
+else
TARGETS += yosys-smtbmc
yosys-smtbmc: backends/smt2/smtbmc.py
- $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
+ $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new
$(Q) chmod +x $@.new
$(Q) mv $@.new $@
+endif
$(eval $(call add_share_file,share/python3,backends/smt2/smtio.py))
endif
endif
-
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index dce7c25de..081dcda99 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -32,15 +32,18 @@ struct Smt2Worker
CellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
- bool bvmode, memmode, wiresmode, verbose, statebv, statedt;
+ bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;
dict<IdString, int> &mod_stbv_width;
- int idcounter, statebv_width;
+ int idcounter = 0, statebv_width = 0;
std::vector<std::string> decls, trans, hier, dtmembers;
std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver;
std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
pool<Cell*> recursive_cells, registers;
+ pool<SigBit> clock_posedge, clock_negedge;
+ vector<string> ex_state_eq, ex_input_eq;
+
std::map<RTLIL::SigBit, std::pair<int, int>> fcache;
std::map<Cell*, int> memarrays;
std::map<int, int> bvsizes;
@@ -104,16 +107,24 @@ struct Smt2Worker
decls.push_back(decl_str + "\n");
}
- Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, dict<IdString, int> &mod_stbv_width) :
+ Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode,
+ dict<IdString, int> &mod_stbv_width, dict<IdString, dict<IdString, pair<bool, bool>>> &mod_clk_cache) :
ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode),
- verbose(verbose), statebv(statebv), statedt(statedt), mod_stbv_width(mod_stbv_width), idcounter(0), statebv_width(0)
+ verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width)
{
+ pool<SigBit> noclock;
+
makebits(stringf("%s_is", get_id(module)));
for (auto cell : module->cells())
- for (auto &conn : cell->connections()) {
+ for (auto &conn : cell->connections())
+ {
+ if (GetSize(conn.second) == 0)
+ continue;
+
bool is_input = ct.cell_input(cell->type, conn.first);
bool is_output = ct.cell_output(cell->type, conn.first);
+
if (is_output && !is_input)
for (auto bit : sigmap(conn.second)) {
if (bit_driver.count(bit))
@@ -123,6 +134,66 @@ struct Smt2Worker
else if (is_output || !is_input)
log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n",
log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type));
+
+ if (cell->type.in("$mem") && conn.first.in("\\RD_CLK", "\\WR_CLK"))
+ {
+ SigSpec clk = sigmap(conn.second);
+ for (int i = 0; i < GetSize(clk); i++)
+ {
+ if (clk[i].wire == nullptr)
+ continue;
+
+ if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_ENABLE" : "\\WR_CLK_ENABLE")[i] != State::S1)
+ continue;
+
+ if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_POLARITY" : "\\WR_CLK_POLARITY")[i] == State::S1)
+ clock_posedge.insert(clk[i]);
+ else
+ clock_negedge.insert(clk[i]);
+ }
+ }
+ else
+ if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_") && conn.first.in("\\CLK", "\\C"))
+ {
+ bool posedge = (cell->type == "$_DFF_N_") || (cell->type == "$dff" && cell->getParam("\\CLK_POLARITY").as_bool());
+ for (auto bit : sigmap(conn.second)) {
+ if (posedge)
+ clock_posedge.insert(bit);
+ else
+ clock_negedge.insert(bit);
+ }
+ }
+ else
+ if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first))
+ {
+ for (auto bit : sigmap(conn.second)) {
+ if (mod_clk_cache.at(cell->type).at(conn.first).first)
+ clock_posedge.insert(bit);
+ if (mod_clk_cache.at(cell->type).at(conn.first).second)
+ clock_negedge.insert(bit);
+ }
+ }
+ else
+ {
+ for (auto bit : sigmap(conn.second))
+ noclock.insert(bit);
+ }
+ }
+
+ for (auto bit : noclock) {
+ clock_posedge.erase(bit);
+ clock_negedge.erase(bit);
+ }
+
+ for (auto wire : module->wires())
+ {
+ if (!wire->port_input || GetSize(wire) != 1)
+ continue;
+ SigBit bit = sigmap(wire);
+ if (clock_posedge.count(bit))
+ mod_clk_cache[module->name][wire->name].first = true;
+ if (clock_negedge.count(bit))
+ mod_clk_cache[module->name][wire->name].second = true;
}
}
@@ -326,7 +397,8 @@ struct Smt2Worker
if (type == 's' || type == 'd' || type == 'b') {
width = max(width, GetSize(cell->getPort("\\A")));
- width = max(width, GetSize(cell->getPort("\\B")));
+ if (cell->hasPort("\\B"))
+ width = max(width, GetSize(cell->getPort("\\B")));
}
if (cell->hasPort("\\A")) {
@@ -344,6 +416,7 @@ struct Smt2Worker
for (char ch : expr) {
if (ch == 'A') processed_expr += get_bv(sig_a);
else if (ch == 'B') processed_expr += get_bv(sig_b);
+ else if (ch == 'P') processed_expr += get_bv(cell->getPort("\\B"));
else if (ch == 'L') processed_expr += is_signed ? "a" : "l";
else if (ch == 'U') processed_expr += is_signed ? "s" : "u";
else processed_expr += ch;
@@ -437,6 +510,7 @@ struct Smt2Worker
if (cell->type == "$_ANDNOT_") return export_gate(cell, "(and A (not B))");
if (cell->type == "$_ORNOT_") return export_gate(cell, "(or A (not B))");
if (cell->type == "$_MUX_") return export_gate(cell, "(ite S B A)");
+ if (cell->type == "$_NMUX_") return export_gate(cell, "(not (ite S B A))");
if (cell->type == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))");
if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))");
if (cell->type == "$_AOI4_") return export_gate(cell, "(not (or (and A B) (and C D)))");
@@ -455,14 +529,16 @@ struct Smt2Worker
return;
}
- if (cell->type.in("$anyconst", "$anyseq"))
+ if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq"))
{
registers.insert(cell);
string infostr = cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell);
if (cell->attributes.count("\\reg"))
infostr += " " + cell->attributes.at("\\reg").decode_string();
- decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, infostr.c_str()));
+ decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort("\\Y")), infostr.c_str()));
makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y")));
+ if (cell->type == "$anyseq")
+ ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter));
register_bv(cell->getPort("\\Y"), idcounter++);
recursive_cells.erase(cell);
return;
@@ -480,7 +556,9 @@ struct Smt2Worker
if (cell->type.in("$shift", "$shiftx")) {
if (cell->getParam("\\B_SIGNED").as_bool()) {
- /* FIXME */
+ return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) "
+ "(bvlshr A B) (bvlshr A (bvneg B)))",
+ GetSize(cell->getPort("\\B")), 0), 's');
} else {
return export_bvop(cell, "(bvlshr A B)", 's');
}
@@ -506,6 +584,13 @@ struct Smt2Worker
if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd');
if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd');
+ if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool") &&
+ 2*GetSize(cell->getPort("\\A").chunks()) < GetSize(cell->getPort("\\A"))) {
+ bool is_and = cell->type == "$reduce_and";
+ string bits(GetSize(cell->getPort("\\A")), is_and ? '1' : '0');
+ return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b');
+ }
+
if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true);
if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false);
if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false);
@@ -516,7 +601,7 @@ struct Smt2Worker
if (cell->type == "$logic_and") return export_reduce(cell, "(and (or A) (or B))", false);
if (cell->type == "$logic_or") return export_reduce(cell, "(or A B)", false);
- if (cell->type == "$mux" || cell->type == "$pmux")
+ if (cell->type.in("$mux", "$pmux"))
{
int width = GetSize(cell->getPort("\\Y"));
std::string processed_expr = get_bv(cell->getPort("\\A"));
@@ -554,17 +639,30 @@ struct Smt2Worker
int rd_ports = cell->getParam("\\RD_PORTS").as_int();
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
- decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d\n", get_id(cell), abits, width, rd_ports, wr_ports));
+ bool async_read = false;
+ if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) {
+ if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_zero())
+ log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module));
+ async_read = true;
+ }
+
+ decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync"));
+
+ string memstate;
+ if (async_read) {
+ memstate = stringf("%s#%d#final", get_id(module), arrayid);
+ } else {
+ memstate = stringf("%s#%d#0", get_id(module), arrayid);
+ }
if (statebv)
{
int mem_size = cell->getParam("\\SIZE").as_int();
int mem_offset = cell->getParam("\\OFFSET").as_int();
- makebits(stringf("%s#%d#0", get_id(module), arrayid), width*mem_size, get_id(cell));
-
- decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d#0| state))\n",
- get_id(module), get_id(cell), get_id(module), width*mem_size, get_id(module), arrayid));
+ makebits(memstate, width*mem_size, get_id(cell));
+ decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n",
+ get_id(module), get_id(cell), get_id(module), width*mem_size, memstate.c_str()));
for (int i = 0; i < rd_ports; i++)
{
@@ -584,9 +682,9 @@ struct Smt2Worker
read_expr += "0";
for (int k = 0; k < mem_size; k++)
- read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s#%d#0| state))\n %s)",
+ read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)",
get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(),
- width*(k+1)-1, width*k, get_id(module), arrayid, read_expr.c_str());
+ width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str());
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n",
get_id(module), idcounter, get_id(module), width, read_expr.c_str(), log_signal(data_sig)));
@@ -600,14 +698,14 @@ struct Smt2Worker
else
{
if (statedt)
- dtmembers.push_back(stringf(" (|%s#%d#0| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
- get_id(module), arrayid, abits, width, get_id(cell)));
+ dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ memstate.c_str(), abits, width, get_id(cell)));
else
- decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
- get_id(module), arrayid, get_id(module), abits, width, get_id(cell)));
+ decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ memstate.c_str(), get_id(module), abits, width, get_id(cell)));
- decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n",
- get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid));
+ decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n",
+ get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str()));
for (int i = 0; i < rd_ports; i++)
{
@@ -622,8 +720,8 @@ struct Smt2Worker
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
- decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) (|%s_m:R%dA %s| state))) ; %s\n",
- get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, get_id(module), i, get_id(cell), log_signal(data_sig)));
+ decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n",
+ get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig)));
decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n",
get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter));
@@ -646,6 +744,9 @@ struct Smt2Worker
for (auto &conn : cell->connections())
{
+ if (GetSize(conn.second) == 0)
+ continue;
+
Wire *w = m->wire(conn.first);
SigSpec sig = sigmap(conn.second);
@@ -669,7 +770,7 @@ struct Smt2Worker
if (statebv)
makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type));
- if (statedt)
+ else if (statedt)
dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n",
get_id(module), get_id(cell->name), get_id(cell->type)));
else
@@ -713,17 +814,30 @@ struct Smt2Worker
decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\'))
decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width));
+ if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig)))
+ decls.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire),
+ clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : ""));
if (bvmode && GetSize(sig) > 1) {
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n",
get_id(module), get_id(wire), get_id(module), GetSize(sig), get_bv(sig).c_str()));
+ if (wire->port_input)
+ ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))",
+ get_id(module), get_id(wire), get_id(module), get_id(wire)));
} else {
for (int i = 0; i < GetSize(sig); i++)
- if (GetSize(sig) > 1)
+ if (GetSize(sig) > 1) {
decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n",
get_id(module), get_id(wire), i, get_id(module), get_bool(sig[i]).c_str()));
- else
+ if (wire->port_input)
+ ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))",
+ get_id(module), get_id(wire), i, get_id(module), get_id(wire), i));
+ } else {
decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n",
get_id(module), get_id(wire), get_id(module), get_bool(sig[i]).c_str()));
+ if (wire->port_input)
+ ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))",
+ get_id(module), get_id(wire), get_id(module), get_id(wire)));
+ }
}
}
}
@@ -775,8 +889,8 @@ struct Smt2Worker
string name_a = get_bool(cell->getPort("\\A"));
string name_en = get_bool(cell->getPort("\\EN"));
- decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id,
- cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
+ string infostr = (cell->name[0] == '$' && cell->attributes.count("\\src")) ? cell->attributes.at("\\src").decode_string() : get_id(cell);
+ decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str()));
if (cell->type == "$cover")
decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
@@ -811,8 +925,14 @@ struct Smt2Worker
Module *m = module->design->module(cell->type);
log_assert(m != nullptr);
+ hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n",
+ get_id(module), get_id(cell->type), cell_state.c_str()));
+
for (auto &conn : cell->connections())
{
+ if (GetSize(conn.second) == 0)
+ continue;
+
Wire *w = m->wire(conn.first);
SigSpec sig = sigmap(conn.second);
@@ -842,6 +962,7 @@ struct Smt2Worker
std::string expr_d = get_bool(cell->getPort("\\D"));
std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q"))));
+ ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort("\\Q")).c_str(), get_bool(cell->getPort("\\Q"), "other_state").c_str()));
}
if (cell->type.in("$ff", "$dff"))
@@ -849,13 +970,16 @@ struct Smt2Worker
std::string expr_d = get_bv(cell->getPort("\\D"));
std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q"))));
+ ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Q")).c_str(), get_bv(cell->getPort("\\Q"), "other_state").c_str()));
}
- if (cell->type == "$anyconst")
+ if (cell->type.in("$anyconst", "$allconst"))
{
std::string expr_d = get_bv(cell->getPort("\\Y"));
std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state");
trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Y"))));
+ if (cell->type == "$anyconst")
+ ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Y")).c_str(), get_bv(cell->getPort("\\Y"), "other_state").c_str()));
}
if (cell->type == "$mem")
@@ -866,11 +990,25 @@ struct Smt2Worker
int width = cell->getParam("\\WIDTH").as_int();
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
+ bool async_read = false;
+ string initial_memstate, final_memstate;
+
+ if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) {
+ log_assert(cell->getParam("\\WR_CLK_ENABLE").is_fully_zero());
+ async_read = true;
+ initial_memstate = stringf("%s#%d#0", get_id(module), arrayid);
+ final_memstate = stringf("%s#%d#final", get_id(module), arrayid);
+ }
+
if (statebv)
{
int mem_size = cell->getParam("\\SIZE").as_int();
int mem_offset = cell->getParam("\\OFFSET").as_int();
+ if (async_read) {
+ makebits(final_memstate, width*mem_size, get_id(cell));
+ }
+
for (int i = 0; i < wr_ports; i++)
{
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
@@ -909,6 +1047,15 @@ struct Smt2Worker
}
else
{
+ if (async_read) {
+ if (statedt)
+ dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ initial_memstate.c_str(), abits, width, get_id(cell)));
+ else
+ decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ initial_memstate.c_str(), get_id(module), abits, width, get_id(cell)));
+ }
+
for (int i = 0; i < wr_ports; i++)
{
SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits);
@@ -944,6 +1091,10 @@ struct Smt2Worker
std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, wr_ports);
std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid);
trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell)));
+ ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid));
+
+ if (async_read)
+ hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(cell)));
Const init_data = cell->getParam("\\INIT");
int memsize = cell->getParam("\\SIZE").as_int();
@@ -954,20 +1105,27 @@ struct Smt2Worker
break;
Const initword = init_data.extract(i*width, width, State::Sx);
+ Const initmask = initword;
bool gen_init_constr = false;
- for (auto bit : initword.bits)
- if (bit == State::S0 || bit == State::S1)
+ for (int k = 0; k < GetSize(initword); k++) {
+ if (initword[k] == State::S0 || initword[k] == State::S1) {
gen_init_constr = true;
+ initmask[k] = State::S1;
+ } else {
+ initmask[k] = State::S0;
+ initword[k] = State::S0;
+ }
+ }
if (gen_init_constr)
{
if (statebv)
/* FIXME */;
else
- init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]",
+ init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]",
get_id(module), arrayid, Const(i, abits).as_string().c_str(),
- initword.as_string().c_str(), get_id(cell), i));
+ initmask.as_string().c_str(), initword.as_string().c_str(), get_id(cell), i));
}
}
}
@@ -983,6 +1141,37 @@ struct Smt2Worker
hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name)));
trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n",
get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
+ ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n",
+ get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name)));
+ }
+
+ if (forallmode)
+ {
+ string expr = ex_state_eq.empty() ? "true" : "(and";
+ if (!ex_state_eq.empty()) {
+ if (GetSize(ex_state_eq) == 1) {
+ expr = "\n " + ex_state_eq.front() + "\n";
+ } else {
+ for (auto &str : ex_state_eq)
+ expr += stringf("\n %s", str.c_str());
+ expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), get_id(module), expr.c_str()));
+
+ expr = ex_input_eq.empty() ? "true" : "(and";
+ if (!ex_input_eq.empty()) {
+ if (GetSize(ex_input_eq) == 1) {
+ expr = "\n " + ex_input_eq.front() + "\n";
+ } else {
+ for (auto &str : ex_input_eq)
+ expr += stringf("\n %s", str.c_str());
+ expr += "\n)";
+ }
+ }
+ decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n",
+ get_id(module), get_id(module), get_id(module), expr.c_str()));
}
string assert_expr = assert_list.empty() ? "true" : "(and";
@@ -1073,7 +1262,7 @@ struct Smt2Worker
struct Smt2Backend : public Backend {
Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1229,10 +1418,11 @@ struct Smt2Backend : public Backend {
log("from non-zero to zero in the test design.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::ifstream template_f;
bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false;
+ bool forallmode = false;
log_header(design, "Executing SMT2 backend.\n");
@@ -1286,7 +1476,7 @@ struct Smt2Backend : public Backend {
int indent = 0;
while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t'))
indent++;
- if (line.substr(indent, 2) == "%%")
+ if (line.compare(indent, 2, "%%") == 0)
break;
*f << line << std::endl;
}
@@ -1336,17 +1526,30 @@ struct Smt2Backend : public Backend {
}
dict<IdString, int> mod_stbv_width;
+ dict<IdString, dict<IdString, pair<bool, bool>>> mod_clk_cache;
Module *topmod = design->top_module();
std::string topmod_id;
for (auto module : sorted_modules)
+ for (auto cell : module->cells())
+ if (cell->type.in("$allconst", "$allseq"))
+ goto found_forall;
+ if (0) {
+ found_forall:
+ forallmode = true;
+ *f << stringf("; yosys-smt2-forall\n");
+ if (!statebv && !statedt)
+ log_error("Forall-exists problems are only supported in -stbv or -stdt mode.\n");
+ }
+
+ for (auto module : sorted_modules)
{
- if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn())
+ if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn())
continue;
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
- Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, mod_stbv_width);
+ Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode, mod_stbv_width, mod_clk_cache);
worker.run();
worker.write(*f);
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index c8151c266..3d6d3e1b3 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -32,6 +32,7 @@ cexfile = None
aimfile = None
aiwfile = None
aigheader = True
+btorwitfile = None
vlogtbfile = None
vlogtbtop = None
inconstr = list()
@@ -86,12 +87,15 @@ yosys-smtbmc [options] <yosys_smt2_output>
--aig <aim_filename>:<aiw_filename>
like above, but for map files and witness files that do not
- share a filename prefix (or use differen file extensions).
+ share a filename prefix (or use different file extensions).
--aig-noheader
the AIGER witness file does not include the status and
properties lines.
+ --btorwit <btor_witness_filename>
+ read a BTOR witness.
+
--noinfo
only run the core proof, do not collect and print any
additional information (e.g. which assert failed)
@@ -99,8 +103,8 @@ yosys-smtbmc [options] <yosys_smt2_output>
--presat
check if the design with assumptions but without assertions
is SAT before checking if assertions are UNSAT. This will
- detect if there are contradicting assumtions. In some cases
- this will also help to "warmup" the solver, potentially
+ detect if there are contradicting assumptions. In some cases
+ this will also help to "warm up" the solver, potentially
yielding a speedup.
--final-only
@@ -145,14 +149,14 @@ yosys-smtbmc [options] <yosys_smt2_output>
--append <num_steps>
add <num_steps> time steps at the end of the trace
when creating a counter example (this additional time
- steps will still be constrained by assumtions)
+ steps will still be constrained by assumptions)
""" + so.helpmsg())
sys.exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
- ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "presat",
+ ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat",
"dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
"smtc-init", "smtc-top=", "noinit"])
except:
@@ -189,6 +193,8 @@ for o, a in opts:
aiwfile = a + ".aiw"
elif o == "--aig-noheader":
aigheader = False
+ elif o == "--btorwit":
+ btorwitfile = a
elif o == "--dump-vcd":
vcdfile = a
elif o == "--dump-vlogtb":
@@ -338,7 +344,7 @@ def get_constr_expr(db, state, final=False, getvalues=False):
if state not in db:
return ([], [], []) if getvalues else "true"
- netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*)\](?=[ )]|$)')
+ netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*|\S*)\](?=[ )]|$)')
def replace_netref(match):
state_sel = match.group(2)
@@ -575,6 +581,103 @@ if aimfile is not None:
num_steps = max(num_steps, step+1)
step += 1
+if btorwitfile is not None:
+ with open(btorwitfile, "r") as f:
+ step = None
+ suffix = None
+ altsuffix = None
+ header_okay = False
+
+ for line in f:
+ line = line.strip()
+
+ if line == "sat":
+ header_okay = True
+ continue
+
+ if not header_okay:
+ continue
+
+ if line == "" or line[0] == "b" or line[0] == "j":
+ continue
+
+ if line == ".":
+ break
+
+ if line[0] == '#' or line[0] == '@':
+ step = int(line[1:])
+ suffix = line
+ altsuffix = suffix
+ if suffix[0] == "@":
+ altsuffix = "#" + suffix[1:]
+ else:
+ altsuffix = "@" + suffix[1:]
+ continue
+
+ line = line.split()
+
+ if len(line) == 0:
+ continue
+
+ if line[-1].endswith(suffix):
+ line[-1] = line[-1][0:len(line[-1]) - len(suffix)]
+
+ if line[-1].endswith(altsuffix):
+ line[-1] = line[-1][0:len(line[-1]) - len(altsuffix)]
+
+ if line[-1][0] == "$":
+ continue
+
+ # BV assignments
+ if len(line) == 3 and line[1][0] != "[":
+ value = line[1]
+ name = line[2]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.net_exists(topmod, path):
+ continue
+
+ width = smt.net_width(topmod, path)
+
+ if width == 1:
+ assert value in ["0", "1"]
+ value = "true" if value == "1" else "false"
+ else:
+ value = "#b" + value
+
+ smtexpr = "(= [%s] %s)" % (name, value)
+ constr_assumes[step].append((btorwitfile, smtexpr))
+
+ # Array assignments
+ if len(line) == 4 and line[1][0] == "[":
+ index = line[1]
+ value = line[2]
+ name = line[3]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.mem_exists(topmod, path):
+ continue
+
+ meminfo = smt.mem_info(topmod, path)
+
+ if meminfo[1] == 1:
+ assert value in ["0", "1"]
+ value = "true" if value == "1" else "false"
+ else:
+ value = "#b" + value
+
+ assert index[0] == "["
+ assert index[-1] == "]"
+ index = "#b" + index[1:-1]
+
+ smtexpr = "(= (select [%s] %s) %s)" % (name, index, value)
+ constr_assumes[step].append((btorwitfile, smtexpr))
+
+ skip_steps = step
+ num_steps = step+1
+
def write_vcd_trace(steps_start, steps_stop, index):
filename = vcdfile.replace("%", index)
print_msg("Writing trace to VCD file: %s" % (filename))
@@ -589,12 +692,16 @@ def write_vcd_trace(steps_start, steps_stop, index):
if n.startswith("$"):
hidden_net = True
if not hidden_net:
- vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath))
+ edge = smt.net_clock(topmod, netpath)
+ if edge is None:
+ vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath))
+ else:
+ vcd.add_clock([topmod] + netpath, edge)
path_list.append(netpath)
mem_trace_data = dict()
for mempath in sorted(smt.hiermems(topmod)):
- abits, width, rports, wports = smt.mem_info(topmod, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath)
expr_id = list()
expr_list = list()
@@ -644,6 +751,9 @@ def write_vcd_trace(steps_start, steps_stop, index):
data = ["x"] * width
gotread = False
+ if len(wdata) == 0 and len(rdata) != 0:
+ wdata = [[]] * len(rdata)
+
assert len(rdata) == len(wdata)
for i in range(len(wdata)):
@@ -663,7 +773,8 @@ def write_vcd_trace(steps_start, steps_stop, index):
else:
buf[k] = tdata[i][k]
- tdata.append(data[:])
+ if not asyncwr:
+ tdata.append(data[:])
for j_data in wdata[i]:
if j_data["A"] != addr:
@@ -676,6 +787,9 @@ def write_vcd_trace(steps_start, steps_stop, index):
if M[k] == "1":
data[k] = D[k]
+ if asyncwr:
+ tdata.append(data[:])
+
assert len(tdata) == len(rdata)
netpath = mempath[:]
@@ -708,9 +822,12 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
if vlogtbtop is not None:
for item in vlogtbtop.split("."):
- assert item in smt.modinfo[vlogtb_topmod].cells
- vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state)
- vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item]
+ if item in smt.modinfo[vlogtb_topmod].cells:
+ vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state)
+ vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item]
+ else:
+ print_msg("Vlog top module '%s' not found: no cell '%s' in module '%s'" % (vlogtbtop, item, vlogtb_topmod))
+ break
with open(filename, "w") as f:
print("`ifndef VERILATOR", file=f)
@@ -782,7 +899,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
mems = sorted(smt.hiermems(vlogtb_topmod))
for mempath in mems:
- abits, width, rports, wports = smt.mem_info(vlogtb_topmod, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(vlogtb_topmod, mempath)
addr_expr_list = list()
data_expr_list = list()
@@ -885,7 +1002,7 @@ def write_constr_trace(steps_start, steps_stop, index):
mems = sorted(smt.hiermems(constr_topmod))
for mempath in mems:
- abits, width, rports, wports = smt.mem_info(constr_topmod, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(constr_topmod, mempath)
addr_expr_list = list()
data_expr_list = list()
@@ -972,7 +1089,7 @@ def print_anyconsts_worker(mod, state, path):
for fun, info in smt.modinfo[mod].anyconsts.items():
if info[1] is None:
- print_msg("Value for anyconst in %s (%s): %d" % (path, info, smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
+ print_msg("Value for anyconst in %s (%s): %d" % (path, info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
else:
print_msg("Value for anyconst %s.%s (%s): %d" % (path, info[1], info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
@@ -999,24 +1116,166 @@ def get_cover_list(mod, base):
return cover_expr, cover_desc
+states = list()
+asserts_antecedent_cache = [list()]
+asserts_consequent_cache = [list()]
+asserts_cache_dirty = False
+
+def smt_state(step):
+ smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
+ states.append("s%d" % step)
+
+def smt_assert(expr):
+ if expr == "true":
+ return
+
+ smt.write("(assert %s)" % expr)
+
+def smt_assert_antecedent(expr):
+ if expr == "true":
+ return
+
+ smt.write("(assert %s)" % expr)
+
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_antecedent_cache[-1].append(expr)
+
+def smt_assert_consequent(expr):
+ if expr == "true":
+ return
+
+ smt.write("(assert %s)" % expr)
+
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_consequent_cache[-1].append(expr)
+
+def smt_forall_assert():
+ if not smt.forall:
+ return
+
+ global asserts_cache_dirty
+ asserts_cache_dirty = False
+
+ def make_assert_expr(asserts_cache):
+ expr = list()
+ for lst in asserts_cache:
+ expr += lst
+
+ assert len(expr) != 0
+
+ if len(expr) == 1:
+ expr = expr[0]
+ else:
+ expr = "(and %s)" % (" ".join(expr))
+ return expr
+
+ antecedent_expr = make_assert_expr(asserts_antecedent_cache)
+ consequent_expr = make_assert_expr(asserts_consequent_cache)
+
+ states_db = set(states)
+ used_states_db = set()
+ new_antecedent_expr = list()
+ new_consequent_expr = list()
+ assert_expr = list()
+
+ def make_new_expr(new_expr, expr):
+ cursor = 0
+ while cursor < len(expr):
+ l = 1
+ if expr[cursor] in '|"':
+ while cursor+l+1 < len(expr) and expr[cursor] != expr[cursor+l]:
+ l += 1
+ l += 1
+ elif expr[cursor] not in '() ':
+ while cursor+l < len(expr) and expr[cursor+l] not in '|"() ':
+ l += 1
+
+ word = expr[cursor:cursor+l]
+ if word in states_db:
+ used_states_db.add(word)
+ word += "_"
+
+ new_expr.append(word)
+ cursor += l
+
+ make_new_expr(new_antecedent_expr, antecedent_expr)
+ make_new_expr(new_consequent_expr, consequent_expr)
+
+ new_antecedent_expr = ["".join(new_antecedent_expr)]
+ new_consequent_expr = ["".join(new_consequent_expr)]
+
+ if states[0] in used_states_db:
+ new_antecedent_expr.append("(|%s_ex_state_eq| %s %s_)" % (topmod, states[0], states[0]))
+ for s in states:
+ if s in used_states_db:
+ new_antecedent_expr.append("(|%s_ex_input_eq| %s %s_)" % (topmod, s, s))
+
+ if len(new_antecedent_expr) == 0:
+ new_antecedent_expr = "true"
+ elif len(new_antecedent_expr) == 1:
+ new_antecedent_expr = new_antecedent_expr[0]
+ else:
+ new_antecedent_expr = "(and %s)" % (" ".join(new_antecedent_expr))
+
+ if len(new_consequent_expr) == 0:
+ new_consequent_expr = "true"
+ elif len(new_consequent_expr) == 1:
+ new_consequent_expr = new_consequent_expr[0]
+ else:
+ new_consequent_expr = "(and %s)" % (" ".join(new_consequent_expr))
+
+ assert_expr.append("(assert (forall (")
+ first_state = True
+ for s in states:
+ if s in used_states_db:
+ assert_expr.append("%s(%s_ |%s_s|)" % ("" if first_state else " ", s, topmod))
+ first_state = False
+ assert_expr.append(") (=> %s %s)))" % (new_antecedent_expr, new_consequent_expr))
+
+ smt.write("".join(assert_expr))
+
+def smt_push():
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_antecedent_cache.append(list())
+ asserts_consequent_cache.append(list())
+ smt.write("(push 1)")
+
+def smt_pop():
+ global asserts_cache_dirty
+ asserts_cache_dirty = True
+ asserts_antecedent_cache.pop()
+ asserts_consequent_cache.pop()
+ smt.write("(pop 1)")
+
+def smt_check_sat():
+ if asserts_cache_dirty:
+ smt_forall_assert()
+ return smt.check_sat()
if tempind:
- retstatus = False
+ retstatus = "FAILED"
skip_counter = step_size
for step in range(num_steps, -1, -1):
- smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+ if smt.forall:
+ print_msg("Temporal induction not supported for exists-forall problems.")
+ break
+
+ smt_state(step)
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step))
if step == num_steps:
- smt.write("(assert (not (and (|%s_a| s%d) %s)))" % (topmod, step, get_constr_expr(constr_asserts, step)))
+ smt_assert("(not (and (|%s_a| s%d) %s))" % (topmod, step, get_constr_expr(constr_asserts, step)))
else:
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step, step+1))
- smt.write("(assert (|%s_a| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step, step+1))
+ smt_assert("(|%s_a| s%d)" % (topmod, step))
+ smt_assert(get_constr_expr(constr_asserts, step))
if step > num_steps-skip_steps:
print_msg("Skipping induction in step %d.." % (step))
@@ -1030,9 +1289,9 @@ if tempind:
skip_counter = 0
print_msg("Trying induction in step %d.." % (step))
- if smt.check_sat() == "sat":
+ if smt_check_sat() == "sat":
if step == 0:
- print("%s Temporal induction failed!" % smt.timestamp())
+ print_msg("Temporal induction failed!")
print_anyconsts(num_steps)
print_failed_asserts(num_steps)
write_trace(step, num_steps+1, '%')
@@ -1043,8 +1302,8 @@ if tempind:
write_trace(step, num_steps+1, "%d" % step)
else:
- print("%s Temporal induction successful." % smt.timestamp())
- retstatus = True
+ print_msg("Temporal induction successful.")
+ retstatus = "PASSED"
break
elif covermode:
@@ -1062,48 +1321,52 @@ elif covermode:
smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr))
step = 0
- retstatus = False
+ retstatus = "FAILED"
found_failed_assert = False
assert step_size == 1
while step < num_steps:
- smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+ smt_state(step)
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step))
if step == 0:
if noinit:
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
else:
- smt.write("(assert (|%s_i| s0))" % (topmod))
- smt.write("(assert (|%s_is| s0))" % (topmod))
+ smt_assert_antecedent("(|%s_i| s0)" % (topmod))
+ smt_assert_antecedent("(|%s_is| s0)" % (topmod))
else:
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
while "1" in cover_mask:
print_msg("Checking cover reachability in step %d.." % (step))
- smt.write("(push 1)")
- smt.write("(assert (distinct (covers_%d s%d) #b%s))" % (coveridx, step, "0" * len(cover_desc)))
+ smt_push()
+ smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc)))
- if smt.check_sat() == "unsat":
- smt.write("(pop 1)")
+ if smt_check_sat() == "unsat":
+ smt_pop()
break
if append_steps > 0:
for i in range(step+1, step+1+append_steps):
print_msg("Appending additional step %d." % i)
- smt.write("(declare-fun s%d () |%s_s|)" % (i, topmod))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, i))
- smt.write("(assert (|%s_u| s%d))" % (topmod, i))
- smt.write("(assert (|%s_h| s%d))" % (topmod, i))
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, i-1, i))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, i))
+ smt_state(i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i))
print_msg("Re-solving with appended steps..")
- assert smt.check_sat() == "sat"
+ if smt_check_sat() == "unsat":
+ print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
+ found_failed_assert = True
+ retstatus = "FAILED"
+ break
reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step)))
assert len(reached_covers) == len(cover_desc)
@@ -1130,14 +1393,14 @@ elif covermode:
break
coveridx += 1
- smt.write("(pop 1)")
+ smt_pop()
smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask))
if found_failed_assert:
break
if "1" not in cover_mask:
- retstatus = True
+ retstatus = "PASSED"
break
step += 1
@@ -1149,29 +1412,29 @@ elif covermode:
else: # not tempind, covermode
step = 0
- retstatus = True
+ retstatus = "PASSED"
while step < num_steps:
- smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step))
+ smt_state(step)
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step))
if step == 0:
if noinit:
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
else:
- smt.write("(assert (|%s_i| s0))" % (topmod))
- smt.write("(assert (|%s_is| s0))" % (topmod))
+ smt_assert_antecedent("(|%s_i| s0)" % (topmod))
+ smt_assert_antecedent("(|%s_is| s0)" % (topmod))
else:
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step))
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
if step < skip_steps:
if assume_skipped is not None and step >= assume_skipped:
print_msg("Skipping step %d (and assuming pass).." % (step))
- smt.write("(assert (|%s_a| s%d))" % (topmod, step))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, step))
+ smt_assert("(|%s_a| s%d)" % (topmod, step))
+ smt_assert(get_constr_expr(constr_asserts, step))
else:
print_msg("Skipping step %d.." % (step))
step += 1
@@ -1180,12 +1443,12 @@ else: # not tempind, covermode
last_check_step = step
for i in range(1, step_size):
if step+i < num_steps:
- smt.write("(declare-fun s%d () |%s_s|)" % (step+i, topmod))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step+i))
- smt.write("(assert (|%s_u| s%d))" % (topmod, step+i))
- smt.write("(assert (|%s_h| s%d))" % (topmod, step+i))
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step+i-1, step+i))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, step+i))
+ smt_state(step+i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step+i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, step+i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step+i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step+i-1, step+i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, step+i))
last_check_step = step+i
if not gentrace:
@@ -1195,9 +1458,9 @@ else: # not tempind, covermode
else:
print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step))
- if smt.check_sat() == "unsat":
- print("%s Warmup failed!" % smt.timestamp())
- retstatus = False
+ if smt_check_sat() == "unsat":
+ print("%s Assumptions are unsatisfiable!" % smt.timestamp())
+ retstatus = "PREUNSAT"
break
if not final_only:
@@ -1205,36 +1468,40 @@ else: # not tempind, covermode
print_msg("Checking assertions in step %d.." % (step))
else:
print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step))
- smt.write("(push 1)")
+ smt_push()
- smt.write("(assert (not (and %s)))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
+ smt_assert("(not (and %s))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] +
[get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)]))
- if smt.check_sat() == "sat":
+ if smt_check_sat() == "sat":
print("%s BMC failed!" % smt.timestamp())
if append_steps > 0:
for i in range(last_check_step+1, last_check_step+1+append_steps):
print_msg("Appending additional step %d." % i)
- smt.write("(declare-fun s%d () |%s_s|)" % (i, topmod))
- smt.write("(assert (not (|%s_is| s%d)))" % (topmod, i))
- smt.write("(assert (|%s_u| s%d))" % (topmod, i))
- smt.write("(assert (|%s_h| s%d))" % (topmod, i))
- smt.write("(assert (|%s_t| s%d s%d))" % (topmod, i-1, i))
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, i))
- assert smt.check_sat() == "sat"
+ smt_state(i)
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i))
+ smt_assert_consequent("(|%s_u| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i))
+ smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i))
+ print_msg("Re-solving with appended steps..")
+ if smt_check_sat() == "unsat":
+ print("%s Cannot appended steps without violating assumptions!" % smt.timestamp())
+ retstatus = "FAILED"
+ break
print_anyconsts(step)
for i in range(step, last_check_step+1):
print_failed_asserts(i)
write_trace(0, last_check_step+1+append_steps, '%')
- retstatus = False
+ retstatus = "FAILED"
break
- smt.write("(pop 1)")
+ smt_pop()
if (constr_final_start is not None) or (last_check_step+1 != num_steps):
for i in range(step, last_check_step+1):
- smt.write("(assert (|%s_a| s%d))" % (topmod, i))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
+ smt_assert("(|%s_a| s%d)" % (topmod, i))
+ smt_assert(get_constr_expr(constr_asserts, i))
if constr_final_start is not None:
for i in range(step, last_check_step+1):
@@ -1242,32 +1509,32 @@ else: # not tempind, covermode
continue
print_msg("Checking final constraints in step %d.." % (i))
- smt.write("(push 1)")
+ smt_push()
- smt.write("(assert %s)" % get_constr_expr(constr_assumes, i, final=True))
- smt.write("(assert (not %s))" % get_constr_expr(constr_asserts, i, final=True))
+ smt_assert_consequent(get_constr_expr(constr_assumes, i, final=True))
+ smt_assert("(not %s)" % get_constr_expr(constr_asserts, i, final=True))
- if smt.check_sat() == "sat":
+ if smt_check_sat() == "sat":
print("%s BMC failed!" % smt.timestamp())
print_anyconsts(i)
print_failed_asserts(i, final=True)
write_trace(0, i+1, '%')
- retstatus = False
+ retstatus = "FAILED"
break
- smt.write("(pop 1)")
+ smt_pop()
if not retstatus:
break
else: # gentrace
for i in range(step, last_check_step+1):
- smt.write("(assert (|%s_a| s%d))" % (topmod, i))
- smt.write("(assert %s)" % get_constr_expr(constr_asserts, i))
+ smt_assert("(|%s_a| s%d)" % (topmod, i))
+ smt_assert(get_constr_expr(constr_asserts, i))
print_msg("Solving for step %d.." % (last_check_step))
- if smt.check_sat() != "sat":
+ if smt_check_sat() != "sat":
print("%s No solution found!" % smt.timestamp())
- retstatus = False
+ retstatus = "FAILED"
break
elif dumpall:
@@ -1276,7 +1543,7 @@ else: # not tempind, covermode
step += step_size
- if gentrace:
+ if gentrace and retstatus:
print_anyconsts(0)
write_trace(0, num_steps, '%')
@@ -1284,5 +1551,5 @@ else: # not tempind, covermode
smt.write("(exit)")
smt.wait()
-print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)"))
-sys.exit(0 if retstatus else 1)
+print_msg("Status: %s" % retstatus)
+sys.exit(0 if retstatus == "PASSED" else 1)
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index abf8e812d..34bf7ef38 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -16,10 +16,67 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
-import sys, subprocess, re, os
+import sys, re, os, signal
+import subprocess
+if os.name == "posix":
+ import resource
from copy import deepcopy
from select import select
from time import time
+from queue import Queue, Empty
+from threading import Thread
+
+
+# This is needed so that the recursive SMT2 S-expression parser
+# does not run out of stack frames when parsing large expressions
+if os.name == "posix":
+ smtio_reclimit = 64 * 1024
+ if sys.getrecursionlimit() < smtio_reclimit:
+ sys.setrecursionlimit(smtio_reclimit)
+
+ current_rlimit_stack = resource.getrlimit(resource.RLIMIT_STACK)
+ if current_rlimit_stack[0] != resource.RLIM_INFINITY:
+ smtio_stacksize = 128 * 1024 * 1024
+ if os.uname().sysname == "Darwin":
+ # MacOS has rather conservative stack limits
+ smtio_stacksize = 16 * 1024 * 1024
+ if current_rlimit_stack[1] != resource.RLIM_INFINITY:
+ smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1])
+ if current_rlimit_stack[0] < smtio_stacksize:
+ try:
+ resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1]))
+ except ValueError:
+ # couldn't get more stack, just run with what we have
+ pass
+
+
+# currently running solvers (so we can kill them)
+running_solvers = dict()
+forced_shutdown = False
+solvers_index = 0
+
+def force_shutdown(signum, frame):
+ global forced_shutdown
+ if not forced_shutdown:
+ forced_shutdown = True
+ if signum is not None:
+ print("<%s>" % signal.Signals(signum).name)
+ for p in running_solvers.values():
+ # os.killpg(os.getpgid(p.pid), signal.SIGTERM)
+ os.kill(p.pid, signal.SIGTERM)
+ sys.exit(1)
+
+if os.name == "posix":
+ signal.signal(signal.SIGHUP, force_shutdown)
+signal.signal(signal.SIGINT, force_shutdown)
+signal.signal(signal.SIGTERM, force_shutdown)
+
+def except_hook(exctype, value, traceback):
+ if not forced_shutdown:
+ sys.__excepthook__(exctype, value, traceback)
+ force_shutdown(None, None)
+
+sys.excepthook = except_hook
hex_dict = {
@@ -40,24 +97,33 @@ class SmtModInfo:
self.memories = dict()
self.wires = set()
self.wsize = dict()
+ self.clocks = dict()
self.cells = dict()
self.asserts = dict()
self.covers = dict()
self.anyconsts = dict()
self.anyseqs = dict()
+ self.allconsts = dict()
+ self.allseqs = dict()
+ self.asize = dict()
class SmtIo:
def __init__(self, opts=None):
+ global solvers_index
+
self.logic = None
self.logic_qf = True
self.logic_ax = True
self.logic_uf = True
self.logic_bv = True
self.logic_dt = False
+ self.forall = False
self.produce_models = True
self.smt2cache = [list()]
self.p = None
+ self.p_index = solvers_index
+ solvers_index += 1
if opts is not None:
self.logic = opts.logic
@@ -91,23 +157,41 @@ class SmtIo:
self.topmod = None
self.setup_done = False
+ def __del__(self):
+ if self.p is not None and not forced_shutdown:
+ os.killpg(os.getpgid(self.p.pid), signal.SIGTERM)
+ if running_solvers is not None:
+ del running_solvers[self.p_index]
+
def setup(self):
assert not self.setup_done
+ if self.forall:
+ self.unroll = False
+
if self.solver == "yices":
- self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['yices-smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
if self.solver == "z3":
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
if self.solver == "cvc4":
- self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
if self.solver == "mathsat":
self.popen_vargs = ['mathsat'] + self.solver_opts
if self.solver == "boolector":
- self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
+ if self.noincr:
+ self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts
+ else:
+ self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
self.unroll = True
if self.solver == "abc":
@@ -126,9 +210,10 @@ class SmtIo:
if self.dummy_file is not None:
self.dummy_fd = open(self.dummy_file, "w")
if not self.noincr:
- self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ self.p_open()
if self.unroll:
+ assert not self.forall
self.logic_uf = False
self.unroll_idcnt = 0
self.unroll_buffer = ""
@@ -148,17 +233,17 @@ class SmtIo:
self.setup_done = True
+ for stmt in self.info_stmts:
+ self.write(stmt)
+
if self.produce_models:
self.write("(set-option :produce-models true)")
self.write("(set-logic %s)" % self.logic)
- for stmt in self.info_stmts:
- self.write(stmt)
-
def timestamp(self):
secs = int(time() - self.start_time)
- return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60)
+ return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60)
def replace_in_stmt(self, stmt, pat, repl):
if stmt == pat:
@@ -209,6 +294,65 @@ class SmtIo:
return stmt
+ def p_thread_main(self):
+ while True:
+ data = self.p.stdout.readline().decode("ascii")
+ if data == "": break
+ self.p_queue.put(data)
+ self.p_queue.put("")
+ self.p_running = False
+
+ def p_open(self):
+ assert self.p is None
+ try:
+ self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ except FileNotFoundError:
+ print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True)
+ sys.exit(1)
+ running_solvers[self.p_index] = self.p
+ self.p_running = True
+ self.p_next = None
+ self.p_queue = Queue()
+ self.p_thread = Thread(target=self.p_thread_main)
+ self.p_thread.start()
+
+ def p_write(self, data, flush):
+ assert self.p is not None
+ self.p.stdin.write(bytes(data, "ascii"))
+ if flush: self.p.stdin.flush()
+
+ def p_read(self):
+ assert self.p is not None
+ if self.p_next is not None:
+ data = self.p_next
+ self.p_next = None
+ return data
+ if not self.p_running:
+ return ""
+ return self.p_queue.get()
+
+ def p_poll(self, timeout=0.1):
+ assert self.p is not None
+ assert self.p_running
+ if self.p_next is not None:
+ return False
+ try:
+ self.p_next = self.p_queue.get(True, timeout)
+ return False
+ except Empty:
+ return True
+
+ def p_close(self):
+ assert self.p is not None
+ self.p.stdin.close()
+ self.p_thread.join()
+ assert not self.p_running
+ del running_solvers[self.p_index]
+ self.p = None
+ self.p_next = None
+ self.p_queue = None
+ self.p_thread = None
+
def write(self, stmt, unroll=True):
if stmt.startswith(";"):
self.info(stmt)
@@ -281,20 +425,17 @@ class SmtIo:
if self.solver != "dummy":
if self.noincr:
if self.p is not None and not stmt.startswith("(get-"):
- self.p.stdin.close()
- self.p = None
+ self.p_close()
if stmt == "(push 1)":
self.smt2cache.append(list())
elif stmt == "(pop 1)":
self.smt2cache.pop()
else:
if self.p is not None:
- self.p.stdin.write(bytes(stmt + "\n", "ascii"))
- self.p.stdin.flush()
+ self.p_write(stmt + "\n", True)
self.smt2cache[-1].append(stmt)
else:
- self.p.stdin.write(bytes(stmt + "\n", "ascii"))
- self.p.stdin.flush()
+ self.p_write(stmt + "\n", True)
def info(self, stmt):
if not stmt.startswith("; yosys-smt2-"):
@@ -314,6 +455,11 @@ class SmtIo:
if self.logic is None:
self.logic_dt = True
+ if fields[1] == "yosys-smt2-forall":
+ if self.logic is None:
+ self.logic_qf = False
+ self.forall = True
+
if fields[1] == "yosys-smt2-module":
self.curmod = fields[2]
self.modinfo[self.curmod] = SmtModInfo()
@@ -337,12 +483,19 @@ class SmtIo:
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
if fields[1] == "yosys-smt2-memory":
- self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]))
+ self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]), fields[7] == "async")
if fields[1] == "yosys-smt2-wire":
self.modinfo[self.curmod].wires.add(fields[2])
self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3])
+ if fields[1] == "yosys-smt2-clock":
+ for edge in fields[3:]:
+ if fields[2] not in self.modinfo[self.curmod].clocks:
+ self.modinfo[self.curmod].clocks[fields[2]] = edge
+ elif self.modinfo[self.curmod].clocks[fields[2]] != edge:
+ self.modinfo[self.curmod].clocks[fields[2]] = "event"
+
if fields[1] == "yosys-smt2-assert":
self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3]
@@ -350,10 +503,20 @@ class SmtIo:
self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
if fields[1] == "yosys-smt2-anyconst":
- self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[3], None if len(fields) <= 4 else fields[4])
+ self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
if fields[1] == "yosys-smt2-anyseq":
- self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[3], None if len(fields) <= 4 else fields[4])
+ self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-allconst":
+ self.modinfo[self.curmod].allconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
+
+ if fields[1] == "yosys-smt2-allseq":
+ self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5])
+ self.modinfo[self.curmod].asize[fields[2]] = int(fields[3])
def hiernets(self, top, regs_only=False):
def hiernets_worker(nets, mod, cursor):
@@ -370,7 +533,8 @@ class SmtIo:
def hieranyconsts(self, top):
def worker(results, mod, cursor):
for name, value in sorted(self.modinfo[mod].anyconsts.items()):
- results.append((cursor, name, value[0], value[1]))
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
worker(results, celltype, cursor + [cellname])
@@ -381,7 +545,32 @@ class SmtIo:
def hieranyseqs(self, top):
def worker(results, mod, cursor):
for name, value in sorted(self.modinfo[mod].anyseqs.items()):
- results.append((cursor, name, value[0], value[1]))
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
+ for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
+ worker(results, celltype, cursor + [cellname])
+
+ results = list()
+ worker(results, top, [])
+ return results
+
+ def hierallconsts(self, top):
+ def worker(results, mod, cursor):
+ for name, value in sorted(self.modinfo[mod].allconsts.items()):
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
+ for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
+ worker(results, celltype, cursor + [cellname])
+
+ results = list()
+ worker(results, top, [])
+ return results
+
+ def hierallseqs(self, top):
+ def worker(results, mod, cursor):
+ for name, value in sorted(self.modinfo[mod].allseqs.items()):
+ width = self.modinfo[mod].asize[name]
+ results.append((cursor, name, value[0], value[1], width))
for cellname, celltype in sorted(self.modinfo[mod].cells.items()):
worker(results, celltype, cursor + [cellname])
@@ -408,7 +597,7 @@ class SmtIo:
if self.solver == "dummy":
line = self.dummy_fd.readline().strip()
else:
- line = self.p.stdout.readline().decode("ascii").strip()
+ line = self.p_read().strip()
if self.dummy_file is not None:
self.dummy_fd.write(line + "\n")
@@ -421,12 +610,14 @@ class SmtIo:
if count_brackets == 0:
break
if self.solver != "dummy" and self.p.poll():
- print("SMT Solver terminated unexpectedly: %s" % "".join(stmt))
+ print("%s Solver terminated unexpectedly: %s" % (self.timestamp(), "".join(stmt)), flush=True)
sys.exit(1)
stmt = "".join(stmt)
if stmt.startswith("(error"):
- print("SMT Solver Error: %s" % stmt, file=sys.stderr)
+ print("%s Solver Error: %s" % (self.timestamp(), stmt), flush=True)
+ if self.solver != "dummy":
+ self.p_close()
sys.exit(1)
return stmt
@@ -441,15 +632,13 @@ class SmtIo:
if self.solver != "dummy":
if self.noincr:
if self.p is not None:
- self.p.stdin.close()
- self.p = None
- self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ self.p_close()
+ self.p_open()
for cache_ctx in self.smt2cache:
for cache_stmt in cache_ctx:
- self.p.stdin.write(bytes(cache_stmt + "\n", "ascii"))
+ self.p_write(cache_stmt + "\n", False)
- self.p.stdin.write(bytes("(check-sat)\n", "ascii"))
- self.p.stdin.flush()
+ self.p_write("(check-sat)\n", True)
if self.timeinfo:
i = 0
@@ -457,7 +646,7 @@ class SmtIo:
count = 0
num_bs = 0
- while select([self.p.stdout], [], [], 0.1) == ([], [], []):
+ while self.p_poll():
count += 1
if count < 25:
@@ -486,11 +675,43 @@ class SmtIo:
print("\b \b" * num_bs, end="", file=sys.stderr)
sys.stderr.flush()
+ else:
+ count = 0
+ while self.p_poll(60):
+ count += 1
+ msg = None
+
+ if count == 1:
+ msg = "1 minute"
+
+ elif count in [5, 10, 15, 30]:
+ msg = "%d minutes" % count
+
+ elif count == 60:
+ msg = "1 hour"
+
+ elif count % 60 == 0:
+ msg = "%d hours" % (count // 60)
+
+ if msg is not None:
+ print("%s waiting for solver (%s)" % (self.timestamp(), msg), flush=True)
+
result = self.read()
+
if self.debug_file:
print("(set-info :status %s)" % result, file=self.debug_file)
print("(check-sat)", file=self.debug_file)
self.debug_file.flush()
+
+ if result not in ["sat", "unsat"]:
+ if result == "":
+ print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
+ else:
+ print("%s Unexpected response from solver: %s" % (self.timestamp(), result), flush=True)
+ if self.solver != "dummy":
+ self.p_close()
+ sys.exit(1)
+
return result
def parse(self, stmt):
@@ -545,6 +766,9 @@ class SmtIo:
return h
def bv2bin(self, v):
+ if type(v) is list and len(v) == 3 and v[0] == "_" and v[1].startswith("bv"):
+ x, n = int(v[1][2:]), int(v[2])
+ return "".join("1" if (x & (1 << i)) else "0" for i in range(n-1, -1, -1))
if v == "true": return "1"
if v == "false": return "0"
if v.startswith("#b"):
@@ -568,7 +792,7 @@ class SmtIo:
def get_path(self, mod, path):
assert mod in self.modinfo
- path = path.split(".")
+ path = path.replace("\\", "/").split(".")
for i in range(len(path)-1):
first = ".".join(path[0:i+1])
@@ -613,6 +837,17 @@ class SmtIo:
assert net_path[-1] in self.modinfo[mod].wsize
return self.modinfo[mod].wsize[net_path[-1]]
+ def net_clock(self, mod, net_path):
+ for i in range(len(net_path)-1):
+ assert mod in self.modinfo
+ assert net_path[i] in self.modinfo[mod].cells
+ mod = self.modinfo[mod].cells[net_path[i]]
+
+ assert mod in self.modinfo
+ if net_path[-1] not in self.modinfo[mod].clocks:
+ return None
+ return self.modinfo[mod].clocks[net_path[-1]]
+
def net_exists(self, mod, net_path):
for i in range(len(net_path)-1):
if mod not in self.modinfo: return False
@@ -672,6 +907,7 @@ class SmtIo:
def wait(self):
if self.p is not None:
self.p.wait()
+ self.p_close()
class SmtOpts:
@@ -763,6 +999,7 @@ class MkVcd:
self.f = f
self.t = -1
self.nets = dict()
+ self.clocks = dict()
def add_net(self, path, width):
path = tuple(path)
@@ -770,34 +1007,86 @@ class MkVcd:
key = "n%d" % len(self.nets)
self.nets[path] = (key, width)
+ def add_clock(self, path, edge):
+ path = tuple(path)
+ assert self.t == -1
+ key = "n%d" % len(self.nets)
+ self.nets[path] = (key, 1)
+ self.clocks[path] = (key, edge)
+
def set_net(self, path, bits):
path = tuple(path)
assert self.t >= 0
assert path in self.nets
- print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
+ if path not in self.clocks:
+ print("b%s %s" % (bits, self.nets[path][0]), file=self.f)
+
+ def escape_name(self, name):
+ name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name)
+ if re.match("[\[\]]", name) and name[0] != "\\":
+ name = "\\" + name
+ return name
def set_time(self, t):
assert t >= self.t
if t != self.t:
if self.t == -1:
+ print("$version Generated by Yosys-SMTBMC $end", file=self.f)
+ print("$timescale 1ns $end", file=self.f)
print("$var integer 32 t smt_step $end", file=self.f)
print("$var event 1 ! smt_clock $end", file=self.f)
+
+ def vcdescape(n):
+ if n.startswith("$") or ":" in n:
+ return "\\" + n
+ return n
+
scope = []
for path in sorted(self.nets):
- while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]):
+ key, width = self.nets[path]
+
+ uipath = list(path)
+ if "." in uipath[-1] and not uipath[-1].startswith("$"):
+ uipath = uipath[0:-1] + uipath[-1].split(".")
+ for i in range(len(uipath)):
+ uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i])
+
+ while uipath[:len(scope)] != scope:
print("$upscope $end", file=self.f)
scope = scope[:-1]
- while len(scope)+1 < len(path):
- print("$scope module %s $end" % path[len(scope)], file=self.f)
- scope.append(path[len(scope)-1])
- key, width = self.nets[path]
- print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f)
+
+ while uipath[:-1] != scope:
+ scopename = uipath[len(scope)]
+ print("$scope module %s $end" % vcdescape(scopename), file=self.f)
+ scope.append(uipath[len(scope)])
+
+ if path in self.clocks and self.clocks[path][1] == "event":
+ print("$var event 1 %s %s $end" % (key, vcdescape(uipath[-1])), file=self.f)
+ else:
+ print("$var wire %d %s %s $end" % (width, key, vcdescape(uipath[-1])), file=self.f)
+
for i in range(len(scope)):
print("$upscope $end", file=self.f)
+
print("$enddefinitions $end", file=self.f)
+
self.t = t
assert self.t >= 0
+
+ if self.t > 0:
+ print("#%d" % (10 * self.t - 5), file=self.f)
+ for path in sorted(self.clocks.keys()):
+ if self.clocks[path][1] == "posedge":
+ print("b0 %s" % self.nets[path][0], file=self.f)
+ elif self.clocks[path][1] == "negedge":
+ print("b1 %s" % self.nets[path][0], file=self.f)
+
print("#%d" % (10 * self.t), file=self.f)
print("1!", file=self.f)
print("b%s t" % format(self.t, "032b"), file=self.f)
+ for path in sorted(self.clocks.keys()):
+ if self.clocks[path][1] == "negedge":
+ print("b0 %s" % self.nets[path][0], file=self.f)
+ else:
+ print("b1 %s" % self.nets[path][0], file=self.f)
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index 768969e6b..f755307bf 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -42,7 +42,7 @@ struct SmvWorker
pool<Wire*> partial_assignment_wires;
dict<SigBit, std::pair<const char*, int>> partial_assignment_bits;
- vector<string> assignments, invarspecs;
+ vector<string> inputvars, vars, definitions, assignments, invarspecs;
const char *cid()
{
@@ -61,7 +61,7 @@ struct SmvWorker
{
string name = stringf("_%s", id.c_str());
- if (name.substr(0, 2) == "_\\")
+ if (name.compare(0, 2, "_\\") == 0)
name = "_" + name.substr(2);
for (auto &c : name) {
@@ -195,7 +195,7 @@ struct SmvWorker
return rvalue(sig);
const char *temp_id = cid();
- f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
+// f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig));
int offset = 0;
for (auto bit : sig) {
@@ -210,14 +210,14 @@ struct SmvWorker
void run()
{
f << stringf("MODULE %s\n", cid(module->name));
- f << stringf(" VAR\n");
for (auto wire : module->wires())
{
if (SigSpec(wire) != sigmap(wire))
partial_assignment_wires.insert(wire);
- f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire));
+ if (wire->port_input)
+ inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, log_id(wire)));
if (wire->attributes.count("\\init"))
assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init"))));
@@ -275,8 +275,8 @@ struct SmvWorker
const char *b_shr = rvalue_u(sig_b);
const char *b_shl = cid();
- f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
- assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
+// f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b));
+ definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b)));
string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y);
string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y);
@@ -303,7 +303,7 @@ struct SmvWorker
GetSize(sig_b)-shift_b_width, width_y, expr.c_str());
}
- assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
+ definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
continue;
}
@@ -319,12 +319,12 @@ struct SmvWorker
if (cell->getParam("\\A_SIGNED").as_bool())
{
- assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")),
op.c_str(), rvalue_s(cell->getPort("\\A"), width)));
}
else
{
- assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")),
op.c_str(), rvalue_u(cell->getPort("\\A"), width)));
}
@@ -346,12 +346,12 @@ struct SmvWorker
if (cell->getParam("\\A_SIGNED").as_bool())
{
- assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")),
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width)));
}
else
{
- assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width)));
}
@@ -370,12 +370,12 @@ struct SmvWorker
if (cell->getParam("\\A_SIGNED").as_bool())
{
- assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y));
}
else
{
- assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")),
rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y));
}
@@ -407,7 +407,7 @@ struct SmvWorker
expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width);
}
- assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")),
expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y"))));
continue;
@@ -425,7 +425,7 @@ struct SmvWorker
if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a);
- assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
continue;
}
@@ -444,7 +444,7 @@ struct SmvWorker
if (cell->type == "$reduce_xnor")
expr = "!(" + expr + ")";
- assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y));
continue;
}
@@ -462,7 +462,7 @@ struct SmvWorker
if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b;
if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b;
- assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y));
continue;
}
@@ -474,7 +474,7 @@ struct SmvWorker
string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a);
const char *expr_y = lvalue(cell->getPort("\\Y"));
- assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
+ definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y));
continue;
}
@@ -490,12 +490,13 @@ struct SmvWorker
expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width)));
expr += rvalue(sig_a);
- assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
+ definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str()));
continue;
}
if (cell->type == "$dff")
{
+ vars.push_back(stringf("%s : unsigned word[%d]; -- %s", lvalue(cell->getPort("\\Q")), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D"))));
continue;
}
@@ -503,7 +504,7 @@ struct SmvWorker
if (cell->type.in("$_BUF_", "$_NOT_"))
{
string op = cell->type == "$_NOT_" ? "!" : "";
- assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
+ definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A"))));
continue;
}
@@ -517,49 +518,56 @@ struct SmvWorker
if (cell->type.in("$_XNOR_")) op = "xnor";
if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
- assignments.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
else
if (cell->type.in("$_NAND_", "$_NOR_"))
- assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
else
- assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
continue;
}
if (cell->type == "$_MUX_")
{
- assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")),
+ rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A"))));
+ continue;
+ }
+
+ if (cell->type == "$_NMUX_")
+ {
+ definitions.push_back(stringf("%s := !(bool(%s) ? %s : %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A"))));
continue;
}
if (cell->type == "$_AOI3_")
{
- assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
continue;
}
if (cell->type == "$_OAI3_")
{
- assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C"))));
continue;
}
if (cell->type == "$_AOI4_")
{
- assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
continue;
}
if (cell->type == "$_OAI4_")
{
- assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
+ definitions.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")),
rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D"))));
continue;
}
@@ -567,13 +575,13 @@ struct SmvWorker
if (cell->type[0] == '$')
log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell));
- f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
+// f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type));
for (auto &conn : cell->connections())
if (cell->output(conn.first))
- assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
+ definitions.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first)));
else
- assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
+ definitions.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second)));
}
for (Wire *wire : partial_assignment_wires)
@@ -657,7 +665,25 @@ struct SmvWorker
}
}
- assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
+ definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str()));
+ }
+
+ if (!inputvars.empty()) {
+ f << stringf(" IVAR\n");
+ for (const string &line : inputvars)
+ f << stringf(" %s\n", line.c_str());
+ }
+
+ if (!vars.empty()) {
+ f << stringf(" VAR\n");
+ for (const string &line : vars)
+ f << stringf(" %s\n", line.c_str());
+ }
+
+ if (!definitions.empty()) {
+ f << stringf(" DEFINE\n");
+ for (const string &line : definitions)
+ f << stringf(" %s\n", line.c_str());
}
if (!assignments.empty()) {
@@ -675,7 +701,7 @@ struct SmvWorker
struct SmvBackend : public Backend {
SmvBackend() : Backend("smv", "write design to SMV file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -693,7 +719,7 @@ struct SmvBackend : public Backend {
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::ifstream template_f;
bool verbose = false;
@@ -720,7 +746,7 @@ struct SmvBackend : public Backend {
pool<Module*> modules;
for (auto module : design->modules())
- if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn())
+ if (!module->get_blackbox_attribute() && !module->has_memories_warn() && !module->has_processes_warn())
modules.insert(module);
if (template_f.is_open())
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
index 4101cbf98..6738a4bbd 100644
--- a/backends/spice/spice.cc
+++ b/backends/spice/spice.cc
@@ -132,7 +132,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De
struct SpiceBackend : public Backend {
SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -161,7 +161,7 @@ struct SpiceBackend : public Backend {
log(" set the specified module as design top module\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
std::string top_module_name;
RTLIL::Module *top_module = NULL;
@@ -212,7 +212,7 @@ struct SpiceBackend : public Backend {
for (auto module_it : design->modules_)
{
RTLIL::Module *module = module_it.second;
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
if (module->processes.size() != 0)
diff --git a/backends/table/table.cc b/backends/table/table.cc
index 27b7edfff..796f18059 100644
--- a/backends/table/table.cc
+++ b/backends/table/table.cc
@@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN
struct TableBackend : public Backend {
TableBackend() : Backend("table", "write design as connectivity table") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -48,7 +48,7 @@ struct TableBackend : public Backend {
log("module inputs and outputs are output using cell type and port '-' and with\n");
log("'pi' (primary input) or 'po' (primary output) or 'pio' as direction.\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing TABLE backend.\n");
@@ -67,7 +67,7 @@ struct TableBackend : public Backend {
for (auto module : design->modules())
{
- if (module->get_bool_attribute("\\blackbox"))
+ if (module->get_blackbox_attribute())
continue;
SigMap sigmap(module);
@@ -109,7 +109,7 @@ struct TableBackend : public Backend {
else if (cell->output(conn.first))
*f << "out" << "\t";
else
- *f << "unkown" << "\t";
+ *f << "unknown" << "\t";
*f << log_signal(sigmap(conn.second)) << "\n";
}
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index bdf705056..54d0f6148 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -33,11 +33,11 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal;
-int auto_name_counter, auto_name_offset, auto_name_digits;
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit;
+int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter;
std::map<RTLIL::IdString, int> auto_name_map;
std::set<RTLIL::IdString> reg_wires, reg_ct;
-std::string auto_prefix;
+std::string auto_prefix, extmem_prefix;
RTLIL::Module *active_module;
dict<RTLIL::SigBit, RTLIL::State> active_initdata;
@@ -126,6 +126,33 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true)
break;
}
+ const pool<string> keywords = {
+ // IEEE 1800-2017 Annex B
+ "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before",
+ "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle",
+ "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint",
+ "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker",
+ "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage",
+ "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually",
+ "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function",
+ "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies",
+ "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface",
+ "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint",
+ "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor",
+ "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive",
+ "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent",
+ "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat",
+ "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until",
+ "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify",
+ "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on",
+ "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1",
+ "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with",
+ "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while",
+ "wildcard", "wire", "with", "within", "wor", "xnor", "xor",
+ };
+ if (keywords.count(str))
+ do_escape = true;
+
if (do_escape)
return "\\" + std::string(str) + " ";
return std::string(str);
@@ -156,10 +183,16 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string &reg_name)
return true;
}
-void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false)
+void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false)
{
+ bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
if (width < 0)
width = data.bits.size() - offset;
+ if (width == 0) {
+ // See IEEE 1364-2005 Clause 5.1.14.
+ f << "{0{1'b0}}";
+ return;
+ }
if (nostr)
goto dump_hex;
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
@@ -167,9 +200,9 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
int32_t val = 0;
for (int i = offset+width-1; i >= offset; i--) {
log_assert(i < (int)data.bits.size());
- if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1)
+ if (data.bits[i] != State::S0 && data.bits[i] != State::S1)
goto dump_hex;
- if (data.bits[i] == RTLIL::S1)
+ if (data.bits[i] == State::S1)
val |= 1 << (i - offset);
}
if (decimal)
@@ -186,11 +219,11 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
for (int i = offset; i < offset+width; i++) {
log_assert(i < (int)data.bits.size());
switch (data.bits[i]) {
- case RTLIL::S0: bin_digits.push_back('0'); break;
- case RTLIL::S1: bin_digits.push_back('1'); break;
+ case State::S0: bin_digits.push_back('0'); break;
+ case State::S1: bin_digits.push_back('1'); break;
case RTLIL::Sx: bin_digits.push_back('x'); break;
case RTLIL::Sz: bin_digits.push_back('z'); break;
- case RTLIL::Sa: bin_digits.push_back('z'); break;
+ case RTLIL::Sa: bin_digits.push_back('?'); break;
case RTLIL::Sm: log_error("Found marker state in final netlist.");
}
}
@@ -219,6 +252,12 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
hex_digits.push_back('z');
continue;
}
+ if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') {
+ if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?')
+ goto dump_bin;
+ hex_digits.push_back('?');
+ continue;
+ }
int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0');
hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10);
}
@@ -234,17 +273,18 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
for (int i = offset+width-1; i >= offset; i--) {
log_assert(i < (int)data.bits.size());
switch (data.bits[i]) {
- case RTLIL::S0: f << stringf("0"); break;
- case RTLIL::S1: f << stringf("1"); break;
+ case State::S0: f << stringf("0"); break;
+ case State::S1: f << stringf("1"); break;
case RTLIL::Sx: f << stringf("x"); break;
case RTLIL::Sz: f << stringf("z"); break;
- case RTLIL::Sa: f << stringf("z"); break;
+ case RTLIL::Sa: f << stringf("?"); break;
case RTLIL::Sm: log_error("Found marker state in final netlist.");
}
}
}
} else {
- f << stringf("\"");
+ if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
+ f << stringf("\"");
std::string str = data.decode_string();
for (size_t i = 0; i < str.size(); i++) {
if (str[i] == '\n')
@@ -262,7 +302,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
else
f << str[i];
}
- f << stringf("\"");
+ if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0)
+ f << stringf("\"");
}
}
@@ -313,6 +354,10 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
{
+ if (GetSize(sig) == 0) {
+ f << "\"\"";
+ return;
+ }
if (sig.is_chunk()) {
dump_sigchunk(f, sig.as_chunk());
} else {
@@ -326,26 +371,29 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
}
}
-void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false)
+void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false)
{
if (noattr)
return;
+ if (attr2comment)
+ as_comment = true;
for (auto it = attributes.begin(); it != attributes.end(); ++it) {
- f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str());
+ if (it->first == "\\init" && regattr) continue;
+ f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str());
f << stringf(" = ");
- if (modattr && (it->second == Const(0, 1) || it->second == Const(0)))
+ if (modattr && (it->second == State::S0 || it->second == Const(0)))
f << stringf(" 0 ");
- else if (modattr && (it->second == Const(1, 1) || it->second == Const(1)))
+ else if (modattr && (it->second == State::S1 || it->second == Const(1)))
f << stringf(" 1 ");
else
- dump_const(f, it->second, -1, 0, false, false, attr2comment);
- f << stringf(" %s%c", attr2comment ? "*/" : "*)", term);
+ dump_const(f, it->second, -1, 0, false, as_comment);
+ f << stringf(" %s%c", as_comment ? "*/" : "*)", term);
}
}
void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
{
- dump_attributes(f, indent, wire->attributes);
+ dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name));
#if 0
if (wire->port_input && !wire->port_output)
f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : "");
@@ -388,7 +436,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
{
dump_attributes(f, indent, memory->attributes);
- f << stringf("%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1);
+ f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset);
}
void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
@@ -511,6 +559,20 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type == "$_NMUX_") {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = !(");
+ dump_cell_expr_port(f, cell, "S", false);
+ f << stringf(" ? ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", false);
+ f << stringf(" : ");
+ dump_cell_expr_port(f, cell, "A", false);
+ f << stringf(");\n");
+ return true;
+ }
+
if (cell->type.in("$_AOI3_", "$_OAI3_")) {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort("\\Y"));
@@ -543,7 +605,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type.substr(0, 6) == "$_DFF_")
+ if (cell->type.begins_with("$_DFF_"))
{
std::string reg_name = cellname(cell);
bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
@@ -584,7 +646,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type.substr(0, 8) == "$_DFFSR_")
+ if (cell->type.begins_with("$_DFFSR_"))
{
char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
@@ -678,13 +740,45 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
#undef HANDLE_UNIOP
#undef HANDLE_BINOP
- if (cell->type == "$shiftx")
+ if (cell->type == "$shift")
{
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort("\\Y"));
f << stringf(" = ");
+ if (cell->getParam("\\B_SIGNED").as_bool())
+ {
+ f << stringf("$signed(");
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(")");
+ f << stringf(" < 0 ? ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" << - ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ f << stringf(" : ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" >> ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ }
+ else
+ {
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" >> ");
+ dump_sigspec(f, cell->getPort("\\B"));
+ }
+ f << stringf(";\n");
+ return true;
+ }
+
+ if (cell->type == "$shiftx")
+ {
+ std::string temp_id = next_auto_id();
+ f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str());
dump_sigspec(f, cell->getPort("\\A"));
- f << stringf("[");
+ f << stringf(";\n");
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = %s[", temp_id.c_str());
if (cell->getParam("\\B_SIGNED").as_bool())
f << stringf("$signed(");
dump_sigspec(f, cell->getPort("\\B"));
@@ -710,7 +804,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type == "$pmux" || cell->type == "$pmux_safe")
+ if (cell->type == "$pmux")
{
int width = cell->parameters["\\WIDTH"].as_int();
int s_width = cell->getPort("\\S").size();
@@ -722,18 +816,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1);
dump_attributes(f, indent + " ", cell->attributes);
- if (cell->type != "$pmux_safe" && !noattr)
+ if (!noattr)
f << stringf("%s" " (* parallel_case *)\n", indent.c_str());
f << stringf("%s" " casez (s)", indent.c_str());
- if (cell->type != "$pmux_safe")
- f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
+ f << stringf(noattr ? " // synopsys parallel_case\n" : "\n");
for (int i = 0; i < s_width; i++)
{
f << stringf("%s" " %d'b", indent.c_str(), s_width);
for (int j = s_width-1; j >= 0; j--)
- f << stringf("%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?');
+ f << stringf("%c", j == i ? '1' : '?');
f << stringf(":\n");
f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width);
@@ -757,6 +850,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type == "$tribuf")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_sigspec(f, cell->getPort("\\EN"));
+ f << stringf(" ? ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int());
+ return true;
+ }
+
if (cell->type == "$slice")
{
f << stringf("%s" "assign ", indent.c_str());
@@ -779,6 +884,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type == "$lut")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_const(f, cell->parameters.at("\\LUT"));
+ f << stringf(" >> ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(";\n");
+ return true;
+ }
+
if (cell->type == "$dffsr")
{
SigSpec sig_clk = cell->getPort("\\CLK");
@@ -832,7 +950,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe")
+ if (cell->type.in("$dff", "$adff", "$dffe"))
{
RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
bool pol_clk, pol_arst = false, pol_en = false;
@@ -897,12 +1015,49 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type == "$dlatch")
+ {
+ RTLIL::SigSpec sig_en;
+ bool pol_en = false;
+
+ sig_en = cell->getPort("\\EN");
+ pol_en = cell->parameters["\\EN_POLARITY"].as_bool();
+
+ std::string reg_name = cellname(cell);
+ bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name);
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
+ dump_reg_init(f, cell->getPort("\\Q"));
+ f << ";\n";
+ }
+
+ f << stringf("%s" "always @*\n", indent.c_str());
+
+ f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!");
+ dump_sigspec(f, sig_en);
+ f << stringf(")\n");
+
+ f << stringf("%s" " %s = ", indent.c_str(), reg_name.c_str());
+ dump_cell_expr_port(f, cell, "D", false);
+ f << stringf(";\n");
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Q"));
+ f << stringf(" = %s;\n", reg_name.c_str());
+ }
+
+ return true;
+ }
+
if (cell->type == "$mem")
{
RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string();
std::string mem_id = id(cell->parameters["\\MEMID"].decode_string());
int abits = cell->parameters["\\ABITS"].as_int();
int size = cell->parameters["\\SIZE"].as_int();
+ int offset = cell->parameters["\\OFFSET"].as_int();
int width = cell->parameters["\\WIDTH"].as_int();
bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef());
@@ -911,17 +1066,67 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
// initial begin
// memid[0] = ...
// end
- f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0);
+ f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset);
if (use_init)
{
- f << stringf("%s" "initial begin\n", indent.c_str());
- for (int i=0; i<size; i++)
+ if (extmem)
+ {
+ std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++);
+
+ std::string extmem_filename_esc;
+ for (auto c : extmem_filename)
+ {
+ if (c == '\n')
+ extmem_filename_esc += "\\n";
+ else if (c == '\t')
+ extmem_filename_esc += "\\t";
+ else if (c < 32)
+ extmem_filename_esc += stringf("\\%03o", c);
+ else if (c == '"')
+ extmem_filename_esc += "\\\"";
+ else if (c == '\\')
+ extmem_filename_esc += "\\\\";
+ else
+ extmem_filename_esc += c;
+ }
+ f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent.c_str(), extmem_filename_esc.c_str(), mem_id.c_str());
+
+ std::ofstream extmem_f(extmem_filename, std::ofstream::trunc);
+ if (extmem_f.fail())
+ log_error("Can't open file `%s' for writing: %s\n", extmem_filename.c_str(), strerror(errno));
+ else
+ {
+ for (int i=0; i<size; i++)
+ {
+ RTLIL::Const element = cell->parameters["\\INIT"].extract(i*width, width);
+ for (int j=0; j<element.size(); j++)
+ {
+ switch (element[element.size()-j-1])
+ {
+ case State::S0: extmem_f << '0'; break;
+ case State::S1: extmem_f << '1'; break;
+ case State::Sx: extmem_f << 'x'; break;
+ case State::Sz: extmem_f << 'z'; break;
+ case State::Sa: extmem_f << '_'; break;
+ case State::Sm: log_error("Found marker state in final netlist.");
+ }
+ }
+ extmem_f << '\n';
+ }
+ }
+
+ }
+ else
{
- f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i);
- dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
- f << stringf(";\n");
+ f << stringf("%s" "initial begin\n", indent.c_str());
+ for (int i=0; i<size; i++)
+ {
+ f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i);
+ dump_const(f, cell->parameters["\\INIT"].extract(i*width, width));
+ f << stringf(";\n");
+ }
+ f << stringf("%s" "end\n", indent.c_str());
}
- f << stringf("%s" "end\n", indent.c_str());
}
// create a map : "edge clk" -> expressions within that clock domain
@@ -944,43 +1149,46 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool();
rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool();
rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool();
+ if (use_rd_clk)
{
- std::ostringstream os;
- dump_sigspec(os, sig_rd_clk);
- clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
- if( clk_to_lof_body.count(clk_domain_str) == 0 )
- clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
- }
- if (use_rd_clk && !rd_transparent)
- {
- // for clocked read ports make something like:
- // reg [..] temp_id;
- // always @(posedge clk)
- // if (rd_en) temp_id <= array_reg[r_addr];
- // assign r_data = temp_id;
- std::string temp_id = next_auto_id();
- lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
{
std::ostringstream os;
- if (sig_rd_en != RTLIL::SigBit(true))
+ dump_sigspec(os, sig_rd_clk);
+ clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str());
+ if( clk_to_lof_body.count(clk_domain_str) == 0 )
+ clk_to_lof_body[clk_domain_str] = std::vector<std::string>();
+ }
+ if (!rd_transparent)
+ {
+ // for clocked read ports make something like:
+ // reg [..] temp_id;
+ // always @(posedge clk)
+ // if (rd_en) temp_id <= array_reg[r_addr];
+ // assign r_data = temp_id;
+ std::string temp_id = next_auto_id();
+ lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) );
{
- os << stringf("if (");
- dump_sigspec(os, sig_rd_en);
- os << stringf(") ");
+ std::ostringstream os;
+ if (sig_rd_en != RTLIL::SigBit(true))
+ {
+ os << stringf("if (");
+ dump_sigspec(os, sig_rd_en);
+ os << stringf(") ");
+ }
+ os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
+ dump_sigspec(os, sig_rd_addr);
+ os << stringf("];\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ }
+ {
+ std::ostringstream os;
+ dump_sigspec(os, sig_rd_data);
+ std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
+ clk_to_lof_body[""].push_back(line);
}
- os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
- dump_sigspec(os, sig_rd_addr);
- os << stringf("];\n");
- clk_to_lof_body[clk_domain_str].push_back(os.str());
}
+ else
{
- std::ostringstream os;
- dump_sigspec(os, sig_rd_data);
- std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str());
- clk_to_lof_body[""].push_back(line);
- }
- } else {
- if (rd_transparent) {
// for rd-transparent read-ports make something like:
// reg [..] temp_id;
// always @(posedge clk)
@@ -1000,15 +1208,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str());
clk_to_lof_body[""].push_back(line);
}
- } else {
- // for non-clocked read-ports make something like:
- // assign r_data = array_reg[r_addr];
- std::ostringstream os, os2;
- dump_sigspec(os, sig_rd_data);
- dump_sigspec(os2, sig_rd_addr);
- std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
- clk_to_lof_body[""].push_back(line);
}
+ } else {
+ // for non-clocked read-ports make something like:
+ // assign r_data = array_reg[r_addr];
+ std::ostringstream os, os2;
+ dump_sigspec(os, sig_rd_data);
+ dump_sigspec(os2, sig_rd_addr);
+ std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str());
+ clk_to_lof_body[""].push_back(line);
}
}
@@ -1110,6 +1318,118 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
+ if (cell->type.in("$assert", "$assume", "$cover"))
+ {
+ f << stringf("%s" "always @* if (", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\EN"));
+ f << stringf(") %s(", cell->type.c_str()+1);
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf(");\n");
+ return true;
+ }
+
+ if (cell->type.in("$specify2", "$specify3"))
+ {
+ f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
+
+ SigSpec en = cell->getPort("\\EN");
+ if (en != State::S1) {
+ f << stringf("if (");
+ dump_sigspec(f, cell->getPort("\\EN"));
+ f << stringf(") ");
+ }
+
+ f << "(";
+ if (cell->type == "$specify3" && cell->getParam("\\EDGE_EN").as_bool())
+ f << (cell->getParam("\\EDGE_POL").as_bool() ? "posedge ": "negedge ");
+
+ dump_sigspec(f, cell->getPort("\\SRC"));
+
+ f << " ";
+ if (cell->getParam("\\SRC_DST_PEN").as_bool())
+ f << (cell->getParam("\\SRC_DST_POL").as_bool() ? "+": "-");
+ f << (cell->getParam("\\FULL").as_bool() ? "*> ": "=> ");
+
+ if (cell->type == "$specify3") {
+ f << "(";
+ dump_sigspec(f, cell->getPort("\\DST"));
+ f << " ";
+ if (cell->getParam("\\DAT_DST_PEN").as_bool())
+ f << (cell->getParam("\\DAT_DST_POL").as_bool() ? "+": "-");
+ f << ": ";
+ dump_sigspec(f, cell->getPort("\\DAT"));
+ f << ")";
+ } else {
+ dump_sigspec(f, cell->getPort("\\DST"));
+ }
+
+ bool bak_decimal = decimal;
+ decimal = 1;
+
+ f << ") = (";
+ dump_const(f, cell->getParam("\\T_RISE_MIN"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_RISE_TYP"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_RISE_MAX"));
+ f << ", ";
+ dump_const(f, cell->getParam("\\T_FALL_MIN"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_FALL_TYP"));
+ f << ":";
+ dump_const(f, cell->getParam("\\T_FALL_MAX"));
+ f << ");\n";
+
+ decimal = bak_decimal;
+
+ f << stringf("%s" "endspecify\n", indent.c_str());
+ return true;
+ }
+
+ if (cell->type == "$specrule")
+ {
+ f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str());
+
+ string spec_type = cell->getParam("\\TYPE").decode_string();
+ f << stringf("%s(", spec_type.c_str());
+
+ if (cell->getParam("\\SRC_PEN").as_bool())
+ f << (cell->getParam("\\SRC_POL").as_bool() ? "posedge ": "negedge ");
+ dump_sigspec(f, cell->getPort("\\SRC"));
+
+ if (cell->getPort("\\SRC_EN") != State::S1) {
+ f << " &&& ";
+ dump_sigspec(f, cell->getPort("\\SRC_EN"));
+ }
+
+ f << ", ";
+ if (cell->getParam("\\DST_PEN").as_bool())
+ f << (cell->getParam("\\DST_POL").as_bool() ? "posedge ": "negedge ");
+ dump_sigspec(f, cell->getPort("\\DST"));
+
+ if (cell->getPort("\\DST_EN") != State::S1) {
+ f << " &&& ";
+ dump_sigspec(f, cell->getPort("\\DST_EN"));
+ }
+
+ bool bak_decimal = decimal;
+ decimal = 1;
+
+ f << ", ";
+ dump_const(f, cell->getParam("\\T_LIMIT"));
+
+ if (spec_type == "$setuphold" || spec_type == "$recrem" || spec_type == "$fullskew") {
+ f << ", ";
+ dump_const(f, cell->getParam("\\T_LIMIT2"));
+ }
+
+ f << ");\n";
+ decimal = bak_decimal;
+
+ f << stringf("%s" "endspecify\n", indent.c_str());
+ return true;
+ }
+
// FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
// FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
@@ -1132,8 +1452,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (it != cell->parameters.begin())
f << stringf(",");
f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str());
- bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
- dump_const(f, it->second, -1, 0, false, is_signed);
+ dump_const(f, it->second);
f << stringf(")");
}
f << stringf("\n%s" ")", indent.c_str());
@@ -1180,12 +1499,20 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (defparam && cell->parameters.size() > 0) {
for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) {
f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str());
- bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0;
- dump_const(f, it->second, -1, 0, false, is_signed);
+ dump_const(f, it->second);
f << stringf(";\n");
}
}
+ if (siminit && reg_ct.count(cell->type) && cell->hasPort("\\Q")) {
+ std::stringstream ss;
+ dump_reg_init(ss, cell->getPort("\\Q"));
+ if (!ss.str().empty()) {
+ f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str());
+ f << ss.str();
+ f << ";\n";
+ }
+ }
}
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
@@ -1238,12 +1565,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw
return;
}
+ dump_attributes(f, indent, sw->attributes);
f << stringf("%s" "casez (", indent.c_str());
dump_sigspec(f, sw->signal);
f << stringf(")\n");
bool got_default = false;
for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) {
+ dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true);
if ((*it)->compare.size() == 0) {
if (got_default)
continue;
@@ -1302,6 +1631,8 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
if (sync->type == RTLIL::STa) {
f << stringf("%s" "always @* begin\n", indent.c_str());
+ } else if (sync->type == RTLIL::STi) {
+ f << stringf("%s" "initial begin\n", indent.c_str());
} else {
f << stringf("%s" "always @(", indent.c_str());
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
@@ -1362,14 +1693,15 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
SigSpec sig = active_sigmap(wire);
Const val = wire->attributes.at("\\init");
for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++)
- active_initdata[sig[i]] = val.bits.at(i);
+ if (val[i] == State::S0 || val[i] == State::S1)
+ active_initdata[sig[i]] = val[i];
}
if (!module->processes.empty())
- log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n"
+ log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n"
"can't always be mapped directly to Verilog always blocks. Unintended\n"
"changes in simulation behavior are possible! Use \"proc\" to convert\n"
- "processes to logic networks and registers.", log_id(module));
+ "processes to logic networks and registers.\n", log_id(module));
f << stringf("\n");
for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
@@ -1405,7 +1737,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
}
}
- dump_attributes(f, indent, module->attributes, '\n', true);
+ dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true);
f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str());
bool keep_running = true;
for (int port_id = 1; keep_running; port_id++) {
@@ -1446,7 +1778,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
struct VerilogBackend : public Backend {
VerilogBackend() : Backend("verilog", "write design to Verilog file") { }
- virtual void help()
+ void help() YS_OVERRIDE
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1472,9 +1804,13 @@ struct VerilogBackend : public Backend {
log(" without this option all internal cells are converted to Verilog\n");
log(" expressions.\n");
log("\n");
+ log(" -siminit\n");
+ log(" add initial statements with hierarchical refs to initialize FFs when\n");
+ log(" in -noexpr mode.\n");
+ log("\n");
log(" -nodec\n");
log(" 32-bit constant values are by default dumped as decimal numbers,\n");
- log(" not bit pattern. This option decativates this feature and instead\n");
+ log(" not bit pattern. This option deactivates this feature and instead\n");
log(" will write out all constants in binary.\n");
log("\n");
log(" -decimal\n");
@@ -1482,17 +1818,25 @@ struct VerilogBackend : public Backend {
log("\n");
log(" -nohex\n");
log(" constant values that are compatible with hex output are usually\n");
- log(" dumped as hex values. This option decativates this feature and\n");
+ log(" dumped as hex values. This option deactivates this feature and\n");
log(" instead will write out all constants in binary.\n");
log("\n");
log(" -nostr\n");
log(" Parameters and attributes that are specified as strings in the\n");
log(" original input will be output as strings by this back-end. This\n");
- log(" decativates this feature and instead will write string constants\n");
+ log(" deactivates this feature and instead will write string constants\n");
log(" as binary numbers.\n");
log("\n");
+ log(" -extmem\n");
+ log(" instead of initializing memories using assignments to individual\n");
+ log(" elements, use the '$readmemh' function to read initialization data\n");
+ log(" from a file. This data is written to a file named by appending\n");
+ log(" a sequential index to the Verilog filename and replacing the extension\n");
+ log(" with '.mem', e.g. 'write_verilog -extmem foo.v' writes 'foo-1.mem',\n");
+ log(" 'foo-2.mem' and so on.\n");
+ log("\n");
log(" -defparam\n");
- log(" Use 'defparam' statements instead of the Verilog-2001 syntax for\n");
+ log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n");
log(" cell parameters.\n");
log("\n");
log(" -blackboxes\n");
@@ -1514,7 +1858,7 @@ struct VerilogBackend : public Backend {
log("this command is called on a design with RTLIL processes.\n");
log("\n");
}
- virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
log_header(design, "Executing Verilog backend.\n");
@@ -1526,17 +1870,23 @@ struct VerilogBackend : public Backend {
nodec = false;
nohex = false;
nostr = false;
+ extmem = false;
defparam = false;
decimal = false;
+ siminit = false;
auto_prefix = "";
bool blackboxes = false;
bool selected = false;
+ auto_name_map.clear();
+ reg_wires.clear();
reg_ct.clear();
reg_ct.insert("$dff");
reg_ct.insert("$adff");
+ reg_ct.insert("$dffe");
+ reg_ct.insert("$dlatch");
reg_ct.insert("$_DFF_N_");
reg_ct.insert("$_DFF_P_");
@@ -1594,6 +1944,11 @@ struct VerilogBackend : public Backend {
nostr = true;
continue;
}
+ if (arg == "-extmem") {
+ extmem = true;
+ extmem_counter = 1;
+ continue;
+ }
if (arg == "-defparam") {
defparam = true;
continue;
@@ -1602,6 +1957,10 @@ struct VerilogBackend : public Backend {
decimal = true;
continue;
}
+ if (arg == "-siminit") {
+ siminit = true;
+ continue;
+ }
if (arg == "-blackboxes") {
blackboxes = true;
continue;
@@ -1617,12 +1976,18 @@ struct VerilogBackend : public Backend {
break;
}
extra_args(f, filename, args, argidx);
+ if (extmem)
+ {
+ if (filename.empty())
+ log_cmd_error("Option -extmem must be used with a filename.\n");
+ extmem_prefix = filename.substr(0, filename.rfind('.'));
+ }
design->sort();
*f << stringf("/* Generated by %s */\n", yosys_version_str);
for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) {
- if (it->second->get_bool_attribute("\\blackbox") != blackboxes)
+ if (it->second->get_blackbox_attribute() != blackboxes)
continue;
if (selected && !design->selected_whole_module(it->first)) {
if (design->selected_module(it->first))
@@ -1633,6 +1998,8 @@ struct VerilogBackend : public Backend {
dump_module(*f, "", it->second);
}
+ auto_name_map.clear();
+ reg_wires.clear();
reg_ct.clear();
}
} VerilogBackend;