summaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
authorRuben Undheim <ruben.undheim@gmail.com>2018-08-30 20:46:20 +0200
committerRuben Undheim <ruben.undheim@gmail.com>2018-08-30 20:46:20 +0200
commit5033b51947a6ef02cb785b5622e993335efa750a (patch)
tree7bed18c526bd94917fa2f08e3df12209863698a1 /backends
parentfefe0fc0430f4f173a25e674708aa0f4f0854b31 (diff)
New upstream version 0.7+20180830git0b7a184
Diffstat (limited to 'backends')
-rw-r--r--backends/aiger/Makefile.inc3
-rw-r--r--backends/aiger/aiger.cc754
-rw-r--r--backends/blif/blif.cc82
-rw-r--r--backends/btor/README23
-rw-r--r--backends/btor/btor.cc1876
-rw-r--r--backends/btor/test_cells.sh30
-rwxr-xr-xbackends/btor/verilog2btor.sh37
-rw-r--r--backends/edif/edif.cc181
-rw-r--r--backends/edif/runtest.py121
-rw-r--r--backends/firrtl/.gitignore2
-rw-r--r--backends/firrtl/Makefile.inc3
-rw-r--r--backends/firrtl/firrtl.cc677
-rw-r--r--backends/firrtl/test.sh25
-rw-r--r--backends/firrtl/test.v63
-rw-r--r--backends/ilang/ilang_backend.cc10
-rw-r--r--backends/intersynth/intersynth.cc4
-rw-r--r--backends/json/json.cc18
-rw-r--r--backends/protobuf/.gitignore2
-rw-r--r--backends/protobuf/Makefile.inc8
-rw-r--r--backends/protobuf/protobuf.cc370
-rw-r--r--backends/simplec/.gitignore2
-rw-r--r--backends/simplec/Makefile.inc3
-rw-r--r--backends/simplec/simplec.cc810
-rw-r--r--backends/simplec/test00.sh5
-rw-r--r--backends/simplec/test00_tb.c93
-rw-r--r--backends/simplec/test00_uut.v14
-rw-r--r--backends/smt2/Makefile.inc2
-rw-r--r--backends/smt2/smt2.cc728
-rw-r--r--backends/smt2/smtbmc.py944
-rw-r--r--backends/smt2/smtio.py458
-rw-r--r--backends/smv/smv.cc14
-rw-r--r--backends/spice/spice.cc4
-rw-r--r--backends/table/Makefile.inc3
-rw-r--r--backends/table/table.cc120
-rw-r--r--backends/verilog/verilog_backend.cc223
35 files changed, 6280 insertions, 1432 deletions
diff --git a/backends/aiger/Makefile.inc b/backends/aiger/Makefile.inc
new file mode 100644
index 00000000..0fc37e95
--- /dev/null
+++ b/backends/aiger/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/aiger/aiger.o
+
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
new file mode 100644
index 00000000..c323691a
--- /dev/null
+++ b/backends/aiger/aiger.cc
@@ -0,0 +1,754 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void aiger_encode(std::ostream &f, int x)
+{
+ log_assert(x >= 0);
+
+ while (x & ~0x7f) {
+ f.put((x & 0x7f) | 0x80);
+ x = x >> 7;
+ }
+
+ f.put(x);
+}
+
+struct AigerWriter
+{
+ Module *module;
+ bool zinit_mode;
+ SigMap sigmap;
+
+ dict<SigBit, bool> init_map;
+ pool<SigBit> input_bits, output_bits;
+ dict<SigBit, SigBit> not_map, ff_map, alias_map;
+ dict<SigBit, pair<SigBit, SigBit>> and_map;
+ vector<pair<SigBit, SigBit>> asserts, assumes;
+ vector<pair<SigBit, SigBit>> liveness, fairness;
+ pool<SigBit> initstate_bits;
+
+ vector<pair<int, int>> aig_gates;
+ vector<int> aig_latchin, aig_latchinit, aig_outputs;
+ int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0;
+ int aig_b = 0, aig_c = 0, aig_j = 0, aig_f = 0;
+
+ dict<SigBit, int> aig_map;
+ dict<SigBit, int> ordered_outputs;
+ dict<SigBit, int> ordered_latches;
+
+ dict<SigBit, int> init_inputs;
+ int initstate_ff = 0;
+
+ int mkgate(int a0, int a1)
+ {
+ aig_m++, aig_a++;
+ aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0));
+ return 2*aig_m;
+ }
+
+ int bit2aig(SigBit bit)
+ {
+ if (aig_map.count(bit) == 0)
+ {
+ aig_map[bit] = -1;
+
+ if (initstate_bits.count(bit)) {
+ log_assert(initstate_ff > 0);
+ aig_map[bit] = initstate_ff;
+ } else
+ if (not_map.count(bit)) {
+ int a = bit2aig(not_map.at(bit)) ^ 1;
+ aig_map[bit] = a;
+ } else
+ if (and_map.count(bit)) {
+ auto args = and_map.at(bit);
+ int a0 = bit2aig(args.first);
+ int a1 = bit2aig(args.second);
+ aig_map[bit] = mkgate(a0, a1);
+ } else
+ if (alias_map.count(bit)) {
+ aig_map[bit] = bit2aig(alias_map.at(bit));
+ }
+
+ if (bit == State::Sx || bit == State::Sz)
+ log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n");
+ }
+
+ log_assert(aig_map.at(bit) >= 0);
+ return aig_map.at(bit);
+ }
+
+ AigerWriter(Module *module, bool zinit_mode) : module(module), zinit_mode(zinit_mode), sigmap(module)
+ {
+ pool<SigBit> undriven_bits;
+ pool<SigBit> unused_bits;
+
+ // promote public wires
+ for (auto wire : module->wires())
+ if (wire->name[0] == '\\')
+ sigmap.add(wire);
+
+ // promote input wires
+ for (auto wire : module->wires())
+ if (wire->port_input)
+ sigmap.add(wire);
+
+ // promote output wires
+ for (auto wire : module->wires())
+ if (wire->port_output)
+ sigmap.add(wire);
+
+ for (auto wire : module->wires())
+ {
+ if (wire->attributes.count("\\init")) {
+ SigSpec initsig = sigmap(wire);
+ Const initval = wire->attributes.at("\\init");
+ for (int i = 0; i < GetSize(wire) && i < GetSize(initval); i++)
+ if (initval[i] == State::S0 || initval[i] == State::S1)
+ init_map[initsig[i]] = initval[i] == State::S1;
+ }
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ SigBit wirebit(wire, i);
+ SigBit bit = sigmap(wirebit);
+
+ if (bit.wire == nullptr) {
+ if (wire->port_output) {
+ aig_map[wirebit] = (bit == State::S1) ? 1 : 0;
+ output_bits.insert(wirebit);
+ }
+ continue;
+ }
+
+ undriven_bits.insert(bit);
+ unused_bits.insert(bit);
+
+ if (wire->port_input)
+ input_bits.insert(bit);
+
+ if (wire->port_output) {
+ if (bit != wirebit)
+ alias_map[wirebit] = bit;
+ output_bits.insert(wirebit);
+ }
+ }
+ }
+
+ for (auto bit : input_bits)
+ undriven_bits.erase(bit);
+
+ for (auto bit : output_bits)
+ unused_bits.erase(bit);
+
+ for (auto cell : module->cells())
+ {
+ if (cell->type == "$_NOT_")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
+ unused_bits.erase(A);
+ undriven_bits.erase(Y);
+ not_map[Y] = A;
+ continue;
+ }
+
+ if (cell->type.in("$_FF_", "$_DFF_N_", "$_DFF_P_"))
+ {
+ SigBit D = sigmap(cell->getPort("\\D").as_bit());
+ SigBit Q = sigmap(cell->getPort("\\Q").as_bit());
+ unused_bits.erase(D);
+ undriven_bits.erase(Q);
+ ff_map[Q] = D;
+ continue;
+ }
+
+ if (cell->type == "$_AND_")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit B = sigmap(cell->getPort("\\B").as_bit());
+ SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
+ unused_bits.erase(A);
+ unused_bits.erase(B);
+ undriven_bits.erase(Y);
+ and_map[Y] = make_pair(A, B);
+ continue;
+ }
+
+ if (cell->type == "$initstate")
+ {
+ SigBit Y = sigmap(cell->getPort("\\Y").as_bit());
+ undriven_bits.erase(Y);
+ initstate_bits.insert(Y);
+ continue;
+ }
+
+ if (cell->type == "$assert")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
+ unused_bits.erase(A);
+ unused_bits.erase(EN);
+ asserts.push_back(make_pair(A, EN));
+ continue;
+ }
+
+ if (cell->type == "$assume")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
+ unused_bits.erase(A);
+ unused_bits.erase(EN);
+ assumes.push_back(make_pair(A, EN));
+ continue;
+ }
+
+ if (cell->type == "$live")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
+ unused_bits.erase(A);
+ unused_bits.erase(EN);
+ liveness.push_back(make_pair(A, EN));
+ continue;
+ }
+
+ if (cell->type == "$fair")
+ {
+ SigBit A = sigmap(cell->getPort("\\A").as_bit());
+ SigBit EN = sigmap(cell->getPort("\\EN").as_bit());
+ unused_bits.erase(A);
+ unused_bits.erase(EN);
+ fairness.push_back(make_pair(A, EN));
+ continue;
+ }
+
+ if (cell->type == "$anyconst")
+ {
+ for (auto bit : sigmap(cell->getPort("\\Y"))) {
+ undriven_bits.erase(bit);
+ ff_map[bit] = bit;
+ }
+ continue;
+ }
+
+ if (cell->type == "$anyseq")
+ {
+ for (auto bit : sigmap(cell->getPort("\\Y"))) {
+ undriven_bits.erase(bit);
+ input_bits.insert(bit);
+ }
+ continue;
+ }
+
+ log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell));
+ }
+
+ for (auto bit : unused_bits)
+ undriven_bits.erase(bit);
+
+ if (!undriven_bits.empty()) {
+ undriven_bits.sort();
+ for (auto bit : undriven_bits) {
+ log_warning("Treating undriven bit %s.%s like $anyseq.\n", log_id(module), log_signal(bit));
+ input_bits.insert(bit);
+ }
+ log_warning("Treating a total of %d undriven bits in %s like $anyseq.\n", GetSize(undriven_bits), log_id(module));
+ }
+
+ init_map.sort();
+ input_bits.sort();
+ output_bits.sort();
+ not_map.sort();
+ ff_map.sort();
+ and_map.sort();
+
+ aig_map[State::S0] = 0;
+ aig_map[State::S1] = 1;
+
+ for (auto bit : input_bits) {
+ aig_m++, aig_i++;
+ aig_map[bit] = 2*aig_m;
+ }
+
+ if (zinit_mode)
+ {
+ for (auto it : ff_map) {
+ if (init_map.count(it.first))
+ continue;
+ aig_m++, aig_i++;
+ init_inputs[it.first] = 2*aig_m;
+ }
+ }
+
+ int fair_live_inputs_cnt = GetSize(liveness);
+ int fair_live_inputs_m = aig_m;
+
+ aig_m += fair_live_inputs_cnt;
+ aig_i += fair_live_inputs_cnt;
+
+ for (auto it : ff_map) {
+ aig_m++, aig_l++;
+ aig_map[it.first] = 2*aig_m;
+ ordered_latches[it.first] = aig_l-1;
+ if (init_map.count(it.first) == 0)
+ aig_latchinit.push_back(2);
+ else
+ aig_latchinit.push_back(init_map.at(it.first) ? 1 : 0);
+ }
+
+ if (!initstate_bits.empty() || !init_inputs.empty()) {
+ aig_m++, aig_l++;
+ initstate_ff = 2*aig_m+1;
+ aig_latchinit.push_back(0);
+ }
+
+ int fair_live_latches_cnt = GetSize(fairness) + 2*GetSize(liveness);
+ int fair_live_latches_m = aig_m;
+ int fair_live_latches_l = aig_l;
+
+ aig_m += fair_live_latches_cnt;
+ aig_l += fair_live_latches_cnt;
+
+ for (int i = 0; i < fair_live_latches_cnt; i++)
+ aig_latchinit.push_back(0);
+
+ if (zinit_mode)
+ {
+ for (auto it : ff_map)
+ {
+ int l = ordered_latches[it.first];
+
+ if (aig_latchinit.at(l) == 1)
+ aig_map[it.first] ^= 1;
+
+ if (aig_latchinit.at(l) == 2)
+ {
+ int gated_ffout = mkgate(aig_map[it.first], initstate_ff^1);
+ int gated_initin = mkgate(init_inputs[it.first], initstate_ff);
+ aig_map[it.first] = mkgate(gated_ffout^1, gated_initin^1)^1;
+ }
+ }
+ }
+
+ for (auto it : ff_map) {
+ int a = bit2aig(it.second);
+ int l = ordered_latches[it.first];
+ if (zinit_mode && aig_latchinit.at(l) == 1)
+ aig_latchin.push_back(a ^ 1);
+ else
+ aig_latchin.push_back(a);
+ }
+
+ if (!initstate_bits.empty() || !init_inputs.empty())
+ aig_latchin.push_back(1);
+
+ for (auto bit : output_bits) {
+ aig_o++;
+ ordered_outputs[bit] = aig_o-1;
+ aig_outputs.push_back(bit2aig(bit));
+ }
+
+ for (auto it : asserts) {
+ aig_b++;
+ int bit_a = bit2aig(it.first);
+ int bit_en = bit2aig(it.second);
+ aig_outputs.push_back(mkgate(bit_a^1, bit_en));
+ }
+
+ for (auto it : assumes) {
+ aig_c++;
+ int bit_a = bit2aig(it.first);
+ int bit_en = bit2aig(it.second);
+ aig_outputs.push_back(mkgate(bit_a^1, bit_en)^1);
+ }
+
+ for (auto it : liveness)
+ {
+ int input_m = ++fair_live_inputs_m;
+ int latch_m1 = ++fair_live_latches_m;
+ int latch_m2 = ++fair_live_latches_m;
+
+ log_assert(GetSize(aig_latchin) == fair_live_latches_l);
+ fair_live_latches_l += 2;
+
+ int bit_a = bit2aig(it.first);
+ int bit_en = bit2aig(it.second);
+ int bit_s = 2*input_m;
+ int bit_q1 = 2*latch_m1;
+ int bit_q2 = 2*latch_m2;
+
+ int bit_d1 = mkgate(mkgate(bit_s, bit_en)^1, bit_q1^1)^1;
+ int bit_d2 = mkgate(mkgate(bit_d1, bit_a)^1, bit_q2^1)^1;
+
+ aig_j++;
+ aig_latchin.push_back(bit_d1);
+ aig_latchin.push_back(bit_d2);
+ aig_outputs.push_back(mkgate(bit_q1, bit_q2^1));
+ }
+
+ for (auto it : fairness)
+ {
+ int latch_m = ++fair_live_latches_m;
+
+ log_assert(GetSize(aig_latchin) == fair_live_latches_l);
+ fair_live_latches_l += 1;
+
+ int bit_a = bit2aig(it.first);
+ int bit_en = bit2aig(it.second);
+ int bit_q = 2*latch_m;
+
+ aig_f++;
+ aig_latchin.push_back(mkgate(mkgate(bit_q^1, bit_en^1)^1, bit_a^1));
+ aig_outputs.push_back(bit_q^1);
+ }
+ }
+
+ void write_aiger(std::ostream &f, bool ascii_mode, bool miter_mode, bool symbols_mode)
+ {
+ int aig_obc = aig_o + aig_b + aig_c;
+ int aig_obcj = aig_obc + aig_j;
+ int aig_obcjf = aig_obcj + aig_f;
+
+ log_assert(aig_m == aig_i + aig_l + aig_a);
+ log_assert(aig_l == GetSize(aig_latchin));
+ log_assert(aig_l == GetSize(aig_latchinit));
+ log_assert(aig_obcjf == GetSize(aig_outputs));
+
+ if (miter_mode) {
+ if (aig_b || aig_c || aig_j || aig_f)
+ log_error("Running AIGER back-end in -miter mode, but design contains $assert, $assume, $live and/or $fair cells!\n");
+ f << stringf("%s %d %d %d 0 %d %d\n", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_a, aig_o);
+ } else {
+ f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a);
+ if (aig_b || aig_c || aig_j || aig_f)
+ f << stringf(" %d %d %d %d", aig_b, aig_c, aig_j, aig_f);
+ f << stringf("\n");
+ }
+
+ if (ascii_mode)
+ {
+ for (int i = 0; i < aig_i; i++)
+ f << stringf("%d\n", 2*i+2);
+
+ for (int i = 0; i < aig_l; i++) {
+ if (zinit_mode || aig_latchinit.at(i) == 0)
+ f << stringf("%d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i));
+ else if (aig_latchinit.at(i) == 1)
+ f << stringf("%d %d 1\n", 2*(aig_i+i)+2, aig_latchin.at(i));
+ else if (aig_latchinit.at(i) == 2)
+ f << stringf("%d %d %d\n", 2*(aig_i+i)+2, aig_latchin.at(i), 2*(aig_i+i)+2);
+ }
+
+ for (int i = 0; i < aig_obc; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("1\n");
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obcj; i < aig_obcjf; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = 0; i < aig_a; i++)
+ f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second);
+ }
+ else
+ {
+ for (int i = 0; i < aig_l; i++) {
+ if (zinit_mode || aig_latchinit.at(i) == 0)
+ f << stringf("%d\n", aig_latchin.at(i));
+ else if (aig_latchinit.at(i) == 1)
+ f << stringf("%d 1\n", aig_latchin.at(i));
+ else if (aig_latchinit.at(i) == 2)
+ f << stringf("%d %d\n", aig_latchin.at(i), 2*(aig_i+i)+2);
+ }
+
+ for (int i = 0; i < aig_obc; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("1\n");
+
+ for (int i = aig_obc; i < aig_obcj; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = aig_obcj; i < aig_obcjf; i++)
+ f << stringf("%d\n", aig_outputs.at(i));
+
+ for (int i = 0; i < aig_a; i++) {
+ int lhs = 2*(aig_i+aig_l+i)+2;
+ int rhs0 = aig_gates.at(i).first;
+ int rhs1 = aig_gates.at(i).second;
+ int delta0 = lhs - rhs0;
+ int delta1 = rhs0 - rhs1;
+ aiger_encode(f, delta0);
+ aiger_encode(f, delta1);
+ }
+ }
+
+ if (symbols_mode)
+ {
+ dict<string, vector<string>> symbols;
+
+ for (auto wire : module->wires())
+ {
+ if (wire->name[0] == '$')
+ continue;
+
+ SigSpec sig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ if (sig[i].wire == nullptr) {
+ if (wire->port_output)
+ sig[i] = SigBit(wire, i);
+ else
+ continue;
+ }
+
+ if (wire->port_input) {
+ int a = aig_map.at(sig[i]);
+ log_assert((a & 1) == 0);
+ if (GetSize(wire) != 1)
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s[%d]", log_id(wire), i));
+ else
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("%s", log_id(wire)));
+ }
+
+ if (wire->port_output) {
+ int o = ordered_outputs.at(SigSpec(wire, i));
+ if (GetSize(wire) != 1)
+ symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s[%d]", log_id(wire), i));
+ else
+ symbols[stringf("%c%d", miter_mode ? 'b' : 'o', o)].push_back(stringf("%s", log_id(wire)));
+ }
+
+ if (init_inputs.count(sig[i])) {
+ int a = init_inputs.at(sig[i]);
+ log_assert((a & 1) == 0);
+ if (GetSize(wire) != 1)
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s[%d]", log_id(wire), i));
+ else
+ symbols[stringf("i%d", (a >> 1)-1)].push_back(stringf("init:%s", log_id(wire)));
+ }
+
+ if (ordered_latches.count(sig[i])) {
+ int l = ordered_latches.at(sig[i]);
+ const char *p = (zinit_mode && (aig_latchinit.at(l) == 1)) ? "!" : "";
+ if (GetSize(wire) != 1)
+ symbols[stringf("l%d", l)].push_back(stringf("%s%s[%d]", p, log_id(wire), i));
+ else
+ symbols[stringf("l%d", l)].push_back(stringf("%s%s", p, log_id(wire)));
+ }
+ }
+ }
+
+ symbols.sort();
+
+ for (auto &sym : symbols) {
+ f << sym.first;
+ std::sort(sym.second.begin(), sym.second.end());
+ for (auto &s : sym.second)
+ f << " " << s;
+ f << std::endl;
+ }
+ }
+
+ f << stringf("c\nGenerated by %s\n", yosys_version_str);
+ }
+
+ void write_map(std::ostream &f, bool verbose_map)
+ {
+ dict<int, string> input_lines;
+ dict<int, string> init_lines;
+ dict<int, string> output_lines;
+ dict<int, string> latch_lines;
+ dict<int, string> wire_lines;
+
+ for (auto wire : module->wires())
+ {
+ if (!verbose_map && wire->name[0] == '$')
+ continue;
+
+ SigSpec sig = sigmap(wire);
+
+ for (int i = 0; i < GetSize(wire); i++)
+ {
+ if (aig_map.count(sig[i]) == 0 || sig[i].wire == nullptr)
+ continue;
+
+ int a = aig_map.at(sig[i]);
+
+ if (verbose_map)
+ wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire));
+
+ if (wire->port_input) {
+ log_assert((a & 1) == 0);
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire));
+ }
+
+ if (wire->port_output) {
+ int o = ordered_outputs.at(sig[i]);
+ output_lines[o] += stringf("output %d %d %s\n", o, i, log_id(wire));
+ }
+
+ if (init_inputs.count(sig[i])) {
+ int a = init_inputs.at(sig[i]);
+ log_assert((a & 1) == 0);
+ init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, i, log_id(wire));
+ }
+
+ if (ordered_latches.count(sig[i])) {
+ int l = ordered_latches.at(sig[i]);
+ if (zinit_mode && (aig_latchinit.at(l) == 1))
+ latch_lines[l] += stringf("invlatch %d %d %s\n", l, i, log_id(wire));
+ else
+ latch_lines[l] += stringf("latch %d %d %s\n", l, i, log_id(wire));
+ }
+ }
+ }
+
+ input_lines.sort();
+ for (auto &it : input_lines)
+ f << it.second;
+
+ init_lines.sort();
+ for (auto &it : init_lines)
+ f << it.second;
+
+ output_lines.sort();
+ for (auto &it : output_lines)
+ f << it.second;
+
+ latch_lines.sort();
+ for (auto &it : latch_lines)
+ f << it.second;
+
+ wire_lines.sort();
+ for (auto &it : wire_lines)
+ f << it.second;
+ }
+};
+
+struct AigerBackend : public Backend {
+ AigerBackend() : Backend("aiger", "write design to AIGER file") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_aiger [options] [filename]\n");
+ log("\n");
+ log("Write the current design to an AIGER file. The design must be flattened and\n");
+ log("must not contain any cell types except $_AND_, $_NOT_, simple FF types,\n");
+ log("$assert and $assume cells, and $initstate cells.\n");
+ log("\n");
+ log("$assert and $assume cells are converted to AIGER bad state properties and\n");
+ log("invariant constraints.\n");
+ log("\n");
+ log(" -ascii\n");
+ log(" write ASCII version of AGIER format\n");
+ log("\n");
+ log(" -zinit\n");
+ log(" convert FFs to zero-initialized FFs, adding additional inputs for\n");
+ log(" uninitialized FFs.\n");
+ log("\n");
+ log(" -miter\n");
+ log(" design outputs are AIGER bad state properties\n");
+ log("\n");
+ log(" -symbols\n");
+ log(" include a symbol table in the generated AIGER file\n");
+ log("\n");
+ log(" -map <filename>\n");
+ log(" write an extra file with port and latch symbols\n");
+ log("\n");
+ log(" -vmap <filename>\n");
+ log(" like -map, but more verbose\n");
+ log("\n");
+ }
+ 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;
+ std::string map_filename;
+
+ log_header(design, "Executing AIGER backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-ascii") {
+ ascii_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-zinit") {
+ zinit_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-miter") {
+ miter_mode = true;
+ continue;
+ }
+ if (args[argidx] == "-symbols") {
+ symbols_mode = true;
+ continue;
+ }
+ if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) {
+ map_filename = args[++argidx];
+ continue;
+ }
+ if (map_filename.empty() && args[argidx] == "-vmap" && argidx+1 < args.size()) {
+ map_filename = args[++argidx];
+ verbose_map = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ Module *top_module = design->top_module();
+
+ if (top_module == nullptr)
+ log_error("Can't find top module in current design!\n");
+
+ AigerWriter writer(top_module, zinit_mode);
+ writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode);
+
+ if (!map_filename.empty()) {
+ std::ofstream mapf;
+ mapf.open(map_filename.c_str(), std::ofstream::trunc);
+ if (mapf.fail())
+ log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno));
+ writer.write_map(mapf, verbose_map);
+ }
+ }
+} AigerBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
index d9d0cc17..0db5ff27 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
@@ -112,7 +115,7 @@ struct BlifDumper
str[i] = '?';
if (sig.wire->width != 1)
- str += stringf("[%d]", sig.offset);
+ str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset);
cstr_buf.push_back(str);
return cstr_buf.back().c_str();
@@ -240,106 +243,118 @@ 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")));
+ 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")));
+ 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 == "$_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") {
@@ -361,7 +376,7 @@ struct BlifDumper
}
f << " 1\n";
}
- continue;
+ goto internal_cell;
}
if (!config->icells_mode && cell->type == "$sop") {
@@ -389,7 +404,7 @@ struct BlifDumper
}
f << " 1\n";
}
- continue;
+ goto internal_cell;
}
f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
@@ -409,6 +424,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())
@@ -441,7 +464,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");
@@ -499,6 +522,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");
@@ -506,7 +534,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;
@@ -575,6 +603,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;
diff --git a/backends/btor/README b/backends/btor/README
deleted file mode 100644
index efcf0d8f..00000000
--- 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 bbe90e85..58d2a862 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,1093 +17,1112 @@
*
*/
-// [[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
-
#include "kernel/rtlil.h"
#include "kernel/register.h"
#include "kernel/sigtools.h"
#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_assert(cell_recursion_guard.count(cell) == 0);
+ cell_recursion_guard.insert(cell);
+ btorf_push(log_id(cell));
+
+ if (cell->type.in("$add", "$sub", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx",
+ "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
{
- log("writing 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.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 == "$_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")
{
- ++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;
+ int nid_a = get_sig_nid(cell->getPort("\\A"), width, false);
+ int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed);
+
+ 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 return it->second;
+ else
+ {
+ 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;
}
- else // case when the wire is not basic wire
+
+ if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
{
- log("case of non-basic wire - %s\n", cstr(wire->name));
- auto it = line_ref.find(wire->name);
- if(it==std::end(line_ref))
- {
- 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);
-
- 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 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);
}
- else
- {
- log(" -- already processed wire\n");
- return it->second;
+
+ 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;
}
- 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("$_OAI3_", "$_AOI3_"))
{
- ++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;
+ 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);
+ }
+
+ 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);
+ }
+
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
+ add_nid_sig(nid3, 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("$_OAI4_", "$_AOI4_"))
{
- 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 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;
}
- 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("$lt", "$le", "$eq", "$eqx", "$ne", "$nex", "$ge", "$gt"))
{
- if(width<0)
- width = data->bits.size() - offset;
+ 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 = "ne";
+ 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);
+ }
- std::string data_str = data->as_string();
- //if(offset > 0)
- data_str = data_str.substr(offset, width);
+ SigSpec sig = sigmap(cell->getPort("\\Y"));
- ++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;
+ 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;
}
- 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("$not", "$neg", "$_NOT_"))
{
- RTLIL::Const data_const(chunk->data);
- l=dump_const(&data_const, chunk->width, chunk->offset);
+ 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;
}
- else
+
+ if (cell->type.in("$logic_and", "$logic_or", "$logic_not"))
{
- if (chunk->width == chunk->wire->width && chunk->offset == 0)
- l = dump_wire(chunk->wire);
+ 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 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;
+ 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;
}
+
+ 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("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor"))
{
- if (s.is_chunk())
- {
- l = dump_sigchunk(&s.chunks().front());
+ 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;
}
- 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;
+
+ 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("$mux", "$_MUX_"))
{
- l = it->second;
+ 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);
+
+ add_nid_sig(nid, sig_y);
+ goto okay;
}
- if (expected_width != s.size())
+ if (cell->type == "$pmux")
{
- 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 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;
}
- 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.in("$dff", "$ff", "$_DFF_P_", "$_DFF_N", "$_FF_"))
+ {
+ 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;
+ }
+ }
+
+ 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));
+
+ 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);
+
+ if (!initval.is_fully_undef()) {
+ int nid_init_val = get_sig_nid(initval);
+ 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;
}
- 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("$anyconst", "$anyseq"))
{
- 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_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);
}
- //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")
+
+ 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(Const(1, 1));
+ int zero_nid = get_sig_nid(Const(0, 1));
+ 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 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);
+ int nid = next_nid++;
+ int nid_head = nid;
+
+ if (cell->name[0] == '$')
+ btorf("%d state %d\n", nid, sid);
+ else
+ btorf("%d state %d %s\n", nid, sid, log_id(cell));
+
+ if (asyncwr)
{
- 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)
+ for (int port = 0; port < wrports; port++)
{
- ++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());
- }
- else if(cell->type == "$reduce_xnor")
- {
- ++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());
+ 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;
}
- ++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" )
+
+ 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();
- 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")
- {
- if(l1_signed)
- op = s_cell_type_translation.at(cell->type.str());
- }
+ SigSpec ra = sig_rd_addr.extract(port*abits, abits);
+ SigSpec rd = sig_rd_data.extract(port*width, width);
+
+ int ra_nid = get_sig_nid(ra);
+ int rd_nid = next_nid++;
- str = stringf ("%d %s %d %d %d", line_num, op.c_str(), output_width, l1, l2);
- f << stringf("%s\n", str.c_str());
+ btorf("%d read %d %d %d\n", rd_nid, data_sid, nid_head, ra_nid);
- line_ref[cell->name]=line_num;
+ add_nid_sig(rd_nid, rd);
}
- else if(cell->type == "$add" || cell->type == "$sub" || cell->type == "$mul" || cell->type == "$div" ||
- cell->type == "$mod" )
- {
- //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")
- {
- 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());
- 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());
- }
- line_ref[cell->name]=line_num;
+ if (!asyncwr)
+ {
+ ff_todo.push_back(make_pair(nid, cell));
}
- else if(cell->type == "$shr" || cell->type == "$shl" || cell->type == "$sshr" || cell->type == "$sshl" || cell->type == "$shift" || cell->type == "$shiftx")
+ else
{
- 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;
- }
+ int nid2 = next_nid++;
+ btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head);
+ }
- 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;
+ 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)
+ {
+ 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]);
}
- line_ref[cell->name] = cell_output;
- }
- else if(cell->type == "$logic_and" || cell->type == "$logic_or")//no direct translation in btor
+
+ if (to_width < 0 || first_undef < to_width)
{
- 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;
+ int sid = get_bv_sid(GetSize(sig));
+
+ int nid_input = next_nid++;
+ 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);
}
- 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;
- }
- 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);
- }
- 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());
- }
- if(cell->type == "$dffsr")
- {
- 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);
+ 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);
- f << stringf("%s\n", str.c_str());
- int next = line_num;
- if(cell->type == "$adff")
+ for (int j = 0; j < GetSize(c); j++)
+ nidbits.push_back(make_pair(nid, j));
+
+ i += GetSize(c)-1;
+ continue;
+ }
+ else
{
- 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());
+ if (bit_cell.count(bit) == 0)
+ log_error("No driver for signal bit %s.\n", log_signal(bit));
+ 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 sid = get_bv_sid(GetSize(wire));
+ int nid = get_sig_nid(wire);
+ btorf("%d output %d %d %s\n", next_nid++, sid, nid, log_id(wire));
+
+ btorf_pop(stringf("output %s", 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)
+ 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")
+ if (cell->type == "$assume")
{
- continue;//nothing to do
- }
- else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr")
- {
- 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++;
+ btorf("%d bad %d\n", nid, nid_en_and_not_a);
}
- }
- }
- 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)));
+
+ 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);
+ }
- log("writing output wires\n");
- for (auto &it : outputs) {
- RTLIL::Wire *wire = it.second;
- dump_wire(wire);
+ 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 00000000..e0f1a051
--- /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 dfd7f1a8..00000000
--- 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 d16f1831..5f9ec54f 100644
--- a/backends/edif/edif.cc
+++ b/backends/edif/edif.cc
@@ -31,65 +31,66 @@ USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#define EDIF_DEF(_id) edif_names(RTLIL::unescape_id(_id), true).c_str()
+#define EDIF_DEFR(_id, _ren, _bl, _br) edif_names(RTLIL::unescape_id(_id), true, _ren, _bl, _br).c_str()
#define EDIF_REF(_id) edif_names(RTLIL::unescape_id(_id), false).c_str()
-namespace
+struct EdifNames
{
- struct EdifNames
- {
- int counter;
- std::set<std::string> generated_names, used_names;
- std::map<std::string, std::string> name_map;
+ int counter;
+ char delim_left, delim_right;
+ std::set<std::string> generated_names, used_names;
+ std::map<std::string, std::string> name_map;
- EdifNames() : counter(1) { }
+ EdifNames() : counter(1), delim_left('['), delim_right(']') { }
- std::string operator()(std::string id, bool define)
- {
- if (define) {
- std::string new_id = operator()(id, false);
- return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
- }
+ std::string operator()(std::string id, bool define, bool port_rename = false, int range_left = 0, int range_right = 0)
+ {
+ if (define) {
+ std::string new_id = operator()(id, false);
+ if (port_rename)
+ return stringf("(rename %s \"%s%c%d:%d%c\")", new_id.c_str(), id.c_str(), delim_left, range_left, range_right, delim_right);
+ return new_id != id ? stringf("(rename %s \"%s\")", new_id.c_str(), id.c_str()) : id;
+ }
- if (name_map.count(id) > 0)
- return name_map.at(id);
- if (generated_names.count(id) > 0)
- goto do_rename;
- if (id == "GND" || id == "VCC")
- goto do_rename;
+ if (name_map.count(id) > 0)
+ return name_map.at(id);
+ if (generated_names.count(id) > 0)
+ goto do_rename;
+ if (id == "GND" || id == "VCC")
+ goto do_rename;
- for (size_t i = 0; i < id.size(); i++) {
- if ('A' <= id[i] && id[i] <= 'Z')
- continue;
- if ('a' <= id[i] && id[i] <= 'z')
- continue;
- if ('0' <= id[i] && id[i] <= '9' && i > 0)
- continue;
- if (id[i] == '_' && i > 0 && i != id.size()-1)
- continue;
- goto do_rename;
- }
+ for (size_t i = 0; i < id.size(); i++) {
+ if ('A' <= id[i] && id[i] <= 'Z')
+ continue;
+ if ('a' <= id[i] && id[i] <= 'z')
+ continue;
+ if ('0' <= id[i] && id[i] <= '9' && i > 0)
+ continue;
+ if (id[i] == '_' && i > 0 && i != id.size()-1)
+ continue;
+ goto do_rename;
+ }
- used_names.insert(id);
- return id;
+ used_names.insert(id);
+ return id;
- do_rename:;
- std::string gen_name;
- while (1) {
- gen_name = stringf("id%05d", counter++);
- if (generated_names.count(gen_name) == 0 &&
- used_names.count(gen_name) == 0)
- break;
- }
- generated_names.insert(gen_name);
- name_map[id] = gen_name;
- return gen_name;
+ do_rename:;
+ std::string gen_name;
+ while (1) {
+ gen_name = stringf("id%05d", counter++);
+ if (generated_names.count(gen_name) == 0 &&
+ used_names.count(gen_name) == 0)
+ break;
}
- };
-}
+ generated_names.insert(gen_name);
+ name_map[id] = gen_name;
+ return gen_name;
+ }
+};
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");
@@ -105,17 +106,21 @@ 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(" -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");
+ log("\n");
log("Unfortunately there are different \"flavors\" of the EDIF file format. This\n");
log("command generates EDIF files for the Xilinx place&route tools. It might be\n");
log("necessary to make small modifications to this command when a different tool\n");
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;
std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports;
bool nogndvcc = false;
CellTypes ct(design);
@@ -132,6 +137,19 @@ struct EdifBackend : public Backend {
nogndvcc = true;
continue;
}
+ if (args[argidx] == "-pvector" && argidx+1 < args.size()) {
+ std::string parray;
+ port_rename = true;
+ parray = args[++argidx];
+ if (parray == "par") {
+ edif_names.delim_left = '(';edif_names.delim_right = ')';
+ } else if (parray == "ang") {
+ edif_names.delim_left = '<';edif_names.delim_right = '>';
+ } else {
+ edif_names.delim_left = '[';edif_names.delim_right = ']';
+ }
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
@@ -214,8 +232,18 @@ struct EdifBackend : public Backend {
}
if (port_it.second == 1)
*f << stringf(" (port %s (direction %s))\n", EDIF_DEF(port_it.first), dir);
- else
- *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(port_it.first), port_it.second, dir);
+ else {
+ int b[2] = {port_it.second-1, 0};
+ auto m = design->module(cell_it.first);
+ if (m) {
+ auto w = m->wire(port_it.first);
+ if (w) {
+ b[w->upto ? 0 : 1] = w->start_offset;
+ b[w->upto ? 1 : 0] = w->start_offset+GetSize(w)-1;
+ }
+ }
+ *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(port_it.first, port_rename, b[0], b[1]), port_it.second, dir);
+ }
}
*f << stringf(" )\n");
*f << stringf(" )\n");
@@ -283,10 +311,13 @@ struct EdifBackend : public Backend {
RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire));
net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name)));
} else {
- *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEF(wire->name), wire->width, dir);
+ 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);
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), i));
+ net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1));
}
}
}
@@ -323,20 +354,48 @@ struct EdifBackend : public Backend {
for (auto &p : cell->connections()) {
RTLIL::SigSpec sig = sigmap(p.second);
for (int i = 0; i < GetSize(sig); i++)
- if (sig.size() == 1)
+ if (sig[i].wire == NULL && sig[i] != RTLIL::State::S0 && sig[i] != RTLIL::State::S1)
+ 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)));
- else
- net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", EDIF_REF(p.first), i, EDIF_REF(cell->name)));
+ else {
+ int member_idx = GetSize(sig)-i-1;
+ auto m = design->module(cell->type);
+ if (m) {
+ auto w = m->wire(p.first);
+ 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)));
+ }
}
}
for (auto &it : net_join_db) {
RTLIL::SigBit sig = it.first;
- if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1)
- continue;
- std::string netname = log_signal(sig);
- for (size_t i = 0; i < netname.size(); i++)
- if (netname[i] == ' ' || netname[i] == '\\')
- netname.erase(netname.begin() + i--);
+ 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());
+ sig = RTLIL::State::S0;
+ } 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_abort();
+ }
+ }
+ std::string netname;
+ if (sig == RTLIL::State::S0)
+ netname = "GND_NET";
+ else if (sig == RTLIL::State::S1)
+ netname = "VCC_NET";
+ else {
+ netname = log_signal(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));
for (auto &ref : it.second)
*f << stringf(" %s\n", ref.c_str());
diff --git a/backends/edif/runtest.py b/backends/edif/runtest.py
new file mode 100644
index 00000000..826876a8
--- /dev/null
+++ b/backends/edif/runtest.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+
+import os
+import numpy as np
+
+enable_upto = True
+enable_offset = True
+enable_hierarchy = True
+enable_logic = True
+
+def make_module(f, modname, width, subs):
+ print("module %s (A, B, C, X, Y, Z);" % modname, file=f)
+ inbits = list()
+ outbits = list()
+
+ for p in "ABC":
+ offset = np.random.randint(10) if enable_offset else 0
+ if enable_upto and np.random.randint(2):
+ print(" input [%d:%d] %s;" % (offset, offset+width-1, p), file=f)
+ else:
+ print(" input [%d:%d] %s;" % (offset+width-1, offset, p), file=f)
+ for i in range(offset, offset+width):
+ inbits.append("%s[%d]" % (p, i))
+
+ for p in "XYZ":
+ offset = np.random.randint(10) if enable_offset else 0
+ if enable_upto and np.random.randint(2):
+ print(" output [%d:%d] %s;" % (offset, offset+width-1, p), file=f)
+ else:
+ print(" output [%d:%d] %s;" % (offset+width-1, offset, p), file=f)
+ for i in range(offset, offset+width):
+ outbits.append("%s[%d]" % (p, i))
+
+ instidx = 0
+ subcandidates = list(subs.keys())
+
+ while len(outbits) > 0:
+ submod = None
+ if len(subcandidates):
+ submod = np.random.choice(subcandidates)
+ subcandidates.remove(submod)
+
+ if submod is None or 3*subs[submod] >= len(outbits):
+ for bit in outbits:
+ if enable_logic:
+ print(" assign %s = %s & ~%s;" % (bit, np.random.choice(inbits), np.random.choice(inbits)), file=f)
+ else:
+ print(" assign %s = %s;" % (bit, np.random.choice(inbits)), file=f)
+ break
+
+ instidx += 1
+ print(" %s inst%d (" % (submod, instidx), file=f)
+
+ for p in "ABC":
+ print(" .%s({%s})," % (p, ",".join(np.random.choice(inbits, subs[submod]))), file=f)
+
+ for p in "XYZ":
+ bits = list(np.random.choice(outbits, subs[submod], False))
+ for bit in bits:
+ outbits.remove(bit)
+ print(" .%s({%s})%s" % (p, ",".join(bits), "," if p != "Z" else ""), file=f)
+
+ print(" );", file=f);
+
+ print("endmodule", file=f)
+
+with open("test_top.v", "w") as f:
+ if enable_hierarchy:
+ make_module(f, "sub1", 2, {})
+ make_module(f, "sub2", 3, {})
+ make_module(f, "sub3", 4, {})
+ make_module(f, "sub4", 8, {"sub1": 2, "sub2": 3, "sub3": 4})
+ make_module(f, "sub5", 8, {"sub1": 2, "sub2": 3, "sub3": 4})
+ make_module(f, "sub6", 8, {"sub1": 2, "sub2": 3, "sub3": 4})
+ make_module(f, "top", 32, {"sub4": 8, "sub5": 8, "sub6": 8})
+ else:
+ make_module(f, "top", 32, {})
+
+os.system("set -x; ../../yosys -p 'synth_xilinx -top top; write_edif -pvector par test_syn.edif' test_top.v")
+
+with open("test_syn.tcl", "w") as f:
+ print("read_edif test_syn.edif", file=f)
+ print("link_design", file=f)
+ print("write_verilog -force test_syn.v", file=f)
+
+os.system("set -x; vivado -nojournal -nolog -mode batch -source test_syn.tcl")
+
+with open("test_tb.v", "w") as f:
+ print("module tb;", file=f)
+ print(" reg [31:0] A, B, C;", file=f)
+ print(" wire [31:0] X, Y, Z;", file=f)
+ print("", file=f)
+ print(" top uut (", file=f)
+ print(" .A(A),", file=f)
+ print(" .B(B),", file=f)
+ print(" .C(C),", file=f)
+ print(" .X(X),", file=f)
+ print(" .Y(Y),", file=f)
+ print(" .Z(Z)", file=f)
+ print(" );", file=f)
+ print("", file=f)
+ print(" initial begin", file=f)
+ for i in range(100):
+ print(" A = 32'h%08x;" % np.random.randint(2**32), file=f)
+ print(" B = 32'h%08x;" % np.random.randint(2**32), file=f)
+ print(" C = 32'h%08x;" % np.random.randint(2**32), file=f)
+ print(" #10;", file=f)
+ print(" $display(\"%x %x %x\", X, Y, Z);", file=f)
+ print(" #10;", file=f)
+ print(" $finish;", file=f)
+ print(" end", file=f)
+ print("endmodule", file=f)
+
+os.system("set -x; iverilog -o test_gold test_tb.v test_top.v")
+os.system("set -x; iverilog -o test_gate test_tb.v test_syn.v ../../techlibs/xilinx/cells_sim.v")
+
+os.system("set -x; ./test_gold > test_gold.out")
+os.system("set -x; ./test_gate > test_gate.out")
+
+os.system("set -x; md5sum test_gold.out test_gate.out")
+
diff --git a/backends/firrtl/.gitignore b/backends/firrtl/.gitignore
new file mode 100644
index 00000000..a2ac93ab
--- /dev/null
+++ b/backends/firrtl/.gitignore
@@ -0,0 +1,2 @@
+test.fir
+test_out.v
diff --git a/backends/firrtl/Makefile.inc b/backends/firrtl/Makefile.inc
new file mode 100644
index 00000000..fdf100d3
--- /dev/null
+++ b/backends/firrtl/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/firrtl/firrtl.o
+
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
new file mode 100644
index 00000000..94236d0b
--- /dev/null
+++ b/backends/firrtl/firrtl.cc
@@ -0,0 +1,677 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/cellaigs.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+pool<string> used_names;
+dict<IdString, string> namecache;
+int autoid_counter;
+
+typedef unsigned FDirection;
+static const FDirection NODIRECTION = 0x0;
+static const FDirection IN = 0x1;
+static const FDirection OUT = 0x2;
+static const FDirection INOUT = 0x3;
+
+// Get a port direction with respect to a specific module.
+FDirection getPortFDirection(IdString id, Module *module)
+{
+ Wire *wire = module->wires_.at(id);
+ FDirection direction = NODIRECTION;
+ if (wire && wire->port_id)
+ {
+ if (wire->port_input)
+ direction |= IN;
+ if (wire->port_output)
+ direction |= OUT;
+ }
+ return direction;
+}
+
+string next_id()
+{
+ string new_id;
+
+ while (1) {
+ new_id = stringf("_%d", autoid_counter++);
+ if (used_names.count(new_id) == 0) break;
+ }
+
+ used_names.insert(new_id);
+ return new_id;
+}
+
+const char *make_id(IdString id)
+{
+ if (namecache.count(id) != 0)
+ return namecache.at(id).c_str();
+
+ string new_id = log_id(id);
+
+ for (int i = 0; i < GetSize(new_id); i++)
+ {
+ char &ch = new_id[i];
+ if ('a' <= ch && ch <= 'z') continue;
+ if ('A' <= ch && ch <= 'Z') continue;
+ if ('0' <= ch && ch <= '9' && i != 0) continue;
+ if ('_' == ch) continue;
+ ch = '_';
+ }
+
+ while (used_names.count(new_id) != 0)
+ new_id += '_';
+
+ namecache[id] = new_id;
+ used_names.insert(new_id);
+ return namecache.at(id).c_str();
+}
+
+struct FirrtlWorker
+{
+ Module *module;
+ std::ostream &f;
+
+ dict<SigBit, pair<string, int>> reverse_wire_map;
+ string unconn_id;
+ RTLIL::Design *design;
+ std::string indent;
+
+ void register_reverse_wire_map(string id, SigSpec sig)
+ {
+ for (int i = 0; i < GetSize(sig); i++)
+ reverse_wire_map[sig[i]] = make_pair(id, i);
+ }
+
+ FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ")
+ {
+ }
+
+ string make_expr(const SigSpec &sig)
+ {
+ string expr;
+
+ for (auto chunk : sig.chunks())
+ {
+ string new_expr;
+
+ if (chunk.wire == nullptr)
+ {
+ std::vector<RTLIL::State> bits = chunk.data;
+ new_expr = stringf("UInt<%d>(\"h", GetSize(bits));
+
+ while (GetSize(bits) % 4 != 0)
+ bits.push_back(State::S0);
+
+ for (int i = GetSize(bits)-4; i >= 0; i -= 4)
+ {
+ int val = 0;
+ if (bits[i+0] == State::S1) val += 1;
+ if (bits[i+1] == State::S1) val += 2;
+ if (bits[i+2] == State::S1) val += 4;
+ if (bits[i+3] == State::S1) val += 8;
+ new_expr.push_back(val < 10 ? '0' + val : 'a' + val - 10);
+ }
+
+ new_expr += "\")";
+ }
+ else if (chunk.offset == 0 && chunk.width == chunk.wire->width)
+ {
+ new_expr = make_id(chunk.wire->name);
+ }
+ else
+ {
+ string wire_id = make_id(chunk.wire->name);
+ new_expr = stringf("bits(%s, %d, %d)", wire_id.c_str(), chunk.offset + chunk.width - 1, chunk.offset);
+ }
+
+ if (expr.empty())
+ expr = new_expr;
+ else
+ expr = "cat(" + new_expr + ", " + expr + ")";
+ }
+
+ return expr;
+ }
+
+ std::string fid(RTLIL::IdString internal_id)
+ {
+ const char *str = internal_id.c_str();
+ return *str == '\\' ? str + 1 : str;
+ }
+
+
+ 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 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);
+ wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), cell_type.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 secondName = make_expr(secondSig);
+ // Find the direction for this port.
+ FDirection dir = getPortFDirection(it->first, instModule);
+ std::string source, sink;
+ switch (dir) {
+ case INOUT:
+ log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", log_id(cell_type), log_signal(it->second));
+ case OUT:
+ source = firstName;
+ sink = secondName;
+ break;
+ case NODIRECTION:
+ log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", log_id(cell_type), log_signal(it->second));
+ /* FALL_THROUGH */
+ case IN:
+ source = secondName;
+ sink = firstName;
+ break;
+ default:
+ log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", log_id(cell_type), log_signal(it->second), dir);
+ break;
+ }
+ wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sink.c_str(), source.c_str()));
+ }
+ }
+ wire_exprs.push_back(stringf("\n"));
+
+ }
+
+ void run()
+ {
+ f << stringf(" module %s:\n", make_id(module->name));
+ vector<string> port_decls, wire_decls, cell_exprs, wire_exprs;
+
+ for (auto wire : module->wires())
+ {
+ const auto wireName = make_id(wire->name);
+ 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",
+ wireName, wire->width));
+ }
+ else
+ {
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width));
+ }
+ }
+
+ for (auto cell : module->cells())
+ {
+ // Is this cell is a module instance?
+ if (cell->type[0] != '$')
+ {
+ process_instance(cell, wire_exprs);
+ continue;
+ }
+ 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()) {
+ a_expr = "asSInt(" + a_expr + ")";
+ }
+
+ // 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;
+ if (cell->type == "$not") primop = "not";
+ if (cell->type == "$neg") primop = "neg";
+ 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") {
+ primop = "not";
+ a_expr = stringf("xorr(%s)", a_expr.c_str());
+ }
+ 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.
+ bool a_signed = cell->parameters.at("\\A_SIGNED").as_bool();
+ int a_width = cell->parameters.at("\\A_WIDTH").as_int();
+ 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))
+ expr = stringf("asUInt(%s)", 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.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"))
+ {
+ 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()) {
+ a_expr = "asSInt(" + a_expr + ")";
+ }
+ // Shift amount is always unsigned, and needn't be padded to result width.
+ if (!cell->type.in("$shr", "$sshr", "$shl", "$sshl")) {
+ if (cell->parameters.at("\\B_SIGNED").as_bool()) {
+ b_expr = "asSInt(" + b_expr + ")";
+ }
+ b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width);
+ }
+
+ a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
+
+ if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) {
+ a_expr = "asUInt(" + a_expr + ")";
+ }
+
+ 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());
+
+ if ((is_signed && !always_uint) || cell->type.in("$sub"))
+ expr = stringf("asUInt(%s)", 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.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"));
+ string s_expr = make_expr(cell->getPort("\\S"));
+ wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), width));
+
+ string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_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.in("$mem"))
+ {
+ string mem_id = make_id(cell->name);
+ int abits = cell->parameters.at("\\ABITS").as_int();
+ int width = cell->parameters.at("\\WIDTH").as_int();
+ int size = cell->parameters.at("\\SIZE").as_int();
+ int rd_ports = cell->parameters.at("\\RD_PORTS").as_int();
+ int wr_ports = cell->parameters.at("\\WR_PORTS").as_int();
+
+ Const initdata = cell->parameters.at("\\INIT");
+ for (State bit : initdata.bits)
+ if (bit != State::Sx)
+ log_error("Memory with initialization data: %s.%s\n", log_id(module), log_id(cell));
+
+ Const rd_clk_enable = cell->parameters.at("\\RD_CLK_ENABLE");
+ Const wr_clk_enable = cell->parameters.at("\\WR_CLK_ENABLE");
+ Const wr_clk_polarity = cell->parameters.at("\\WR_CLK_POLARITY");
+
+ int offset = cell->parameters.at("\\OFFSET").as_int();
+ 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 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);
+ }
+
+ for (int i = 0; i < wr_ports; i++)
+ {
+ if (wr_clk_enable[i] != State::S1)
+ log_error("Unclocked write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell));
+
+ 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));
+
+ SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width);
+ string wen_expr = make_expr(wen_sig[0]);
+
+ for (int i = 1; i < GetSize(wen_sig); i++)
+ 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()));
+ }
+
+ continue;
+ }
+
+ if (cell->type.in("$dff"))
+ {
+ bool clkpol = cell->parameters.at("\\CLK_POLARITY").as_bool();
+ 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()));
+
+ cell_exprs.push_back(stringf(" %s <= %s\n", q_id.c_str(), expr.c_str()));
+ register_reverse_wire_map(q_id, cell->getPort("\\Q"));
+
+ continue;
+ }
+
+ log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell));
+ }
+
+ for (auto conn : module->connections())
+ {
+ string y_id = next_id();
+ int y_width = GetSize(conn.first);
+ string expr = make_expr(conn.second);
+
+ 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(), expr.c_str()));
+ register_reverse_wire_map(y_id, conn.first);
+ }
+
+ for (auto wire : module->wires())
+ {
+ string expr;
+
+ if (wire->port_input)
+ continue;
+
+ int cursor = 0;
+ bool is_valid = false;
+ bool make_unconn_id = false;
+
+ while (cursor < wire->width)
+ {
+ int chunk_width = 1;
+ string new_expr;
+
+ SigBit start_bit(wire, cursor);
+
+ if (reverse_wire_map.count(start_bit))
+ {
+ pair<string, int> start_map = reverse_wire_map.at(start_bit);
+
+ while (cursor+chunk_width < wire->width)
+ {
+ SigBit stop_bit(wire, cursor+chunk_width);
+
+ if (reverse_wire_map.count(stop_bit) == 0)
+ break;
+
+ pair<string, int> stop_map = reverse_wire_map.at(stop_bit);
+ stop_map.second -= chunk_width;
+
+ if (start_map != stop_map)
+ break;
+
+ chunk_width++;
+ }
+
+ new_expr = stringf("bits(%s, %d, %d)", start_map.first.c_str(),
+ start_map.second + chunk_width - 1, start_map.second);
+ is_valid = true;
+ }
+ else
+ {
+ if (unconn_id.empty()) {
+ unconn_id = next_id();
+ make_unconn_id = true;
+ }
+ new_expr = unconn_id;
+ }
+
+ if (expr.empty())
+ expr = new_expr;
+ else
+ expr = "cat(" + new_expr + ", " + expr + ")";
+
+ cursor += chunk_width;
+ }
+
+ if (is_valid) {
+ if (make_unconn_id) {
+ wire_decls.push_back(stringf(" wire %s: UInt<1>\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_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name)));
+ }
+ }
+
+ for (auto str : port_decls)
+ f << str;
+
+ f << stringf("\n");
+
+ for (auto str : wire_decls)
+ f << str;
+
+ f << stringf("\n");
+
+ for (auto str : cell_exprs)
+ f << str;
+
+ f << stringf("\n");
+
+ for (auto str : wire_exprs)
+ f << str;
+ }
+};
+
+struct FirrtlBackend : public Backend {
+ FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { }
+ 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("\n");
+ }
+ 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;
+ }
+ extra_args(f, filename, args, argidx);
+
+ log_header(design, "Executing FIRRTL backend.\n");
+
+ Module *top = design->top_module();
+
+ if (top == nullptr)
+ log_error("No top module found!\n");
+
+ namecache.clear();
+ autoid_counter = 0;
+
+ for (auto module : design->modules()) {
+ make_id(module->name);
+ for (auto wire : module->wires())
+ if (wire->port_id)
+ make_id(wire->name);
+ }
+
+ *f << stringf("circuit %s:\n", make_id(top->name));
+
+ for (auto module : design->modules())
+ {
+ FirrtlWorker worker(module, *f, design);
+ worker.run();
+ }
+
+ namecache.clear();
+ autoid_counter = 0;
+ }
+} FirrtlBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/firrtl/test.sh b/backends/firrtl/test.sh
new file mode 100644
index 00000000..fe7e3a32
--- /dev/null
+++ b/backends/firrtl/test.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -ex
+
+cd ../../
+make
+cd backends/firrtl
+
+../../yosys -q -p 'prep -nordff; write_firrtl test.fir' $1
+
+firrtl -i test.fir -o test_out.v -ll Info
+
+../../yosys -p "
+ read_verilog $1
+ rename Top gold
+
+ read_verilog test_out.v
+ rename Top gate
+
+ prep
+ memory_map
+ miter -equiv -flatten gold gate miter
+ hierarchy -top miter
+
+ sat -verify -prove trigger 0 -set-init-zero -seq 10 miter
+"
diff --git a/backends/firrtl/test.v b/backends/firrtl/test.v
new file mode 100644
index 00000000..c6d62a84
--- /dev/null
+++ b/backends/firrtl/test.v
@@ -0,0 +1,63 @@
+module test(
+ input clk, wen,
+ input [7:0] uns,
+ input signed [7:0] a, b,
+ input signed [23:0] c,
+ input signed [2:0] sel,
+ output [15:0] s, d, y, z, u, q, p, mul, div, mod, mux, And, Or, Xor, eq, neq, gt, lt, geq, leq, eqx, shr, sshr, shl, sshl, Land, Lor, Lnot, Not, Neg, pos, Andr, Orr, Xorr, Xnorr, Reduce_bool,
+ output [7:0] PMux
+);
+ //initial begin
+ //$display("shr = %b", shr);
+ //end
+ assign s = a+{b[6:2], 2'b1};
+ assign d = a-b;
+ assign y = x;
+ assign z[7:0] = s+d;
+ assign z[15:8] = s-d;
+ assign p = a & b | x;
+ assign mul = a * b;
+ assign div = a / b;
+ assign mod = a % b;
+ assign mux = x[0] ? a : b;
+ assign And = a & b;
+ assign Or = a | b;
+ assign Xor = a ^ b;
+ assign Not = ~a;
+ assign Neg = -a;
+ assign eq = a == b;
+ assign neq = a != b;
+ assign gt = a > b;
+ assign lt = a < b;
+ assign geq = a >= b;
+ assign leq = a <= b;
+ assign eqx = a === b;
+ assign shr = a >> b; //0111111111000000
+ assign sshr = a >>> b;
+ assign shl = a << b;
+ assign sshl = a <<< b;
+ assign Land = a && b;
+ assign Lor = a || b;
+ assign Lnot = !a;
+ assign pos = $signed(uns);
+ assign Andr = &a;
+ assign Orr = |a;
+ assign Xorr = ^a;
+ assign Xnorr = ~^a;
+ always @*
+ if(!a) begin
+ Reduce_bool = a;
+ end else begin
+ Reduce_bool = b;
+ end
+ //always @(sel or c or a)
+ // begin
+ // case (sel)
+ // 3'b000: PMux = a;
+ // 3'b001: PMux = c[7:0];
+ // 3'b010: PMux = c[15:8];
+ // 3'b100: PMux = c[23:16];
+ // endcase
+ // end
+
+endmodule
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
index 16d1a97f..4c58ea08 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/ilang/ilang_backend.cc
@@ -219,7 +219,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 +382,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 +395,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 +422,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 +445,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;
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
index 34cb52fb..2eb08dbe 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();
diff --git a/backends/json/json.cc b/backends/json/json.cc
index 4baffa33..f5c68798 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -93,8 +93,10 @@ struct JsonWriter
f << get_string(param.second.decode_string());
else if (GetSize(param.second.bits) > 32)
f << get_string(param.second.as_string());
- else
+ else if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0)
f << stringf("%d", param.second.as_int());
+ else
+ f << stringf("%u", param.second.as_int());
first = false;
}
}
@@ -201,6 +203,8 @@ struct JsonWriter
void write_design(Design *design_)
{
design = design_;
+ design->sort();
+
f << stringf("{\n");
f << stringf(" \"creator\": %s,\n", get_string(yosys_version_str).c_str());
f << stringf(" \"modules\": {\n");
@@ -248,7 +252,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");
@@ -333,6 +337,10 @@ struct JsonBackend : public Backend {
log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n");
log("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("\n");
log("For example the following Verilog code:\n");
log("\n");
log(" module test(input x, y);\n");
@@ -452,7 +460,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;
@@ -476,7 +484,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");
@@ -493,7 +501,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;
diff --git a/backends/protobuf/.gitignore b/backends/protobuf/.gitignore
new file mode 100644
index 00000000..849b38d4
--- /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 00000000..834cad42
--- /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 00000000..f56147ce
--- /dev/null
+++ b/backends/protobuf/protobuf.cc
@@ -0,0 +1,370 @@
+/*
+ * 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);
+
+ 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()) {
+ std::ofstream *ff = new std::ofstream;
+ ff->open(filename.c_str(), std::ofstream::trunc);
+ 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/.gitignore b/backends/simplec/.gitignore
new file mode 100644
index 00000000..f0879616
--- /dev/null
+++ b/backends/simplec/.gitignore
@@ -0,0 +1,2 @@
+test00_tb
+test00_uut.c
diff --git a/backends/simplec/Makefile.inc b/backends/simplec/Makefile.inc
new file mode 100644
index 00000000..fee1376c
--- /dev/null
+++ b/backends/simplec/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/simplec/simplec.o
+
diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc
new file mode 100644
index 00000000..349bc5a6
--- /dev/null
+++ b/backends/simplec/simplec.cc
@@ -0,0 +1,810 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/sigtools.h"
+#include "kernel/utils.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct HierDirtyFlags;
+
+static pool<string> reserved_cids;
+static dict<IdString, string> id2cid;
+
+static string cid(IdString id)
+{
+ if (id2cid.count(id) == 0)
+ {
+ string s = id.str();
+ if (GetSize(s) < 2) log_abort();
+
+ if (s[0] == '\\')
+ s = s.substr(1);
+
+ if ('0' <= s[0] && s[0] <= '9') {
+ s = "_" + s;
+ }
+
+ for (int i = 0; i < GetSize(s); i++) {
+ if ('0' <= s[i] && s[i] <= '9') continue;
+ if ('A' <= s[i] && s[i] <= 'Z') continue;
+ if ('a' <= s[i] && s[i] <= 'z') continue;
+ s[i] = '_';
+ }
+
+ while (reserved_cids.count(s))
+ s += "_";
+
+ reserved_cids.insert(s);
+ id2cid[id] = s;
+ }
+
+ return id2cid.at(id);
+}
+
+struct HierDirtyFlags
+{
+ int dirty;
+ Module *module;
+ IdString hiername;
+ HierDirtyFlags *parent;
+ pool<SigBit> dirty_bits;
+ pool<Cell*> dirty_cells;
+ pool<SigBit> sticky_dirty_bits;
+ dict<IdString, HierDirtyFlags*> children;
+ string prefix, log_prefix;
+
+ HierDirtyFlags(Module *module, IdString hiername, HierDirtyFlags *parent, const string &prefix, const string &log_prefix) :
+ dirty(0), module(module), hiername(hiername), parent(parent), prefix(prefix), log_prefix(log_prefix)
+ {
+ for (Cell *cell : module->cells()) {
+ Module *mod = module->design->module(cell->type);
+ if (mod) children[cell->name] = new HierDirtyFlags(mod, cell->name, this,
+ prefix + cid(cell->name) + ".", log_prefix + "." + prefix + log_id(cell->name));
+ }
+ }
+
+ ~HierDirtyFlags()
+ {
+ for (auto &child : children)
+ delete child.second;
+ }
+
+ void set_dirty(SigBit bit)
+ {
+ if (dirty_bits.count(bit))
+ return;
+
+ dirty_bits.insert(bit);
+ sticky_dirty_bits.insert(bit);
+
+ HierDirtyFlags *p = this;
+ while (p != nullptr) {
+ p->dirty++;
+ p = p->parent;
+ }
+ }
+
+ void unset_dirty(SigBit bit)
+ {
+ if (dirty_bits.count(bit) == 0)
+ return;
+
+ dirty_bits.erase(bit);
+
+ HierDirtyFlags *p = this;
+ while (p != nullptr) {
+ p->dirty--;
+ log_assert(p->dirty >= 0);
+ p = p->parent;
+ }
+ }
+
+ void set_dirty(Cell *cell)
+ {
+ if (dirty_cells.count(cell))
+ return;
+
+ dirty_cells.insert(cell);
+
+ HierDirtyFlags *p = this;
+ while (p != nullptr) {
+ p->dirty++;
+ p = p->parent;
+ }
+ }
+
+ void unset_dirty(Cell *cell)
+ {
+ if (dirty_cells.count(cell) == 0)
+ return;
+
+ dirty_cells.erase(cell);
+
+ HierDirtyFlags *p = this;
+ while (p != nullptr) {
+ p->dirty--;
+ log_assert(p->dirty >= 0);
+ p = p->parent;
+ }
+ }
+};
+
+struct SimplecWorker
+{
+ bool verbose = false;
+ int max_uintsize = 32;
+
+ Design *design;
+ dict<Module*, SigMap> sigmaps;
+
+ vector<string> signal_declarations;
+ pool<int> generated_sigtypes;
+
+ vector<string> util_declarations;
+ pool<string> generated_utils;
+
+ vector<string> struct_declarations;
+ pool<IdString> generated_structs;
+
+ vector<string> funct_declarations;
+
+ dict<Module*, dict<SigBit, pool<tuple<Cell*, IdString, int>>>> bit2cell;
+ dict<Module*, dict<SigBit, pool<SigBit>>> bit2output;
+ dict<Module*, pool<SigBit>> driven_bits;
+
+ dict<Cell*, int> topoidx;
+
+ pool<string> activated_cells;
+ pool<string> reactivated_cells;
+
+ SimplecWorker(Design *design) : design(design)
+ {
+ }
+
+ string sigtype(int n)
+ {
+ string struct_name = stringf("signal%d_t", n);
+
+ if (generated_sigtypes.count(n) == 0)
+ {
+ signal_declarations.push_back("");
+ signal_declarations.push_back(stringf("#ifndef YOSYS_SIMPLEC_SIGNAL%d_T", n));
+ signal_declarations.push_back(stringf("#define YOSYS_SIMPLEC_SIGNAL%d_T", n));
+ signal_declarations.push_back(stringf("typedef struct {"));
+
+ for (int k = 8; k <= max_uintsize; k = 2*k)
+ if (n <= k && k <= max_uintsize) {
+ signal_declarations.push_back(stringf(" uint%d_t value_%d_0 : %d;", k, n-1, n));
+ goto end_struct;
+ }
+
+ for (int k = 0; k < n; k += max_uintsize) {
+ int bits = std::min(max_uintsize, n-k);
+ signal_declarations.push_back(stringf(" uint%d_t value_%d_%d : %d;", max_uintsize, k+bits-1, k, bits));
+ }
+
+ end_struct:
+ signal_declarations.push_back(stringf("} signal%d_t;", n));
+ signal_declarations.push_back(stringf("#endif"));
+ generated_sigtypes.insert(n);
+ }
+
+ return struct_name;
+ }
+
+ void util_ifdef_guard(string s)
+ {
+ for (int i = 0; i < GetSize(s); i++)
+ if ('a' <= s[i] && s[i] <= 'z')
+ s[i] -= 'a' - 'A';
+
+ util_declarations.push_back("");
+ util_declarations.push_back(stringf("#ifndef %s", s.c_str()));
+ util_declarations.push_back(stringf("#define %s", s.c_str()));
+ }
+
+ string util_get_bit(const string &signame, int n, int idx)
+ {
+ if (n == 1 && idx == 0)
+ return signame + ".value_0_0";
+
+ string util_name = stringf("yosys_simplec_get_bit_%d_of_%d", idx, n);
+
+ if (generated_utils.count(util_name) == 0)
+ {
+ util_ifdef_guard(util_name);
+ util_declarations.push_back(stringf("static inline bool %s(const %s *sig)", util_name.c_str(), sigtype(n).c_str()));
+ util_declarations.push_back(stringf("{"));
+
+ int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
+ string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize);
+
+ util_declarations.push_back(stringf(" return (sig->%s >> %d) & 1;", value_name.c_str(), word_offset));
+
+ util_declarations.push_back(stringf("}"));
+ util_declarations.push_back(stringf("#endif"));
+ generated_utils.insert(util_name);
+ }
+
+ return stringf("%s(&%s)", util_name.c_str(), signame.c_str());
+ }
+
+ string util_set_bit(const string &signame, int n, int idx, const string &expr)
+ {
+ if (n == 1 && idx == 0)
+ return stringf(" %s.value_0_0 = %s;", signame.c_str(), expr.c_str());
+
+ string util_name = stringf("yosys_simplec_set_bit_%d_of_%d", idx, n);
+
+ if (generated_utils.count(util_name) == 0)
+ {
+ util_ifdef_guard(util_name);
+ util_declarations.push_back(stringf("static inline void %s(%s *sig, bool value)", util_name.c_str(), sigtype(n).c_str()));
+ util_declarations.push_back(stringf("{"));
+
+ int word_idx = idx / max_uintsize, word_offset = idx % max_uintsize;
+ string value_name = stringf("value_%d_%d", std::min(n-1, (word_idx+1)*max_uintsize-1), word_idx*max_uintsize);
+
+ #if 0
+ util_declarations.push_back(stringf(" if (value)"));
+ util_declarations.push_back(stringf(" sig->%s |= 1UL << %d;", value_name.c_str(), word_offset));
+ util_declarations.push_back(stringf(" else"));
+ util_declarations.push_back(stringf(" sig->%s &= ~(1UL << %d);", value_name.c_str(), word_offset));
+ #else
+ util_declarations.push_back(stringf(" sig->%s = (sig->%s & ~((uint%d_t)1 << %d)) | ((uint%d_t)value << %d);",
+ value_name.c_str(), value_name.c_str(), max_uintsize, word_offset, max_uintsize, word_offset));
+ #endif
+
+ util_declarations.push_back(stringf("}"));
+ util_declarations.push_back(stringf("#endif"));
+ generated_utils.insert(util_name);
+ }
+
+ return stringf(" %s(&%s, %s);", util_name.c_str(), signame.c_str(), expr.c_str());
+ }
+
+ void create_module_struct(Module *mod)
+ {
+ if (generated_structs.count(mod->name))
+ return;
+
+ generated_structs.insert(mod->name);
+ sigmaps[mod].set(mod);
+
+ for (Wire *w : mod->wires())
+ {
+ if (w->port_output)
+ for (auto bit : SigSpec(w))
+ bit2output[mod][sigmaps.at(mod)(bit)].insert(bit);
+ }
+
+ for (Cell *c : mod->cells())
+ {
+ for (auto &conn : c->connections())
+ {
+ if (!c->input(conn.first)) {
+ for (auto bit : sigmaps.at(mod)(conn.second))
+ driven_bits[mod].insert(bit);
+ continue;
+ }
+
+ int idx = 0;
+ for (auto bit : sigmaps.at(mod)(conn.second))
+ bit2cell[mod][bit].insert(tuple<Cell*, IdString, int>(c, conn.first, idx++));
+ }
+
+ if (design->module(c->type))
+ create_module_struct(design->module(c->type));
+ }
+
+ TopoSort<IdString> topo;
+
+ for (Cell *c : mod->cells())
+ {
+ topo.node(c->name);
+
+ for (auto &conn : c->connections())
+ {
+ if (!c->input(conn.first))
+ continue;
+
+ for (auto bit : sigmaps.at(mod)(conn.second))
+ for (auto &it : bit2cell[mod][bit])
+ topo.edge(c->name, std::get<0>(it)->name);
+ }
+ }
+
+ topo.analyze_loops = false;
+ topo.sort();
+
+ for (int i = 0; i < GetSize(topo.sorted); i++)
+ topoidx[mod->cell(topo.sorted[i])] = i;
+
+ string ifdef_name = stringf("yosys_simplec_%s_state_t", cid(mod->name).c_str());
+
+ for (int i = 0; i < GetSize(ifdef_name); i++)
+ if ('a' <= ifdef_name[i] && ifdef_name[i] <= 'z')
+ ifdef_name[i] -= 'a' - 'A';
+
+ struct_declarations.push_back("");
+ struct_declarations.push_back(stringf("#ifndef %s", ifdef_name.c_str()));
+ struct_declarations.push_back(stringf("#define %s", ifdef_name.c_str()));
+ struct_declarations.push_back(stringf("struct %s_state_t", cid(mod->name).c_str()));
+ struct_declarations.push_back("{");
+
+ struct_declarations.push_back(" // Input Ports");
+ for (Wire *w : mod->wires())
+ if (w->port_input)
+ struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
+
+ struct_declarations.push_back("");
+ struct_declarations.push_back(" // Output Ports");
+ for (Wire *w : mod->wires())
+ if (!w->port_input && w->port_output)
+ struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
+
+ struct_declarations.push_back("");
+ struct_declarations.push_back(" // Internal Wires");
+ for (Wire *w : mod->wires())
+ if (!w->port_input && !w->port_output)
+ struct_declarations.push_back(stringf(" %s %s; // %s", sigtype(w->width).c_str(), cid(w->name).c_str(), log_id(w)));
+
+ for (Cell *c : mod->cells())
+ if (design->module(c->type))
+ struct_declarations.push_back(stringf(" struct %s_state_t %s; // %s", cid(c->type).c_str(), cid(c->name).c_str(), log_id(c)));
+
+ struct_declarations.push_back(stringf("};"));
+ struct_declarations.push_back("#endif");
+ }
+
+ void eval_cell(HierDirtyFlags *work, Cell *cell)
+ {
+ if (cell->type.in("$_BUF_", "$_NOT_"))
+ {
+ SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
+ SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
+
+ string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
+ string expr;
+
+ if (cell->type == "$_BUF_") expr = a_expr;
+ if (cell->type == "$_NOT_") expr = "!" + a_expr;
+
+ log_assert(y.wire);
+ funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
+ stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+
+ work->set_dirty(y);
+ return;
+ }
+
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
+ {
+ SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
+ SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
+ SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
+
+ string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
+ string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
+ string expr;
+
+ if (cell->type == "$_AND_") expr = stringf("%s & %s", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_NAND_") expr = stringf("!(%s & %s)", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_OR_") expr = stringf("%s | %s", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_NOR_") expr = stringf("!(%s | %s)", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_XOR_") expr = stringf("%s ^ %s", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_XNOR_") expr = stringf("!(%s ^ %s)", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_ANDNOT_") expr = stringf("%s & (!%s)", a_expr.c_str(), b_expr.c_str());
+ if (cell->type == "$_ORNOT_") expr = stringf("%s | (!%s)", a_expr.c_str(), b_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) +
+ stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+
+ work->set_dirty(y);
+ return;
+ }
+
+ if (cell->type.in("$_AOI3_", "$_OAI3_"))
+ {
+ SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
+ SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
+ SigBit c = sigmaps.at(work->module)(cell->getPort("\\C"));
+ SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
+
+ string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
+ string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
+ string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0";
+ string expr;
+
+ if (cell->type == "$_AOI3_") expr = stringf("!((%s & %s) | %s)", a_expr.c_str(), b_expr.c_str(), c_expr.c_str());
+ if (cell->type == "$_OAI3_") expr = stringf("!((%s | %s) & %s)", a_expr.c_str(), b_expr.c_str(), c_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) +
+ stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+
+ work->set_dirty(y);
+ return;
+ }
+
+ if (cell->type.in("$_AOI4_", "$_OAI4_"))
+ {
+ SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
+ SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
+ SigBit c = sigmaps.at(work->module)(cell->getPort("\\C"));
+ SigBit d = sigmaps.at(work->module)(cell->getPort("\\D"));
+ SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
+
+ string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
+ string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
+ string c_expr = c.wire ? util_get_bit(work->prefix + cid(c.wire->name), c.wire->width, c.offset) : c.data ? "1" : "0";
+ string d_expr = d.wire ? util_get_bit(work->prefix + cid(d.wire->name), d.wire->width, d.offset) : d.data ? "1" : "0";
+ string expr;
+
+ if (cell->type == "$_AOI4_") expr = stringf("!((%s & %s) | (%s & %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_expr.c_str());
+ if (cell->type == "$_OAI4_") expr = stringf("!((%s | %s) & (%s | %s))", a_expr.c_str(), b_expr.c_str(), c_expr.c_str(), d_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) +
+ stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+
+ work->set_dirty(y);
+ return;
+ }
+
+ if (cell->type == "$_MUX_")
+ {
+ SigBit a = sigmaps.at(work->module)(cell->getPort("\\A"));
+ SigBit b = sigmaps.at(work->module)(cell->getPort("\\B"));
+ SigBit s = sigmaps.at(work->module)(cell->getPort("\\S"));
+ SigBit y = sigmaps.at(work->module)(cell->getPort("\\Y"));
+
+ string a_expr = a.wire ? util_get_bit(work->prefix + cid(a.wire->name), a.wire->width, a.offset) : a.data ? "1" : "0";
+ string b_expr = b.wire ? util_get_bit(work->prefix + cid(b.wire->name), b.wire->width, b.offset) : b.data ? "1" : "0";
+ 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());
+
+ log_assert(y.wire);
+ funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) +
+ stringf(" // %s (%s)", log_id(cell), log_id(cell->type)));
+
+ work->set_dirty(y);
+ return;
+ }
+
+ log_error("No C model for %s available at the moment (FIXME).\n", log_id(cell->type));
+ }
+
+ void eval_dirty(HierDirtyFlags *work)
+ {
+ while (work->dirty)
+ {
+ if (verbose && (!work->dirty_bits.empty() || !work->dirty_cells.empty()))
+ log(" In %s:\n", work->log_prefix.c_str());
+
+ while (!work->dirty_bits.empty() || !work->dirty_cells.empty())
+ {
+ if (!work->dirty_bits.empty())
+ {
+ SigSpec dirtysig(work->dirty_bits);
+ dirtysig.sort_and_unify();
+
+ for (SigChunk chunk : dirtysig.chunks()) {
+ if (chunk.wire == nullptr)
+ continue;
+ if (verbose)
+ log(" Propagating %s.%s[%d:%d].\n", work->log_prefix.c_str(), log_id(chunk.wire), chunk.offset+chunk.width-1, chunk.offset);
+ funct_declarations.push_back(stringf(" // Updated signal in %s: %s", work->log_prefix.c_str(), log_signal(chunk)));
+ }
+
+ for (SigBit bit : dirtysig)
+ {
+ if (bit2output[work->module].count(bit) && work->parent)
+ for (auto outbit : bit2output[work->module][bit])
+ {
+ Module *parent_mod = work->parent->module;
+ Cell *parent_cell = parent_mod->cell(work->hiername);
+
+ IdString port_name = outbit.wire->name;
+ int port_offset = outbit.offset;
+ SigBit parent_bit = sigmaps.at(parent_mod)(parent_cell->getPort(port_name)[port_offset]);
+
+ log_assert(bit.wire && parent_bit.wire);
+ funct_declarations.push_back(util_set_bit(work->parent->prefix + cid(parent_bit.wire->name), parent_bit.wire->width, parent_bit.offset,
+ util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset)));
+ work->parent->set_dirty(parent_bit);
+
+ if (verbose)
+ log(" Propagating %s.%s[%d] -> %s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset,
+ work->parent->log_prefix.c_str(), log_id(parent_bit.wire), parent_bit.offset);
+ }
+
+ for (auto &port : bit2cell[work->module][bit])
+ {
+ if (work->children.count(std::get<0>(port)->name))
+ {
+ HierDirtyFlags *child = work->children.at(std::get<0>(port)->name);
+ SigBit child_bit = sigmaps.at(child->module)(SigBit(child->module->wire(std::get<1>(port)), std::get<2>(port)));
+ log_assert(bit.wire && child_bit.wire);
+
+ funct_declarations.push_back(util_set_bit(work->prefix + cid(child->hiername) + "." + cid(child_bit.wire->name),
+ child_bit.wire->width, child_bit.offset, util_get_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset)));
+ child->set_dirty(child_bit);
+
+ if (verbose)
+ log(" Propagating %s.%s[%d] -> %s.%s.%s[%d].\n", work->log_prefix.c_str(), log_id(bit.wire), bit.offset,
+ work->log_prefix.c_str(), log_id(std::get<0>(port)), log_id(child_bit.wire), child_bit.offset);
+ } else {
+ if (verbose)
+ log(" Marking cell %s.%s (via %s.%s[%d]).\n", work->log_prefix.c_str(), log_id(std::get<0>(port)),
+ work->log_prefix.c_str(), log_id(bit.wire), bit.offset);
+ work->set_dirty(std::get<0>(port));
+ }
+ }
+ work->unset_dirty(bit);
+ }
+ }
+
+ if (!work->dirty_cells.empty())
+ {
+ Cell *cell = nullptr;
+ for (auto c : work->dirty_cells)
+ if (cell == nullptr || topoidx.at(cell) < topoidx.at(c))
+ cell = c;
+
+ string hiername = work->log_prefix + "." + log_id(cell);
+
+ if (verbose)
+ log(" Evaluating %s (%s, best of %d).\n", hiername.c_str(), log_id(cell->type), GetSize(work->dirty_cells));
+
+ if (activated_cells.count(hiername))
+ reactivated_cells.insert(hiername);
+ activated_cells.insert(hiername);
+
+ eval_cell(work, cell);
+ work->unset_dirty(cell);
+ }
+ }
+
+ for (auto &child : work->children)
+ eval_dirty(child.second);
+ }
+ }
+
+ void eval_sticky_dirty(HierDirtyFlags *work)
+ {
+ Module *mod = work->module;
+
+ for (Wire *w : mod->wires())
+ for (SigBit bit : SigSpec(w))
+ {
+ SigBit canonical_bit = sigmaps.at(mod)(bit);
+
+ if (canonical_bit == bit)
+ continue;
+
+ if (work->sticky_dirty_bits.count(canonical_bit) == 0)
+ continue;
+
+ if (bit.wire == nullptr || canonical_bit.wire == nullptr)
+ continue;
+
+ funct_declarations.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset,
+ util_get_bit(work->prefix + cid(canonical_bit.wire->name), canonical_bit.wire->width, canonical_bit.offset).c_str()));
+
+ if (verbose)
+ log(" Propagating alias %s.%s[%d] -> %s.%s[%d].\n",
+ work->log_prefix.c_str(), log_id(canonical_bit.wire), canonical_bit.offset,
+ work->log_prefix.c_str(), log_id(bit.wire), bit.offset);
+ }
+
+ work->sticky_dirty_bits.clear();
+
+ for (auto &child : work->children)
+ eval_sticky_dirty(child.second);
+ }
+
+ void make_func(HierDirtyFlags *work, const string &func_name, const vector<string> &preamble)
+ {
+ log("Generating function %s():\n", func_name.c_str());
+
+ activated_cells.clear();
+ reactivated_cells.clear();
+
+ funct_declarations.push_back("");
+ funct_declarations.push_back(stringf("static void %s(struct %s_state_t *state)", func_name.c_str(), cid(work->module->name).c_str()));
+ funct_declarations.push_back("{");
+ for (auto &line : preamble)
+ funct_declarations.push_back(line);
+ eval_dirty(work);
+ eval_sticky_dirty(work);
+ funct_declarations.push_back("}");
+
+ log(" Activated %d cells (%d activated more than once).\n", GetSize(activated_cells), GetSize(reactivated_cells));
+ }
+
+ void eval_init(HierDirtyFlags *work, vector<string> &preamble)
+ {
+ Module *module = work->module;
+
+ for (Wire *w : module->wires())
+ {
+ if (w->attributes.count("\\init"))
+ {
+ SigSpec sig = sigmaps.at(module)(w);
+ Const val = w->attributes.at("\\init");
+ val.bits.resize(GetSize(sig), State::Sx);
+
+ for (int i = 0; i < GetSize(sig); i++)
+ if (val[i] == State::S0 || val[i] == State::S1) {
+ SigBit bit = sig[i];
+ preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false"));
+ work->set_dirty(bit);
+ }
+ }
+
+ for (SigBit bit : SigSpec(w))
+ {
+ SigBit val = sigmaps.at(module)(bit);
+
+ if (val == State::S0 || val == State::S1)
+ preamble.push_back(util_set_bit(work->prefix + cid(bit.wire->name), bit.wire->width, bit.offset, val == State::S1 ? "true" : "false"));
+
+ if (driven_bits.at(module).count(val) == 0)
+ work->set_dirty(val);
+ }
+ }
+
+ work->set_dirty(State::S0);
+ work->set_dirty(State::S1);
+
+ for (auto &child : work->children)
+ eval_init(child.second, preamble);
+ }
+
+ void make_init_func(HierDirtyFlags *work)
+ {
+ vector<string> preamble;
+ eval_init(work, preamble);
+ make_func(work, cid(work->module->name) + "_init", preamble);
+ }
+
+ void make_eval_func(HierDirtyFlags *work)
+ {
+ Module *mod = work->module;
+ vector<string> preamble;
+
+ for (Wire *w : mod->wires()) {
+ if (w->port_input)
+ for (SigBit bit : sigmaps.at(mod)(w))
+ work->set_dirty(bit);
+ }
+
+ make_func(work, cid(work->module->name) + "_eval", preamble);
+ }
+
+ void make_tick_func(HierDirtyFlags* /* work */)
+ {
+ // FIXME
+ }
+
+ void run(Module *mod)
+ {
+ create_module_struct(mod);
+
+ HierDirtyFlags work(mod, IdString(), nullptr, "state->", log_id(mod->name));
+
+ make_init_func(&work);
+ make_eval_func(&work);
+ make_tick_func(&work);
+ }
+
+ void write(std::ostream &f)
+ {
+ f << "#include <stdint.h>" << std::endl;
+ f << "#include <stdbool.h>" << std::endl;
+
+ for (auto &line : signal_declarations)
+ f << line << std::endl;
+
+ for (auto &line : util_declarations)
+ f << line << std::endl;
+
+ for (auto &line : struct_declarations)
+ f << line << std::endl;
+
+ for (auto &line : funct_declarations)
+ f << line << std::endl;
+ }
+};
+
+struct SimplecBackend : public Backend {
+ SimplecBackend() : Backend("simplec", "convert design to simple C code") { }
+ 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("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");
+ log(" -verbose\n");
+ log(" this will print the recursive walk used to export the modules.\n");
+ log("\n");
+ log(" -i8, -i16, -i32, -i64\n");
+ log(" set the maximum integer bit width to use in the generated code.\n");
+ log("\n");
+ log("THIS COMMAND IS UNDER CONSTRUCTION\n");
+ log("\n");
+ }
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ reserved_cids.clear();
+ id2cid.clear();
+
+ SimplecWorker worker(design);
+
+ log_header(design, "Executing SIMPLEC backend.\n");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ if (args[argidx] == "-verbose") {
+ worker.verbose = true;
+ continue;
+ }
+ if (args[argidx] == "-i8") {
+ worker.max_uintsize = 8;
+ continue;
+ }
+ if (args[argidx] == "-i16") {
+ worker.max_uintsize = 16;
+ continue;
+ }
+ if (args[argidx] == "-i32") {
+ worker.max_uintsize = 32;
+ continue;
+ }
+ if (args[argidx] == "-i64") {
+ worker.max_uintsize = 64;
+ continue;
+ }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ Module *topmod = design->top_module();
+
+ if (topmod == nullptr)
+ log_error("Current design has no top module.\n");
+
+ worker.run(topmod);
+ worker.write(*f);
+ }
+} SimplecBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/simplec/test00.sh b/backends/simplec/test00.sh
new file mode 100644
index 00000000..ede75727
--- /dev/null
+++ b/backends/simplec/test00.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -ex
+../../yosys -p 'synth -top test; write_simplec -verbose -i8 test00_uut.c' test00_uut.v
+clang -o test00_tb test00_tb.c
+./test00_tb
diff --git a/backends/simplec/test00_tb.c b/backends/simplec/test00_tb.c
new file mode 100644
index 00000000..7fac4826
--- /dev/null
+++ b/backends/simplec/test00_tb.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <assert.h>
+#include "test00_uut.c"
+
+uint32_t xorshift32()
+{
+ static uint32_t x32 = 314159265;
+ x32 ^= x32 << 13;
+ x32 ^= x32 >> 17;
+ x32 ^= x32 << 5;
+ return x32;
+}
+
+int main()
+{
+ struct test_state_t state;
+ uint32_t a, b, c, x, y, z, w;
+ bool first_eval = true;
+
+ for (int i = 0; i < 10; i++)
+ {
+ a = xorshift32();
+ b = xorshift32();
+ c = xorshift32();
+
+ x = (a & b) | c;
+ y = a & (b | c);
+ z = a ^ b ^ c;
+ w = z;
+
+ state.a.value_7_0 = a;
+ state.a.value_15_8 = a >> 8;
+ state.a.value_23_16 = a >> 16;
+ state.a.value_31_24 = a >> 24;
+
+ state.b.value_7_0 = b;
+ state.b.value_15_8 = b >> 8;
+ state.b.value_23_16 = b >> 16;
+ state.b.value_31_24 = b >> 24;
+
+ state.c.value_7_0 = c;
+ state.c.value_15_8 = c >> 8;
+ state.c.value_23_16 = c >> 16;
+ state.c.value_31_24 = c >> 24;
+
+ if (first_eval) {
+ first_eval = false;
+ test_init(&state);
+ } else {
+ test_eval(&state);
+ }
+
+ uint32_t uut_x = 0;
+ uut_x |= (uint32_t)state.x.value_7_0;
+ uut_x |= (uint32_t)state.x.value_15_8 << 8;
+ uut_x |= (uint32_t)state.x.value_23_16 << 16;
+ uut_x |= (uint32_t)state.x.value_31_24 << 24;
+
+ uint32_t uut_y = 0;
+ uut_y |= (uint32_t)state.y.value_7_0;
+ uut_y |= (uint32_t)state.y.value_15_8 << 8;
+ uut_y |= (uint32_t)state.y.value_23_16 << 16;
+ uut_y |= (uint32_t)state.y.value_31_24 << 24;
+
+ uint32_t uut_z = 0;
+ uut_z |= (uint32_t)state.z.value_7_0;
+ uut_z |= (uint32_t)state.z.value_15_8 << 8;
+ uut_z |= (uint32_t)state.z.value_23_16 << 16;
+ uut_z |= (uint32_t)state.z.value_31_24 << 24;
+
+ uint32_t uut_w = 0;
+ uut_w |= (uint32_t)state.w.value_7_0;
+ uut_w |= (uint32_t)state.w.value_15_8 << 8;
+ uut_w |= (uint32_t)state.w.value_23_16 << 16;
+ uut_w |= (uint32_t)state.w.value_31_24 << 24;
+
+ printf("---\n");
+ printf("A: 0x%08x\n", a);
+ printf("B: 0x%08x\n", b);
+ printf("C: 0x%08x\n", c);
+ printf("X: 0x%08x 0x%08x\n", x, uut_x);
+ printf("Y: 0x%08x 0x%08x\n", y, uut_y);
+ printf("Z: 0x%08x 0x%08x\n", z, uut_z);
+ printf("W: 0x%08x 0x%08x\n", w, uut_w);
+
+ assert(x == uut_x);
+ assert(y == uut_y);
+ assert(z == uut_z);
+ assert(w == uut_w);
+ }
+
+ return 0;
+}
diff --git a/backends/simplec/test00_uut.v b/backends/simplec/test00_uut.v
new file mode 100644
index 00000000..744dbe9e
--- /dev/null
+++ b/backends/simplec/test00_uut.v
@@ -0,0 +1,14 @@
+module test(input [31:0] a, b, c, output [31:0] x, y, z, w);
+ unit_x unit_x_inst (.a(a), .b(b), .c(c), .x(x));
+ 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 eacda273..dce82f01 100644
--- a/backends/smt2/Makefile.inc
+++ b/backends/smt2/Makefile.inc
@@ -6,7 +6,7 @@ ifneq ($(CONFIG),emcc)
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 $@
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index ddac6900..e2777ae0 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -32,14 +32,18 @@ struct Smt2Worker
CellTypes ct;
SigMap sigmap;
RTLIL::Module *module;
- bool bvmode, memmode, wiresmode, verbose;
- int idcounter;
+ bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode;
+ dict<IdString, int> &mod_stbv_width;
+ int idcounter = 0, statebv_width = 0;
- std::vector<std::string> decls, trans, hier;
+ 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;
@@ -63,17 +67,64 @@ struct Smt2Worker
return get_id(obj->name);
}
- Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose) :
- ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode),
- wiresmode(wiresmode), verbose(verbose), idcounter(0)
+ void makebits(std::string name, int width = 0, std::string comment = std::string())
+ {
+ std::string decl_str;
+
+ if (statebv)
+ {
+ if (width == 0) {
+ decl_str = stringf("(define-fun |%s| ((state |%s_s|)) Bool (= ((_ extract %d %d) state) #b1))", name.c_str(), get_id(module), statebv_width, statebv_width);
+ statebv_width += 1;
+ } else {
+ decl_str = stringf("(define-fun |%s| ((state |%s_s|)) (_ BitVec %d) ((_ extract %d %d) state))", name.c_str(), get_id(module), width, statebv_width+width-1, statebv_width);
+ statebv_width += width;
+ }
+ }
+ else if (statedt)
+ {
+ if (width == 0) {
+ decl_str = stringf(" (|%s| Bool)", name.c_str());
+ } else {
+ decl_str = stringf(" (|%s| (_ BitVec %d))", name.c_str(), width);
+ }
+ }
+ else
+ {
+ if (width == 0) {
+ decl_str = stringf("(declare-fun |%s| (|%s_s|) Bool)", name.c_str(), get_id(module));
+ } else {
+ decl_str = stringf("(declare-fun |%s| (|%s_s|) (_ BitVec %d))", name.c_str(), get_id(module), width);
+ }
+ }
+
+ if (!comment.empty())
+ decl_str += " ; " + comment;
+
+ if (statedt)
+ dtmembers.push_back(decl_str + "\n");
+ else
+ decls.push_back(decl_str + "\n");
+ }
+
+ 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), forallmode(forallmode), mod_stbv_width(mod_stbv_width)
{
- decls.push_back(stringf("(declare-sort |%s_s| 0)\n", get_id(module)));
- decls.push_back(stringf("(declare-fun |%s_is| (|%s_s|) Bool)\n", get_id(module), get_id(module)));
+ 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))
@@ -83,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;
}
}
@@ -162,8 +273,7 @@ struct Smt2Worker
if (fcache.count(bit) == 0) {
if (verbose) log("%*s-> external bool: %s\n", 2+2*GetSize(recursive_cells), "",
log_signal(bit));
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
- get_id(module), idcounter, get_id(module), log_signal(bit)));
+ makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(bit));
register_bool(bit, idcounter++);
}
@@ -237,8 +347,7 @@ struct Smt2Worker
log_signal(sig.extract(i, j)));
for (auto bit : sig.extract(i, j))
log_assert(bit_driver.count(bit) == 0);
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
- get_id(module), idcounter, get_id(module), j, log_signal(sig.extract(i, j))));
+ makebits(stringf("%s#%d", get_id(module), idcounter), j, log_signal(sig.extract(i, j)));
subexpr.push_back(stringf("(|%s#%d| %s)", get_id(module), idcounter, state_name));
register_bv(sig.extract(i, j), idcounter++);
}
@@ -288,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")) {
@@ -382,8 +492,7 @@ struct Smt2Worker
if (cell->type.in("$_FF_", "$_DFF_P_", "$_DFF_N_"))
{
registers.insert(cell);
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
- get_id(module), idcounter, get_id(module), log_signal(cell->getPort("\\Q"))));
+ makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(cell->getPort("\\Q")));
register_bool(cell->getPort("\\Q"), idcounter++);
recursive_cells.erase(cell);
return;
@@ -397,6 +506,8 @@ struct Smt2Worker
if (cell->type == "$_NOR_") return export_gate(cell, "(not (or A B))");
if (cell->type == "$_XOR_") return export_gate(cell, "(xor A B)");
if (cell->type == "$_XNOR_") return export_gate(cell, "(not (xor A B))");
+ 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 == "$_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))");
@@ -410,20 +521,22 @@ struct Smt2Worker
if (cell->type.in("$ff", "$dff"))
{
registers.insert(cell);
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
- get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q"))));
+ makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")));
register_bv(cell->getPort("\\Q"), idcounter++);
recursive_cells.erase(cell);
return;
}
- if (cell->type.in("$anyconst", "$anyseq"))
+ if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq"))
{
registers.insert(cell);
- decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
- cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell)));
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
- get_id(module), idcounter, get_id(module), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y"))));
+ 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 %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;
@@ -467,6 +580,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);
@@ -513,30 +633,97 @@ struct Smt2Worker
int abits = cell->getParam("\\ABITS").as_int();
int width = cell->getParam("\\WIDTH").as_int();
int rd_ports = cell->getParam("\\RD_PORTS").as_int();
+ int wr_ports = cell->getParam("\\WR_PORTS").as_int();
- 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)));
+ 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\n", get_id(cell), abits, width, rd_ports));
- 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("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync"));
- for (int i = 0; i < rd_ports; i++)
+ 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)
{
- SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(abits*i, abits);
- SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width);
- std::string addr = get_bv(addr_sig);
+ int mem_size = cell->getParam("\\SIZE").as_int();
+ int mem_offset = cell->getParam("\\OFFSET").as_int();
+
+ 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++)
+ {
+ SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(abits*i, abits);
+ SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width);
+ std::string addr = get_bv(addr_sig);
+
+ if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
+ log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
+
+ 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)));
- if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
- log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
- "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
+ std::string read_expr = "#b";
+ for (int k = 0; k < width; k++)
+ read_expr += "0";
- decls.push_back(stringf("(define-fun |%s_m:%d %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)));
+ for (int k = 0; k < mem_size; k++)
+ 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, memstate.c_str(), read_expr.c_str());
- decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) %s)) ; %s\n",
- get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, addr.c_str(), log_signal(data_sig)));
- register_bv(data_sig, idcounter++);
+ 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)));
+
+ 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));
+
+ register_bv(data_sig, idcounter++);
+ }
+ }
+ else
+ {
+ if (statedt)
+ 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| (|%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| state))\n",
+ get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str()));
+
+ for (int i = 0; i < rd_ports; i++)
+ {
+ SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(abits*i, abits);
+ SigSpec data_sig = cell->getPort("\\RD_DATA").extract(width*i, width);
+ std::string addr = get_bv(addr_sig);
+
+ if (cell->getParam("\\RD_CLK_ENABLE").extract(i).as_bool())
+ log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
+
+ 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| 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));
+
+ register_bv(data_sig, idcounter++);
+ }
}
registers.insert(cell);
@@ -553,32 +740,38 @@ struct Smt2Worker
for (auto &conn : cell->connections())
{
+ if (GetSize(conn.second) == 0)
+ continue;
+
Wire *w = m->wire(conn.first);
SigSpec sig = sigmap(conn.second);
if (w->port_output && !w->port_input) {
if (GetSize(w) > 1) {
if (bvmode) {
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) (_ BitVec %d)) ; %s\n",
- get_id(module), idcounter, get_id(module), GetSize(w), log_signal(sig)));
+ makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(w), log_signal(sig));
register_bv(sig, idcounter++);
} else {
for (int i = 0; i < GetSize(w); i++) {
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
- get_id(module), idcounter, get_id(module), log_signal(sig[i])));
+ makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig[i]));
register_bool(sig[i], idcounter++);
}
}
} else {
- decls.push_back(stringf("(declare-fun |%s#%d| (|%s_s|) Bool) ; %s\n",
- get_id(module), idcounter, get_id(module), log_signal(sig)));
+ makebits(stringf("%s#%d", get_id(module), idcounter), 0, log_signal(sig));
register_bool(sig, idcounter++);
}
}
}
- decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n",
- get_id(module), get_id(cell->name), get_id(module), get_id(cell->type)));
+ if (statebv)
+ makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type));
+ 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
+ decls.push_back(stringf("(declare-fun |%s_h %s| (|%s_s|) |%s_s|)\n",
+ get_id(module), get_id(cell->name), get_id(module), get_id(cell->type)));
hiercells.insert(cell);
hiercells_queue.insert(cell);
@@ -617,17 +810,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)));
+ }
}
}
}
@@ -639,31 +845,104 @@ struct Smt2Worker
if (wire->attributes.count("\\init")) {
RTLIL::SigSpec sig = sigmap(wire);
Const val = wire->attributes.at("\\init");
- val.bits.resize(GetSize(sig));
+ val.bits.resize(GetSize(sig), State::Sx);
if (bvmode && GetSize(sig) > 1) {
- init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire)));
+ Const mask(State::S1, GetSize(sig));
+ bool use_mask = false;
+ for (int i = 0; i < GetSize(sig); i++)
+ if (val[i] != State::S0 && val[i] != State::S1) {
+ val[i] = State::S0;
+ mask[i] = State::S0;
+ use_mask = true;
+ }
+ if (use_mask)
+ init_list.push_back(stringf("(= (bvand %s #b%s) #b%s) ; %s", get_bv(sig).c_str(), mask.as_string().c_str(), val.as_string().c_str(), get_id(wire)));
+ else
+ init_list.push_back(stringf("(= %s #b%s) ; %s", get_bv(sig).c_str(), val.as_string().c_str(), get_id(wire)));
} else {
for (int i = 0; i < GetSize(sig); i++)
- init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val.bits[i] == State::S1 ? "true" : "false", get_id(wire)));
+ if (val[i] == State::S0 || val[i] == State::S1)
+ init_list.push_back(stringf("(= %s %s) ; %s", get_bool(sig[i]).c_str(), val[i] == State::S1 ? "true" : "false", get_id(wire)));
}
}
if (verbose) log("=> export logic driving asserts\n");
- vector<string> assert_list, assume_list;
+ int assert_id = 0, assume_id = 0, cover_id = 0;
+ vector<string> assert_list, assume_list, cover_list;
+
for (auto cell : module->cells())
- if (cell->type.in("$assert", "$assume")) {
+ {
+ if (cell->type.in("$assert", "$assume", "$cover"))
+ {
+ int &id = cell->type == "$assert" ? assert_id :
+ cell->type == "$assume" ? assume_id :
+ cell->type == "$cover" ? cover_id : *(int*)nullptr;
+
+ char postfix = cell->type == "$assert" ? 'a' :
+ cell->type == "$assume" ? 'u' :
+ cell->type == "$cover" ? 'c' : 0;
+
string name_a = get_bool(cell->getPort("\\A"));
string name_en = get_bool(cell->getPort("\\EN"));
- decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter,
+ 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)));
- decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n",
- get_id(module), idcounter, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
- if (cell->type == "$assert")
- assert_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
+
+ if (cell->type == "$cover")
+ decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n",
+ get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
else
- assume_list.push_back(stringf("(|%s#%d| state)", get_id(module), idcounter++));
+ decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (or %s (not %s))) ; %s\n",
+ get_id(module), postfix, id, get_id(module), name_a.c_str(), name_en.c_str(), get_id(cell)));
+
+ if (cell->type == "$assert")
+ assert_list.push_back(stringf("(|%s_a %d| state)", get_id(module), id));
+ else if (cell->type == "$assume")
+ assume_list.push_back(stringf("(|%s_u %d| state)", get_id(module), id));
+
+ id++;
}
+ }
+
+ if (verbose) log("=> export logic driving hierarchical cells\n");
+
+ for (auto cell : module->cells())
+ if (module->design->module(cell->type) != nullptr)
+ export_cell(cell);
+
+ while (!hiercells_queue.empty())
+ {
+ std::set<RTLIL::Cell*> queue;
+ queue.swap(hiercells_queue);
+
+ for (auto cell : queue)
+ {
+ string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
+ 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);
+
+ if (bvmode || GetSize(w) == 1) {
+ hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(),
+ get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w)));
+ } else {
+ for (int i = 0; i < GetSize(w); i++)
+ hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(),
+ get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i));
+ }
+ }
+ }
+ }
for (int iter = 1; !registers.empty(); iter++)
{
@@ -679,6 +958,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"))
@@ -686,13 +966,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")
@@ -703,24 +986,111 @@ struct Smt2Worker
int width = cell->getParam("\\WIDTH").as_int();
int wr_ports = cell->getParam("\\WR_PORTS").as_int();
- for (int i = 0; i < wr_ports; i++)
+ 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);
+ SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width);
+ SigSpec mask_sig = cell->getPort("\\WR_EN").extract(width*i, width);
+
+ std::string addr = get_bv(addr_sig);
+ std::string data = get_bv(data_sig);
+ std::string mask = get_bv(mask_sig);
+
+ decls.push_back(stringf("(define-fun |%s_m:W%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)));
+ addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell));
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig)));
+ data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell));
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig)));
+ mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell));
+
+ std::string data_expr;
+
+ for (int k = mem_size-1; k >= 0; k--) {
+ std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))",
+ data.c_str(), mask.c_str(), width*(k+1)-1, width*k, get_id(module), arrayid, i, mask.c_str());
+ data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))",
+ addr.c_str(), Const(k+mem_offset, abits).as_string().c_str(), new_data.c_str(),
+ width*(k+1)-1, width*k, get_id(module), arrayid, i);
+ }
+
+ decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n",
+ get_id(module), arrayid, i+1, get_id(module), width*mem_size, data_expr.c_str(), get_id(cell)));
+ }
+ }
+ else
{
- std::string addr = get_bv(cell->getPort("\\WR_ADDR").extract(abits*i, abits));
- std::string data = get_bv(cell->getPort("\\WR_DATA").extract(width*i, width));
- std::string mask = get_bv(cell->getPort("\\WR_EN").extract(width*i, width));
+ 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);
+ SigSpec data_sig = cell->getPort("\\WR_DATA").extract(width*i, width);
+ SigSpec mask_sig = cell->getPort("\\WR_EN").extract(width*i, width);
+
+ std::string addr = get_bv(addr_sig);
+ std::string data = get_bv(data_sig);
+ std::string mask = get_bv(mask_sig);
+
+ decls.push_back(stringf("(define-fun |%s_m:W%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)));
+ addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell));
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig)));
+ data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell));
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig)));
+ mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell));
- data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))",
- data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str());
+ data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))",
+ data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str());
- decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) "
- "(store (|%s#%d#%d| state) %s %s)) ; %s\n",
- get_id(module), arrayid, i+1, get_id(module), abits, width,
- get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(cell)));
+ decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) "
+ "(store (|%s#%d#%d| state) %s %s)) ; %s\n",
+ get_id(module), arrayid, i+1, get_id(module), abits, width,
+ get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(cell)));
+ }
}
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();
@@ -737,46 +1107,20 @@ struct Smt2Worker
if (bit == State::S0 || bit == State::S1)
gen_init_constr = true;
- if (gen_init_constr) {
- init_list.push_back(stringf("(= (select (|%s#%d#0| state) #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));
+ if (gen_init_constr)
+ {
+ if (statebv)
+ /* FIXME */;
+ else
+ init_list.push_back(stringf("(= (select (|%s#%d#0| state) #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));
}
}
}
}
}
- if (verbose) log("=> export logic driving hierarchical cells\n");
-
- while (!hiercells_queue.empty())
- {
- std::set<RTLIL::Cell*> queue;
- queue.swap(hiercells_queue);
-
- for (auto cell : queue)
- {
- string cell_state = stringf("(|%s_h %s| state)", get_id(module), get_id(cell->name));
- Module *m = module->design->module(cell->type);
- log_assert(m != nullptr);
-
- for (auto &conn : cell->connections())
- {
- Wire *w = m->wire(conn.first);
- SigSpec sig = sigmap(conn.second);
-
- if (bvmode || GetSize(w) == 1) {
- hier.push_back(stringf(" (= %s (|%s_n %s| %s)) ; %s.%s\n", (GetSize(w) > 1 ? get_bv(sig) : get_bool(sig)).c_str(),
- get_id(cell->type), get_id(w), cell_state.c_str(), get_id(cell->type), get_id(w)));
- } else {
- for (int i = 0; i < GetSize(w); i++)
- hier.push_back(stringf(" (= %s (|%s_n %s %d| %s)) ; %s.%s[%d]\n", get_bool(sig[i]).c_str(),
- get_id(cell->type), get_id(w), i, cell_state.c_str(), get_id(cell->type), get_id(w), i));
- }
- }
- }
- }
-
if (verbose) log("=> finalizing SMT2 representation of %s.\n", log_id(module));
for (auto c : hiercells) {
@@ -786,6 +1130,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";
@@ -832,6 +1207,18 @@ struct Smt2Worker
{
f << stringf("; yosys-smt2-module %s\n", get_id(module));
+ if (statebv) {
+ f << stringf("(define-sort |%s_s| () (_ BitVec %d))\n", get_id(module), statebv_width);
+ mod_stbv_width[module->name] = statebv_width;
+ } else
+ if (statedt) {
+ f << stringf("(declare-datatype |%s_s| ((|%s_mk|\n", get_id(module), get_id(module));
+ for (auto it : dtmembers)
+ f << it;
+ f << stringf(")))\n");
+ } else
+ f << stringf("(declare-sort |%s_s| 0)\n", get_id(module));
+
for (auto it : decls)
f << it;
@@ -864,44 +1251,92 @@ 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");
log(" write_smt2 [options] [filename]\n");
log("\n");
log("Write a SMT-LIBv2 [1] description of the current design. For a module with name\n");
- log("'<mod>' this will declare the sort '<mod>_s' (state of the module) and the\n");
- log("functions operating on that state.\n");
+ log("'<mod>' this will declare the sort '<mod>_s' (state of the module) and will\n");
+ log("define and declare functions operating on that state.\n");
+ log("\n");
+ log("The following SMT2 functions are generated for a module with name '<mod>'.\n");
+ log("Some declarations/definitions are printed with a special comment. A prover\n");
+ log("using the SMT2 files can use those comments to collect all relevant metadata\n");
+ log("about the design.\n");
+ log("\n");
+ log(" ; yosys-smt2-module <mod>\n");
+ log(" (declare-sort |<mod>_s| 0)\n");
+ log(" The sort representing a state of module <mod>.\n");
+ log("\n");
+ log(" (define-fun |<mod>_h| ((state |<mod>_s|)) Bool (...))\n");
+ log(" This function must be asserted for each state to establish the\n");
+ log(" design hierarchy.\n");
+ log("\n");
+ log(" ; yosys-smt2-input <wirename> <width>\n");
+ log(" ; yosys-smt2-output <wirename> <width>\n");
+ log(" ; yosys-smt2-register <wirename> <width>\n");
+ log(" ; yosys-smt2-wire <wirename> <width>\n");
+ log(" (define-fun |<mod>_n <wirename>| (|<mod>_s|) (_ BitVec <width>))\n");
+ log(" (define-fun |<mod>_n <wirename>| (|<mod>_s|) Bool)\n");
+ log(" For each port, register, and wire with the 'keep' attribute set an\n");
+ log(" accessor function is generated. Single-bit wires are returned as Bool,\n");
+ log(" multi-bit wires as BitVec.\n");
+ log("\n");
+ log(" ; yosys-smt2-cell <submod> <instancename>\n");
+ log(" (declare-fun |<mod>_h <instancename>| (|<mod>_s|) |<submod>_s|)\n");
+ log(" There is a function like that for each hierarchical instance. It\n");
+ log(" returns the sort that represents the state of the sub-module that\n");
+ log(" implements the instance.\n");
+ log("\n");
+ log(" (declare-fun |<mod>_is| (|<mod>_s|) Bool)\n");
+ log(" This function must be asserted 'true' for initial states, and 'false'\n");
+ log(" otherwise.\n");
+ log("\n");
+ log(" (define-fun |<mod>_i| ((state |<mod>_s|)) Bool (...))\n");
+ log(" This function must be asserted 'true' for initial states. For\n");
+ log(" non-initial states it must be left unconstrained.\n");
log("\n");
- log("The '<mod>_s' sort represents a module state. Additional '<mod>_n' functions\n");
- log("are provided that can be used to access the values of the signals in the module.\n");
- log("By default only ports, registers, and wires with the 'keep' attribute set are\n");
- log("made available via such functions. With the -nobv option, multi-bit wires are\n");
- log("exported as separate functions of type Bool for the individual bits. Without\n");
- log("-nobv multi-bit wires are exported as single functions of type BitVec.\n");
+ log(" (define-fun |<mod>_t| ((state |<mod>_s|) (next_state |<mod>_s|)) Bool (...))\n");
+ log(" This function evaluates to 'true' if the states 'state' and\n");
+ log(" 'next_state' form a valid state transition.\n");
log("\n");
- log("The '<mod>_t' function evaluates to 'true' when the given pair of states\n");
- log("describes a valid state transition.\n");
+ log(" (define-fun |<mod>_a| ((state |<mod>_s|)) Bool (...))\n");
+ log(" This function evaluates to 'true' if all assertions hold in the state.\n");
log("\n");
- log("The '<mod>_a' function evaluates to 'true' when the given state satisfies\n");
- log("the asserts in the module.\n");
+ log(" (define-fun |<mod>_u| ((state |<mod>_s|)) Bool (...))\n");
+ log(" This function evaluates to 'true' if all assumptions hold in the state.\n");
log("\n");
- log("The '<mod>_u' function evaluates to 'true' when the given state satisfies\n");
- log("the assumptions in the module.\n");
+ log(" ; yosys-smt2-assert <id> <filename:linenum>\n");
+ log(" (define-fun |<mod>_a <id>| ((state |<mod>_s|)) Bool (...))\n");
+ log(" Each $assert cell is converted into one of this functions. The function\n");
+ log(" evaluates to 'true' if the assert statement holds in the state.\n");
log("\n");
- log("The '<mod>_i' function evaluates to 'true' when the given state conforms\n");
- log("to the initial state. Furthermore the '<mod>_is' function should be asserted\n");
- log("to be true for initial states in addition to '<mod>_i', and should be\n");
- log("asserted to be false for non-initial states.\n");
+ log(" ; yosys-smt2-assume <id> <filename:linenum>\n");
+ log(" (define-fun |<mod>_u <id>| ((state |<mod>_s|)) Bool (...))\n");
+ log(" Each $assume cell is converted into one of this functions. The function\n");
+ log(" evaluates to 'true' if the assume statement holds in the state.\n");
log("\n");
- log("For hierarchical designs, the '<mod>_h' function must be asserted for each\n");
- log("state to establish the design hierarchy. The '<mod>_h <cellname>' function\n");
- log("evaluates to the state corresponding to the given cell within <mod>.\n");
+ log(" ; yosys-smt2-cover <id> <filename:linenum>\n");
+ log(" (define-fun |<mod>_c <id>| ((state |<mod>_s|)) Bool (...))\n");
+ log(" Each $cover cell is converted into one of this functions. The function\n");
+ log(" evaluates to 'true' if the cover statement is activated in the state.\n");
+ log("\n");
+ log("Options:\n");
log("\n");
log(" -verbose\n");
log(" this will print the recursive walk used to export the modules.\n");
log("\n");
+ log(" -stbv\n");
+ log(" Use a BitVec sort to represent a state instead of an uninterpreted\n");
+ log(" sort. As a side-effect this will prevent use of arrays to model\n");
+ log(" memories.\n");
+ log("\n");
+ log(" -stdt\n");
+ log(" Use SMT-LIB 2.6 style datatypes to represent a state instead of an\n");
+ log(" uninterpreted sort.\n");
+ log("\n");
log(" -nobv\n");
log(" disable support for BitVec (FixedSizeBitVectors theory). without this\n");
log(" option multi-bit wires are represented using the BitVec sort and\n");
@@ -972,10 +1407,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;
+ bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false;
+ bool forallmode = false;
log_header(design, "Executing SMT2 backend.\n");
@@ -992,6 +1428,16 @@ struct Smt2Backend : public Backend {
log_warning("Options -bv and -mem are now the default. Support for -bv and -mem will be removed in the future.\n");
continue;
}
+ if (args[argidx] == "-stbv") {
+ statebv = true;
+ statedt = false;
+ continue;
+ }
+ if (args[argidx] == "-stdt") {
+ statebv = false;
+ statedt = true;
+ continue;
+ }
if (args[argidx] == "-nobv") {
bvmode = false;
memmode = false;
@@ -1033,6 +1479,12 @@ struct Smt2Backend : public Backend {
if (!memmode)
*f << stringf("; yosys-smt2-nomem\n");
+ if (statebv)
+ *f << stringf("; yosys-smt2-stbv\n");
+
+ if (statedt)
+ *f << stringf("; yosys-smt2-stdt\n");
+
std::vector<RTLIL::Module*> sorted_modules;
// extract module dependencies
@@ -1062,17 +1514,31 @@ struct Smt2Backend : public Backend {
module_deps.erase(sorted_modules.at(sorted_modules_idx++));
}
+ 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())
continue;
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
- Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose);
+ 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 04c25f91..6af2a5ac 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -22,21 +22,32 @@ import os, sys, getopt, re
from smtio import SmtIo, SmtOpts, MkVcd
from collections import defaultdict
+got_topt = False
skip_steps = 0
step_size = 1
num_steps = 20
+append_steps = 0
vcdfile = None
cexfile = None
+aimfile = None
+aiwfile = None
+aigheader = True
vlogtbfile = None
+vlogtbtop = None
inconstr = list()
outconstr = None
gentrace = False
+covermode = False
tempind = False
dumpall = False
assume_skipped = None
final_only = False
topmod = None
noinfo = False
+presat = False
+smtcinit = False
+smtctop = None
+noinit = False
so = SmtOpts()
@@ -56,6 +67,9 @@ yosys-smtbmc [options] <yosys_smt2_output>
-i
instead of BMC run temporal induction
+ -c
+ instead of regular BMC run cover analysis
+
-m <module_name>
name of the top module
@@ -65,10 +79,30 @@ yosys-smtbmc [options] <yosys_smt2_output>
--cex <cex_filename>
read cex file as written by ABC's "write_cex -n"
+ --aig <prefix>
+ read AIGER map file (as written by Yosys' "write_aiger -map")
+ and AIGER witness file. The file names are <prefix>.aim for
+ the map file and <prefix>.aiw for the witness file.
+
+ --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).
+
+ --aig-noheader
+ the AIGER witness file does not include the status and
+ properties lines.
+
--noinfo
only run the core proof, do not collect and print any
additional information (e.g. which assert failed)
+ --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
+ yielding a speedup.
+
--final-only
only check final constraints, assume base case
@@ -85,25 +119,48 @@ yosys-smtbmc [options] <yosys_smt2_output>
--dump-vlogtb <verilog_filename>
write trace as Verilog test bench
+ --vlogtb-top <hierarchical_name>
+ use the given entity as top module for the generated
+ Verilog test bench. The <hierarchical_name> is relative
+ to the design top module without the top module name.
+
--dump-smtc <constr_filename>
write trace as constraints file
+ --smtc-init
+ write just the last state as initial constraint to smtc file
+
+ --smtc-top <old>[:<new>]
+ replace <old> with <new> in constraints dumped to smtc
+ file and only dump object below <old> in design hierarchy.
+
+ --noinit
+ do not assume initial conditions in state 0
+
--dump-all
when using -g or -i, create a dump file for each
step. The character '%' is replaces in all dump
filenames with the step number.
+
+ --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)
""" + so.helpmsg())
sys.exit(1)
try:
- opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igm:", so.longopts +
- ["final-only", "assume-skipped=", "smtc=", "cex=", "dump-vcd=", "dump-vlogtb=", "dump-smtc=", "dump-all", "noinfo"])
+ opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts +
+ ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "presat",
+ "dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
+ "smtc-init", "smtc-top=", "noinit"])
except:
usage()
for o, a in opts:
if o == "-t":
+ got_topt = True
a = a.split(":")
if len(a) == 1:
num_steps = int(a[0])
@@ -115,7 +172,7 @@ for o, a in opts:
step_size = int(a[1])
num_steps = int(a[2])
else:
- assert 0
+ assert False
elif o == "--assume-skipped":
assume_skipped = int(a)
elif o == "--final-only":
@@ -124,20 +181,46 @@ for o, a in opts:
inconstr.append(a)
elif o == "--cex":
cexfile = a
+ elif o == "--aig":
+ if ":" in a:
+ aimfile, aiwfile = a.split(":")
+ else:
+ aimfile = a + ".aim"
+ aiwfile = a + ".aiw"
+ elif o == "--aig-noheader":
+ aigheader = False
elif o == "--dump-vcd":
vcdfile = a
elif o == "--dump-vlogtb":
vlogtbfile = a
+ elif o == "--vlogtb-top":
+ vlogtbtop = a
elif o == "--dump-smtc":
outconstr = a
+ elif o == "--smtc-init":
+ smtcinit = True
+ elif o == "--smtc-top":
+ smtctop = a.split(":")
+ if len(smtctop) == 1:
+ smtctop.append("")
+ assert len(smtctop) == 2
+ smtctop = tuple(smtctop)
elif o == "--dump-all":
dumpall = True
+ elif o == "--presat":
+ presat = True
elif o == "--noinfo":
noinfo = True
+ elif o == "--noinit":
+ noinit = True
+ elif o == "--append":
+ append_steps = int(a)
elif o == "-i":
tempind = True
elif o == "-g":
gentrace = True
+ elif o == "-c":
+ covermode = True
elif o == "-m":
topmod = a
elif so.handle(o, a):
@@ -148,6 +231,8 @@ for o, a in opts:
if len(args) != 1:
usage()
+if sum([tempind, gentrace, covermode]) > 1:
+ usage()
constr_final_start = None
constr_asserts = defaultdict(list)
@@ -182,12 +267,11 @@ for fn in inconstr:
current_states = set(["final-%d" % i for i in range(0, num_steps+1)])
constr_final_start = 0
elif len(tokens) == 2:
- i = int(tokens[1])
- assert i < 0
- current_states = set(["final-%d" % i for i in range(-i, num_steps+1)])
- constr_final_start = -i if constr_final_start is None else min(constr_final_start, -i)
+ arg = abs(int(tokens[1]))
+ current_states = set(["final-%d" % i for i in range(arg, num_steps+1)])
+ constr_final_start = arg if constr_final_start is None else min(constr_final_start, arg)
else:
- assert 0
+ assert False
continue
if tokens[0] == "state":
@@ -206,18 +290,17 @@ for fn in inconstr:
for i in range(lower, upper+1):
current_states.add(i)
else:
- assert 0
+ assert False
continue
if tokens[0] == "always":
if len(tokens) == 1:
current_states = set(range(0, num_steps+1))
elif len(tokens) == 2:
- i = int(tokens[1])
- assert i < 0
- current_states = set(range(-i, num_steps+1))
+ arg = abs(int(tokens[1]))
+ current_states = set(range(arg, num_steps+1))
else:
- assert 0
+ assert False
continue
if tokens[0] == "assert":
@@ -244,7 +327,7 @@ for fn in inconstr:
so.logic = " ".join(tokens[1:])
continue
- assert 0
+ assert False
def get_constr_expr(db, state, final=False, getvalues=False):
@@ -255,7 +338,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)
@@ -318,6 +401,11 @@ assert topmod is not None
assert topmod in smt.modinfo
if cexfile is not None:
+ if not got_topt:
+ assume_skipped = 0
+ skip_steps = 0
+ num_steps = 0
+
with open(cexfile, "r") as f:
cex_regex = re.compile(r'([^\[@=]+)(\[\d+\])?([^@=]*)(@\d+)=([01])')
for entry in f.read().split():
@@ -349,6 +437,144 @@ if cexfile is not None:
# print("cex@%d: %s" % (step, smtexpr))
constr_assumes[step].append((cexfile, smtexpr))
+ if not got_topt:
+ skip_steps = max(skip_steps, step)
+ num_steps = max(num_steps, step+1)
+
+if aimfile is not None:
+ input_map = dict()
+ init_map = dict()
+ latch_map = dict()
+
+ if not got_topt:
+ assume_skipped = 0
+ skip_steps = 0
+ num_steps = 0
+
+ with open(aimfile, "r") as f:
+ for entry in f.read().splitlines():
+ entry = entry.split()
+
+ if entry[0] == "input":
+ input_map[int(entry[1])] = (entry[3], int(entry[2]))
+ continue
+
+ if entry[0] == "init":
+ init_map[int(entry[1])] = (entry[3], int(entry[2]))
+ continue
+
+ if entry[0] in ["latch", "invlatch"]:
+ latch_map[int(entry[1])] = (entry[3], int(entry[2]), entry[0] == "invlatch")
+ continue
+
+ if entry[0] in ["output", "wire"]:
+ continue
+
+ assert False
+
+ with open(aiwfile, "r") as f:
+ got_state = False
+ got_ffinit = False
+ step = 0
+
+ if not aigheader:
+ got_state = True
+
+ for entry in f.read().splitlines():
+ if len(entry) == 0 or entry[0] in "bcjfu.":
+ continue
+
+ if not got_state:
+ got_state = True
+ assert entry == "1"
+ continue
+
+ if not got_ffinit:
+ got_ffinit = True
+ if len(init_map) == 0:
+ for i in range(len(entry)):
+ if entry[i] == "x":
+ continue
+
+ if i in latch_map:
+ value = int(entry[i])
+ name = latch_map[i][0]
+ bitidx = latch_map[i][1]
+ invert = latch_map[i][2]
+
+ if invert:
+ value = 1 - value
+
+ path = smt.get_path(topmod, name)
+ width = smt.net_width(topmod, path)
+
+ if width == 1:
+ assert bitidx == 0
+ smtexpr = "(= [%s] %s)" % (name, "true" if value else "false")
+ else:
+ smtexpr = "(= ((_ extract %d %d) [%s]) #b%d)" % (bitidx, bitidx, name, value)
+
+ constr_assumes[0].append((cexfile, smtexpr))
+ continue
+
+ for i in range(len(entry)):
+ if entry[i] == "x":
+ continue
+
+ if (step == 0) and (i in init_map):
+ value = int(entry[i])
+ name = init_map[i][0]
+ bitidx = init_map[i][1]
+
+ path = smt.get_path(topmod, name)
+
+ if not smt.net_exists(topmod, path):
+ match = re.match(r"(.*)\[(\d+)\]$", path[-1])
+ if match:
+ path[-1] = match.group(1)
+ addr = int(match.group(2))
+
+ if not match or not smt.mem_exists(topmod, path):
+ print_msg("Ignoring init value for unknown net: %s" % (name))
+ continue
+
+ meminfo = smt.mem_info(topmod, path)
+ smtexpr = "(select [%s] #b%s)" % (".".join(path), bin(addr)[2:].zfill(meminfo[0]))
+ width = meminfo[1]
+
+ else:
+ smtexpr = "[%s]" % name
+ width = smt.net_width(topmod, path)
+
+ if width == 1:
+ assert bitidx == 0
+ smtexpr = "(= %s %s)" % (smtexpr, "true" if value else "false")
+ else:
+ smtexpr = "(= ((_ extract %d %d) %s) #b%d)" % (bitidx, bitidx, smtexpr, value)
+
+ constr_assumes[0].append((cexfile, smtexpr))
+
+ if i in input_map:
+ value = int(entry[i])
+ name = input_map[i][0]
+ bitidx = input_map[i][1]
+
+ path = smt.get_path(topmod, name)
+ width = smt.net_width(topmod, path)
+
+ if width == 1:
+ assert bitidx == 0
+ smtexpr = "(= [%s] %s)" % (name, "true" if value else "false")
+ else:
+ smtexpr = "(= ((_ extract %d %d) [%s]) #b%d)" % (bitidx, bitidx, name, value)
+
+ constr_assumes[step].append((cexfile, smtexpr))
+
+ if not got_topt:
+ skip_steps = max(skip_steps, step)
+ num_steps = max(num_steps, step+1)
+ step += 1
+
def write_vcd_trace(steps_start, steps_stop, index):
filename = vcdfile.replace("%", index)
print_msg("Writing trace to VCD file: %s" % (filename))
@@ -363,14 +589,123 @@ 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, asyncwr = smt.mem_info(topmod, mempath)
+
+ expr_id = list()
+ expr_list = list()
+ for i in range(steps_start, steps_stop):
+ for j in range(rports):
+ expr_id.append(('R', i-steps_start, j, 'A'))
+ expr_id.append(('R', i-steps_start, j, 'D'))
+ expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j))
+ expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j))
+ for j in range(wports):
+ expr_id.append(('W', i-steps_start, j, 'A'))
+ expr_id.append(('W', i-steps_start, j, 'D'))
+ expr_id.append(('W', i-steps_start, j, 'M'))
+ expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j))
+ expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j))
+ expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j))
+
+ rdata = list()
+ wdata = list()
+ addrs = set()
+
+ for eid, edat in zip(expr_id, smt.get_list(expr_list)):
+ t, i, j, f = eid
+
+ if t == 'R':
+ c = rdata
+ elif t == 'W':
+ c = wdata
+ else:
+ assert False
+
+ while len(c) <= i:
+ c.append(list())
+ c = c[i]
+
+ while len(c) <= j:
+ c.append(dict())
+ c = c[j]
+
+ c[f] = smt.bv2bin(edat)
+
+ if f == 'A':
+ addrs.add(c[f])
+
+ for addr in addrs:
+ tdata = list()
+ 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)):
+ if not gotread:
+ for j_data in rdata[i]:
+ if j_data["A"] == addr:
+ data = list(j_data["D"])
+ gotread = True
+ break
+
+ if gotread:
+ buf = data[:]
+ for i in reversed(range(len(tdata))):
+ for k in range(width):
+ if tdata[i][k] == "x":
+ tdata[i][k] = buf[k]
+ else:
+ buf[k] = tdata[i][k]
+
+ if not asyncwr:
+ tdata.append(data[:])
+
+ for j_data in wdata[i]:
+ if j_data["A"] != addr:
+ continue
+
+ D = j_data["D"]
+ M = j_data["M"]
+
+ for k in range(width):
+ if M[k] == "1":
+ data[k] = D[k]
+
+ if asyncwr:
+ tdata.append(data[:])
+
+ assert len(tdata) == len(rdata)
+
+ netpath = mempath[:]
+ netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int(addr, 2))
+ vcd.add_net([topmod] + netpath, width)
+
+ for i in range(steps_start, steps_stop):
+ if i not in mem_trace_data:
+ mem_trace_data[i] = list()
+ mem_trace_data[i].append((netpath, "".join(tdata[i-steps_start])))
+
for i in range(steps_start, steps_stop):
vcd.set_time(i)
value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i)
for path, value in zip(path_list, value_list):
vcd.set_net([topmod] + path, value)
+ if i in mem_trace_data:
+ for path, value in mem_trace_data[i]:
+ vcd.set_net([topmod] + path, value)
vcd.set_time(steps_stop)
@@ -379,18 +714,38 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
filename = vlogtbfile.replace("%", index)
print_msg("Writing trace to Verilog testbench: %s" % (filename))
+ vlogtb_topmod = topmod
+ vlogtb_state = "s@@step_idx@@"
+
+ if vlogtbtop is not None:
+ for item in vlogtbtop.split("."):
+ 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)
print("module testbench;", file=f)
print(" reg [4095:0] vcdfile;", file=f)
- print(" reg clock = 0, genclock = 1;", file=f)
+ print(" reg clock;", file=f)
+ print("`else", file=f)
+ print("module testbench(input clock, output reg genclock);", file=f)
+ print(" initial genclock = 1;", file=f)
+ print("`endif", file=f)
+
+ print(" reg genclock = 1;", file=f)
+ print(" reg [31:0] cycle = 0;", file=f)
primary_inputs = list()
clock_inputs = set()
- for name in smt.modinfo[topmod].inputs:
+ for name in smt.modinfo[vlogtb_topmod].inputs:
if name in ["clk", "clock", "CLK", "CLOCK"]:
clock_inputs.add(name)
- width = smt.modinfo[topmod].wsize[name]
+ width = smt.modinfo[vlogtb_topmod].wsize[name]
primary_inputs.append((name, width))
for name, width in primary_inputs:
@@ -399,27 +754,32 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
else:
print(" reg [%d:0] PI_%s;" % (width-1, name), file=f)
- print(" %s UUT (" % topmod, file=f)
+ print(" %s UUT (" % vlogtb_topmod, file=f)
print(",\n".join(" .{name}(PI_{name})".format(name=name) for name, _ in primary_inputs), file=f)
print(" );", file=f)
+ print("`ifndef VERILATOR", file=f)
print(" initial begin", file=f)
print(" if ($value$plusargs(\"vcd=%s\", vcdfile)) begin", file=f)
print(" $dumpfile(vcdfile);", file=f)
print(" $dumpvars(0, testbench);", file=f)
print(" end", file=f)
+ print(" #5 clock = 0;", file=f)
print(" while (genclock) begin", file=f)
- print(" #5; clock = 0;", file=f)
- print(" #5; clock = 1;", file=f)
+ print(" #5 clock = 0;", file=f)
+ print(" #5 clock = 1;", file=f)
print(" end", file=f)
print(" end", file=f)
+ print("`endif", file=f)
print(" initial begin", file=f)
- regs = sorted(smt.hiernets(topmod, regs_only=True))
- regvals = smt.get_net_bin_list(topmod, regs, "s%d" % steps_start)
+ regs = sorted(smt.hiernets(vlogtb_topmod, regs_only=True))
+ regvals = smt.get_net_bin_list(vlogtb_topmod, regs, vlogtb_state.replace("@@step_idx@@", str(steps_start)))
+ print("`ifndef VERILATOR", file=f)
print(" #1;", file=f)
+ print("`endif", file=f)
for reg, val in zip(regs, regvals):
hidden_net = False
for n in reg:
@@ -427,40 +787,74 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
hidden_net = True
print(" %sUUT.%s = %d'b%s;" % ("// " if hidden_net else "", ".".join(reg), len(val), val), file=f)
- mems = sorted(smt.hiermems(topmod))
+ anyconsts = sorted(smt.hieranyconsts(vlogtb_topmod))
+ for info in anyconsts:
+ if info[3] is not None:
+ modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps_start)), info[0])
+ value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate)))
+ print(" UUT.%s = %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f);
+
+ mems = sorted(smt.hiermems(vlogtb_topmod))
for mempath in mems:
- abits, width, ports = smt.mem_info(topmod, "s%d" % steps_start, mempath)
- mem = smt.mem_expr(topmod, "s%d" % steps_start, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(vlogtb_topmod, mempath)
addr_expr_list = list()
+ data_expr_list = list()
for i in range(steps_start, steps_stop):
- for j in range(ports):
- addr_expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, j))
+ for j in range(rports):
+ addr_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j))
+ data_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j))
- addr_list = set()
- for val in smt.get_list(addr_expr_list):
- addr_list.add(smt.bv2int(val))
+ addr_list = smt.get_list(addr_expr_list)
+ data_list = smt.get_list(data_expr_list)
- expr_list = list()
- for i in addr_list:
- expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits)))
+ addr_data = dict()
+ for addr, data in zip(addr_list, data_list):
+ addr = smt.bv2bin(addr)
+ data = smt.bv2bin(data)
+ if addr not in addr_data:
+ addr_data[addr] = data
- for i, val in zip(addr_list, smt.get_list(expr_list)):
- val = smt.bv2bin(val)
- print(" UUT.%s[%d] = %d'b%s;" % (".".join(mempath), i, len(val), val), file=f)
+ for addr, data in addr_data.items():
+ print(" UUT.%s[%d'b%s] = %d'b%s;" % (".".join(mempath), len(addr), addr, len(data), data), file=f)
+
+ print("", file=f)
+ anyseqs = sorted(smt.hieranyseqs(vlogtb_topmod))
for i in range(steps_start, steps_stop):
pi_names = [[name] for name, _ in primary_inputs if name not in clock_inputs]
- pi_values = smt.get_net_bin_list(topmod, pi_names, "s%d" % i)
+ pi_values = smt.get_net_bin_list(vlogtb_topmod, pi_names, vlogtb_state.replace("@@step_idx@@", str(i)))
- print(" #1;", file=f)
print(" // state %d" % i, file=f)
+
if i > 0:
- print(" @(posedge clock);", file=f)
+ print(" if (cycle == %d) begin" % (i-1), file=f)
+
for name, val in zip(pi_names, pi_values):
- print(" PI_%s <= %d'b%s;" % (".".join(name), len(val), val), file=f)
+ if i > 0:
+ print(" PI_%s <= %d'b%s;" % (".".join(name), len(val), val), file=f)
+ else:
+ print(" PI_%s = %d'b%s;" % (".".join(name), len(val), val), file=f)
+
+ for info in anyseqs:
+ if info[3] is not None:
+ modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), info[0])
+ value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate)))
+ if i > 0:
+ print(" UUT.%s <= %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f);
+ else:
+ print(" UUT.%s = %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f);
+
+ if i > 0:
+ print(" end", file=f)
+ print("", file=f)
+
+ if i == 0:
+ print(" end", file=f)
+ print(" always @(posedge clock) begin", file=f)
- print(" genclock = 0;", file=f)
+ print(" genclock <= cycle < %d;" % (steps_stop-1), file=f)
+ print(" cycle <= cycle + 1;", file=f)
print(" end", file=f)
print("endmodule", file=f)
@@ -470,52 +864,71 @@ def write_constr_trace(steps_start, steps_stop, index):
filename = outconstr.replace("%", index)
print_msg("Writing trace to constraints file: %s" % (filename))
+ constr_topmod = topmod
+ constr_state = "s@@step_idx@@"
+ constr_prefix = ""
+
+ if smtctop is not None:
+ for item in smtctop[0].split("."):
+ assert item in smt.modinfo[constr_topmod].cells
+ constr_state = "(|%s_h %s| %s)" % (constr_topmod, item, constr_state)
+ constr_topmod = smt.modinfo[constr_topmod].cells[item]
+ if smtctop[1] != "":
+ constr_prefix = smtctop[1] + "."
+
+ if smtcinit:
+ steps_start = steps_stop - 1
+
with open(filename, "w") as f:
primary_inputs = list()
- for name in smt.modinfo[topmod].inputs:
- width = smt.modinfo[topmod].wsize[name]
+ for name in smt.modinfo[constr_topmod].inputs:
+ width = smt.modinfo[constr_topmod].wsize[name]
primary_inputs.append((name, width))
- if steps_start == 0:
+ if steps_start == 0 or smtcinit:
print("initial", file=f)
else:
print("state %d" % steps_start, file=f)
- regnames = sorted(smt.hiernets(topmod, regs_only=True))
- regvals = smt.get_net_list(topmod, regnames, "s%d" % steps_start)
+ regnames = sorted(smt.hiernets(constr_topmod, regs_only=True))
+ regvals = smt.get_net_list(constr_topmod, regnames, constr_state.replace("@@step_idx@@", str(steps_start)))
for name, val in zip(regnames, regvals):
- print("assume (= [%s] %s)" % (".".join(name), val), file=f)
+ print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f)
- mems = sorted(smt.hiermems(topmod))
+ mems = sorted(smt.hiermems(constr_topmod))
for mempath in mems:
- abits, width, ports = smt.mem_info(topmod, "s%d" % steps_start, mempath)
- mem = smt.mem_expr(topmod, "s%d" % steps_start, mempath)
+ abits, width, rports, wports, asyncwr = smt.mem_info(constr_topmod, mempath)
addr_expr_list = list()
+ data_expr_list = list()
for i in range(steps_start, steps_stop):
- for j in range(ports):
- addr_expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, j))
+ for j in range(rports):
+ addr_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j))
+ data_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j))
- addr_list = set((smt.bv2int(val) for val in smt.get_list(addr_expr_list)))
+ addr_list = smt.get_list(addr_expr_list)
+ data_list = smt.get_list(data_expr_list)
- expr_list = list()
- for i in addr_list:
- expr_list.append("(select %s #b%s)" % (mem, format(i, "0%db" % abits)))
+ addr_data = dict()
+ for addr, data in zip(addr_list, data_list):
+ if addr not in addr_data:
+ addr_data[addr] = data
- for i, val in zip(addr_list, smt.get_list(expr_list)):
- print("assume (= (select [%s] #b%s) %s)" % (".".join(mempath), format(i, "0%db" % abits), val), file=f)
+ for addr, data in addr_data.items():
+ print("assume (= (select [%s%s] %s) %s)" % (constr_prefix, ".".join(mempath), addr, data), file=f)
for k in range(steps_start, steps_stop):
- print("", file=f)
- print("state %d" % k, file=f)
+ if not smtcinit:
+ print("", file=f)
+ print("state %d" % k, file=f)
pi_names = [[name] for name, _ in sorted(primary_inputs)]
- pi_values = smt.get_net_list(topmod, pi_names, "s%d" % k)
+ pi_values = smt.get_net_list(constr_topmod, pi_names, constr_state.replace("@@step_idx@@", str(k)))
for name, val in zip(pi_names, pi_values):
- print("assume (= [%s] %s)" % (".".join(name), val), file=f)
+ print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f)
def write_trace(steps_start, steps_stop, index):
@@ -529,30 +942,40 @@ def write_trace(steps_start, steps_stop, index):
write_constr_trace(steps_start, steps_stop, index)
-def print_failed_asserts_worker(mod, state, path):
+def print_failed_asserts_worker(mod, state, path, extrainfo):
assert mod in smt.modinfo
+ found_failed_assert = False
if smt.get("(|%s_a| %s)" % (mod, state)) in ["true", "#b1"]:
return
for cellname, celltype in smt.modinfo[mod].cells.items():
- print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
+ if print_failed_asserts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname, extrainfo):
+ found_failed_assert = True
for assertfun, assertinfo in smt.modinfo[mod].asserts.items():
if smt.get("(|%s| %s)" % (assertfun, state)) in ["false", "#b0"]:
- print_msg("Assert failed in %s: %s" % (path, assertinfo))
+ print_msg("Assert failed in %s: %s%s" % (path, assertinfo, extrainfo))
+ found_failed_assert = True
+
+ return found_failed_assert
-def print_failed_asserts(state, final=False):
+def print_failed_asserts(state, final=False, extrainfo=""):
if noinfo: return
loc_list, expr_list, value_list = get_constr_expr(constr_asserts, state, final=final, getvalues=True)
+ found_failed_assert = False
for loc, expr, value in zip(loc_list, expr_list, value_list):
if smt.bv2int(value) == 0:
- print_msg("Assert %s failed: %s" % (loc, expr))
+ print_msg("Assert %s failed: %s%s" % (loc, expr, extrainfo))
+ found_failed_assert = True
if not final:
- print_failed_asserts_worker(topmod, "s%d" % state, topmod)
+ if print_failed_asserts_worker(topmod, "s%d" % state, topmod, extrainfo):
+ found_failed_assert = True
+
+ return found_failed_assert
def print_anyconsts_worker(mod, state, path):
@@ -562,7 +985,10 @@ def print_anyconsts_worker(mod, state, path):
print_anyconsts_worker(celltype, "(|%s_h %s| %s)" % (mod, cellname, state), path + "." + cellname)
for fun, info in smt.modinfo[mod].anyconsts.items():
- print_msg("Value for anyconst in %s (%s): %d" % (path, info, smt.bv2int(smt.get("(|%s| %s)" % (fun, state)))))
+ if info[1] is None:
+ 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)))))
def print_anyconsts(state):
@@ -570,23 +996,183 @@ def print_anyconsts(state):
print_anyconsts_worker(topmod, "s%d" % state, topmod)
+def get_cover_list(mod, base):
+ assert mod in smt.modinfo
+
+ cover_expr = list()
+ cover_desc = list()
+
+ for expr, desc in smt.modinfo[mod].covers.items():
+ cover_expr.append("(ite (|%s| %s) #b1 #b0)" % (expr, base))
+ cover_desc.append(desc)
+
+ for cell, submod in smt.modinfo[mod].cells.items():
+ e, d = get_cover_list(submod, "(|%s_h %s| %s)" % (mod, cell, base))
+ cover_expr += e
+ cover_desc += d
+
+ 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
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))
@@ -600,9 +1186,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, '%')
@@ -613,33 +1199,135 @@ if tempind:
write_trace(step, num_steps+1, "%d" % step)
else:
- print("%s Temporal induction successful." % smt.timestamp())
+ print_msg("Temporal induction successful.")
retstatus = True
break
+elif covermode:
+ cover_expr, cover_desc = get_cover_list(topmod, "state")
+ cover_mask = "1" * len(cover_desc)
+
+ if len(cover_expr) > 1:
+ cover_expr = "(concat %s)" % " ".join(cover_expr)
+ elif len(cover_expr) == 1:
+ cover_expr = cover_expr[0]
+ else:
+ cover_expr = "#b0"
+
+ coveridx = 0
+ smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr))
-else: # not tempind
+ step = 0
+ retstatus = False
+ found_failed_assert = False
+
+ assert step_size == 1
+
+ while step < num_steps:
+ 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_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
+ else:
+ smt_assert_antecedent("(|%s_i| s0)" % (topmod))
+ smt_assert_antecedent("(|%s_is| s0)" % (topmod))
+
+ else:
+ 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_push()
+ smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc)))
+
+ 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_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"
+
+ reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step)))
+ assert len(reached_covers) == len(cover_desc)
+
+ new_cover_mask = []
+
+ for i in range(len(reached_covers)):
+ if reached_covers[i] == "0":
+ new_cover_mask.append(cover_mask[i])
+ continue
+
+ print_msg("Reached cover statement at %s in step %d." % (cover_desc[i], step))
+ new_cover_mask.append("0")
+
+ cover_mask = "".join(new_cover_mask)
+
+ for i in range(step+1+append_steps):
+ if print_failed_asserts(i, extrainfo=" (step %d)" % i):
+ found_failed_assert = True
+
+ write_trace(0, step+1+append_steps, "%d" % coveridx)
+
+ if found_failed_assert:
+ break
+
+ coveridx += 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
+ break
+
+ step += 1
+
+ if "1" in cover_mask:
+ for i in range(len(cover_mask)):
+ if cover_mask[i] == "1":
+ print_msg("Unreached cover statement at %s." % cover_desc[i])
+
+else: # not tempind, covermode
step = 0
retstatus = True
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:
- smt.write("(assert (|%s_i| s0))" % (topmod))
- smt.write("(assert (|%s_is| s0))" % (topmod))
+ if noinit:
+ smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step))
+ else:
+ 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
@@ -648,39 +1336,61 @@ else: # not tempind
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 (|%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:
+ if presat:
+ if last_check_step == step:
+ print_msg("Checking assumptions in step %d.." % (step))
+ 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
+ break
+
if not final_only:
if last_check_step == step:
- print_msg("Checking asserts in step %d.." % (step))
+ print_msg("Checking assertions in step %d.." % (step))
else:
- print_msg("Checking asserts in steps %d to %d.." % (step, last_check_step))
- smt.write("(push 1)")
+ print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step))
+ 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_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))
+ assert smt_check_sat() == "sat"
print_anyconsts(step)
for i in range(step, last_check_step+1):
print_failed_asserts(i)
- write_trace(0, last_check_step+1, '%')
+ write_trace(0, last_check_step+1+append_steps, '%')
retstatus = False
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):
@@ -688,12 +1398,12 @@ else: # not tempind
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)
@@ -701,17 +1411,17 @@ else: # not tempind
retstatus = False
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
break
@@ -722,7 +1432,7 @@ else: # not tempind
step += step_size
- if gentrace:
+ if gentrace and retstatus:
print_anyconsts(0)
write_trace(0, num_steps, '%')
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 865eed1f..3fc823e3 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
#
# yosys -- Yosys Open SYnthesis Suite
#
@@ -17,10 +16,55 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
-import sys, subprocess, re
+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
+ smtio_stacksize = 128 * 1024 * 1024
+ if sys.getrecursionlimit() < smtio_reclimit:
+ sys.setrecursionlimit(smtio_reclimit)
+ if resource.getrlimit(resource.RLIMIT_STACK)[0] < smtio_stacksize:
+ resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, -1))
+
+
+# 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 = {
@@ -41,25 +85,38 @@ 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
self.solver = opts.solver
+ self.solver_opts = opts.solver_opts
self.debug_print = opts.debug_print
self.debug_file = opts.debug_file
self.dummy_file = opts.dummy_file
@@ -70,34 +127,57 @@ class SmtIo:
self.nocomments = opts.nocomments
else:
- self.solver = "z3"
+ self.solver = "yices"
+ self.solver_opts = list()
self.debug_print = False
self.debug_file = None
self.dummy_file = None
- self.timeinfo = True
+ self.timeinfo = os.name != "nt"
self.unroll = False
self.noincr = False
self.info_stmts = list()
self.nocomments = False
+ self.start_time = time()
+
+ self.modinfo = dict()
+ self.curmod = None
+ 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.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
if self.solver == "z3":
- self.popen_vargs = ['z3', '-smt2', '-in']
+ self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
if self.solver == "cvc4":
- self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2']
+ 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.popen_vargs = ['mathsat'] + self.solver_opts
if self.solver == "boolector":
- self.popen_vargs = ['boolector', '--smt2', '-i']
+ self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
self.unroll = True
if self.solver == "abc":
- self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
+ if len(self.solver_opts) > 0:
+ self.popen_vargs = ['yosys-abc', '-S', '; '.join(self.solver_opts)]
+ else:
+ self.popen_vargs = ['yosys-abc', '-S', '%blast; &sweep -C 5000; &syn4; &cec -s -m -C 2000']
self.logic_ax = False
self.unroll = True
self.noincr = True
@@ -109,9 +189,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 = ""
@@ -121,36 +202,27 @@ class SmtIo:
self.unroll_cache = dict()
self.unroll_stack = list()
- self.start_time = time()
-
- self.modinfo = dict()
- self.curmod = None
- self.topmod = None
- self.setup_done = False
-
- def setup(self):
- assert not self.setup_done
-
if self.logic is None:
self.logic = ""
if self.logic_qf: self.logic += "QF_"
if self.logic_ax: self.logic += "A"
if self.logic_uf: self.logic += "UF"
if self.logic_bv: self.logic += "BV"
+ if self.logic_dt: self.logic = "ALL"
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:
@@ -201,18 +273,75 @@ 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
+ self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ 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)
+ if not self.setup_done:
+ self.info_stmts.append(stmt)
+ return
elif not self.setup_done:
self.setup()
stmt = stmt.strip()
if self.nocomments or self.unroll:
- if stmt.startswith(";"):
- return
- stmt = re.sub(r" ;.*", "", stmt)
+ stmt = re.sub(r" *;.*", "", stmt)
+ if stmt == "": return
if unroll and self.unroll:
stmt = self.unroll_buffer + stmt
@@ -271,20 +400,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-"):
@@ -300,6 +426,15 @@ class SmtIo:
if self.logic is None:
self.logic_bv = False
+ if fields[1] == "yosys-smt2-stdt":
+ 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()
@@ -323,17 +458,40 @@ 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]))
+ 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[fields[2]] = fields[3]
+ self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3]
+
+ if fields[1] == "yosys-smt2-cover":
+ 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]
+ 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[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):
@@ -347,6 +505,54 @@ class SmtIo:
hiernets_worker(nets, top, [])
return nets
+ def hieranyconsts(self, top):
+ def worker(results, mod, cursor):
+ for name, value in sorted(self.modinfo[mod].anyconsts.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 hieranyseqs(self, top):
+ def worker(results, mod, cursor):
+ for name, value in sorted(self.modinfo[mod].anyseqs.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 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])
+
+ results = list()
+ worker(results, top, [])
+ return results
+
def hiermems(self, top):
def hiermems_worker(mems, mod, cursor):
for memname in sorted(self.modinfo[mod].memories.keys()):
@@ -366,7 +572,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")
@@ -379,12 +585,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
@@ -399,15 +607,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
@@ -415,7 +621,7 @@ class SmtIo:
count = 0
num_bs = 0
- while select([self.p.stdout], [], [], 0.1) == ([], [], []):
+ while self.p_poll():
count += 1
if count < 25:
@@ -444,11 +650,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):
@@ -503,6 +741,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"):
@@ -539,6 +780,9 @@ class SmtIo:
return [".".join(path)]
def net_expr(self, mod, base, path):
+ if len(path) == 0:
+ return base
+
if len(path) == 1:
assert mod in self.modinfo
if path[0] == "":
@@ -568,23 +812,54 @@ class SmtIo:
assert net_path[-1] in self.modinfo[mod].wsize
return self.modinfo[mod].wsize[net_path[-1]]
- def mem_expr(self, mod, base, path, portidx=None, infomode=False):
+ 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
+ if net_path[i] not in self.modinfo[mod].cells: return False
+ mod = self.modinfo[mod].cells[net_path[i]]
+
+ if mod not in self.modinfo: return False
+ if net_path[-1] not in self.modinfo[mod].wsize: return False
+ return True
+
+ def mem_exists(self, mod, mem_path):
+ for i in range(len(mem_path)-1):
+ if mod not in self.modinfo: return False
+ if mem_path[i] not in self.modinfo[mod].cells: return False
+ mod = self.modinfo[mod].cells[mem_path[i]]
+
+ if mod not in self.modinfo: return False
+ if mem_path[-1] not in self.modinfo[mod].memories: return False
+ return True
+
+ def mem_expr(self, mod, base, path, port=None, infomode=False):
if len(path) == 1:
assert mod in self.modinfo
assert path[0] in self.modinfo[mod].memories
if infomode:
return self.modinfo[mod].memories[path[0]]
- return "(|%s_m%s %s| %s)" % (mod, "" if portidx is None else ":%d" % portidx, path[0], base)
+ return "(|%s_m%s %s| %s)" % (mod, "" if port is None else ":%s" % port, path[0], base)
assert mod in self.modinfo
assert path[0] in self.modinfo[mod].cells
nextmod = self.modinfo[mod].cells[path[0]]
nextbase = "(|%s_h %s| %s)" % (mod, path[0], base)
- return self.mem_expr(nextmod, nextbase, path[1:], portidx=portidx, infomode=infomode)
+ return self.mem_expr(nextmod, nextbase, path[1:], port=port, infomode=infomode)
- def mem_info(self, mod, base, path):
- return self.mem_expr(mod, base, path, infomode=True)
+ def mem_info(self, mod, path):
+ return self.mem_expr(mod, "", path, infomode=True)
def get_net(self, mod_name, net_path, state_name):
return self.get(self.net_expr(mod_name, state_name, net_path))
@@ -607,19 +882,21 @@ class SmtIo:
def wait(self):
if self.p is not None:
self.p.wait()
+ self.p_close()
class SmtOpts:
def __init__(self):
- self.shortopts = "s:v"
+ self.shortopts = "s:S:v"
self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
- self.solver = "z3"
+ self.solver = "yices"
+ self.solver_opts = list()
self.debug_print = False
self.debug_file = None
self.dummy_file = None
self.unroll = False
self.noincr = False
- self.timeinfo = True
+ self.timeinfo = os.name != "nt"
self.logic = None
self.info_stmts = list()
self.nocomments = False
@@ -627,6 +904,8 @@ class SmtOpts:
def handle(self, o, a):
if o == "-s":
self.solver = a
+ elif o == "-S":
+ self.solver_opts.append(a)
elif o == "-v":
self.debug_print = True
elif o == "--unroll":
@@ -634,7 +913,7 @@ class SmtOpts:
elif o == "--noincr":
self.noincr = True
elif o == "--noprogress":
- self.timeinfo = True
+ self.timeinfo = False
elif o == "--dump-smt2":
self.debug_file = open(a, "w")
elif o == "--logic":
@@ -652,8 +931,11 @@ class SmtOpts:
def helpmsg(self):
return """
-s <solver>
- set SMT solver: z3, cvc4, yices, mathsat, boolector, dummy
- default: z3
+ set SMT solver: z3, yices, boolector, cvc4, mathsat, dummy
+ default: yices
+
+ -S <opt>
+ pass <opt> as command line argument to the solver
--logic <smt2_logic>
use the specified SMT2 logic (e.g. QF_AUFBV)
@@ -674,6 +956,7 @@ class SmtOpts:
--noprogress
disable timer display during solving
+ (this option is set implicitly on Windows)
--dump-smt2 <filename>
write smt2 statements to file
@@ -691,6 +974,7 @@ class MkVcd:
self.f = f
self.t = -1
self.nets = dict()
+ self.clocks = dict()
def add_net(self, path, width):
path = tuple(path)
@@ -698,11 +982,25 @@ 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
@@ -710,22 +1008,52 @@ class MkVcd:
if self.t == -1:
print("$var integer 32 t smt_step $end", file=self.f)
print("$var event 1 ! smt_clock $end", file=self.f)
+
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]:
+ 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:
+ print("$scope module %s $end" % uipath[len(scope)], 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, uipath[-1]), file=self.f)
+ else:
+ print("$var wire %d %s %s $end" % (width, key, 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 162ce490..b8383412 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -507,15 +507,19 @@ struct SmvWorker
continue;
}
- if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_"))
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
{
string op;
- if (cell->type.in("$_AND_", "$_NAND_")) op = "&";
- if (cell->type.in("$_OR_", "$_NOR_")) op = "|";
+ if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_")) op = "&";
+ if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_")) op = "|";
if (cell->type.in("$_XOR_")) op = "xor";
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")),
+ 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")),
rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B"))));
@@ -671,7 +675,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");
@@ -689,7 +693,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;
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
index 4101cbf9..b6a3f1e7 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;
diff --git a/backends/table/Makefile.inc b/backends/table/Makefile.inc
new file mode 100644
index 00000000..8cd1dc61
--- /dev/null
+++ b/backends/table/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/table/table.o
+
diff --git a/backends/table/table.cc b/backends/table/table.cc
new file mode 100644
index 00000000..979273dd
--- /dev/null
+++ b/backends/table/table.cc
@@ -0,0 +1,120 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/rtlil.h"
+#include "kernel/register.h"
+#include "kernel/sigtools.h"
+#include "kernel/celltypes.h"
+#include "kernel/log.h"
+#include <string>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TableBackend : public Backend {
+ TableBackend() : Backend("table", "write design as connectivity table") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" write_table [options] [filename]\n");
+ log("\n");
+ log("Write the current design as connectivity table. The output is a tab-separated\n");
+ log("ASCII table with the following columns:\n");
+ log("\n");
+ log(" module name\n");
+ log(" cell name\n");
+ log(" cell type\n");
+ log(" cell port\n");
+ log(" direction\n");
+ log(" signal\n");
+ log("\n");
+ 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");
+ }
+ 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");
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++)
+ {
+ // if (args[argidx] == "-top" && argidx+1 < args.size()) {
+ // top_module_name = args[++argidx];
+ // continue;
+ // }
+ break;
+ }
+ extra_args(f, filename, args, argidx);
+
+ design->sort();
+
+ for (auto module : design->modules())
+ {
+ if (module->get_bool_attribute("\\blackbox"))
+ continue;
+
+ SigMap sigmap(module);
+
+ for (auto wire : module->wires())
+ {
+ if (wire->port_id == 0)
+ continue;
+
+ *f << log_id(module) << "\t";
+ *f << log_id(wire) << "\t";
+ *f << "-" << "\t";
+ *f << "-" << "\t";
+
+ if (wire->port_input && wire->port_output)
+ *f << "pio" << "\t";
+ else if (wire->port_input)
+ *f << "pi" << "\t";
+ else if (wire->port_output)
+ *f << "po" << "\t";
+ else
+ log_abort();
+
+ *f << log_signal(sigmap(wire)) << "\n";
+ }
+
+ for (auto cell : module->cells())
+ for (auto conn : cell->connections())
+ {
+ *f << log_id(module) << "\t";
+ *f << log_id(cell) << "\t";
+ *f << log_id(cell->type) << "\t";
+ *f << log_id(conn.first) << "\t";
+
+ if (cell->input(conn.first) && cell->output(conn.first))
+ *f << "inout" << "\t";
+ else if (cell->input(conn.first))
+ *f << "in" << "\t";
+ else if (cell->output(conn.first))
+ *f << "out" << "\t";
+ else
+ *f << "unkown" << "\t";
+
+ *f << log_signal(sigmap(conn.second)) << "\n";
+ }
+ }
+ }
+} TableBackend;
+
+PRIVATE_NAMESPACE_END
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index a617215f..44e4e5f9 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -33,13 +33,15 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nostr, defparam;
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal;
int auto_name_counter, auto_name_offset, auto_name_digits;
std::map<RTLIL::IdString, int> auto_name_map;
std::set<RTLIL::IdString> reg_wires, reg_ct;
std::string auto_prefix;
RTLIL::Module *active_module;
+dict<RTLIL::SigBit, RTLIL::State> active_initdata;
+SigMap active_sigmap;
void reset_auto_counter_id(RTLIL::IdString id, bool may_rename)
{
@@ -159,23 +161,73 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
if (width < 0)
width = data.bits.size() - offset;
if (nostr)
- goto dump_bits;
+ goto dump_hex;
if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) {
if (width == 32 && !no_decimal && !nodec) {
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)
- goto dump_bits;
+ goto dump_hex;
if (data.bits[i] == RTLIL::S1)
val |= 1 << (i - offset);
}
- if (set_signed && val < 0)
+ if (decimal)
+ f << stringf("%d", val);
+ else if (set_signed && val < 0)
f << stringf("-32'sd%u", -val);
else
f << stringf("32'%sd%u", set_signed ? "s" : "", val);
} else {
- dump_bits:
+ dump_hex:
+ if (nohex)
+ goto dump_bin;
+ vector<char> bin_digits, hex_digits;
+ 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 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::Sm: log_error("Found marker state in final netlist.");
+ }
+ }
+ if (GetSize(bin_digits) == 0)
+ goto dump_bin;
+ while (GetSize(bin_digits) % 4 != 0)
+ if (bin_digits.back() == '1')
+ bin_digits.push_back('0');
+ else
+ bin_digits.push_back(bin_digits.back());
+ for (int i = 0; i < GetSize(bin_digits); i += 4)
+ {
+ char bit_3 = bin_digits[i+3];
+ char bit_2 = bin_digits[i+2];
+ char bit_1 = bin_digits[i+1];
+ char bit_0 = bin_digits[i+0];
+ if (bit_3 == 'x' || bit_2 == 'x' || bit_1 == 'x' || bit_0 == 'x') {
+ if (bit_3 != 'x' || bit_2 != 'x' || bit_1 != 'x' || bit_0 != 'x')
+ goto dump_bin;
+ hex_digits.push_back('x');
+ continue;
+ }
+ if (bit_3 == 'z' || bit_2 == 'z' || bit_1 == 'z' || bit_0 == 'z') {
+ if (bit_3 != 'z' || bit_2 != 'z' || bit_1 != 'z' || bit_0 != 'z')
+ goto dump_bin;
+ hex_digits.push_back('z');
+ 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);
+ }
+ f << stringf("%d'%sh", width, set_signed ? "s" : "");
+ for (int i = GetSize(hex_digits)-1; i >= 0; i--)
+ f << hex_digits[i];
+ }
+ if (0) {
+ dump_bin:
f << stringf("%d'%sb", width, set_signed ? "s" : "");
if (width == 0)
f << stringf("0");
@@ -214,6 +266,26 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o
}
}
+void dump_reg_init(std::ostream &f, SigSpec sig)
+{
+ Const initval;
+ bool gotinit = false;
+
+ for (auto bit : active_sigmap(sig)) {
+ if (active_initdata.count(bit)) {
+ initval.bits.push_back(active_initdata.at(bit));
+ gotinit = true;
+ } else {
+ initval.bits.push_back(State::Sx);
+ }
+ }
+
+ if (gotinit) {
+ f << " = ";
+ dump_const(f, initval);
+ }
+}
+
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decimal = false)
{
if (chunk.wire == NULL) {
@@ -302,12 +374,12 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
if (wire->port_input && wire->port_output)
f << stringf("%s" "inout%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
if (reg_wires.count(wire->name)) {
- f << stringf("%s" "reg%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
+ f << stringf("%s" "reg%s %s", indent.c_str(), range.c_str(), id(wire->name).c_str());
if (wire->attributes.count("\\init")) {
- f << stringf("%s" "initial %s = ", indent.c_str(), id(wire->name).c_str());
+ f << stringf(" = ");
dump_const(f, wire->attributes.at("\\init"));
- f << stringf(";\n");
}
+ f << stringf(";\n");
} else if (!wire->port_input && !wire->port_output)
f << stringf("%s" "wire%s %s;\n", indent.c_str(), range.c_str(), id(wire->name).c_str());
#endif
@@ -400,7 +472,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) {
+ if (cell->type.in("$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_")) {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort("\\Y"));
f << stringf(" = ");
@@ -408,16 +480,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf("~(");
dump_cell_expr_port(f, cell, "A", false);
f << stringf(" ");
- if (cell->type.in("$_AND_", "$_NAND_"))
+ if (cell->type.in("$_AND_", "$_NAND_", "$_ANDNOT_"))
f << stringf("&");
- if (cell->type.in("$_OR_", "$_NOR_"))
+ if (cell->type.in("$_OR_", "$_NOR_", "$_ORNOT_"))
f << stringf("|");
if (cell->type.in("$_XOR_", "$_XNOR_"))
f << stringf("^");
dump_attributes(f, "", cell->attributes, ' ');
f << stringf(" ");
+ if (cell->type.in("$_ANDNOT_", "$_ORNOT_"))
+ f << stringf("~(");
dump_cell_expr_port(f, cell, "B", false);
- if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_"))
+ if (cell->type.in("$_NAND_", "$_NOR_", "$_XNOR_", "$_ANDNOT_", "$_ORNOT_"))
f << stringf(")");
f << stringf(";\n");
return true;
@@ -474,8 +548,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
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 %s;\n", indent.c_str(), reg_name.c_str());
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
+ dump_reg_init(f, cell->getPort("\\Q"));
+ f << ";\n";
+ }
dump_attributes(f, indent, cell->attributes);
f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
@@ -514,8 +591,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
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 %s;\n", indent.c_str(), reg_name.c_str());
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
+ dump_reg_init(f, cell->getPort("\\Q"));
+ f << ";\n";
+ }
dump_attributes(f, indent, cell->attributes);
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
@@ -598,6 +678,23 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
#undef HANDLE_UNIOP
#undef HANDLE_BINOP
+ if (cell->type == "$shiftx")
+ {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort("\\Y"));
+ f << stringf(" = ");
+ dump_sigspec(f, cell->getPort("\\A"));
+ f << stringf("[");
+ if (cell->getParam("\\B_SIGNED").as_bool())
+ f << stringf("$signed(");
+ dump_sigspec(f, cell->getPort("\\B"));
+ if (cell->getParam("\\B_SIGNED").as_bool())
+ f << stringf(")");
+ f << stringf(" +: %d", cell->getParam("\\Y_WIDTH").as_int());
+ f << stringf("];\n");
+ return true;
+ }
+
if (cell->type == "$mux")
{
f << stringf("%s" "assign ", indent.c_str());
@@ -698,8 +795,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
std::string reg_name = cellname(cell);
bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
- if (!out_is_reg_wire)
- f << stringf("%s" "reg [%d:0] %s;\n", indent.c_str(), width-1, reg_name.c_str());
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str());
+ dump_reg_init(f, sig_q);
+ f << ";\n";
+ }
for (int i = 0; i < width; i++) {
f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
@@ -754,8 +854,11 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
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;\n", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str());
+ 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 @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
dump_sigspec(f, sig_clk);
@@ -794,6 +897,42 @@ 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();
@@ -806,7 +945,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
// for memory block make something like:
// reg [7:0] memid [3:0];
// initial begin
- // memid[0] <= ...
+ // 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);
if (use_init)
@@ -814,7 +953,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
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);
+ 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");
}
@@ -912,7 +1051,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
int nwrite_ports = cell->parameters["\\WR_PORTS"].as_int();
RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
bool wr_clk_posedge;
- SigMap sigmap(active_module);
+
// write ports
for (int i=0; i < nwrite_ports; i++)
{
@@ -937,7 +1076,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
int start_i = i, width = 1;
SigBit wen_bit = sig_wr_en[i];
- while (i+1 < GetSize(sig_wr_en) && sigmap(sig_wr_en[i+1]) == sigmap(wen_bit))
+ while (i+1 < GetSize(sig_wr_en) && active_sigmap(sig_wr_en[i+1]) == active_sigmap(wen_bit))
i++, width++;
if (wen_bit == State::S0)
@@ -1251,6 +1390,16 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
reg_wires.clear();
reset_auto_counter(module);
active_module = module;
+ active_sigmap.set(module);
+ active_initdata.clear();
+
+ for (auto wire : module->wires())
+ if (wire->attributes.count("\\init")) {
+ 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 (!module->processes.empty())
log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n"
@@ -1327,11 +1476,13 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
f << stringf("%s" "endmodule\n", indent.c_str());
active_module = NULL;
+ active_sigmap.clear();
+ active_initdata.clear();
}
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");
@@ -1362,6 +1513,14 @@ struct VerilogBackend : public Backend {
log(" not bit pattern. This option decativates this feature and instead\n");
log(" will write out all constants in binary.\n");
log("\n");
+ log(" -decimal\n");
+ log(" dump 32-bit constants in decimal and without size and radix\n");
+ 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(" 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");
@@ -1391,7 +1550,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");
@@ -1401,8 +1560,10 @@ struct VerilogBackend : public Backend {
attr2comment = false;
noexpr = false;
nodec = false;
+ nohex = false;
nostr = false;
defparam = false;
+ decimal = false;
auto_prefix = "";
bool blackboxes = false;
@@ -1412,6 +1573,8 @@ struct VerilogBackend : public Backend {
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_");
@@ -1461,6 +1624,10 @@ struct VerilogBackend : public Backend {
nodec = true;
continue;
}
+ if (arg == "-nohex") {
+ nohex = true;
+ continue;
+ }
if (arg == "-nostr") {
nostr = true;
continue;
@@ -1469,6 +1636,10 @@ struct VerilogBackend : public Backend {
defparam = true;
continue;
}
+ if (arg == "-decimal") {
+ decimal = true;
+ continue;
+ }
if (arg == "-blackboxes") {
blackboxes = true;
continue;