diff options
Diffstat (limited to 'backends')
26 files changed, 4842 insertions, 1588 deletions
diff --git a/backends/aiger/Makefile.inc b/backends/aiger/Makefile.inc index 0fc37e95c..4a4cf30bd 100644 --- a/backends/aiger/Makefile.inc +++ b/backends/aiger/Makefile.inc @@ -1,3 +1,4 @@ OBJS += backends/aiger/aiger.o +OBJS += backends/aiger/xaiger.o diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 526e50a49..a51e3648c 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -70,37 +70,41 @@ struct AigerWriter int bit2aig(SigBit bit) { - if (aig_map.count(bit) == 0) - { - aig_map[bit] = -1; - - if (initstate_bits.count(bit)) { - log_assert(initstate_ff > 0); - aig_map[bit] = initstate_ff; - } else - if (not_map.count(bit)) { - int a = bit2aig(not_map.at(bit)) ^ 1; - aig_map[bit] = a; - } else - if (and_map.count(bit)) { - auto args = and_map.at(bit); - int a0 = bit2aig(args.first); - int a1 = bit2aig(args.second); - aig_map[bit] = mkgate(a0, a1); - } else - if (alias_map.count(bit)) { - aig_map[bit] = bit2aig(alias_map.at(bit)); - } + auto it = aig_map.find(bit); + if (it != aig_map.end()) { + log_assert(it->second >= 0); + return it->second; + } - if (bit == State::Sx || bit == State::Sz) - log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); + // NB: Cannot use iterator returned from aig_map.insert() + // since this function is called recursively + + int a = -1; + if (not_map.count(bit)) { + a = bit2aig(not_map.at(bit)) ^ 1; + } else + if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + a = mkgate(a0, a1); + } else + if (alias_map.count(bit)) { + a = bit2aig(alias_map.at(bit)); + } else + if (initstate_bits.count(bit)) { + a = initstate_ff; } - log_assert(aig_map.at(bit) >= 0); - return aig_map.at(bit); + if (bit == State::Sx || bit == State::Sz) + log_error("Design contains 'x' or 'z' bits. Use 'setundef' to replace those constants.\n"); + + log_assert(a >= 0); + aig_map[bit] = a; + return a; } - AigerWriter(Module *module, bool zinit_mode) : module(module), zinit_mode(zinit_mode), sigmap(module) + AigerWriter(Module *module, bool zinit_mode, bool imode, bool omode, bool bmode, bool lmode) : module(module), zinit_mode(zinit_mode), sigmap(module) { pool<SigBit> undriven_bits; pool<SigBit> unused_bits; @@ -293,6 +297,10 @@ struct AigerWriter aig_map[bit] = 2*aig_m; } + if (imode && input_bits.empty()) { + aig_m++, aig_i++; + } + if (zinit_mode) { for (auto it : ff_map) { @@ -362,6 +370,12 @@ struct AigerWriter aig_latchin.push_back(a); } + if (lmode && aig_l == 0) { + aig_m++, aig_l++; + aig_latchinit.push_back(0); + aig_latchin.push_back(0); + } + if (!initstate_bits.empty() || !init_inputs.empty()) aig_latchin.push_back(1); @@ -371,6 +385,11 @@ struct AigerWriter aig_outputs.push_back(bit2aig(bit)); } + if (omode && output_bits.empty()) { + aig_o++; + aig_outputs.push_back(0); + } + for (auto it : asserts) { aig_b++; int bit_a = bit2aig(it.first); @@ -378,6 +397,11 @@ struct AigerWriter aig_outputs.push_back(mkgate(bit_a^1, bit_en)); } + if (bmode && asserts.empty()) { + aig_b++; + aig_outputs.push_back(0); + } + for (auto it : assumes) { aig_c++; int bit_a = bit2aig(it.first); @@ -657,7 +681,7 @@ struct AigerWriter struct AigerBackend : public Backend { AigerBackend() : Backend("aiger", "write design to AIGER file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -671,7 +695,7 @@ struct AigerBackend : public Backend { log("invariant constraints.\n"); log("\n"); log(" -ascii\n"); - log(" write ASCII version of AGIER format\n"); + log(" write ASCII version of AIGER format\n"); log("\n"); log(" -zinit\n"); log(" convert FFs to zero-initialized FFs, adding additional inputs for\n"); @@ -689,14 +713,23 @@ struct AigerBackend : public Backend { log(" -vmap <filename>\n"); log(" like -map, but more verbose\n"); log("\n"); + log(" -I, -O, -B, -L\n"); + log(" If the design contains no input/output/assert/flip-flop then create one\n"); + log(" dummy input/output/bad_state-pin or latch to make the tools reading the\n"); + log(" AIGER file happy.\n"); + log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool ascii_mode = false; bool zinit_mode = false; bool miter_mode = false; bool symbols_mode = false; bool verbose_map = false; + bool imode = false; + bool omode = false; + bool bmode = false; + bool lmode = false; std::string map_filename; log_header(design, "Executing AIGER backend.\n"); @@ -729,19 +762,44 @@ struct AigerBackend : public Backend { verbose_map = true; continue; } + if (args[argidx] == "-I") { + imode = true; + continue; + } + if (args[argidx] == "-O") { + omode = true; + continue; + } + if (args[argidx] == "-B") { + bmode = true; + continue; + } + if (args[argidx] == "-L") { + lmode = true; + continue; + } break; } - extra_args(f, filename, args, argidx); + extra_args(f, filename, args, argidx, !ascii_mode); Module *top_module = design->top_module(); if (top_module == nullptr) log_error("Can't find top module in current design!\n"); - AigerWriter writer(top_module, zinit_mode); + if (!design->selected_whole_module(top_module)) + log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module)); + + if (!top_module->processes.empty()) + log_error("Found unmapped processes in module %s: unmapped processes are not supported in AIGER backend!\n", log_id(top_module)); + if (!top_module->memories.empty()) + log_error("Found unmapped memories in module %s: unmapped memories are not supported in AIGER backend!\n", log_id(top_module)); + + AigerWriter writer(top_module, zinit_mode, imode, omode, bmode, lmode); writer.write_aiger(*f, ascii_mode, miter_mode, symbols_mode); if (!map_filename.empty()) { + rewrite_filename(filename); std::ofstream mapf; mapf.open(map_filename.c_str(), std::ofstream::trunc); if (mapf.fail()) diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc new file mode 100644 index 000000000..b72dd6890 --- /dev/null +++ b/backends/aiger/xaiger.cc @@ -0,0 +1,806 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * 2019 Eddie Hung <eddie@fpgeh.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +// https://stackoverflow.com/a/46137633 +#ifdef _MSC_VER +#include <stdlib.h> +#define bswap32 _byteswap_ulong +#elif defined(__APPLE__) +#include <libkern/OSByteOrder.h> +#define bswap32 OSSwapInt32 +#elif defined(__GNUC__) +#define bswap32 __builtin_bswap32 +#else +#include <cstdint> +inline static uint32_t bswap32(uint32_t x) +{ + // https://stackoverflow.com/a/27796212 + register uint32_t value = number_to_be_reversed; + uint8_t lolo = (value >> 0) & 0xFF; + uint8_t lohi = (value >> 8) & 0xFF; + uint8_t hilo = (value >> 16) & 0xFF; + uint8_t hihi = (value >> 24) & 0xFF; + return (hihi << 24) + | (hilo << 16) + | (lohi << 8) + | (lolo << 0); +} +#endif + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" +#include "kernel/utils.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +inline int32_t to_big_endian(int32_t i32) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return bswap32(i32); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return i32; +#else +#error "Unknown endianness" +#endif +} + +void aiger_encode(std::ostream &f, int x) +{ + log_assert(x >= 0); + + while (x & ~0x7f) { + f.put((x & 0x7f) | 0x80); + x = x >> 7; + } + + f.put(x); +} + +struct XAigerWriter +{ + Module *module; + SigMap sigmap; + + pool<SigBit> input_bits, output_bits; + dict<SigBit, SigBit> not_map, alias_map; + dict<SigBit, pair<SigBit, SigBit>> and_map; + vector<SigBit> ci_bits, co_bits; + dict<SigBit, Cell*> ff_bits; + dict<SigBit, float> arrival_times; + + vector<pair<int, int>> aig_gates; + vector<int> aig_outputs; + int aig_m = 0, aig_i = 0, aig_l = 0, aig_o = 0, aig_a = 0; + + dict<SigBit, int> aig_map; + dict<SigBit, int> ordered_outputs; + + vector<Cell*> box_list; + + int mkgate(int a0, int a1) + { + aig_m++, aig_a++; + aig_gates.push_back(a0 > a1 ? make_pair(a0, a1) : make_pair(a1, a0)); + return 2*aig_m; + } + + int bit2aig(SigBit bit) + { + auto it = aig_map.find(bit); + if (it != aig_map.end()) { + log_assert(it->second >= 0); + return it->second; + } + + // NB: Cannot use iterator returned from aig_map.insert() + // since this function is called recursively + + int a = -1; + if (not_map.count(bit)) { + a = bit2aig(not_map.at(bit)) ^ 1; + } else + if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + a = mkgate(a0, a1); + } else + if (alias_map.count(bit)) { + a = bit2aig(alias_map.at(bit)); + } + + if (bit == State::Sx || bit == State::Sz) { + log_debug("Design contains 'x' or 'z' bits. Treating as 1'b0.\n"); + a = aig_map.at(State::S0); + } + + log_assert(a >= 0); + aig_map[bit] = a; + return a; + } + + XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module) + { + pool<SigBit> undriven_bits; + pool<SigBit> unused_bits; + + // promote public wires + for (auto wire : module->wires()) + if (wire->name[0] == '\\') + sigmap.add(wire); + + // promote input wires + for (auto wire : module->wires()) + if (wire->port_input) + sigmap.add(wire); + + // promote keep wires + for (auto wire : module->wires()) + if (wire->get_bool_attribute(ID::keep)) + sigmap.add(wire); + + for (auto wire : module->wires()) + for (int i = 0; i < GetSize(wire); i++) + { + SigBit wirebit(wire, i); + SigBit bit = sigmap(wirebit); + + if (bit.wire == nullptr) { + if (wire->port_output) { + aig_map[wirebit] = (bit == State::S1) ? 1 : 0; + output_bits.insert(wirebit); + } + continue; + } + + undriven_bits.insert(bit); + unused_bits.insert(bit); + + bool keep = wire->get_bool_attribute(ID::keep); + if (wire->port_input || keep) + input_bits.insert(bit); + + if (wire->port_output || keep) { + if (bit != wirebit) + alias_map[wirebit] = bit; + output_bits.insert(wirebit); + } + } + + dict<IdString,dict<IdString,int>> arrival_cache; + for (auto cell : module->cells()) { + RTLIL::Module* inst_module = module->design->module(cell->type); + if (!cell->has_keep_attr()) { + if (cell->type == "$_NOT_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + undriven_bits.erase(Y); + not_map[Y] = A; + continue; + } + + if (cell->type == "$_AND_") + { + SigBit A = sigmap(cell->getPort("\\A").as_bit()); + SigBit B = sigmap(cell->getPort("\\B").as_bit()); + SigBit Y = sigmap(cell->getPort("\\Y").as_bit()); + unused_bits.erase(A); + unused_bits.erase(B); + undriven_bits.erase(Y); + and_map[Y] = make_pair(A, B); + continue; + } + + if (cell->type == "$__ABC9_FF_" && + // The presence of an abc9_mergeability attribute indicates + // that we do want to pass this flop to ABC + cell->attributes.count("\\abc9_mergeability")) + { + SigBit D = sigmap(cell->getPort("\\D").as_bit()); + SigBit Q = sigmap(cell->getPort("\\Q").as_bit()); + unused_bits.erase(D); + undriven_bits.erase(Q); + alias_map[Q] = D; + auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell)); + log_assert(r.second); + if (input_bits.erase(Q)) + log_assert(Q.wire->attributes.count(ID::keep)); + continue; + } + + if (inst_module) { + bool abc9_flop = false; + auto it = cell->attributes.find("\\abc9_box_seq"); + if (it != cell->attributes.end()) { + int abc9_box_seq = it->second.as_int(); + if (GetSize(box_list) <= abc9_box_seq) + box_list.resize(abc9_box_seq+1); + box_list[abc9_box_seq] = cell; + // Only flop boxes may have arrival times + abc9_flop = inst_module->get_bool_attribute("\\abc9_flop"); + if (!abc9_flop) + continue; + } + + auto &cell_arrivals = arrival_cache[cell->type]; + for (const auto &conn : cell->connections()) { + auto r = cell_arrivals.insert(conn.first); + auto &arrival = r.first->second; + if (r.second) { + auto port_wire = inst_module->wire(conn.first); + if (port_wire->port_output) { + auto it = port_wire->attributes.find("\\abc9_arrival"); + if (it != port_wire->attributes.end()) { + if (it->second.flags != 0) + log_error("Attribute 'abc9_arrival' on port '%s' of module '%s' is not an integer.\n", log_id(port_wire), log_id(cell->type)); + arrival = it->second.as_int(); + } + } + } + if (arrival) + for (auto bit : sigmap(conn.second)) + arrival_times[bit] = arrival; + } + + if (abc9_flop) + continue; + } + } + + bool cell_known = inst_module || cell->known(); + for (const auto &c : cell->connections()) { + if (c.second.is_fully_const()) continue; + auto port_wire = inst_module ? inst_module->wire(c.first) : nullptr; + auto is_input = (port_wire && port_wire->port_input) || !cell_known || cell->input(c.first); + auto is_output = (port_wire && port_wire->port_output) || !cell_known || cell->output(c.first); + if (!is_input && !is_output) + log_error("Connection '%s' on cell '%s' (type '%s') not recognised!\n", log_id(c.first), log_id(cell), log_id(cell->type)); + + if (is_input) + for (auto b : c.second) { + Wire *w = b.wire; + if (!w) continue; + // Do not add as PO if bit is already a PI + if (input_bits.count(b)) + continue; + if (!w->port_output || !cell_known) { + SigBit I = sigmap(b); + if (I != b) + alias_map[b] = I; + output_bits.insert(b); + } + } + } + + //log_warning("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + } + + dict<IdString, std::vector<IdString>> box_ports; + for (auto cell : box_list) { + log_assert(cell); + + RTLIL::Module* box_module = module->design->module(cell->type); + log_assert(box_module); + log_assert(box_module->attributes.count("\\abc9_box_id")); + + auto r = box_ports.insert(cell->type); + if (r.second) { + // Make carry in the last PI, and carry out the last PO + // since ABC requires it this way + IdString carry_in, carry_out; + for (const auto &port_name : box_module->ports) { + auto w = box_module->wire(port_name); + log_assert(w); + if (w->get_bool_attribute("\\abc9_carry")) { + if (w->port_input) { + if (carry_in != IdString()) + log_error("Module '%s' contains more than one 'abc9_carry' input port.\n", log_id(box_module)); + carry_in = port_name; + } + if (w->port_output) { + if (carry_out != IdString()) + log_error("Module '%s' contains more than one 'abc9_carry' output port.\n", log_id(box_module)); + carry_out = port_name; + } + } + else + r.first->second.push_back(port_name); + } + + if (carry_in != IdString() && carry_out == IdString()) + log_error("Module '%s' contains an 'abc9_carry' input port but no output port.\n", log_id(box_module)); + if (carry_in == IdString() && carry_out != IdString()) + log_error("Module '%s' contains an 'abc9_carry' output port but no input port.\n", log_id(box_module)); + if (carry_in != IdString()) { + r.first->second.push_back(carry_in); + r.first->second.push_back(carry_out); + } + } + + for (auto port_name : r.first->second) { + auto w = box_module->wire(port_name); + log_assert(w); + auto rhs = cell->connections_.at(port_name, SigSpec()); + rhs.append(Const(State::Sx, GetSize(w)-GetSize(rhs))); + if (w->port_input) + for (auto b : rhs) { + SigBit I = sigmap(b); + if (b == RTLIL::Sx) + b = State::S0; + else if (I != b) { + if (I == RTLIL::Sx) + alias_map[b] = State::S0; + else + alias_map[b] = I; + } + co_bits.emplace_back(b); + unused_bits.erase(I); + } + if (w->port_output) + for (const auto &b : rhs) { + SigBit O = sigmap(b); + if (O != b) + alias_map[O] = b; + ci_bits.emplace_back(b); + undriven_bits.erase(O); + // If PI and CI, then must be a (* keep *) wire + if (input_bits.erase(O)) { + log_assert(output_bits.count(O)); + log_assert(O.wire->get_bool_attribute(ID::keep)); + } + } + } + + // Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box + if (box_module->get_bool_attribute("\\abc9_flop")) { + SigSpec rhs = module->wire(stringf("%s.abc9_ff.Q", cell->name.c_str())); + if (rhs.empty()) + log_error("'%s.abc9_ff.Q' is not a wire present in module '%s'.\n", log_id(cell), log_id(module)); + + for (auto b : rhs) { + SigBit I = sigmap(b); + if (b == RTLIL::Sx) + b = State::S0; + else if (I != b) { + if (I == RTLIL::Sx) + alias_map[b] = State::S0; + else + alias_map[b] = I; + } + co_bits.emplace_back(b); + unused_bits.erase(I); + } + } + } + + for (auto bit : input_bits) + undriven_bits.erase(bit); + for (auto bit : output_bits) + unused_bits.erase(sigmap(bit)); + for (auto bit : unused_bits) + undriven_bits.erase(bit); + + // Make all undriven bits a primary input + for (auto bit : undriven_bits) { + input_bits.insert(bit); + undriven_bits.erase(bit); + } + + if (holes_mode) { + struct sort_by_port_id { + bool operator()(const RTLIL::SigBit& a, const RTLIL::SigBit& b) const { + return a.wire->port_id < b.wire->port_id || + (a.wire->port_id == b.wire->port_id && a.offset < b.offset); + } + }; + input_bits.sort(sort_by_port_id()); + output_bits.sort(sort_by_port_id()); + } + + aig_map[State::S0] = 0; + aig_map[State::S1] = 1; + + for (const auto &bit : input_bits) { + aig_m++, aig_i++; + log_assert(!aig_map.count(bit)); + aig_map[bit] = 2*aig_m; + } + + for (const auto &i : ff_bits) { + const Cell *cell = i.second; + const SigBit &q = sigmap(cell->getPort("\\Q")); + aig_m++, aig_i++; + log_assert(!aig_map.count(q)); + aig_map[q] = 2*aig_m; + } + + for (auto &bit : ci_bits) { + aig_m++, aig_i++; + // 1'bx may exist here due to a box output + // that has been padded to its full width + if (bit == State::Sx) + continue; + log_assert(!aig_map.count(bit)); + aig_map[bit] = 2*aig_m; + } + + for (auto bit : co_bits) { + ordered_outputs[bit] = aig_o++; + aig_outputs.push_back(bit2aig(bit)); + } + + for (const auto &bit : output_bits) { + ordered_outputs[bit] = aig_o++; + int aig; + // Unlike bit2aig() which checks aig_map first, for + // inout/keep bits, since aig_map will point to + // the PI, first attempt to find the NOT/AND driver + // before resorting to an aig_map lookup (which + // could be another PO) + if (input_bits.count(bit)) { + if (not_map.count(bit)) { + aig = bit2aig(not_map.at(bit)) ^ 1; + } else if (and_map.count(bit)) { + auto args = and_map.at(bit); + int a0 = bit2aig(args.first); + int a1 = bit2aig(args.second); + aig = mkgate(a0, a1); + } + else + aig = aig_map.at(bit); + } + else + aig = bit2aig(bit); + aig_outputs.push_back(aig); + } + + for (auto &i : ff_bits) { + const SigBit &d = i.first; + aig_o++; + aig_outputs.push_back(aig_map.at(d)); + } + } + + void write_aiger(std::ostream &f, bool ascii_mode) + { + int aig_obc = aig_o; + int aig_obcj = aig_obc; + int aig_obcjf = aig_obcj; + + log_assert(aig_m == aig_i + aig_l + aig_a); + log_assert(aig_obcjf == GetSize(aig_outputs)); + + f << stringf("%s %d %d %d %d %d", ascii_mode ? "aag" : "aig", aig_m, aig_i, aig_l, aig_o, aig_a); + f << stringf("\n"); + + if (ascii_mode) + { + for (int i = 0; i < aig_i; i++) + f << stringf("%d\n", 2*i+2); + + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) + f << stringf("%d %d %d\n", 2*(aig_i+aig_l+i)+2, aig_gates.at(i).first, aig_gates.at(i).second); + } + else + { + for (int i = 0; i < aig_obc; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("1\n"); + + for (int i = aig_obc; i < aig_obcj; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = aig_obcj; i < aig_obcjf; i++) + f << stringf("%d\n", aig_outputs.at(i)); + + for (int i = 0; i < aig_a; i++) { + int lhs = 2*(aig_i+aig_l+i)+2; + int rhs0 = aig_gates.at(i).first; + int rhs1 = aig_gates.at(i).second; + int delta0 = lhs - rhs0; + int delta1 = rhs0 - rhs1; + aiger_encode(f, delta0); + aiger_encode(f, delta1); + } + } + + f << "c"; + + auto write_buffer = [](std::stringstream &buffer, int i32) { + int32_t i32_be = to_big_endian(i32); + buffer.write(reinterpret_cast<const char*>(&i32_be), sizeof(i32_be)); + }; + std::stringstream h_buffer; + auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); + write_h_buffer(1); + log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits)); + write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size()); + log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits)); + write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits)); + log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits)); + write_h_buffer(input_bits.size() + ff_bits.size()); + log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits)); + write_h_buffer(output_bits.size() + ff_bits.size()); + log_debug("boxNum = %d\n", GetSize(box_list)); + write_h_buffer(box_list.size()); + + auto write_buffer_float = [](std::stringstream &buffer, float f32) { + buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32)); + }; + std::stringstream i_buffer; + auto write_i_buffer = std::bind(write_buffer_float, std::ref(i_buffer), std::placeholders::_1); + for (auto bit : input_bits) + write_i_buffer(arrival_times.at(bit, 0)); + //std::stringstream o_buffer; + //auto write_o_buffer = std::bind(write_buffer_float, std::ref(o_buffer), std::placeholders::_1); + //for (auto bit : output_bits) + // write_o_buffer(0); + + if (!box_list.empty() || !ff_bits.empty()) { + dict<IdString, std::tuple<int,int,int>> cell_cache; + + int box_count = 0; + for (auto cell : box_list) { + log_assert(cell); + + RTLIL::Module* box_module = module->design->module(cell->type); + log_assert(box_module); + + auto r = cell_cache.insert(cell->type); + auto &v = r.first->second; + if (r.second) { + int box_inputs = 0, box_outputs = 0; + for (auto port_name : box_module->ports) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + if (w->port_input) + box_inputs += GetSize(w); + if (w->port_output) + box_outputs += GetSize(w); + } + + // For flops only, create an extra 1-bit input that drives a new wire + // called "<cell>.abc9_ff.Q" that is used below + if (box_module->get_bool_attribute("\\abc9_flop")) + box_inputs++; + + std::get<0>(v) = box_inputs; + std::get<1>(v) = box_outputs; + std::get<2>(v) = box_module->attributes.at("\\abc9_box_id").as_int(); + } + + write_h_buffer(std::get<0>(v)); + write_h_buffer(std::get<1>(v)); + write_h_buffer(std::get<2>(v)); + write_h_buffer(box_count++); + } + + std::stringstream r_buffer; + auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); + log_debug("flopNum = %d\n", GetSize(ff_bits)); + write_r_buffer(ff_bits.size()); + + std::stringstream s_buffer; + auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1); + write_s_buffer(ff_bits.size()); + + for (const auto &i : ff_bits) { + const SigBit &d = i.first; + const Cell *cell = i.second; + + int mergeability = cell->attributes.at(ID(abc9_mergeability)).as_int(); + log_assert(mergeability > 0); + write_r_buffer(mergeability); + + Const init = cell->attributes.at(ID(abc9_init)); + log_assert(GetSize(init) == 1); + if (init == State::S1) + write_s_buffer(1); + else if (init == State::S0) + write_s_buffer(0); + else { + log_assert(init == State::Sx); + write_s_buffer(0); + } + + write_i_buffer(arrival_times.at(d, 0)); + //write_o_buffer(0); + } + + f << "r"; + std::string buffer_str = r_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + + f << "s"; + buffer_str = s_buffer.str(); + buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + + RTLIL::Module *holes_module = module->design->module(stringf("%s$holes", module->name.c_str())); + if (holes_module) { + std::stringstream a_buffer; + XAigerWriter writer(holes_module, true /* holes_mode */); + writer.write_aiger(a_buffer, false /*ascii_mode*/); + + f << "a"; + std::string buffer_str = a_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + } + } + + f << "h"; + std::string buffer_str = h_buffer.str(); + int32_t buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + + f << "i"; + buffer_str = i_buffer.str(); + buffer_size_be = to_big_endian(buffer_str.size()); + f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + f.write(buffer_str.data(), buffer_str.size()); + //f << "o"; + //buffer_str = o_buffer.str(); + //buffer_size_be = to_big_endian(buffer_str.size()); + //f.write(reinterpret_cast<const char*>(&buffer_size_be), sizeof(buffer_size_be)); + //f.write(buffer_str.data(), buffer_str.size()); + + f << stringf("Generated by %s\n", yosys_version_str); + + module->design->scratchpad_set_int("write_xaiger.num_ands", and_map.size()); + module->design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size()); + module->design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size()); + module->design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size()); + } + + void write_map(std::ostream &f) + { + dict<int, string> input_lines; + dict<int, string> output_lines; + + for (auto wire : module->wires()) + { + SigSpec sig = sigmap(wire); + + for (int i = 0; i < GetSize(wire); i++) + { + RTLIL::SigBit b(wire, i); + if (input_bits.count(b)) { + int a = aig_map.at(b); + log_assert((a & 1) == 0); + input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire)); + } + + if (output_bits.count(b)) { + int o = ordered_outputs.at(b); + int init = 2; + output_lines[o] += stringf("output %d %d %s %d\n", o - GetSize(co_bits), i, log_id(wire), init); + continue; + } + } + } + + input_lines.sort(); + for (auto &it : input_lines) + f << it.second; + log_assert(input_lines.size() == input_bits.size()); + + int box_count = 0; + for (auto cell : box_list) + f << stringf("box %d %d %s\n", box_count++, 0, log_id(cell->name)); + + output_lines.sort(); + for (auto &it : output_lines) + f << it.second; + log_assert(output_lines.size() == output_bits.size()); + } +}; + +struct XAigerBackend : public Backend { + XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_xaiger [options] [filename]\n"); + log("\n"); + log("Write the top module (according to the (* top *) attribute or if only one module\n"); + log("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, $_ABC9_FF_, or"); + log("non (* abc9_box_id *) cells will be converted into psuedo-inputs and\n"); + log("pseudo-outputs. Whitebox contents will be taken from the '<module-name>$holes'\n"); + log("module, if it exists.\n"); + log("\n"); + log(" -ascii\n"); + log(" write ASCII version of AIGER format\n"); + log("\n"); + log(" -map <filename>\n"); + log(" write an extra file with port and box symbols\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool ascii_mode = false; + std::string map_filename; + + log_header(design, "Executing XAIGER backend.\n"); + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-ascii") { + ascii_mode = true; + continue; + } + if (map_filename.empty() && args[argidx] == "-map" && argidx+1 < args.size()) { + map_filename = args[++argidx]; + continue; + } + break; + } + extra_args(f, filename, args, argidx, !ascii_mode); + + Module *top_module = design->top_module(); + + if (top_module == nullptr) + log_error("Can't find top module in current design!\n"); + + if (!design->selected_whole_module(top_module)) + log_cmd_error("Can't handle partially selected module %s!\n", log_id(top_module)); + + if (!top_module->processes.empty()) + log_error("Found unmapped processes in module %s: unmapped processes are not supported in XAIGER backend!\n", log_id(top_module)); + if (!top_module->memories.empty()) + log_error("Found unmapped memories in module %s: unmapped memories are not supported in XAIGER backend!\n", log_id(top_module)); + + XAigerWriter writer(top_module); + writer.write_aiger(*f, ascii_mode); + + if (!map_filename.empty()) { + std::ofstream mapf; + mapf.open(map_filename.c_str(), std::ofstream::trunc); + if (mapf.fail()) + log_error("Can't open file `%s' for writing: %s\n", map_filename.c_str(), strerror(errno)); + writer.write_map(mapf); + } + } +} XAigerBackend; + +PRIVATE_NAMESPACE_END diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc index f9230a1e6..b6e38c16c 100644 --- a/backends/blif/blif.cc +++ b/backends/blif/blif.cc @@ -38,8 +38,10 @@ struct BlifDumperConfig bool impltf_mode; bool gates_mode; bool cname_mode; + bool iname_mode; bool param_mode; bool attr_mode; + bool iattr_mode; bool blackbox_mode; bool noalias_mode; @@ -48,7 +50,8 @@ struct BlifDumperConfig std::string true_type, true_out, false_type, false_out, undef_type, undef_out; BlifDumperConfig() : icells_mode(false), conn_mode(false), impltf_mode(false), gates_mode(false), - cname_mode(false), param_mode(false), attr_mode(false), blackbox_mode(false), noalias_mode(false) { } + cname_mode(false), iname_mode(false), param_mode(false), attr_mode(false), iattr_mode(false), + blackbox_mode(false), noalias_mode(false) { } }; struct BlifDumper @@ -137,7 +140,7 @@ struct BlifDumper return "subckt"; if (!design->modules_.count(RTLIL::escape_id(cell_type))) return "gate"; - if (design->modules_.at(RTLIL::escape_id(cell_type))->get_bool_attribute("\\blackbox")) + if (design->modules_.at(RTLIL::escape_id(cell_type))->get_blackbox_attribute()) return "gate"; return "subckt"; } @@ -193,7 +196,7 @@ struct BlifDumper } f << stringf("\n"); - if (module->get_bool_attribute("\\blackbox")) { + if (module->get_blackbox_attribute()) { f << stringf(".blackbox\n"); f << stringf(".end\n"); return; @@ -240,118 +243,125 @@ struct BlifDumper if (!config->icells_mode && cell->type == "$_NOT_") { f << stringf(".names %s %s\n0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_AND_") { f << stringf(".names %s %s %s\n11 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_OR_") { f << stringf(".names %s %s %s\n1- 1\n-1 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_XOR_") { f << stringf(".names %s %s %s\n10 1\n01 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_NAND_") { f << stringf(".names %s %s %s\n0- 1\n-0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_NOR_") { f << stringf(".names %s %s %s\n00 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_XNOR_") { f << stringf(".names %s %s %s\n11 1\n00 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_ANDNOT_") { f << stringf(".names %s %s %s\n10 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_ORNOT_") { f << stringf(".names %s %s %s\n1- 1\n-0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_AOI3_") { f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_OAI3_") { f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_AOI4_") { f << stringf(".names %s %s %s %s %s\n-0-0 1\n-00- 1\n0--0 1\n0-0- 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_OAI4_") { f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\C")), cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_MUX_") { f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n", cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y"))); - continue; + goto internal_cell; + } + + if (!config->icells_mode && cell->type == "$_NMUX_") { + f << stringf(".names %s %s %s %s\n0-0 1\n-01 1\n", + cstr(cell->getPort("\\A")), cstr(cell->getPort("\\B")), + cstr(cell->getPort("\\S")), cstr(cell->getPort("\\Y"))); + goto internal_cell; } if (!config->icells_mode && cell->type == "$_FF_") { f << stringf(".latch %s %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DFF_N_") { f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DFF_P_") { f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\C")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DLATCH_N_") { f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$_DLATCH_P_") { f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort("\\D")), cstr(cell->getPort("\\Q")), cstr(cell->getPort("\\E")), cstr_init(cell->getPort("\\Q"))); - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$lut") { @@ -367,13 +377,13 @@ struct BlifDumper f << stringf("\n"); RTLIL::SigSpec mask = cell->parameters.at("\\LUT"); for (int i = 0; i < (1 << width); i++) - if (mask[i] == RTLIL::S1) { + if (mask[i] == State::S1) { for (int j = width-1; j >= 0; j--) { f << ((i>>j)&1 ? '1' : '0'); } f << " 1\n"; } - continue; + goto internal_cell; } if (!config->icells_mode && cell->type == "$sop") { @@ -401,17 +411,31 @@ struct BlifDumper } f << " 1\n"; } - continue; + goto internal_cell; } f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type)); for (auto &conn : cell->connections()) - for (int i = 0; i < conn.second.size(); i++) { - if (conn.second.size() == 1) - f << stringf(" %s", cstr(conn.first)); - else - f << stringf(" %s[%d]", cstr(conn.first), i); - f << stringf("=%s", cstr(conn.second.extract(i, 1))); + { + if (conn.second.size() == 1) { + f << stringf(" %s=%s", cstr(conn.first), cstr(conn.second[0])); + continue; + } + + Module *m = design->module(cell->type); + Wire *w = m ? m->wire(conn.first) : nullptr; + + if (w == nullptr) { + for (int i = 0; i < GetSize(conn.second); i++) + f << stringf(" %s[%d]=%s", cstr(conn.first), i, cstr(conn.second[i])); + } else { + for (int i = 0; i < std::min(GetSize(conn.second), GetSize(w)); i++) { + SigBit sig(w, i); + f << stringf(" %s[%d]=%s", cstr(conn.first), sig.wire->upto ? + sig.wire->start_offset+sig.wire->width-sig.offset-1 : + sig.wire->start_offset+sig.offset, cstr(conn.second[i])); + } + } } f << stringf("\n"); @@ -421,6 +445,14 @@ struct BlifDumper dump_params(".attr", cell->attributes); if (config->param_mode) dump_params(".param", cell->parameters); + + if (0) { + internal_cell: + if (config->iname_mode) + f << stringf(".cname %s\n", cstr(cell->name)); + if (config->iattr_mode) + dump_params(".attr", cell->attributes); + } } for (auto &conn : module->connections()) @@ -453,7 +485,7 @@ struct BlifDumper struct BlifBackend : public Backend { BlifBackend() : Backend("blif", "write design to BLIF file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -511,6 +543,11 @@ struct BlifBackend : public Backend { log(" -cname\n"); log(" use the non-standard .cname statement to write cell names\n"); log("\n"); + log(" -iname, -iattr\n"); + log(" enable -cname and -attr functionality for .names statements\n"); + log(" (the .cname and .attr statements will be included in the BLIF\n"); + log(" output after the truth table for the .names statement)\n"); + log("\n"); log(" -blackbox\n"); log(" write blackbox cells with .blackbox statement.\n"); log("\n"); @@ -518,7 +555,7 @@ struct BlifBackend : public Backend { log(" do not write definitions for the $true, $false and $undef wires.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string top_module_name; std::string buf_type, buf_in, buf_out; @@ -587,6 +624,14 @@ struct BlifBackend : public Backend { config.attr_mode = true; continue; } + if (args[argidx] == "-iname") { + config.iname_mode = true; + continue; + } + if (args[argidx] == "-iattr") { + config.iattr_mode = true; + continue; + } if (args[argidx] == "-blackbox") { config.blackbox_mode = true; continue; @@ -616,7 +661,7 @@ struct BlifBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox") && !config.blackbox_mode) + if (module->get_blackbox_attribute() && !config.blackbox_mode) continue; if (module->processes.size() != 0) diff --git a/backends/btor/README b/backends/btor/README deleted file mode 100644 index efcf0d8f5..000000000 --- a/backends/btor/README +++ /dev/null @@ -1,23 +0,0 @@ - -This is the Yosys BTOR backend. -It is developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy - -Master git repository for the BTOR backend: -https://github.com/ahmedirfan1983/yosys - - -[[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking -Johannes Kepler University, Linz, Austria -http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf - - -Todos: ------- - -- Add checks for unsupported stuff - - unsupported cell types - - async resets - - etc.. - -- Add support for $lut cells - diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index bbe90e85f..c1da4b127 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -2,7 +2,6 @@ * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> - * Copyright (C) 2014 Ahmed Irfan <irfan@fbk.eu> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,9 +17,10 @@ * */ -// [[CITE]] BTOR: Bit-Precise Modelling of Word-Level Problems for Model Checking -// Johannes Kepler University, Linz, Austria -// http://fmv.jku.at/papers/BrummayerBiereLonsing-BPR08.pdf +// [[CITE]] Btor2 , BtorMC and Boolector 3.0 +// Aina Niemetz, Mathias Preiner, Clifford Wolf, Armin Biere +// Computer Aided Verification - 30th International Conference, CAV 2018 +// https://cs.stanford.edu/people/niemetz/publication/2018/niemetzpreinerwolfbiere-cav18/ #include "kernel/rtlil.h" #include "kernel/register.h" @@ -28,1083 +28,1240 @@ #include "kernel/celltypes.h" #include "kernel/log.h" #include <string> -#include <math.h> USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -struct BtorDumperConfig +struct BtorWorker { - bool subckt_mode; - bool conn_mode; - bool impltf_mode; + std::ostream &f; + SigMap sigmap; + RTLIL::Module *module; + bool verbose; + bool single_bad; - std::string buf_type, buf_in, buf_out; - std::string true_type, true_out, false_type, false_out; + int next_nid = 1; + int initstate_nid = -1; - BtorDumperConfig() : subckt_mode(false), conn_mode(false), impltf_mode(false) { } -}; + // <width> => <sid> + dict<int, int> sorts_bv; -struct WireInfo -{ - RTLIL::IdString cell_name; - const RTLIL::SigChunk *chunk; + // (<address-width>, <data-width>) => <sid> + dict<pair<int, int>, int> sorts_mem; - WireInfo(RTLIL::IdString c, const RTLIL::SigChunk* ch) : cell_name(c), chunk(ch) { } -}; + // SigBit => (<nid>, <bitidx>) + dict<SigBit, pair<int, int>> bit_nid; -struct WireInfoOrder -{ - bool operator() (const WireInfo& x, const WireInfo& y) + // <nid> => <bvwidth> + dict<int, int> nid_width; + + // SigSpec => <nid> + dict<SigSpec, int> sig_nid; + + // bit to driving cell + dict<SigBit, Cell*> bit_cell; + + // nids for constants + dict<Const, int> consts; + + // ff inputs that need to be evaluated (<nid>, <ff_cell>) + vector<pair<int, Cell*>> ff_todo; + + pool<Cell*> cell_recursion_guard; + vector<int> bad_properties; + dict<SigBit, bool> initbits; + pool<Wire*> statewires; + string indent; + + void btorf(const char *fmt, ...) { - return x.chunk < y.chunk; + va_list ap; + va_start(ap, fmt); + f << indent << vstringf(fmt, ap); + va_end(ap); } -}; -struct BtorDumper -{ - std::ostream &f; - RTLIL::Module *module; - RTLIL::Design *design; - BtorDumperConfig *config; - CellTypes ct; + void btorf_push(const string &id) + { + if (verbose) { + f << indent << stringf(" ; begin %s\n", id.c_str()); + indent += " "; + } + } - SigMap sigmap; - std::map<RTLIL::IdString, std::set<WireInfo,WireInfoOrder>> inter_wire_map;//<wire, dependency list> for mapping the intermediate wires that are output of some cell - std::map<RTLIL::IdString, int> line_ref;//mapping of ids to line_num of the btor file - std::map<RTLIL::SigSpec, int> sig_ref;//mapping of sigspec to the line_num of the btor file - int line_num;//last line number of btor file - std::string str;//temp string for writing file - std::map<RTLIL::IdString, bool> basic_wires;//input wires and registers - RTLIL::IdString curr_cell; //current cell being dumped - std::map<std::string, std::string> cell_type_translation, s_cell_type_translation; //RTLIL to BTOR translation - std::map<int, std::set<std::pair<int,int>>> mem_next; // memory (line_number)'s set of condition and write - BtorDumper(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig *config) : - f(f), module(module), design(design), config(config), ct(design), sigmap(module) + void btorf_pop(const string &id) { - line_num=0; - str.clear(); - for(auto it=module->wires_.begin(); it!=module->wires_.end(); ++it) - { - if(it->second->port_input) - { - basic_wires[it->first]=true; - } - else - { - basic_wires[it->first]=false; - } - inter_wire_map[it->first].clear(); + if (verbose) { + indent = indent.substr(4); + f << indent << stringf(" ; end %s\n", id.c_str()); } - curr_cell.clear(); - //assert - cell_type_translation["$assert"] = "root"; - //unary - cell_type_translation["$not"] = "not"; - cell_type_translation["$neg"] = "neg"; - cell_type_translation["$reduce_and"] = "redand"; - cell_type_translation["$reduce_or"] = "redor"; - cell_type_translation["$reduce_xor"] = "redxor"; - cell_type_translation["$reduce_bool"] = "redor"; - //binary - cell_type_translation["$and"] = "and"; - cell_type_translation["$or"] = "or"; - cell_type_translation["$xor"] = "xor"; - cell_type_translation["$xnor"] = "xnor"; - cell_type_translation["$shr"] = "srl"; - cell_type_translation["$shl"] = "sll"; - cell_type_translation["$sshr"] = "sra"; - cell_type_translation["$sshl"] = "sll"; - cell_type_translation["$shift"] = "srl"; - cell_type_translation["$shiftx"] = "srl"; - cell_type_translation["$lt"] = "ult"; - cell_type_translation["$le"] = "ulte"; - cell_type_translation["$gt"] = "ugt"; - cell_type_translation["$ge"] = "ugte"; - cell_type_translation["$eq"] = "eq"; - cell_type_translation["$eqx"] = "eq"; - cell_type_translation["$ne"] = "ne"; - cell_type_translation["$nex"] = "ne"; - cell_type_translation["$add"] = "add"; - cell_type_translation["$sub"] = "sub"; - cell_type_translation["$mul"] = "mul"; - cell_type_translation["$mod"] = "urem"; - cell_type_translation["$div"] = "udiv"; - //mux - cell_type_translation["$mux"] = "cond"; - //reg - cell_type_translation["$dff"] = "next"; - cell_type_translation["$adff"] = "next"; - cell_type_translation["$dffsr"] = "next"; - //memories - //nothing here - //slice - cell_type_translation["$slice"] = "slice"; - //concat - cell_type_translation["$concat"] = "concat"; - - //signed cell type translation - //binary - s_cell_type_translation["$modx"] = "srem"; - s_cell_type_translation["$mody"] = "smod"; - s_cell_type_translation["$div"] = "sdiv"; - s_cell_type_translation["$lt"] = "slt"; - s_cell_type_translation["$le"] = "slte"; - s_cell_type_translation["$gt"] = "sgt"; - s_cell_type_translation["$ge"] = "sgte"; + } + int get_bv_sid(int width) + { + if (sorts_bv.count(width) == 0) { + int nid = next_nid++; + btorf("%d sort bitvec %d\n", nid, width); + sorts_bv[width] = nid; + } + return sorts_bv.at(width); } - vector<shared_str> cstr_buf; + int get_mem_sid(int abits, int dbits) + { + pair<int, int> key(abits, dbits); + if (sorts_mem.count(key) == 0) { + int addr_sid = get_bv_sid(abits); + int data_sid = get_bv_sid(dbits); + int nid = next_nid++; + btorf("%d sort array %d %d\n", nid, addr_sid, data_sid); + sorts_mem[key] = nid; + } + return sorts_mem.at(key); + } - const char *cstr(const RTLIL::IdString id) + void add_nid_sig(int nid, const SigSpec &sig) { - str = RTLIL::unescape_id(id); - for (size_t i = 0; i < str.size(); ++i) - if (str[i] == '#' || str[i] == '=') - str[i] = '?'; - cstr_buf.push_back(str); - return cstr_buf.back().c_str(); + if (verbose) + f << indent << stringf("; %d %s\n", nid, log_signal(sig)); + + for (int i = 0; i < GetSize(sig); i++) + bit_nid[sig[i]] = make_pair(nid, i); + + sig_nid[sig] = nid; + nid_width[nid] = GetSize(sig); } - int dump_wire(RTLIL::Wire* wire) + void export_cell(Cell *cell) { - if(basic_wires[wire->name]) - { - log("writing wire %s\n", cstr(wire->name)); - auto it = line_ref.find(wire->name); - if(it==std::end(line_ref)) - { - ++line_num; - line_ref[wire->name]=line_num; - str = stringf("%d var %d %s", line_num, wire->width, cstr(wire->name)); - f << stringf("%s\n", str.c_str()); - return line_num; - } - else return it->second; + if (cell_recursion_guard.count(cell)) { + string cell_list; + for (auto c : cell_recursion_guard) + cell_list += stringf("\n %s", log_id(c)); + log_error("Found topological loop while processing cell %s. Active cells:%s\n", log_id(cell), cell_list.c_str()); } - else // case when the wire is not basic wire + + cell_recursion_guard.insert(cell); + btorf_push(log_id(cell)); + + if (cell->type.in("$add", "$sub", "$mul", "$and", "$or", "$xor", "$xnor", "$shl", "$sshl", "$shr", "$sshr", "$shift", "$shiftx", + "$concat", "$_AND_", "$_NAND_", "$_OR_", "$_NOR_", "$_XOR_", "$_XNOR_")) { - log("case of non-basic wire - %s\n", cstr(wire->name)); - auto it = line_ref.find(wire->name); - if(it==std::end(line_ref)) + string btor_op; + if (cell->type == "$add") btor_op = "add"; + if (cell->type == "$sub") btor_op = "sub"; + if (cell->type == "$mul") btor_op = "mul"; + if (cell->type.in("$shl", "$sshl")) btor_op = "sll"; + if (cell->type == "$shr") btor_op = "srl"; + if (cell->type == "$sshr") btor_op = "sra"; + if (cell->type.in("$shift", "$shiftx")) btor_op = "shift"; + if (cell->type.in("$and", "$_AND_")) btor_op = "and"; + if (cell->type.in("$or", "$_OR_")) btor_op = "or"; + if (cell->type.in("$xor", "$_XOR_")) btor_op = "xor"; + if (cell->type == "$concat") btor_op = "concat"; + if (cell->type == "$_NAND_") btor_op = "nand"; + if (cell->type == "$_NOR_") btor_op = "nor"; + if (cell->type.in("$xnor", "$_XNOR_")) btor_op = "xnor"; + log_assert(!btor_op.empty()); + + int width = GetSize(cell->getPort("\\Y")); + width = std::max(width, GetSize(cell->getPort("\\A"))); + width = std::max(width, GetSize(cell->getPort("\\B"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false; + + if (btor_op == "shift" && !b_signed) + btor_op = "srl"; + + if (cell->type.in("$shl", "$sshl", "$shr", "$sshr")) + b_signed = false; + + if (cell->type == "$sshr" && !a_signed) + btor_op = "srl"; + + int sid = get_bv_sid(width); + int nid; + + if (btor_op == "shift") { - std::set<WireInfo, WireInfoOrder>& dep_set = inter_wire_map.at(wire->name); - int wire_line = 0; - int wire_width = 0; - for(auto dep_set_it=dep_set.begin(); dep_set_it!=dep_set.end(); ++dep_set_it) - { - RTLIL::IdString cell_id = dep_set_it->cell_name; - if(cell_id == curr_cell) - break; - log(" -- found cell %s\n", cstr(cell_id)); - RTLIL::Cell* cell = module->cells_.at(cell_id); - const RTLIL::SigSpec* cell_output = get_cell_output(cell); - int cell_line = dump_cell(cell); + int nid_a = get_sig_nid(cell->getPort("\\A"), width, false); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); - if(dep_set.size()==1 && wire->width == cell_output->size()) - { - wire_line = cell_line; - break; - } - else - { - int prev_wire_line=0; //previously dumped wire line - int start_bit=0; - for(unsigned j=0; j<cell_output->chunks().size(); ++j) - { - start_bit+=cell_output->chunks().at(j).width; - if(cell_output->chunks().at(j).wire->name == wire->name) - { - prev_wire_line = wire_line; - wire_line = ++line_num; - str = stringf("%d slice %d %d %d %d;1", line_num, cell_output->chunks().at(j).width, - cell_line, start_bit-1, start_bit-cell_output->chunks().at(j).width); - f << stringf("%s\n", str.c_str()); - wire_width += cell_output->chunks().at(j).width; - if(prev_wire_line!=0) - { - ++line_num; - str = stringf("%d concat %d %d %d", line_num, wire_width, wire_line, prev_wire_line); - f << stringf("%s\n", str.c_str()); - wire_line = line_num; - } - } - } - } - } - if(dep_set.size()==0) - { - log(" - checking sigmap\n"); - RTLIL::SigSpec s = RTLIL::SigSpec(wire); - wire_line = dump_sigspec(&s, s.size()); - line_ref[wire->name]=wire_line; - } - line_ref[wire->name]=wire_line; - return wire_line; + int nid_r = next_nid++; + btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b); + + int nid_b_neg = next_nid++; + btorf("%d neg %d %d\n", nid_b_neg, sid, nid_b); + + int nid_l = next_nid++; + btorf("%d sll %d %d %d\n", nid_l, sid, nid_a, nid_b_neg); + + int sid_bit = get_bv_sid(1); + int nid_zero = get_sig_nid(Const(0, width)); + int nid_b_ltz = next_nid++; + btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero); + + nid = next_nid++; + btorf("%d ite %d %d %d %d\n", nid, sid, nid_b_ltz, nid_l, nid_r); } else { - log(" -- already processed wire\n"); - return it->second; + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + nid = next_nid++; + btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) < width) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); + nid = nid2; } + + add_nid_sig(nid, sig); + goto okay; } - log_abort(); - return -1; - } - int dump_memory(const RTLIL::Memory* memory) - { - log("writing memory %s\n", cstr(memory->name)); - auto it = line_ref.find(memory->name); - if(it==std::end(line_ref)) + if (cell->type.in("$div", "$mod")) { - ++line_num; - int address_bits = ceil_log2(memory->size); - str = stringf("%d array %d %d", line_num, memory->width, address_bits); - line_ref[memory->name]=line_num; - f << stringf("%s\n", str.c_str()); - return line_num; + string btor_op; + if (cell->type == "$div") btor_op = "div"; + if (cell->type == "$mod") btor_op = "rem"; + log_assert(!btor_op.empty()); + + int width = GetSize(cell->getPort("\\Y")); + width = std::max(width, GetSize(cell->getPort("\\A"))); + width = std::max(width, GetSize(cell->getPort("\\B"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false; + + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + int sid = get_bv_sid(width); + int nid = next_nid++; + btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b); + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) < width) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; } - else return it->second; - } - int dump_memory_next(const RTLIL::Memory* memory) - { - auto mem_it = line_ref.find(memory->name); - int address_bits = ceil_log2(memory->size); - if(mem_it==std::end(line_ref)) - { - log("can not write next of a memory that is not dumped yet\n"); - log_abort(); - } - else - { - auto acond_list_it = mem_next.find(mem_it->second); - if(acond_list_it!=std::end(mem_next)) + if (cell->type.in("$_ANDNOT_", "$_ORNOT_")) { - std::set<std::pair<int,int>>& cond_list = acond_list_it->second; - if(cond_list.empty()) - { - return 0; - } - auto it=cond_list.begin(); - ++line_num; - str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, mem_it->second); - f << stringf("%s\n", str.c_str()); - ++it; - for(; it!=cond_list.end(); ++it) - { - ++line_num; - str = stringf("%d acond %d %d %d %d %d", line_num, memory->width, address_bits, it->first, it->second, line_num-1); - f << stringf("%s\n", str.c_str()); - } - ++line_num; - str = stringf("%d anext %d %d %d %d", line_num, memory->width, address_bits, mem_it->second, line_num-1); - f << stringf("%s\n", str.c_str()); - return 1; + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = get_sig_nid(cell->getPort("\\B")); + + int nid1 = next_nid++; + int nid2 = next_nid++; + + if (cell->type == "$_ANDNOT_") { + btorf("%d not %d %d\n", nid1, sid, nid_b); + btorf("%d and %d %d %d\n", nid2, sid, nid_a, nid1); + } + + if (cell->type == "$_ORNOT_") { + btorf("%d not %d %d\n", nid1, sid, nid_b); + btorf("%d or %d %d %d\n", nid2, sid, nid_a, nid1); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + add_nid_sig(nid2, sig); + goto okay; } - return 0; - } - } - int dump_const(const RTLIL::Const* data, int width, int offset) - { - log("writing const \n"); - if((data->flags & RTLIL::CONST_FLAG_STRING) == 0) + if (cell->type.in("$_OAI3_", "$_AOI3_")) { - if(width<0) - width = data->bits.size() - offset; + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = get_sig_nid(cell->getPort("\\B")); + int nid_c = get_sig_nid(cell->getPort("\\C")); + + int nid1 = next_nid++; + int nid2 = next_nid++; + int nid3 = next_nid++; + + if (cell->type == "$_OAI3_") { + btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c); + btorf("%d not %d %d\n", nid3, sid, nid2); + } - std::string data_str = data->as_string(); - //if(offset > 0) - data_str = data_str.substr(offset, width); + if (cell->type == "$_AOI3_") { + btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c); + btorf("%d not %d %d\n", nid3, sid, nid2); + } - ++line_num; - str = stringf("%d const %d %s", line_num, width, data_str.c_str()); - f << stringf("%s\n", str.c_str()); - return line_num; + SigSpec sig = sigmap(cell->getPort("\\Y")); + add_nid_sig(nid3, sig); + goto okay; } - else - log("writing const error\n"); - log_abort(); - return -1; - } - int dump_sigchunk(const RTLIL::SigChunk* chunk) - { - log("writing sigchunk\n"); - int l=-1; - if(chunk->wire == NULL) + if (cell->type.in("$_OAI4_", "$_AOI4_")) { - RTLIL::Const data_const(chunk->data); - l=dump_const(&data_const, chunk->width, chunk->offset); + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = get_sig_nid(cell->getPort("\\B")); + int nid_c = get_sig_nid(cell->getPort("\\C")); + int nid_d = get_sig_nid(cell->getPort("\\D")); + + int nid1 = next_nid++; + int nid2 = next_nid++; + int nid3 = next_nid++; + int nid4 = next_nid++; + + if (cell->type == "$_OAI4_") { + btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d); + btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2); + btorf("%d not %d %d\n", nid4, sid, nid3); + } + + if (cell->type == "$_AOI4_") { + btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); + btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d); + btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2); + btorf("%d not %d %d\n", nid4, sid, nid3); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + add_nid_sig(nid4, sig); + goto okay; } - else + + if (cell->type.in("$lt", "$le", "$eq", "$eqx", "$ne", "$nex", "$ge", "$gt")) { - if (chunk->width == chunk->wire->width && chunk->offset == 0) - l = dump_wire(chunk->wire); - else - { - int wire_line_num = dump_wire(chunk->wire); - log_assert(wire_line_num>0); - ++line_num; - str = stringf("%d slice %d %d %d %d;2", line_num, chunk->width, wire_line_num, - chunk->width + chunk->offset - 1, chunk->offset); - f << stringf("%s\n", str.c_str()); - l = line_num; + string btor_op; + if (cell->type == "$lt") btor_op = "lt"; + if (cell->type == "$le") btor_op = "lte"; + if (cell->type.in("$eq", "$eqx")) btor_op = "eq"; + if (cell->type.in("$ne", "$nex")) btor_op = "neq"; + if (cell->type == "$ge") btor_op = "gte"; + if (cell->type == "$gt") btor_op = "gt"; + log_assert(!btor_op.empty()); + + int width = 1; + width = std::max(width, GetSize(cell->getPort("\\A"))); + width = std::max(width, GetSize(cell->getPort("\\B"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + bool b_signed = cell->hasParam("\\B_SIGNED") ? cell->getParam("\\B_SIGNED").as_bool() : false; + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + int nid_b = get_sig_nid(cell->getPort("\\B"), width, b_signed); + + int nid = next_nid++; + if (cell->type.in("$lt", "$le", "$ge", "$gt")) { + btorf("%d %c%s %d %d %d\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b); + } else { + btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b); + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) > 1) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d uext %d %d %d\n", nid2, sid, nid, GetSize(sig) - 1); + nid = nid2; } + + add_nid_sig(nid, sig); + goto okay; } - return l; - } - int dump_sigspec(const RTLIL::SigSpec* sig, int expected_width) - { - log("writing sigspec\n"); - RTLIL::SigSpec s = sigmap(*sig); - int l = -1; - auto it = sig_ref.find(s); - if(it == std::end(sig_ref)) + if (cell->type.in("$not", "$neg", "$_NOT_")) { - if (s.is_chunk()) - { - l = dump_sigchunk(&s.chunks().front()); + string btor_op; + if (cell->type.in("$not", "$_NOT_")) btor_op = "not"; + if (cell->type == "$neg") btor_op = "neg"; + log_assert(!btor_op.empty()); + + int width = GetSize(cell->getPort("\\Y")); + width = std::max(width, GetSize(cell->getPort("\\A"))); + + bool a_signed = cell->hasParam("\\A_SIGNED") ? cell->getParam("\\A_SIGNED").as_bool() : false; + + int sid = get_bv_sid(width); + int nid_a = get_sig_nid(cell->getPort("\\A"), width, a_signed); + + int nid = next_nid++; + btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a); + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) < width) { + int sid = get_bv_sid(GetSize(sig)); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, GetSize(sig)-1); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; + } + + if (cell->type.in("$logic_and", "$logic_or", "$logic_not")) + { + string btor_op; + if (cell->type == "$logic_and") btor_op = "and"; + if (cell->type == "$logic_or") btor_op = "or"; + if (cell->type == "$logic_not") btor_op = "not"; + log_assert(!btor_op.empty()); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_b = btor_op != "not" ? get_sig_nid(cell->getPort("\\B")) : 0; + + if (GetSize(cell->getPort("\\A")) > 1) { + int nid_red_a = next_nid++; + btorf("%d redor %d %d\n", nid_red_a, sid, nid_a); + nid_a = nid_red_a; + } + + if (btor_op != "not" && GetSize(cell->getPort("\\B")) > 1) { + int nid_red_b = next_nid++; + btorf("%d redor %d %d\n", nid_red_b, sid, nid_b); + nid_b = nid_red_b; } + + int nid = next_nid++; + if (btor_op != "not") + btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b); else - { - int l1, l2, w1, w2; - l1 = dump_sigchunk(&s.chunks().front()); - log_assert(l1>0); - w1 = s.chunks().front().width; - for (unsigned i=1; i < s.chunks().size(); ++i) - { - l2 = dump_sigchunk(&s.chunks().at(i)); - log_assert(l2>0); - w2 = s.chunks().at(i).width; - ++line_num; - str = stringf("%d concat %d %d %d", line_num, w1+w2, l2, l1); - f << stringf("%s\n", str.c_str()); - l1=line_num; - w1+=w2; - } - l = line_num; + btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a); + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) > 1) { + int sid = get_bv_sid(GetSize(sig)); + int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); + int nid2 = next_nid++; + btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); + nid = nid2; } - sig_ref[s] = l; + + add_nid_sig(nid, sig); + goto okay; } - else + + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool", "$reduce_xor", "$reduce_xnor")) { - l = it->second; + string btor_op; + if (cell->type == "$reduce_and") btor_op = "redand"; + if (cell->type.in("$reduce_or", "$reduce_bool")) btor_op = "redor"; + if (cell->type.in("$reduce_xor", "$reduce_xnor")) btor_op = "redxor"; + log_assert(!btor_op.empty()); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + + int nid = next_nid++; + btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a); + + if (cell->type == "$reduce_xnor") { + int nid2 = next_nid++; + btorf("%d not %d %d %d\n", nid2, sid, nid); + nid = nid2; + } + + SigSpec sig = sigmap(cell->getPort("\\Y")); + + if (GetSize(sig) > 1) { + int sid = get_bv_sid(GetSize(sig)); + int zeros_nid = get_sig_nid(Const(0, GetSize(sig)-1)); + int nid2 = next_nid++; + btorf("%d concat %d %d %d\n", nid2, sid, zeros_nid, nid); + nid = nid2; + } + + add_nid_sig(nid, sig); + goto okay; } - if (expected_width != s.size()) + if (cell->type.in("$mux", "$_MUX_", "$_NMUX_")) { - log(" - changing width of sigspec\n"); - //TODO: this block may not be needed anymore, due to explicit type conversion by "splice" command - if(expected_width > s.size()) - { - //TODO: case the signal is signed - ++line_num; - str = stringf ("%d zero %d", line_num, expected_width - s.size()); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf ("%d concat %d %d %d", line_num, expected_width, line_num-1, l); - f << stringf("%s\n", str.c_str()); - l = line_num; + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + int nid_a = get_sig_nid(sig_a); + int nid_b = get_sig_nid(sig_b); + int nid_s = get_sig_nid(sig_s); + + int sid = get_bv_sid(GetSize(sig_y)); + int nid = next_nid++; + btorf("%d ite %d %d %d %d\n", nid, sid, nid_s, nid_b, nid_a); + + if (cell->type == "$_NMUX_") { + int tmp = nid; + nid = next_nid++; + btorf("%d not %d %d\n", nid, sid, tmp); } - else if(expected_width < s.size()) - { - ++line_num; - str = stringf ("%d slice %d %d %d %d;3", line_num, expected_width, l, expected_width-1, 0); - f << stringf("%s\n", str.c_str()); - l = line_num; + + add_nid_sig(nid, sig_y); + goto okay; + } + + if (cell->type == "$pmux") + { + SigSpec sig_a = sigmap(cell->getPort("\\A")); + SigSpec sig_b = sigmap(cell->getPort("\\B")); + SigSpec sig_s = sigmap(cell->getPort("\\S")); + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + int width = GetSize(sig_a); + int sid = get_bv_sid(width); + int nid = get_sig_nid(sig_a); + + for (int i = 0; i < GetSize(sig_s); i++) { + int nid_b = get_sig_nid(sig_b.extract(i*width, width)); + int nid_s = get_sig_nid(sig_s.extract(i)); + int nid2 = next_nid++; + btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid); + nid = nid2; } + + add_nid_sig(nid, sig_y); + goto okay; } - log_assert(l>0); - return l; - } - int dump_cell(const RTLIL::Cell* cell) - { - auto it = line_ref.find(cell->name); - if(it==std::end(line_ref)) + if (cell->type.in("$dff", "$ff", "$_DFF_P_", "$_DFF_N", "$_FF_")) { - curr_cell = cell->name; - //assert cell - if(cell->type == "$assert") - { - log("writing assert cell - %s\n", cstr(cell->type)); - const RTLIL::SigSpec* expr = &cell->getPort(RTLIL::IdString("\\A")); - const RTLIL::SigSpec* en = &cell->getPort(RTLIL::IdString("\\EN")); - log_assert(expr->size() == 1); - log_assert(en->size() == 1); - int expr_line = dump_sigspec(expr, 1); - int en_line = dump_sigspec(en, 1); - int one_line = ++line_num; - str = stringf("%d one 1", line_num); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at("$eq").c_str(), 1, en_line, one_line); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at("$mux").c_str(), 1, line_num-1, - expr_line, one_line); - f << stringf("%s\n", str.c_str()); - int cell_line = ++line_num; - str = stringf("%d %s %d %d", line_num, cell_type_translation.at("$assert").c_str(), 1, -1*(line_num-1)); - //multiplying the line number with -1, which means logical negation - //the reason for negative sign is that the properties in btor are given as "negation of the original property" - //bug identified by bobosoft - //http://www.reddit.com/r/yosys/comments/1w3xig/btor_backend_bug/ - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=cell_line; + SigSpec sig_d = sigmap(cell->getPort("\\D")); + SigSpec sig_q = sigmap(cell->getPort("\\Q")); + + IdString symbol; + + if (sig_q.is_wire()) { + Wire *w = sig_q.as_wire(); + if (w->port_id == 0) { + statewires.insert(w); + symbol = w->name; + } } - //unary cells - else if(cell->type == "$not" || cell->type == "$neg" || cell->type == "$pos" || cell->type == "$reduce_and" || - cell->type == "$reduce_or" || cell->type == "$reduce_xor" || cell->type == "$reduce_bool") + + Const initval; + for (int i = 0; i < GetSize(sig_q); i++) + if (initbits.count(sig_q[i])) + initval.bits.push_back(initbits.at(sig_q[i]) ? State::S1 : State::S0); + else + initval.bits.push_back(State::Sx); + + int nid_init_val = -1; + + if (!initval.is_fully_undef()) + nid_init_val = get_sig_nid(initval, -1, false, true); + + int sid = get_bv_sid(GetSize(sig_q)); + int nid = next_nid++; + + if (symbol.empty()) + btorf("%d state %d\n", nid, sid); + else + btorf("%d state %d %s\n", nid, sid, log_id(symbol)); + + if (nid_init_val >= 0) { + int nid_init = next_nid++; + if (verbose) + btorf("; initval = %s\n", log_signal(initval)); + btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); + } + + ff_todo.push_back(make_pair(nid, cell)); + add_nid_sig(nid, sig_q); + goto okay; + } + + if (cell->type.in("$anyconst", "$anyseq")) + { + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + int sid = get_bv_sid(GetSize(sig_y)); + int nid = next_nid++; + + btorf("%d state %d\n", nid, sid); + + if (cell->type == "$anyconst") { + int nid2 = next_nid++; + btorf("%d next %d %d %d\n", nid2, sid, nid, nid); + } + + add_nid_sig(nid, sig_y); + goto okay; + } + + if (cell->type == "$initstate") + { + SigSpec sig_y = sigmap(cell->getPort("\\Y")); + + if (initstate_nid < 0) { - log("writing unary cell - %s\n", cstr(cell->type)); - int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - w = w>output_width ? w:output_width; //padding of w - int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w); - int cell_line = l; - if(cell->type != "$pos") - { - cell_line = ++line_num; - bool reduced = (cell->type == "$not" || cell->type == "$neg") ? false : true; - str = stringf ("%d %s %d %d", cell_line, cell_type_translation.at(cell->type.str()).c_str(), reduced?output_width:w, l); - f << stringf("%s\n", str.c_str()); - } - if(output_width < w && (cell->type == "$not" || cell->type == "$neg" || cell->type == "$pos")) - { - ++line_num; - str = stringf ("%d slice %d %d %d %d;4", line_num, output_width, cell_line, output_width-1, 0); - f << stringf("%s\n", str.c_str()); - cell_line = line_num; - } - line_ref[cell->name]=cell_line; + int sid = get_bv_sid(1); + int one_nid = get_sig_nid(State::S1); + int zero_nid = get_sig_nid(State::S0); + initstate_nid = next_nid++; + btorf("%d state %d\n", initstate_nid, sid); + btorf("%d init %d %d %d\n", next_nid++, sid, initstate_nid, one_nid); + btorf("%d next %d %d %d\n", next_nid++, sid, initstate_nid, zero_nid); } - else if(cell->type == "$reduce_xnor" || cell->type == "$logic_not")//no direct translation in btor + + add_nid_sig(initstate_nid, sig_y); + goto okay; + } + + if (cell->type == "$mem") + { + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int nwords = cell->getParam("\\SIZE").as_int(); + int rdports = cell->getParam("\\RD_PORTS").as_int(); + int wrports = cell->getParam("\\WR_PORTS").as_int(); + + Const wr_clk_en = cell->getParam("\\WR_CLK_ENABLE"); + Const rd_clk_en = cell->getParam("\\RD_CLK_ENABLE"); + + bool asyncwr = wr_clk_en.is_fully_zero(); + + if (!asyncwr && !wr_clk_en.is_fully_ones()) + log_error("Memory %s.%s has mixed async/sync write ports.\n", + log_id(module), log_id(cell)); + + if (!rd_clk_en.is_fully_zero()) + log_error("Memory %s.%s has sync read ports.\n", + log_id(module), log_id(cell)); + + SigSpec sig_rd_addr = sigmap(cell->getPort("\\RD_ADDR")); + SigSpec sig_rd_data = sigmap(cell->getPort("\\RD_DATA")); + + SigSpec sig_wr_addr = sigmap(cell->getPort("\\WR_ADDR")); + SigSpec sig_wr_data = sigmap(cell->getPort("\\WR_DATA")); + SigSpec sig_wr_en = sigmap(cell->getPort("\\WR_EN")); + + int data_sid = get_bv_sid(width); + int bool_sid = get_bv_sid(1); + int sid = get_mem_sid(abits, width); + + Const initdata = cell->getParam("\\INIT"); + initdata.exts(nwords*width); + int nid_init_val = -1; + + if (!initdata.is_fully_undef()) { - log("writing unary cell - %s\n", cstr(cell->type)); - int w = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - log_assert(output_width == 1); - int l = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), w); - if(cell->type == "$logic_not" && w > 1) - { - ++line_num; - str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l); - f << stringf("%s\n", str.c_str()); + bool constword = true; + Const firstword = initdata.extract(0, width); + + for (int i = 1; i < nwords; i++) { + Const thisword = initdata.extract(i*width, width); + if (thisword != firstword) { + constword = false; + break; + } } - else if(cell->type == "$reduce_xnor") + + if (constword) { - ++line_num; - str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_xor").c_str(), output_width, l); - f << stringf("%s\n", str.c_str()); + if (verbose) + btorf("; initval = %s\n", log_signal(firstword)); + nid_init_val = get_sig_nid(firstword, -1, false, true); } - ++line_num; - str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$not").c_str(), output_width, l); - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; - } - //binary cells - else if(cell->type == "$and" || cell->type == "$or" || cell->type == "$xor" || cell->type == "$xnor" || - cell->type == "$lt" || cell->type == "$le" || cell->type == "$eq" || cell->type == "$ne" || - cell->type == "$eqx" || cell->type == "$nex" || cell->type == "$ge" || cell->type == "$gt" ) - { - log("writing binary cell - %s\n", cstr(cell->type)); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - log_assert(!(cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" || - cell->type == "$ge" || cell->type == "$gt") || output_width == 1); - bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool(); - bool l2_signed YS_ATTRIBUTE(unused) = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool(); - int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - - log_assert(l1_signed == l2_signed); - l1_width = l1_width > output_width ? l1_width : output_width; - l1_width = l1_width > l2_width ? l1_width : l2_width; - l2_width = l2_width > l1_width ? l2_width : l1_width; - - int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width); - int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width); - - ++line_num; - std::string op = cell_type_translation.at(cell->type.str()); - if(cell->type == "$lt" || cell->type == "$le" || - cell->type == "$eq" || cell->type == "$ne" || cell->type == "$eqx" || cell->type == "$nex" || - cell->type == "$ge" || cell->type == "$gt") + else { - if(l1_signed) - op = s_cell_type_translation.at(cell->type.str()); + nid_init_val = next_nid++; + btorf("%d state %d\n", nid_init_val, sid); + + for (int i = 0; i < nwords; i++) { + Const thisword = initdata.extract(i*width, width); + if (thisword.is_fully_undef()) + continue; + Const thisaddr(i, abits); + int nid_thisword = get_sig_nid(thisword, -1, false, true); + int nid_thisaddr = get_sig_nid(thisaddr, -1, false, true); + int last_nid_init_val = nid_init_val; + nid_init_val = next_nid++; + if (verbose) + btorf("; initval[%d] = %s\n", i, log_signal(thisword)); + btorf("%d write %d %d %d %d\n", nid_init_val, sid, last_nid_init_val, nid_thisaddr, nid_thisword); + } } + } + - str = stringf ("%d %s %d %d %d", line_num, op.c_str(), output_width, l1, l2); - f << stringf("%s\n", str.c_str()); + int nid = next_nid++; + int nid_head = nid; - line_ref[cell->name]=line_num; + if (cell->name[0] == '$') + btorf("%d state %d\n", nid, sid); + else + btorf("%d state %d %s\n", nid, sid, log_id(cell)); + + if (nid_init_val >= 0) + { + int nid_init = next_nid++; + btorf("%d init %d %d %d\n", nid_init, sid, nid, nid_init_val); } - else if(cell->type == "$add" || cell->type == "$sub" || cell->type == "$mul" || cell->type == "$div" || - cell->type == "$mod" ) + + if (asyncwr) { - //TODO: division by zero case - log("writing binary cell - %s\n", cstr(cell->type)); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool(); - bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool(); - int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - - log_assert(l1_signed == l2_signed); - l1_width = l1_width > output_width ? l1_width : output_width; - l1_width = l1_width > l2_width ? l1_width : l2_width; - l2_width = l2_width > l1_width ? l2_width : l1_width; - - int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width); - int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width); - - ++line_num; - std::string op = cell_type_translation.at(cell->type.str()); - if(cell->type == "$div" && l1_signed) - op = s_cell_type_translation.at(cell->type.str()); - else if(cell->type == "$mod") + for (int port = 0; port < wrports; port++) { - if(l1_signed) - op = s_cell_type_translation.at("$modx"); - else if(l2_signed) - op = s_cell_type_translation.at("$mody"); - } - str = stringf ("%d %s %d %d %d", line_num, op.c_str(), l1_width, l1, l2); - f << stringf("%s\n", str.c_str()); + SigSpec wa = sig_wr_addr.extract(port*abits, abits); + SigSpec wd = sig_wr_data.extract(port*width, width); + SigSpec we = sig_wr_en.extract(port*width, width); - if(output_width < l1_width) - { - ++line_num; - str = stringf ("%d slice %d %d %d %d;5", line_num, output_width, line_num-1, output_width-1, 0); - f << stringf("%s\n", str.c_str()); + int wa_nid = get_sig_nid(wa); + int wd_nid = get_sig_nid(wd); + int we_nid = get_sig_nid(we); + + int nid2 = next_nid++; + btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); + + int nid3 = next_nid++; + btorf("%d not %d %d\n", nid3, data_sid, we_nid); + + int nid4 = next_nid++; + btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); + + int nid5 = next_nid++; + btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); + + int nid6 = next_nid++; + btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); + + int nid7 = next_nid++; + btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); + + int nid8 = next_nid++; + btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); + + int nid9 = next_nid++; + btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); + + nid_head = nid9; } - line_ref[cell->name]=line_num; } - else if(cell->type == "$shr" || cell->type == "$shl" || cell->type == "$sshr" || cell->type == "$sshl" || cell->type == "$shift" || cell->type == "$shiftx") + + for (int port = 0; port < rdports; port++) { - log("writing binary cell - %s\n", cstr(cell->type)); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - bool l1_signed = cell->parameters.at(RTLIL::IdString("\\A_SIGNED")).as_bool(); - //bool l2_signed = cell->parameters.at(RTLIL::IdString("\\B_SIGNED")).as_bool(); - int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - l1_width = 1 << ceil_log2(l1_width); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - //log_assert(l2_width <= ceil_log2(l1_width)) ); - int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), l1_width); - int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), ceil_log2(l1_width)); - int cell_output = ++line_num; - str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), l1_width, l1, l2); - f << stringf("%s\n", str.c_str()); - - if(l2_width > ceil_log2(l1_width)) - { - int extra_width = l2_width - ceil_log2(l1_width); - l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), l2_width); - ++line_num; - str = stringf ("%d slice %d %d %d %d;6", line_num, extra_width, l2, l2_width-1, l2_width-extra_width); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf ("%d one %d", line_num, extra_width); - f << stringf("%s\n", str.c_str()); - int mux = ++line_num; - str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$gt").c_str(), 1, line_num-2, line_num-1); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d %s %d", line_num, l1_signed && cell->type == "$sshr" ? "ones":"zero", l1_width); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf ("%d %s %d %d %d %d", line_num, cell_type_translation.at("$mux").c_str(), l1_width, mux, line_num-1, cell_output); - f << stringf("%s\n", str.c_str()); - cell_output = line_num; - } + SigSpec ra = sig_rd_addr.extract(port*abits, abits); + SigSpec rd = sig_rd_data.extract(port*width, width); - if(output_width < l1_width) - { - ++line_num; - str = stringf ("%d slice %d %d %d %d;5", line_num, output_width, cell_output, output_width-1, 0); - f << stringf("%s\n", str.c_str()); - cell_output = line_num; - } - line_ref[cell->name] = cell_output; + int ra_nid = get_sig_nid(ra); + int rd_nid = next_nid++; + + btorf("%d read %d %d %d\n", rd_nid, data_sid, nid_head, ra_nid); + + add_nid_sig(rd_nid, rd); } - else if(cell->type == "$logic_and" || cell->type == "$logic_or")//no direct translation in btor + + if (!asyncwr) { - log("writing binary cell - %s\n", cstr(cell->type)); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - log_assert(output_width == 1); - int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width); - int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width); - int l1_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - int l2_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - if(l1_width >1) - { - ++line_num; - str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l1); - f << stringf("%s\n", str.c_str()); - l1 = line_num; - } - if(l2_width > 1) - { - ++line_num; - str = stringf ("%d %s %d %d", line_num, cell_type_translation.at("$reduce_or").c_str(), output_width, l2); - f << stringf("%s\n", str.c_str()); - l2 = line_num; + ff_todo.push_back(make_pair(nid, cell)); + } + else + { + int nid2 = next_nid++; + btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head); + } + + goto okay; + } + + log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); + + okay: + btorf_pop(log_id(cell)); + cell_recursion_guard.erase(cell); + } + + int get_sig_nid(SigSpec sig, int to_width = -1, bool is_signed = false, bool is_init = false) + { + int nid = -1; + sigmap.apply(sig); + + for (auto bit : sig) + if (bit == State::Sx) + goto has_undef_bits; + + if (0) + { + has_undef_bits: + SigSpec sig_mask_undef, sig_noundef; + int first_undef = -1; + + for (int i = 0; i < GetSize(sig); i++) + if (sig[i] == State::Sx) { + if (first_undef < 0) + first_undef = i; + sig_mask_undef.append(State::S1); + sig_noundef.append(State::S0); + } else { + sig_mask_undef.append(State::S0); + sig_noundef.append(sig[i]); } - if(cell->type == "$logic_and") - { - ++line_num; - str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$and").c_str(), output_width, l1, l2); + + if (to_width < 0 || first_undef < to_width) + { + int sid = get_bv_sid(GetSize(sig)); + + int nid_input = next_nid++; + if (is_init) + btorf("%d state %d\n", nid_input, sid); + else + btorf("%d input %d\n", nid_input, sid); + + int nid_masked_input; + if (sig_mask_undef.is_fully_ones()) { + nid_masked_input = nid_input; + } else { + int nid_mask_undef = get_sig_nid(sig_mask_undef); + nid_masked_input = next_nid++; + btorf("%d and %d %d %d\n", nid_masked_input, sid, nid_input, nid_mask_undef); } - else if(cell->type == "$logic_or") - { - ++line_num; - str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at("$or").c_str(), output_width, l1, l2); + + if (sig_noundef.is_fully_zero()) { + nid = nid_masked_input; + } else { + int nid_noundef = get_sig_nid(sig_noundef); + nid = next_nid++; + btorf("%d or %d %d %d\n", nid, sid, nid_masked_input, nid_noundef); } - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; - } - //multiplexers - else if(cell->type == "$mux") - { - log("writing mux cell\n"); - int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); - int l1 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width); - int l2 = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width); - int s = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), 1); - ++line_num; - str = stringf ("%d %s %d %d %d %d", - line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, s, l2, l1); - //if s is 0 then l1, if s is 1 then l2 //according to the implementation of mux cell - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; + + goto extend_or_trim; } - else if(cell->type == "$pmux") - { - log("writing pmux cell\n"); - int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); - int select_width = cell->parameters.at(RTLIL::IdString("\\S_WIDTH")).as_int(); - int default_case = dump_sigspec(&cell->getPort(RTLIL::IdString("\\A")), output_width); - int cases = dump_sigspec(&cell->getPort(RTLIL::IdString("\\B")), output_width*select_width); - int select = dump_sigspec(&cell->getPort(RTLIL::IdString("\\S")), select_width); - int *c = new int[select_width]; - - for (int i=0; i<select_width; ++i) - { - ++line_num; - str = stringf ("%d slice 1 %d %d %d", line_num, select, i, i); - f << stringf("%s\n", str.c_str()); - c[i] = line_num; - ++line_num; - str = stringf ("%d slice %d %d %d %d", line_num, output_width, cases, i*output_width+output_width-1, - i*output_width); - f << stringf("%s\n", str.c_str()); - } - - ++line_num; - str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[select_width-1], c[select_width-1]+1, default_case); - f << stringf("%s\n", str.c_str()); - - for (int i=select_width-2; i>=0; --i) - { - ++line_num; - str = stringf ("%d cond %d %d %d %d", line_num, output_width, c[i], c[i]+1, line_num-1); - f << stringf("%s\n", str.c_str()); - } - - line_ref[cell->name]=line_num; - } - //registers - else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr") + + sig = sig_noundef; + } + + if (sig_nid.count(sig) == 0) + { + // <nid>, <bitidx> + vector<pair<int, int>> nidbits; + + // collect all bits + for (int i = 0; i < GetSize(sig); i++) { - //TODO: remodelling of adff cells - log("writing cell - %s\n", cstr(cell->type)); - int output_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); - log(" - width is %d\n", output_width); - int cond = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLK")), 1); - bool polarity = cell->parameters.at(RTLIL::IdString("\\CLK_POLARITY")).as_bool(); - const RTLIL::SigSpec* cell_output = &cell->getPort(RTLIL::IdString("\\Q")); - int value = dump_sigspec(&cell->getPort(RTLIL::IdString("\\D")), output_width); - unsigned start_bit = 0; - for(unsigned i=0; i<cell_output->chunks().size(); ++i) + SigBit bit = sig[i]; + + if (bit_nid.count(bit) == 0) { - output_width = cell_output->chunks().at(i).width; - log_assert( output_width == cell_output->chunks().at(i).wire->width);//full reg is given the next value - int reg = dump_wire(cell_output->chunks().at(i).wire);//register - int slice = value; - if(cell_output->chunks().size()>1) + if (bit.wire == nullptr) { - start_bit+=output_width; - slice = ++line_num; - str = stringf ("%d slice %d %d %d %d;", line_num, output_width, value, start_bit-1, - start_bit-output_width); - f << stringf("%s\n", str.c_str()); + Const c(bit.data); + + while (i+GetSize(c) < GetSize(sig) && sig[i+GetSize(c)].wire == nullptr) + c.bits.push_back(sig[i+GetSize(c)].data); + + if (consts.count(c) == 0) { + int sid = get_bv_sid(GetSize(c)); + int nid = next_nid++; + btorf("%d const %d %s\n", nid, sid, c.as_string().c_str()); + consts[c] = nid; + nid_width[nid] = GetSize(c); + } + + int nid = consts.at(c); + + for (int j = 0; j < GetSize(c); j++) + nidbits.push_back(make_pair(nid, j)); + + i += GetSize(c)-1; + continue; } - if(cell->type == "$dffsr") + else { - int sync_reset = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLR")), 1); - bool sync_reset_pol = cell->parameters.at(RTLIL::IdString("\\CLR_POLARITY")).as_bool(); - int sync_reset_value = dump_sigspec(&cell->getPort(RTLIL::IdString("\\SET")), - output_width); - bool sync_reset_value_pol = cell->parameters.at(RTLIL::IdString("\\SET_POLARITY")).as_bool(); - ++line_num; - str = stringf ("%d %s %d %s%d %s%d %d", line_num, cell_type_translation.at("$mux").c_str(), - output_width, sync_reset_pol ? "":"-", sync_reset, sync_reset_value_pol? "":"-", - sync_reset_value, slice); - f << stringf("%s\n", str.c_str()); - slice = line_num; - } - ++line_num; - str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(), - output_width, polarity?"":"-", cond, slice, reg); + if (bit_cell.count(bit) == 0) + { + SigSpec s = bit; - f << stringf("%s\n", str.c_str()); - int next = line_num; - if(cell->type == "$adff") - { - int async_reset = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ARST")), 1); - bool async_reset_pol = cell->parameters.at(RTLIL::IdString("\\ARST_POLARITY")).as_bool(); - int async_reset_value = dump_const(&cell->parameters.at(RTLIL::IdString("\\ARST_VALUE")), - output_width, 0); - ++line_num; - str = stringf ("%d %s %d %s%d %d %d", line_num, cell_type_translation.at("$mux").c_str(), - output_width, async_reset_pol ? "":"-", async_reset, async_reset_value, next); - f << stringf("%s\n", str.c_str()); + while (i+GetSize(s) < GetSize(sig) && sig[i+GetSize(s)].wire != nullptr && + bit_cell.count(sig[i+GetSize(s)]) == 0) + s.append(sig[i+GetSize(s)]); + + log_warning("No driver for signal %s.\n", log_signal(s)); + + int sid = get_bv_sid(GetSize(s)); + int nid = next_nid++; + btorf("%d input %d\n", nid, sid); + nid_width[nid] = GetSize(s); + + for (int j = 0; j < GetSize(s); j++) + nidbits.push_back(make_pair(nid, j)); + + i += GetSize(s)-1; + continue; + } + else + { + export_cell(bit_cell.at(bit)); + log_assert(bit_nid.count(bit)); + } } - ++line_num; - str = stringf ("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), - output_width, reg, next); - f << stringf("%s\n", str.c_str()); } - line_ref[cell->name]=line_num; - } - //memories - else if(cell->type == "$memrd") - { - log("writing memrd cell\n"); - if (cell->parameters.at("\\CLK_ENABLE").as_bool() == true) - log_error("The btor backen does not support $memrd cells with built-in registers. Run memory_dff with -wr_only.\n"); - str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string(); - int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str()))); - int address_width = cell->parameters.at(RTLIL::IdString("\\ABITS")).as_int(); - int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width); - int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); - ++line_num; - str = stringf("%d read %d %d %d", line_num, data_width, mem, address); - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; + + nidbits.push_back(bit_nid.at(bit)); } - else if(cell->type == "$memwr") + + int width = 0; + int nid = -1; + + // group bits and emit slice-concat chain + for (int i = 0; i < GetSize(nidbits); i++) { - log("writing memwr cell\n"); - if (cell->parameters.at("\\CLK_ENABLE").as_bool() == false) - log_error("The btor backen does not support $memwr cells without built-in registers. Run memory_dff (but with -wr_only).\n"); - int clk = dump_sigspec(&cell->getPort(RTLIL::IdString("\\CLK")), 1); - bool polarity = cell->parameters.at(RTLIL::IdString("\\CLK_POLARITY")).as_bool(); - int enable = dump_sigspec(&cell->getPort(RTLIL::IdString("\\EN")), 1); - int address_width = cell->parameters.at(RTLIL::IdString("\\ABITS")).as_int(); - int address = dump_sigspec(&cell->getPort(RTLIL::IdString("\\ADDR")), address_width); - int data_width = cell->parameters.at(RTLIL::IdString("\\WIDTH")).as_int(); - int data = dump_sigspec(&cell->getPort(RTLIL::IdString("\\DATA")), data_width); - str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string(); - int mem = dump_memory(module->memories.at(RTLIL::IdString(str.c_str()))); - //check if the memory has already next - /* - auto it = mem_next.find(mem); - if(it != std::end(mem_next)) - { - ++line_num; - str = cell->parameters.at(RTLIL::IdString("\\MEMID")).decode_string(); - RTLIL::Memory *memory = module->memories.at(RTLIL::IdString(str.c_str())); - int address_bits = ceil_log2(memory->size); - str = stringf("%d array %d %d", line_num, memory->width, address_bits); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d eq 1 %d %d; mem invar", line_num, mem, line_num - 1); - f << stringf("%s\n", str.c_str()); - mem = line_num - 1; + int nid2 = nidbits[i].first; + int lower = nidbits[i].second; + int upper = lower; + + while (i+1 < GetSize(nidbits) && nidbits[i+1].first == nidbits[i].first && + nidbits[i+1].second == nidbits[i].second+1) + upper++, i++; + + int nid3 = nid2; + + if (lower != 0 || upper+1 != nid_width.at(nid2)) { + int sid = get_bv_sid(upper-lower+1); + nid3 = next_nid++; + btorf("%d slice %d %d %d %d\n", nid3, sid, nid2, upper, lower); } - */ - ++line_num; - if(polarity) - str = stringf("%d one 1", line_num); - else - str = stringf("%d zero 1", line_num); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d eq 1 %d %d", line_num, clk, line_num-1); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d and 1 %d %d", line_num, line_num-1, enable); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d write %d %d %d %d %d", line_num, data_width, address_width, mem, address, data); - f << stringf("%s\n", str.c_str()); - /* - ++line_num; - str = stringf("%d acond %d %d %d %d %d", line_num, data_width, address_width, line_num-2, line_num-1, mem); - f << stringf("%s\n", str.c_str()); - ++line_num; - str = stringf("%d anext %d %d %d %d", line_num, data_width, address_width, mem, line_num-1); - f << stringf("%s\n", str.c_str()); - */ - mem_next[mem].insert(std::make_pair(line_num-1, line_num)); + + int nid4 = nid3; + + if (nid >= 0) { + int sid = get_bv_sid(width+upper-lower+1); + nid4 = next_nid++; + btorf("%d concat %d %d %d\n", nid4, sid, nid3, nid); + } + + width += upper-lower+1; + nid = nid4; } - else if(cell->type == "$slice") + + sig_nid[sig] = nid; + nid_width[nid] = width; + } + + nid = sig_nid.at(sig); + + extend_or_trim: + if (to_width >= 0 && to_width != GetSize(sig)) + { + if (to_width < GetSize(sig)) { - log("writing slice cell\n"); - const RTLIL::SigSpec* input = &cell->getPort(RTLIL::IdString("\\A")); - int input_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - log_assert(input->size() == input_width); - int input_line = dump_sigspec(input, input_width); - const RTLIL::SigSpec* output YS_ATTRIBUTE(unused) = &cell->getPort(RTLIL::IdString("\\Y")); - int output_width = cell->parameters.at(RTLIL::IdString("\\Y_WIDTH")).as_int(); - log_assert(output->size() == output_width); - int offset = cell->parameters.at(RTLIL::IdString("\\OFFSET")).as_int(); - ++line_num; - str = stringf("%d %s %d %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), output_width, input_line, output_width+offset-1, offset); - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; + int sid = get_bv_sid(to_width); + int nid2 = next_nid++; + btorf("%d slice %d %d %d 0\n", nid2, sid, nid, to_width-1); + nid = nid2; } - else if(cell->type == "$concat") + else { - log("writing concat cell\n"); - const RTLIL::SigSpec* input_a = &cell->getPort(RTLIL::IdString("\\A")); - int input_a_width = cell->parameters.at(RTLIL::IdString("\\A_WIDTH")).as_int(); - log_assert(input_a->size() == input_a_width); - int input_a_line = dump_sigspec(input_a, input_a_width); - const RTLIL::SigSpec* input_b = &cell->getPort(RTLIL::IdString("\\B")); - int input_b_width = cell->parameters.at(RTLIL::IdString("\\B_WIDTH")).as_int(); - log_assert(input_b->size() == input_b_width); - int input_b_line = dump_sigspec(input_b, input_b_width); - ++line_num; - str = stringf("%d %s %d %d %d", line_num, cell_type_translation.at(cell->type.str()).c_str(), input_a_width+input_b_width, - input_a_line, input_b_line); - f << stringf("%s\n", str.c_str()); - line_ref[cell->name]=line_num; + int sid = get_bv_sid(to_width); + int nid2 = next_nid++; + btorf("%d %s %d %d %d\n", nid2, is_signed ? "sext" : "uext", + sid, nid, to_width - GetSize(sig)); + nid = nid2; } - curr_cell.clear(); - return line_num; - } - else - { - return it->second; } + + return nid; } - const RTLIL::SigSpec* get_cell_output(RTLIL::Cell* cell) + BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad) : + f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad) { - const RTLIL::SigSpec *output_sig = nullptr; - if (cell->type == "$memrd") - { - output_sig = &cell->getPort(RTLIL::IdString("\\DATA")); - } - else if(cell->type == "$memwr" || cell->type == "$assert") + btorf_push("inputs"); + + for (auto wire : module->wires()) { - //no output + if (wire->attributes.count("\\init")) { + Const attrval = wire->attributes.at("\\init"); + for (int i = 0; i < GetSize(wire) && i < GetSize(attrval); i++) + if (attrval[i] == State::S0 || attrval[i] == State::S1) + initbits[sigmap(SigBit(wire, i))] = (attrval[i] == State::S1); + } + + if (!wire->port_id || !wire->port_input) + continue; + + SigSpec sig = sigmap(wire); + int sid = get_bv_sid(GetSize(sig)); + int nid = next_nid++; + + btorf("%d input %d %s\n", nid, sid, log_id(wire)); + add_nid_sig(nid, sig); } - else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr") + + btorf_pop("inputs"); + + for (auto cell : module->cells()) + for (auto &conn : cell->connections()) { - output_sig = &cell->getPort(RTLIL::IdString("\\Q")); + if (!cell->output(conn.first)) + continue; + + for (auto bit : sigmap(conn.second)) + bit_cell[bit] = cell; } - else + + for (auto wire : module->wires()) { - output_sig = &cell->getPort(RTLIL::IdString("\\Y")); - } - return output_sig; - } + if (!wire->port_id || !wire->port_output) + continue; - void dump_property(RTLIL::Wire *wire) - { - int l = dump_wire(wire); - ++line_num; - str = stringf("%d root 1 %d", line_num, l); - f << stringf("%s\n", str.c_str()); - } + btorf_push(stringf("output %s", log_id(wire))); - void dump() - { - f << stringf(";module %s\n", cstr(module->name)); + int nid = get_sig_nid(wire); + btorf("%d output %d %s\n", next_nid++, nid, log_id(wire)); - log("creating intermediate wires map\n"); - //creating map of intermediate wires as output of some cell - for (auto it = module->cells_.begin(); it != module->cells_.end(); ++it) + btorf_pop(stringf("output %s", log_id(wire))); + } + + for (auto cell : module->cells()) { - RTLIL::Cell *cell = it->second; - const RTLIL::SigSpec* output_sig = get_cell_output(cell); - if(output_sig==nullptr) - continue; - RTLIL::SigSpec s = sigmap(*output_sig); - output_sig = &s; - log(" - %s\n", cstr(it->second->type)); - if (cell->type == "$memrd") - { - for(unsigned i=0; i<output_sig->chunks().size(); ++i) - { - RTLIL::Wire *w = output_sig->chunks().at(i).wire; - RTLIL::IdString wire_id = w->name; - inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i))); - } - } - else if(cell->type == "$memwr") - { - continue;//nothing to do - } - else if(cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffsr") + if (cell->type == "$assume") { - RTLIL::IdString wire_id = output_sig->chunks().front().wire->name; - for(unsigned i=0; i<output_sig->chunks().size(); ++i) - { - RTLIL::Wire *w = output_sig->chunks().at(i).wire; - RTLIL::IdString wire_id = w->name; - inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i))); - basic_wires[wire_id] = true; - } + btorf_push(log_id(cell)); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_en = get_sig_nid(cell->getPort("\\EN")); + int nid_not_en = next_nid++; + int nid_a_or_not_en = next_nid++; + int nid = next_nid++; + + btorf("%d not %d %d\n", nid_not_en, sid, nid_en); + btorf("%d or %d %d %d\n", nid_a_or_not_en, sid, nid_a, nid_not_en); + btorf("%d constraint %d\n", nid, nid_a_or_not_en); + + btorf_pop(log_id(cell)); } - else + + if (cell->type == "$assert") { - for(unsigned i=0; i<output_sig->chunks().size(); ++i) - { - RTLIL::Wire *w = output_sig->chunks().at(i).wire; - RTLIL::IdString wire_id = w->name; - inter_wire_map[wire_id].insert(WireInfo(cell->name,&output_sig->chunks().at(i))); + btorf_push(log_id(cell)); + + int sid = get_bv_sid(1); + int nid_a = get_sig_nid(cell->getPort("\\A")); + int nid_en = get_sig_nid(cell->getPort("\\EN")); + int nid_not_a = next_nid++; + int nid_en_and_not_a = next_nid++; + + btorf("%d not %d %d\n", nid_not_a, sid, nid_a); + btorf("%d and %d %d %d\n", nid_en_and_not_a, sid, nid_en, nid_not_a); + + if (single_bad) { + bad_properties.push_back(nid_en_and_not_a); + } else { + int nid = next_nid++; + string infostr = log_id(cell); + if (infostr[0] == '$' && cell->attributes.count("\\src")) { + infostr = cell->attributes.at("\\src").decode_string().c_str(); + std::replace(infostr.begin(), infostr.end(), ' ', '_'); + } + btorf("%d bad %d %s\n", nid, nid_en_and_not_a, infostr.c_str()); } - } - } - log("writing input\n"); - std::map<int, RTLIL::Wire*> inputs, outputs; - std::vector<RTLIL::Wire*> safety; - - for (auto &wire_it : module->wires_) { - RTLIL::Wire *wire = wire_it.second; - if (wire->port_input) - inputs[wire->port_id] = wire; - if (wire->port_output) { - outputs[wire->port_id] = wire; - if (wire->name.str().find("safety") != std::string::npos ) - safety.push_back(wire); + btorf_pop(log_id(cell)); } } - f << stringf(";inputs\n"); - for (auto &it : inputs) { - RTLIL::Wire *wire = it.second; - dump_wire(wire); + for (auto wire : module->wires()) + { + if (wire->port_id || wire->name[0] == '$') + continue; + + btorf_push(stringf("wire %s", log_id(wire))); + + int sid = get_bv_sid(GetSize(wire)); + int nid = get_sig_nid(sigmap(wire)); + + if (statewires.count(wire)) + continue; + + int this_nid = next_nid++; + btorf("%d uext %d %d %d %s\n", this_nid, sid, nid, 0, log_id(wire)); + + btorf_pop(stringf("wire %s", log_id(wire))); + continue; } - f << stringf("\n"); - log("writing memories\n"); - for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it) + while (!ff_todo.empty()) { - dump_memory(mem_it->second); - } + vector<pair<int, Cell*>> todo; + todo.swap(ff_todo); + + for (auto &it : todo) + { + int nid = it.first; + Cell *cell = it.second; + + btorf_push(stringf("next %s", log_id(cell))); - log("writing output wires\n"); - for (auto &it : outputs) { - RTLIL::Wire *wire = it.second; - dump_wire(wire); + if (cell->type == "$mem") + { + int abits = cell->getParam("\\ABITS").as_int(); + int width = cell->getParam("\\WIDTH").as_int(); + int wrports = cell->getParam("\\WR_PORTS").as_int(); + + SigSpec sig_wr_addr = sigmap(cell->getPort("\\WR_ADDR")); + SigSpec sig_wr_data = sigmap(cell->getPort("\\WR_DATA")); + SigSpec sig_wr_en = sigmap(cell->getPort("\\WR_EN")); + + int data_sid = get_bv_sid(width); + int bool_sid = get_bv_sid(1); + int sid = get_mem_sid(abits, width); + int nid_head = nid; + + for (int port = 0; port < wrports; port++) + { + SigSpec wa = sig_wr_addr.extract(port*abits, abits); + SigSpec wd = sig_wr_data.extract(port*width, width); + SigSpec we = sig_wr_en.extract(port*width, width); + + int wa_nid = get_sig_nid(wa); + int wd_nid = get_sig_nid(wd); + int we_nid = get_sig_nid(we); + + int nid2 = next_nid++; + btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid); + + int nid3 = next_nid++; + btorf("%d not %d %d\n", nid3, data_sid, we_nid); + + int nid4 = next_nid++; + btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3); + + int nid5 = next_nid++; + btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid); + + int nid6 = next_nid++; + btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4); + + int nid7 = next_nid++; + btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6); + + int nid8 = next_nid++; + btorf("%d redor %d %d\n", nid8, bool_sid, we_nid); + + int nid9 = next_nid++; + btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head); + + nid_head = nid9; + } + + int nid2 = next_nid++; + btorf("%d next %d %d %d\n", nid2, sid, nid, nid_head); + } + else + { + SigSpec sig = sigmap(cell->getPort("\\D")); + int nid_q = get_sig_nid(sig); + int sid = get_bv_sid(GetSize(sig)); + btorf("%d next %d %d %d\n", next_nid++, sid, nid, nid_q); + } + + btorf_pop(stringf("next %s", log_id(cell))); + } } - log("writing cells\n"); - for(auto cell_it = module->cells_.begin(); cell_it != module->cells_.end(); ++cell_it) + while (!bad_properties.empty()) { - dump_cell(cell_it->second); - } + vector<int> todo; + bad_properties.swap(todo); - log("writing memory next"); - for(auto mem_it = module->memories.begin(); mem_it != module->memories.end(); ++mem_it) - { - dump_memory_next(mem_it->second); - } + int sid = get_bv_sid(1); + int cursor = 0; - for(auto it: safety) - dump_property(it); + while (cursor+1 < GetSize(todo)) + { + int nid_a = todo[cursor++]; + int nid_b = todo[cursor++]; + int nid = next_nid++; - f << stringf("\n"); + bad_properties.push_back(nid); + btorf("%d or %d %d %d\n", nid, sid, nid_a, nid_b); + } - log("writing outputs info\n"); - f << stringf(";outputs\n"); - for (auto &it : outputs) { - RTLIL::Wire *wire = it.second; - int l = dump_wire(wire); - f << stringf(";%d %s", l, cstr(wire->name)); + if (!bad_properties.empty()) { + if (cursor < GetSize(todo)) + bad_properties.push_back(todo[cursor++]); + log_assert(cursor == GetSize(todo)); + } else { + int nid = next_nid++; + log_assert(cursor == 0); + log_assert(GetSize(todo) == 1); + btorf("%d bad %d\n", nid, todo[cursor]); + } } - f << stringf("\n"); - } - - static void dump(std::ostream &f, RTLIL::Module *module, RTLIL::Design *design, BtorDumperConfig &config) - { - BtorDumper dumper(f, module, design, &config); - dumper.dump(); } }; struct BtorBackend : public Backend { BtorBackend() : Backend("btor", "write design to BTOR file") { } - - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); - log(" write_btor [filename]\n"); + log(" write_btor [options] [filename]\n"); + log("\n"); + log("Write a BTOR description of the current design.\n"); + log("\n"); + log(" -v\n"); + log(" Add comments and indentation to BTOR output file\n"); + log("\n"); + log(" -s\n"); + log(" Output only a single bad property for all asserts\n"); log("\n"); - log("Write the current design to an BTOR file.\n"); } - - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - std::string top_module_name; - std::string buf_type, buf_in, buf_out; - std::string true_type, true_out; - std::string false_type, false_out; - BtorDumperConfig config; + bool verbose = false, single_bad = false; log_header(design, "Executing BTOR backend.\n"); - size_t argidx=1; - extra_args(f, filename, args, argidx); - - if (top_module_name.empty()) - for (auto & mod_it:design->modules_) - if (mod_it.second->get_bool_attribute("\\top")) - top_module_name = mod_it.first.str(); - - *f << stringf("; Generated by %s\n", yosys_version_str); - *f << stringf("; %s developed and maintained by Clifford Wolf <clifford@clifford.at>\n", yosys_version_str); - *f << stringf("; BTOR Backend developed by Ahmed Irfan <irfan@fbk.eu> - Fondazione Bruno Kessler, Trento, Italy\n"); - *f << stringf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"); - - std::vector<RTLIL::Module*> mod_list; - - for (auto module_it : design->modules_) + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { - RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox")) + if (args[argidx] == "-v") { + verbose = true; continue; - - if (module->processes.size() != 0) - log_error("Found unmapped processes in module %s: unmapped processes are not supported in BTOR backend!\n", RTLIL::id2cstr(module->name)); - - if (module->name == RTLIL::escape_id(top_module_name)) { - BtorDumper::dump(*f, module, design, config); - top_module_name.clear(); + } + if (args[argidx] == "-s") { + single_bad = true; continue; } - - mod_list.push_back(module); + break; } + extra_args(f, filename, args, argidx); + + RTLIL::Module *topmod = design->top_module(); + + if (topmod == nullptr) + log_cmd_error("No top module found.\n"); + + *f << stringf("; BTOR description generated by %s for module %s.\n", + yosys_version_str, log_id(topmod)); - if (!top_module_name.empty()) - log_error("Can't find top module `%s'!\n", top_module_name.c_str()); + BtorWorker(*f, topmod, verbose, single_bad); - for (auto module : mod_list) - BtorDumper::dump(*f, module, design, config); + *f << stringf("; end of yosys output\n"); } } BtorBackend; diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh new file mode 100644 index 000000000..e0f1a0514 --- /dev/null +++ b/backends/btor/test_cells.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -ex + +rm -rf test_cells.tmp +mkdir -p test_cells.tmp +cd test_cells.tmp + +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod' + +for fn in test_*.il; do + ../../../yosys -p " + read_ilang $fn + rename gold gate + synth + + read_ilang $fn + miter -equiv -make_assert -flatten gold gate main + hierarchy -top main + write_btor ${fn%.il}.btor + " + boolectormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out + if grep " SATISFIABLE" ${fn%.il}.out; then + echo "Check failed for ${fn%.il}." + exit 1 + fi +done + +echo "OK." + diff --git a/backends/btor/verilog2btor.sh b/backends/btor/verilog2btor.sh deleted file mode 100755 index dfd7f1a85..000000000 --- a/backends/btor/verilog2btor.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh - -# -# Script to write BTOR from Verilog design -# - -if [ "$#" -ne 3 ]; then - echo "Usage: $0 input.v output.btor top-module-name" >&2 - exit 1 -fi -if ! [ -e "$1" ]; then - echo "$1 not found" >&2 - exit 1 -fi - -FULL_PATH=$(readlink -f $1) -DIR=$(dirname $FULL_PATH) - -./yosys -q -p " -read_verilog -sv $1; -hierarchy -top $3; -hierarchy -libdir $DIR; -hierarchy -check; -proc; -opt; opt_expr -mux_undef; opt; -rename -hide;;; -#techmap -map +/pmux2mux.v;; -splice; opt; -memory_dff -wr_only; -memory_collect;; -flatten;; -memory_unpack; -splitnets -driver; -setundef -zero -undriven; -opt;;; -write_btor $2;" - diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index a6534b911..616b754ce 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -90,7 +90,7 @@ struct EdifNames struct EdifBackend : public Backend { EdifBackend() : Backend("edif", "write design to EDIF netlist file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -106,6 +106,13 @@ struct EdifBackend : public Backend { log(" if the design contains constant nets. use \"hilomap\" to map to custom\n"); log(" constant drivers first)\n"); log("\n"); + log(" -gndvccy\n"); + log(" create \"GND\" and \"VCC\" cells with \"Y\" outputs. (the default is \"G\"\n"); + log(" for \"GND\" and \"P\" for \"VCC\".)\n"); + log("\n"); + log(" -attrprop\n"); + log(" create EDIF properties for cell attributes\n"); + log("\n"); log(" -pvector {par|bra|ang}\n"); log(" sets the delimiting character for module port rename clauses to\n"); log(" parentheses, square brackets, or angle brackets.\n"); @@ -116,13 +123,14 @@ struct EdifBackend : public Backend { log("is targeted.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing EDIF backend.\n"); std::string top_module_name; bool port_rename = false; + bool attr_properties = false; std::map<RTLIL::IdString, std::map<RTLIL::IdString, int>> lib_cell_ports; - bool nogndvcc = false; + bool nogndvcc = false, gndvccy = false; CellTypes ct(design); EdifNames edif_names; @@ -137,6 +145,14 @@ struct EdifBackend : public Backend { nogndvcc = true; continue; } + if (args[argidx] == "-gndvccy") { + gndvccy = true; + continue; + } + if (args[argidx] == "-attrprop") { + attr_properties = true; + continue; + } if (args[argidx] == "-pvector" && argidx+1 < args.size()) { std::string parray; port_rename = true; @@ -162,7 +178,7 @@ struct EdifBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; if (top_module_name.empty()) @@ -176,7 +192,7 @@ struct EdifBackend : public Backend { for (auto cell_it : module->cells_) { RTLIL::Cell *cell = cell_it.second; - if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_bool_attribute("\\blackbox")) { + if (!design->modules_.count(cell->type) || design->modules_.at(cell->type)->get_blackbox_attribute()) { lib_cell_ports[cell->type]; for (auto p : cell->connections()) lib_cell_ports[cell->type][p.first] = GetSize(p.second); @@ -203,7 +219,7 @@ struct EdifBackend : public Backend { *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); - *f << stringf(" (interface (port G (direction OUTPUT)))\n"); + *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'G'); *f << stringf(" )\n"); *f << stringf(" )\n"); @@ -211,7 +227,7 @@ struct EdifBackend : public Backend { *f << stringf(" (cellType GENERIC)\n"); *f << stringf(" (view VIEW_NETLIST\n"); *f << stringf(" (viewType NETLIST)\n"); - *f << stringf(" (interface (port P (direction OUTPUT)))\n"); + *f << stringf(" (interface (port %c (direction OUTPUT)))\n", gndvccy ? 'Y' : 'P'); *f << stringf(" )\n"); *f << stringf(" )\n"); } @@ -284,13 +300,33 @@ struct EdifBackend : public Backend { *f << stringf(" (library DESIGN\n"); *f << stringf(" (edifLevel 0)\n"); *f << stringf(" (technology (numberDefinition))\n"); + + auto add_prop = [&](IdString name, Const val) { + if ((val.flags & RTLIL::CONST_FLAG_STRING) != 0) + *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(name), val.decode_string().c_str()); + else if (val.bits.size() <= 32 && RTLIL::SigSpec(val).is_fully_def()) + *f << stringf("\n (property %s (integer %u))", EDIF_DEF(name), val.as_int()); + else { + std::string hex_string = ""; + for (size_t i = 0; i < val.bits.size(); i += 4) { + int digit_value = 0; + if (i+0 < val.bits.size() && val.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1; + if (i+1 < val.bits.size() && val.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2; + if (i+2 < val.bits.size() && val.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4; + if (i+3 < val.bits.size() && val.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8; + char digit_str[2] = { "0123456789abcdef"[digit_value], 0 }; + hex_string = std::string(digit_str) + hex_string; + } + *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(name), GetSize(val.bits), hex_string.c_str()); + } + }; for (auto module : sorted_modules) { - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; SigMap sigmap(module); - std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db; + std::map<RTLIL::SigSpec, std::set<std::pair<std::string, bool>>> net_join_db; *f << stringf(" (cell %s\n", EDIF_DEF(module->name)); *f << stringf(" (cellType GENERIC)\n"); @@ -307,17 +343,26 @@ struct EdifBackend : public Backend { else if (!wire->port_input) dir = "OUTPUT"; if (wire->width == 1) { - *f << stringf(" (port %s (direction %s))\n", EDIF_DEF(wire->name), dir); + *f << stringf(" (port %s (direction %s)", EDIF_DEF(wire->name), dir); + if (attr_properties) + for (auto &p : wire->attributes) + add_prop(p.first, p.second); + *f << ")\n"; RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire)); - net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name))); + net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input)); } else { int b[2]; b[wire->upto ? 0 : 1] = wire->start_offset; b[wire->upto ? 1 : 0] = wire->start_offset + GetSize(wire) - 1; - *f << stringf(" (port (array %s %d) (direction %s))\n", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir); + *f << stringf(" (port (array %s %d) (direction %s)", EDIF_DEFR(wire->name, port_rename, b[0], b[1]), wire->width, dir); + if (attr_properties) + for (auto &p : wire->attributes) + add_prop(p.first, p.second); + + *f << ")\n"; for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); - net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1)); + net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input)); } } } @@ -333,23 +378,11 @@ struct EdifBackend : public Backend { *f << stringf(" (viewRef VIEW_NETLIST (cellRef %s%s))", EDIF_REF(cell->type), lib_cell_ports.count(cell->type) > 0 ? " (libraryRef LIB)" : ""); for (auto &p : cell->parameters) - if ((p.second.flags & RTLIL::CONST_FLAG_STRING) != 0) - *f << stringf("\n (property %s (string \"%s\"))", EDIF_DEF(p.first), p.second.decode_string().c_str()); - else if (p.second.bits.size() <= 32 && RTLIL::SigSpec(p.second).is_fully_def()) - *f << stringf("\n (property %s (integer %u))", EDIF_DEF(p.first), p.second.as_int()); - else { - std::string hex_string = ""; - for (size_t i = 0; i < p.second.bits.size(); i += 4) { - int digit_value = 0; - if (i+0 < p.second.bits.size() && p.second.bits.at(i+0) == RTLIL::State::S1) digit_value |= 1; - if (i+1 < p.second.bits.size() && p.second.bits.at(i+1) == RTLIL::State::S1) digit_value |= 2; - if (i+2 < p.second.bits.size() && p.second.bits.at(i+2) == RTLIL::State::S1) digit_value |= 4; - if (i+3 < p.second.bits.size() && p.second.bits.at(i+3) == RTLIL::State::S1) digit_value |= 8; - char digit_str[2] = { "0123456789abcdef"[digit_value], 0 }; - hex_string = std::string(digit_str) + hex_string; - } - *f << stringf("\n (property %s (string \"%d'h%s\"))", EDIF_DEF(p.first), GetSize(p.second.bits), hex_string.c_str()); - } + add_prop(p.first, p.second); + if (attr_properties) + for (auto &p : cell->attributes) + add_prop(p.first, p.second); + *f << stringf(")\n"); for (auto &p : cell->connections()) { RTLIL::SigSpec sig = sigmap(p.second); @@ -358,7 +391,7 @@ struct EdifBackend : public Backend { log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); else if (sig.size() == 1) - net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name))); + net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first))); else { int member_idx = GetSize(sig)-i-1; auto m = design->module(cell->type); @@ -367,8 +400,8 @@ struct EdifBackend : public Backend { if (w) member_idx = GetSize(w)-i-1; } - net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", - EDIF_REF(p.first), member_idx, EDIF_REF(cell->name))); + net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))", + EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first))); } } } @@ -377,11 +410,13 @@ struct EdifBackend : public Backend { if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) { if (sig == RTLIL::State::Sx) { for (auto &ref : it.second) - log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str()); + log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str()); sig = RTLIL::State::S0; + } else if (sig == RTLIL::State::Sz) { + continue; } else { for (auto &ref : it.second) - log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str()); + log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str()); log_abort(); } } @@ -398,16 +433,45 @@ struct EdifBackend : public Backend { } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) - *f << stringf(" %s\n", ref.c_str()); + *f << stringf(" %s\n", ref.first.c_str()); if (sig.wire == NULL) { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); if (sig == RTLIL::State::S0) - *f << stringf(" (portRef G (instanceRef GND))\n"); + *f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G'); if (sig == RTLIL::State::S1) - *f << stringf(" (portRef P (instanceRef VCC))\n"); + *f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P'); + } + *f << stringf(" )"); + if (attr_properties && sig.wire != NULL) + for (auto &p : sig.wire->attributes) + add_prop(p.first, p.second); + *f << stringf("\n )\n"); + } + for (auto &wire_it : module->wires_) { + RTLIL::Wire *wire = wire_it.second; + if (!wire->get_bool_attribute(ID::keep)) + continue; + for(int i = 0; i < wire->width; i++) { + SigBit raw_sig = RTLIL::SigSpec(wire, i); + SigBit mapped_sig = sigmap(raw_sig); + if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0) + continue; + std::string netname = log_signal(raw_sig); + for (size_t i = 0; i < netname.size(); i++) + if (netname[i] == ' ' || netname[i] == '\\') + netname.erase(netname.begin() + i--); + *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); + auto &refs = net_join_db.at(mapped_sig); + for (auto &ref : refs) + if (ref.second) + *f << stringf(" %s\n", ref.first.c_str()); + *f << stringf(" )"); + if (attr_properties && raw_sig.wire != NULL) + for (auto &p : raw_sig.wire->attributes) + add_prop(p.first, p.second); + *f << stringf("\n )\n"); } - *f << stringf(" ))\n"); } *f << stringf(" )\n"); *f << stringf(" )\n"); diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc index 06cbc9b2b..87db0edf7 100644 --- a/backends/firrtl/firrtl.cc +++ b/backends/firrtl/firrtl.cc @@ -23,7 +23,11 @@ #include "kernel/celltypes.h" #include "kernel/cellaigs.h" #include "kernel/log.h" +#include <algorithm> #include <string> +#include <regex> +#include <vector> +#include <cmath> USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN @@ -32,6 +36,28 @@ pool<string> used_names; dict<IdString, string> namecache; int autoid_counter; +typedef unsigned FDirection; +static const FDirection FD_NODIRECTION = 0x0; +static const FDirection FD_IN = 0x1; +static const FDirection FD_OUT = 0x2; +static const FDirection FD_INOUT = 0x3; +static const int FIRRTL_MAX_DSH_WIDTH_ERROR = 20; // For historic reasons, this is actually one greater than the maximum allowed shift width + +// Get a port direction with respect to a specific module. +FDirection getPortFDirection(IdString id, Module *module) +{ + Wire *wire = module->wires_.at(id); + FDirection direction = FD_NODIRECTION; + if (wire && wire->port_id) + { + if (wire->port_input) + direction |= FD_IN; + if (wire->port_output) + direction |= FD_OUT; + } + return direction; +} + string next_id() { string new_id; @@ -77,6 +103,127 @@ struct FirrtlWorker dict<SigBit, pair<string, int>> reverse_wire_map; string unconn_id; + RTLIL::Design *design; + std::string indent; + + // Define read/write ports and memories. + // We'll collect their definitions and emit the corresponding FIRRTL definitions at the appropriate point in module construction. + // For the moment, we don't handle $readmemh or $readmemb. + // These will be part of a subsequent PR. + struct read_port { + string name; + bool clk_enable; + bool clk_parity; + bool transparent; + RTLIL::SigSpec clk; + RTLIL::SigSpec ena; + RTLIL::SigSpec addr; + read_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr) : name(name), clk_enable(clk_enable), clk_parity(clk_parity), transparent(transparent), clk(clk), ena(ena), addr(addr) { + // Current (3/13/2019) conventions: + // generate a constant 0 for clock and a constant 1 for enable if they are undefined. + if (!clk.is_fully_def()) + this->clk = SigSpec(State::S0); + if (!ena.is_fully_def()) + this->ena = SigSpec(State::S1); + } + string gen_read(const char * indent) { + string addr_expr = make_expr(addr); + string ena_expr = make_expr(ena); + string clk_expr = make_expr(clk); + string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str()); + string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str()); + string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str()); + return addr_str + ena_str + clk_str; + } + }; + struct write_port : read_port { + RTLIL::SigSpec mask; + write_port(string name, bool clk_enable, bool clk_parity, bool transparent, RTLIL::SigSpec clk, RTLIL::SigSpec ena, RTLIL::SigSpec addr, RTLIL::SigSpec mask) : read_port(name, clk_enable, clk_parity, transparent, clk, ena, addr), mask(mask) { + if (!clk.is_fully_def()) + this->clk = SigSpec(RTLIL::Const(0)); + if (!ena.is_fully_def()) + this->ena = SigSpec(RTLIL::Const(0)); + if (!mask.is_fully_def()) + this->ena = SigSpec(RTLIL::Const(1)); + } + string gen_read(const char * /* indent */) { + log_error("gen_read called on write_port: %s\n", name.c_str()); + return stringf("gen_read called on write_port: %s\n", name.c_str()); + } + string gen_write(const char * indent) { + string addr_expr = make_expr(addr); + string ena_expr = make_expr(ena); + string clk_expr = make_expr(clk); + string mask_expr = make_expr(mask); + string mask_str = stringf("%s%s.mask <= %s\n", indent, name.c_str(), mask_expr.c_str()); + string addr_str = stringf("%s%s.addr <= %s\n", indent, name.c_str(), addr_expr.c_str()); + string ena_str = stringf("%s%s.en <= %s\n", indent, name.c_str(), ena_expr.c_str()); + string clk_str = stringf("%s%s.clk <= asClock(%s)\n", indent, name.c_str(), clk_expr.c_str()); + return addr_str + ena_str + clk_str + mask_str; + } + }; + /* Memories defined within this module. */ + struct memory { + Cell *pCell; // for error reporting + string name; // memory name + int abits; // number of address bits + int size; // size (in units) of the memory + int width; // size (in bits) of each element + int read_latency; + int write_latency; + vector<read_port> read_ports; + vector<write_port> write_ports; + std::string init_file; + std::string init_file_srcFileSpec; + string srcLine; + memory(Cell *pCell, string name, int abits, int size, int width) : pCell(pCell), name(name), abits(abits), size(size), width(width), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec("") { + // Provide defaults for abits or size if one (but not the other) is specified. + if (this->abits == 0 && this->size != 0) { + this->abits = ceil_log2(this->size); + } else if (this->abits != 0 && this->size == 0) { + this->size = 1 << this->abits; + } + // Sanity-check this construction. + if (this->name == "") { + log_error("Nameless memory%s\n", this->atLine()); + } + if (this->abits == 0 && this->size == 0) { + log_error("Memory %s has zero address bits and size%s\n", this->name.c_str(), this->atLine()); + } + if (this->width == 0) { + log_error("Memory %s has zero width%s\n", this->name.c_str(), this->atLine()); + } + } + // We need a default constructor for the dict insert. + memory() : pCell(0), read_latency(0), write_latency(1), init_file(""), init_file_srcFileSpec(""){} + + const char *atLine() { + if (srcLine == "") { + if (pCell) { + auto p = pCell->attributes.find("\\src"); + srcLine = " at " + p->second.decode_string(); + } + } + return srcLine.c_str(); + } + void add_memory_read_port(read_port &rp) { + read_ports.push_back(rp); + } + void add_memory_write_port(write_port &wp) { + write_ports.push_back(wp); + } + void add_memory_file(std::string init_file, std::string init_file_srcFileSpec) { + this->init_file = init_file; + this->init_file_srcFileSpec = init_file_srcFileSpec; + } + + }; + dict<string, memory> memories; + + void register_memory(memory &m) + { + memories[m.name] = m; + } void register_reverse_wire_map(string id, SigSpec sig) { @@ -84,11 +231,11 @@ struct FirrtlWorker reverse_wire_map[sig[i]] = make_pair(id, i); } - FirrtlWorker(Module *module, std::ostream &f) : module(module), f(f) + FirrtlWorker(Module *module, std::ostream &f, RTLIL::Design *theDesign) : module(module), f(f), design(theDesign), indent(" ") { } - string make_expr(SigSpec sig) + static string make_expr(const SigSpec &sig) { string expr; @@ -135,6 +282,117 @@ struct FirrtlWorker return expr; } + std::string fid(RTLIL::IdString internal_id) + { + return make_id(internal_id); + } + + std::string cellname(RTLIL::Cell *cell) + { + return fid(cell->name).c_str(); + } + + void process_instance(RTLIL::Cell *cell, vector<string> &wire_exprs) + { + std::string cell_type = fid(cell->type); + std::string instanceOf; + // If this is a parameterized module, its parent module is encoded in the cell type + if (cell->type.begins_with("$paramod")) + { + std::string::iterator it; + for (it = cell_type.begin(); it < cell_type.end(); it++) + { + switch (*it) { + case '\\': /* FALL_THROUGH */ + case '=': /* FALL_THROUGH */ + case '\'': /* FALL_THROUGH */ + case '$': instanceOf.append("_"); break; + default: instanceOf.append(1, *it); break; + } + } + } + else + { + instanceOf = cell_type; + } + + std::string cell_name = cellname(cell); + std::string cell_name_comment; + if (cell_name != fid(cell->name)) + cell_name_comment = " /* " + fid(cell->name) + " */ "; + else + cell_name_comment = ""; + // Find the module corresponding to this instance. + auto instModule = design->module(cell->type); + // If there is no instance for this, just return. + if (instModule == NULL) + { + log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str()); + return; + } + wire_exprs.push_back(stringf("%s" "inst %s%s of %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str())); + + for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) { + if (it->second.size() > 0) { + const SigSpec &secondSig = it->second; + const std::string firstName = cell_name + "." + make_id(it->first); + const std::string secondExpr = make_expr(secondSig); + // Find the direction for this port. + FDirection dir = getPortFDirection(it->first, instModule); + std::string sourceExpr, sinkExpr; + const SigSpec *sinkSig = nullptr; + switch (dir) { + case FD_INOUT: + log_warning("Instance port connection %s.%s is INOUT; treating as OUT\n", cell_type.c_str(), log_signal(it->second)); + /* FALLTHRU */ + case FD_OUT: + sourceExpr = firstName; + sinkExpr = secondExpr; + sinkSig = &secondSig; + break; + case FD_NODIRECTION: + log_warning("Instance port connection %s.%s is NODIRECTION; treating as IN\n", cell_type.c_str(), log_signal(it->second)); + /* FALLTHRU */ + case FD_IN: + sourceExpr = secondExpr; + sinkExpr = firstName; + break; + default: + log_error("Instance port %s.%s unrecognized connection direction 0x%x !\n", cell_type.c_str(), log_signal(it->second), dir); + break; + } + // Check for subfield assignment. + std::string bitsString = "bits("; + if (sinkExpr.compare(0, bitsString.length(), bitsString) == 0) { + if (sinkSig == nullptr) + log_error("Unknown subfield %s.%s\n", cell_type.c_str(), sinkExpr.c_str()); + // Don't generate the assignment here. + // Add the source and sink to the "reverse_wire_map" and we'll output the assignment + // as part of the coalesced subfield assignments for this wire. + register_reverse_wire_map(sourceExpr, *sinkSig); + } else { + wire_exprs.push_back(stringf("\n%s%s <= %s", indent.c_str(), sinkExpr.c_str(), sourceExpr.c_str())); + } + } + } + wire_exprs.push_back(stringf("\n")); + + } + + // Given an expression for a shift amount, and a maximum width, + // generate the FIRRTL expression for equivalent dynamic shift taking into account FIRRTL shift semantics. + std::string gen_dshl(const string b_expr, const int b_width) + { + string result = b_expr; + if (b_width >= FIRRTL_MAX_DSH_WIDTH_ERROR) { + int max_shift_width_bits = FIRRTL_MAX_DSH_WIDTH_ERROR - 1; + string max_shift_string = stringf("UInt<%d>(%d)", max_shift_width_bits, (1<<max_shift_width_bits) - 1); + // Deal with the difference in semantics between FIRRTL and verilog + result = stringf("mux(gt(%s, %s), %s, bits(%s, %d, 0))", b_expr.c_str(), max_shift_string.c_str(), max_shift_string.c_str(), b_expr.c_str(), max_shift_width_bits - 1); + } + return result; + } + void run() { f << stringf(" module %s:\n", make_id(module->name)); @@ -142,58 +400,90 @@ struct FirrtlWorker for (auto wire : module->wires()) { + const auto wireName = make_id(wire->name); + // If a wire has initial data, issue a warning since FIRRTL doesn't currently support it. + if (wire->attributes.count("\\init")) { + log_warning("Initial value (%s) for (%s.%s) not supported\n", + wire->attributes.at("\\init").as_string().c_str(), + log_id(module), log_id(wire)); + } if (wire->port_id) { if (wire->port_input && wire->port_output) log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire)); port_decls.push_back(stringf(" %s %s: UInt<%d>\n", wire->port_input ? "input" : "output", - make_id(wire->name), wire->width)); + wireName, wire->width)); } else { - wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", make_id(wire->name), wire->width)); + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", wireName, wire->width)); } } for (auto cell : module->cells()) { + static Const ndef(0, 0); + + // Is this cell is a module instance? + if (cell->type[0] != '$') + { + process_instance(cell, wire_exprs); + continue; + } + // Not a module instance. Set up cell properties + bool extract_y_bits = false; // Assume no extraction of final bits will be required. + int a_width = cell->parameters.at("\\A_WIDTH", ndef).as_int(); // The width of "A" + int b_width = cell->parameters.at("\\B_WIDTH", ndef).as_int(); // The width of "A" + const int y_width = cell->parameters.at("\\Y_WIDTH", ndef).as_int(); // The width of the result + const bool a_signed = cell->parameters.at("\\A_SIGNED", ndef).as_bool(); + const bool b_signed = cell->parameters.at("\\B_SIGNED", ndef).as_bool(); + bool firrtl_is_signed = a_signed; // The result is signed (subsequent code may change this). + int firrtl_width = 0; + string primop; + bool always_uint = false; + string y_id = make_id(cell->name); + if (cell->type.in("$not", "$logic_not", "$neg", "$reduce_and", "$reduce_or", "$reduce_xor", "$reduce_bool", "$reduce_xnor")) { - string y_id = make_id(cell->name); - bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); - int y_width = cell->parameters.at("\\Y_WIDTH").as_int(); string a_expr = make_expr(cell->getPort("\\A")); wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); - if (cell->parameters.at("\\A_SIGNED").as_bool()) { + if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; } - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + // Don't use the results of logical operations (a single bit) to control padding + if (!(cell->type.in("$eq", "$eqx", "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$reduce_bool", "$logic_not") && y_width == 1) ) { + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + } - string primop; - bool always_uint = false; + // Assume the FIRRTL width is a single bit. + firrtl_width = 1; if (cell->type == "$not") primop = "not"; - if (cell->type == "$neg") primop = "neg"; - if (cell->type == "$logic_not") { + else if (cell->type == "$neg") { + primop = "neg"; + firrtl_is_signed = true; // Result of "neg" is signed (an SInt). + firrtl_width = a_width; + } else if (cell->type == "$logic_not") { primop = "eq"; a_expr = stringf("%s, UInt(0)", a_expr.c_str()); } - if (cell->type == "$reduce_and") primop = "andr"; - if (cell->type == "$reduce_or") primop = "orr"; - if (cell->type == "$reduce_xor") primop = "xorr"; - if (cell->type == "$reduce_xnor") { + else if (cell->type == "$reduce_and") primop = "andr"; + else if (cell->type == "$reduce_or") primop = "orr"; + else if (cell->type == "$reduce_xor") primop = "xorr"; + else if (cell->type == "$reduce_xnor") { primop = "not"; a_expr = stringf("xorr(%s)", a_expr.c_str()); } - if (cell->type == "$reduce_bool") { - primop = "neq"; - a_expr = stringf("%s, UInt(0)", a_expr.c_str()); - } + else if (cell->type == "$reduce_bool") { + primop = "neq"; + // Use the sign of the a_expr and its width as the type (UInt/SInt) and width of the comparand. + a_expr = stringf("%s, %cInt<%d>(0)", a_expr.c_str(), a_signed ? 'S' : 'U', a_width); + } string expr = stringf("%s(%s)", primop.c_str(), a_expr.c_str()); - if ((is_signed && !always_uint)) + if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); @@ -201,99 +491,226 @@ struct FirrtlWorker continue; } - if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$and", "$or", "$eq", "$eqx", - "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl", - "$logic_and", "$logic_or")) + if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$xnor", "$and", "$or", "$eq", "$eqx", + "$gt", "$ge", "$lt", "$le", "$ne", "$nex", "$shr", "$sshr", "$sshl", "$shl", + "$logic_and", "$logic_or", "$pow")) { - string y_id = make_id(cell->name); - bool is_signed = cell->parameters.at("\\A_SIGNED").as_bool(); - int y_width = cell->parameters.at("\\Y_WIDTH").as_int(); string a_expr = make_expr(cell->getPort("\\A")); string b_expr = make_expr(cell->getPort("\\B")); wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); - if (cell->parameters.at("\\A_SIGNED").as_bool()) { + if (a_signed) { a_expr = "asSInt(" + a_expr + ")"; + // Expand the "A" operand to the result width + if (a_width < y_width) { + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_width = y_width; + } } - if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type != "$shr")) { + // Shift amount is always unsigned, and needn't be padded to result width, + // otherwise, we need to cast the b_expr appropriately + if (b_signed && !cell->type.in("$shr", "$sshr", "$shl", "$sshl", "$pow")) { b_expr = "asSInt(" + b_expr + ")"; + // Expand the "B" operand to the result width + if (b_width < y_width) { + b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_width = y_width; + } } - a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); - - if ((cell->type != "$shl") && (cell->type != "$sshl")) { - b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); - } - - if (cell->parameters.at("\\A_SIGNED").as_bool() & (cell->type == "$shr")) { - a_expr = "asUInt(" + a_expr + ")"; + // For the arithmetic ops, expand operand widths to result widths befor performing the operation. + // This corresponds (according to iverilog) to what verilog compilers implement. + if (cell->type.in("$add", "$sub", "$mul", "$div", "$mod", "$xor", "$xnor", "$and", "$or")) + { + if (a_width < y_width) { + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + a_width = y_width; + } + if (b_width < y_width) { + b_expr = stringf("pad(%s, %d)", b_expr.c_str(), y_width); + b_width = y_width; + } + } + // Assume the FIRRTL width is the width of "A" + firrtl_width = a_width; + auto a_sig = cell->getPort("\\A"); + + if (cell->type == "$add") { + primop = "add"; + firrtl_is_signed = a_signed | b_signed; + firrtl_width = max(a_width, b_width); + } else if (cell->type == "$sub") { + primop = "sub"; + firrtl_is_signed = true; + int a_widthInc = (!a_signed && b_signed) ? 2 : (a_signed && !b_signed) ? 1 : 0; + int b_widthInc = (a_signed && !b_signed) ? 2 : (!a_signed && b_signed) ? 1 : 0; + firrtl_width = max(a_width + a_widthInc, b_width + b_widthInc); + } else if (cell->type == "$mul") { + primop = "mul"; + firrtl_is_signed = a_signed | b_signed; + firrtl_width = a_width + b_width; + } else if (cell->type == "$div") { + primop = "div"; + firrtl_is_signed = a_signed | b_signed; + firrtl_width = a_width; + } else if (cell->type == "$mod") { + primop = "rem"; + firrtl_width = min(a_width, b_width); + } else if (cell->type == "$and") { + primop = "and"; + always_uint = true; + firrtl_width = max(a_width, b_width); + } + else if (cell->type == "$or" ) { + primop = "or"; + always_uint = true; + firrtl_width = max(a_width, b_width); + } + else if (cell->type == "$xor") { + primop = "xor"; + always_uint = true; + firrtl_width = max(a_width, b_width); + } + else if (cell->type == "$xnor") { + primop = "xnor"; + always_uint = true; + firrtl_width = max(a_width, b_width); + } + else if ((cell->type == "$eq") | (cell->type == "$eqx")) { + primop = "eq"; + always_uint = true; + firrtl_width = 1; + } + else if ((cell->type == "$ne") | (cell->type == "$nex")) { + primop = "neq"; + always_uint = true; + firrtl_width = 1; + } + else if (cell->type == "$gt") { + primop = "gt"; + always_uint = true; + firrtl_width = 1; + } + else if (cell->type == "$ge") { + primop = "geq"; + always_uint = true; + firrtl_width = 1; + } + else if (cell->type == "$lt") { + primop = "lt"; + always_uint = true; + firrtl_width = 1; + } + else if (cell->type == "$le") { + primop = "leq"; + always_uint = true; + firrtl_width = 1; + } + else if ((cell->type == "$shl") | (cell->type == "$sshl")) { + // FIRRTL will widen the result (y) by the amount of the shift. + // We'll need to offset this by extracting the un-widened portion as Verilog would do. + extract_y_bits = true; + // Is the shift amount constant? + auto b_sig = cell->getPort("\\B"); + if (b_sig.is_fully_const()) { + primop = "shl"; + int shift_amount = b_sig.as_int(); + b_expr = std::to_string(shift_amount); + firrtl_width = a_width + shift_amount; + } else { + primop = "dshl"; + // Convert from FIRRTL left shift semantics. + b_expr = gen_dshl(b_expr, b_width); + firrtl_width = a_width + (1 << b_width) - 1; + } + } + else if ((cell->type == "$shr") | (cell->type == "$sshr")) { + // We don't need to extract a specific range of bits. + extract_y_bits = false; + // Is the shift amount constant? + auto b_sig = cell->getPort("\\B"); + if (b_sig.is_fully_const()) { + primop = "shr"; + int shift_amount = b_sig.as_int(); + b_expr = std::to_string(shift_amount); + firrtl_width = max(1, a_width - shift_amount); + } else { + primop = "dshr"; + firrtl_width = a_width; + } + // We'll need to do some special fixups if the source (and thus result) is signed. + if (firrtl_is_signed) { + // If this is a "logical" shift right, pretend the source is unsigned. + if (cell->type == "$shr") { + a_expr = "asUInt(" + a_expr + ")"; + } + } + } + else if ((cell->type == "$logic_and")) { + primop = "and"; + a_expr = "neq(" + a_expr + ", UInt(0))"; + b_expr = "neq(" + b_expr + ", UInt(0))"; + always_uint = true; + firrtl_width = 1; + } + else if ((cell->type == "$logic_or")) { + primop = "or"; + a_expr = "neq(" + a_expr + ", UInt(0))"; + b_expr = "neq(" + b_expr + ", UInt(0))"; + always_uint = true; + firrtl_width = 1; + } + else if ((cell->type == "$pow")) { + if (a_sig.is_fully_const() && a_sig.as_int() == 2) { + // We'll convert this to a shift. To simplify things, change the a_expr to "1" + // so we can use b_expr directly as a shift amount. + // Only support 2 ** N (i.e., shift left) + // FIRRTL will widen the result (y) by the amount of the shift. + // We'll need to offset this by extracting the un-widened portion as Verilog would do. + a_expr = firrtl_is_signed ? "SInt(1)" : "UInt(1)"; + extract_y_bits = true; + // Is the shift amount constant? + auto b_sig = cell->getPort("\\B"); + if (b_sig.is_fully_const()) { + primop = "shl"; + int shiftAmount = b_sig.as_int(); + if (shiftAmount < 0) { + log_error("Negative power exponent - %d: %s.%s\n", shiftAmount, log_id(module), log_id(cell)); + } + b_expr = std::to_string(shiftAmount); + firrtl_width = a_width + shiftAmount; + } else { + primop = "dshl"; + // Convert from FIRRTL left shift semantics. + b_expr = gen_dshl(b_expr, b_width); + firrtl_width = a_width + (1 << b_width) - 1; + } + } else { + log_error("Non power 2: %s.%s\n", log_id(module), log_id(cell)); + } } - - string primop; - bool always_uint = false; - if (cell->type == "$add") primop = "add"; - if (cell->type == "$sub") primop = "sub"; - if (cell->type == "$mul") primop = "mul"; - if (cell->type == "$div") primop = "div"; - if (cell->type == "$mod") primop = "rem"; - if (cell->type == "$and") { - primop = "and"; - always_uint = true; - } - if (cell->type == "$or" ) { - primop = "or"; - always_uint = true; - } - if (cell->type == "$xor") { - primop = "xor"; - always_uint = true; - } - if ((cell->type == "$eq") | (cell->type == "$eqx")) { - primop = "eq"; - always_uint = true; - } - if ((cell->type == "$ne") | (cell->type == "$nex")) { - primop = "neq"; - always_uint = true; - } - if (cell->type == "$gt") { - primop = "gt"; - always_uint = true; - } - if (cell->type == "$ge") { - primop = "geq"; - always_uint = true; - } - if (cell->type == "$lt") { - primop = "lt"; - always_uint = true; - } - if (cell->type == "$le") { - primop = "leq"; - always_uint = true; - } - if ((cell->type == "$shl") | (cell->type == "$sshl")) primop = "dshl"; - if ((cell->type == "$shr") | (cell->type == "$sshr")) primop = "dshr"; - if ((cell->type == "$logic_and")) { - primop = "and"; - a_expr = "neq(" + a_expr + ", UInt(0))"; - b_expr = "neq(" + b_expr + ", UInt(0))"; - always_uint = true; - } - if ((cell->type == "$logic_or")) { - primop = "or"; - a_expr = "neq(" + a_expr + ", UInt(0))"; - b_expr = "neq(" + b_expr + ", UInt(0))"; - always_uint = true; - } if (!cell->parameters.at("\\B_SIGNED").as_bool()) { b_expr = "asUInt(" + b_expr + ")"; } - string expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); + string expr; + // Deal with $xnor == ~^ (not xor) + if (primop == "xnor") { + expr = stringf("not(xor(%s, %s))", a_expr.c_str(), b_expr.c_str()); + } else { + expr = stringf("%s(%s, %s)", primop.c_str(), a_expr.c_str(), b_expr.c_str()); + } + + // Deal with FIRRTL's "shift widens" semantics, or the need to widen the FIRRTL result. + // If the operation is signed, the FIRRTL width will be 1 one bit larger. + if (extract_y_bits) { + expr = stringf("bits(%s, %d, 0)", expr.c_str(), y_width - 1); + } else if (firrtl_is_signed && (firrtl_width + 1) < y_width) { + expr = stringf("pad(%s, %d)", expr.c_str(), y_width); + } - if ((is_signed && !always_uint) || cell->type.in("$sub")) + if ((firrtl_is_signed && !always_uint)) expr = stringf("asUInt(%s)", expr.c_str()); cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); @@ -304,7 +721,6 @@ struct FirrtlWorker if (cell->type.in("$mux")) { - string y_id = make_id(cell->name); int width = cell->parameters.at("\\WIDTH").as_int(); string a_expr = make_expr(cell->getPort("\\A")); string b_expr = make_expr(cell->getPort("\\B")); @@ -325,6 +741,7 @@ struct FirrtlWorker int abits = cell->parameters.at("\\ABITS").as_int(); int width = cell->parameters.at("\\WIDTH").as_int(); int size = cell->parameters.at("\\SIZE").as_int(); + memory m(cell, mem_id, abits, size, width); int rd_ports = cell->parameters.at("\\RD_PORTS").as_int(); int wr_ports = cell->parameters.at("\\WR_PORTS").as_int(); @@ -341,33 +758,24 @@ struct FirrtlWorker if (offset != 0) log_error("Memory with nonzero offset: %s.%s\n", log_id(module), log_id(cell)); - cell_exprs.push_back(stringf(" mem %s:\n", mem_id.c_str())); - cell_exprs.push_back(stringf(" data-type => UInt<%d>\n", width)); - cell_exprs.push_back(stringf(" depth => %d\n", size)); - - for (int i = 0; i < rd_ports; i++) - cell_exprs.push_back(stringf(" reader => r%d\n", i)); - - for (int i = 0; i < wr_ports; i++) - cell_exprs.push_back(stringf(" writer => w%d\n", i)); - - cell_exprs.push_back(stringf(" read-latency => 0\n")); - cell_exprs.push_back(stringf(" write-latency => 1\n")); - cell_exprs.push_back(stringf(" read-under-write => undefined\n")); - for (int i = 0; i < rd_ports; i++) { if (rd_clk_enable[i] != State::S0) log_error("Clocked read port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); + SigSpec addr_sig = cell->getPort("\\RD_ADDR").extract(i*abits, abits); SigSpec data_sig = cell->getPort("\\RD_DATA").extract(i*width, width); - string addr_expr = make_expr(cell->getPort("\\RD_ADDR").extract(i*abits, abits)); - - cell_exprs.push_back(stringf(" %s.r%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str())); - cell_exprs.push_back(stringf(" %s.r%d.en <= UInt<1>(1)\n", mem_id.c_str(), i)); - cell_exprs.push_back(stringf(" %s.r%d.clk <= asClock(UInt<1>(0))\n", mem_id.c_str(), i)); - - register_reverse_wire_map(stringf("%s.r%d.data", mem_id.c_str(), i), data_sig); + string addr_expr = make_expr(addr_sig); + string name(stringf("%s.r%d", m.name.c_str(), i)); + bool clk_enable = false; + bool clk_parity = true; + bool transparency = false; + SigSpec ena_sig = RTLIL::SigSpec(RTLIL::State::S1, 1); + SigSpec clk_sig = RTLIL::SigSpec(RTLIL::State::S0, 1); + read_port rp(name, clk_enable, clk_parity, transparency, clk_sig, ena_sig, addr_sig); + m.add_memory_read_port(rp); + cell_exprs.push_back(rp.gen_read(indent.c_str())); + register_reverse_wire_map(stringf("%s.data", name.c_str()), data_sig); } for (int i = 0; i < wr_ports; i++) @@ -378,9 +786,16 @@ struct FirrtlWorker if (wr_clk_polarity[i] != State::S1) log_error("Negedge write port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - string addr_expr = make_expr(cell->getPort("\\WR_ADDR").extract(i*abits, abits)); - string data_expr = make_expr(cell->getPort("\\WR_DATA").extract(i*width, width)); - string clk_expr = make_expr(cell->getPort("\\WR_CLK").extract(i)); + string name(stringf("%s.w%d", m.name.c_str(), i)); + bool clk_enable = true; + bool clk_parity = true; + bool transparency = false; + SigSpec addr_sig =cell->getPort("\\WR_ADDR").extract(i*abits, abits); + string addr_expr = make_expr(addr_sig); + SigSpec data_sig =cell->getPort("\\WR_DATA").extract(i*width, width); + string data_expr = make_expr(data_sig); + SigSpec clk_sig = cell->getPort("\\WR_CLK").extract(i); + string clk_expr = make_expr(clk_sig); SigSpec wen_sig = cell->getPort("\\WR_EN").extract(i*width, width); string wen_expr = make_expr(wen_sig[0]); @@ -389,13 +804,57 @@ struct FirrtlWorker if (wen_sig[0] != wen_sig[i]) log_error("Complex write enable on port %d on memory %s.%s.\n", i, log_id(module), log_id(cell)); - cell_exprs.push_back(stringf(" %s.w%d.addr <= %s\n", mem_id.c_str(), i, addr_expr.c_str())); - cell_exprs.push_back(stringf(" %s.w%d.data <= %s\n", mem_id.c_str(), i, data_expr.c_str())); - cell_exprs.push_back(stringf(" %s.w%d.en <= %s\n", mem_id.c_str(), i, wen_expr.c_str())); - cell_exprs.push_back(stringf(" %s.w%d.mask <= UInt<1>(1)\n", mem_id.c_str(), i)); - cell_exprs.push_back(stringf(" %s.w%d.clk <= asClock(%s)\n", mem_id.c_str(), i, clk_expr.c_str())); + SigSpec mask_sig = RTLIL::SigSpec(RTLIL::State::S1, 1); + write_port wp(name, clk_enable, clk_parity, transparency, clk_sig, wen_sig[0], addr_sig, mask_sig); + m.add_memory_write_port(wp); + cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), name.c_str(), data_expr.c_str())); + cell_exprs.push_back(wp.gen_write(indent.c_str())); } + register_memory(m); + continue; + } + if (cell->type.in("$memwr", "$memrd", "$meminit")) + { + std::string cell_type = fid(cell->type); + std::string mem_id = make_id(cell->parameters["\\MEMID"].decode_string()); + int abits = cell->parameters.at("\\ABITS").as_int(); + int width = cell->parameters.at("\\WIDTH").as_int(); + memory *mp = nullptr; + if (cell->type == "$meminit" ) { + log_error("$meminit (%s.%s.%s) currently unsupported\n", log_id(module), log_id(cell), mem_id.c_str()); + } else { + // It's a $memwr or $memrd. Remember the read/write port parameters for the eventual FIRRTL memory definition. + auto addrSig = cell->getPort("\\ADDR"); + auto dataSig = cell->getPort("\\DATA"); + auto enableSig = cell->getPort("\\EN"); + auto clockSig = cell->getPort("\\CLK"); + Const clk_enable = cell->parameters.at("\\CLK_ENABLE"); + Const clk_polarity = cell->parameters.at("\\CLK_POLARITY"); + + // Do we already have an entry for this memory? + if (memories.count(mem_id) == 0) { + memory m(cell, mem_id, abits, 0, width); + register_memory(m); + } + mp = &memories.at(mem_id); + int portNum = 0; + bool transparency = false; + string data_expr = make_expr(dataSig); + if (cell->type.in("$memwr")) { + portNum = (int) mp->write_ports.size(); + write_port wp(stringf("%s.w%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig, dataSig); + mp->add_memory_write_port(wp); + cell_exprs.push_back(stringf("%s%s.data <= %s\n", indent.c_str(), wp.name.c_str(), data_expr.c_str())); + cell_exprs.push_back(wp.gen_write(indent.c_str())); + } else if (cell->type.in("$memrd")) { + portNum = (int) mp->read_ports.size(); + read_port rp(stringf("%s.r%d", mem_id.c_str(), portNum), clk_enable.as_bool(), clk_polarity.as_bool(), transparency, clockSig, enableSig, addrSig); + mp->add_memory_read_port(rp); + cell_exprs.push_back(rp.gen_read(indent.c_str())); + register_reverse_wire_map(stringf("%s.data", rp.name.c_str()), dataSig); + } + } continue; } @@ -405,19 +864,85 @@ struct FirrtlWorker if (clkpol == false) log_error("Negative edge clock on FF %s.%s.\n", log_id(module), log_id(cell)); - string q_id = make_id(cell->name); int width = cell->parameters.at("\\WIDTH").as_int(); string expr = make_expr(cell->getPort("\\D")); string clk_expr = "asClock(" + make_expr(cell->getPort("\\CLK")) + ")"; - wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", q_id.c_str(), width, clk_expr.c_str())); + wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s\n", y_id.c_str(), width, clk_expr.c_str())); - cell_exprs.push_back(stringf(" %s <= %s\n", q_id.c_str(), expr.c_str())); - register_reverse_wire_map(q_id, cell->getPort("\\Q")); + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Q")); continue; } + // This may be a parameterized module - paramod. + if (cell->type.begins_with("$paramod")) + { + process_instance(cell, wire_exprs); + continue; + } + if (cell->type == "$shiftx") { + // assign y = a[b +: y_width]; + // We'll extract the correct bits as part of the primop. + + string a_expr = make_expr(cell->getPort("\\A")); + // Get the initial bit selector + string b_expr = make_expr(cell->getPort("\\B")); + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + + if (cell->getParam("\\B_SIGNED").as_bool()) { + // Use validif to constrain the selection (test the sign bit) + auto b_string = b_expr.c_str(); + int b_sign = cell->parameters.at("\\B_WIDTH").as_int() - 1; + b_expr = stringf("validif(not(bits(%s, %d, %d)), %s)", b_string, b_sign, b_sign, b_string); + } + string expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_expr.c_str()); + + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Y")); + continue; + } + if (cell->type == "$shift") { + // assign y = a >> b; + // where b may be negative + + string a_expr = make_expr(cell->getPort("\\A")); + string b_expr = make_expr(cell->getPort("\\B")); + auto b_string = b_expr.c_str(); + string expr; + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + + if (cell->getParam("\\B_SIGNED").as_bool()) { + // We generate a left or right shift based on the sign of b. + std::string dshl = stringf("bits(dshl(%s, %s), 0, %d)", a_expr.c_str(), gen_dshl(b_expr, b_width).c_str(), y_width); + std::string dshr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); + expr = stringf("mux(%s < 0, %s, %s)", + b_string, + dshl.c_str(), + dshr.c_str() + ); + } else { + expr = stringf("dshr(%s, %s)", a_expr.c_str(), b_string); + } + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Y")); + continue; + } + if (cell->type == "$pos") { + // assign y = a; +// printCell(cell); + string a_expr = make_expr(cell->getPort("\\A")); + // Verilog appears to treat the result as signed, so if the result is wider than "A", + // we need to pad. + if (a_width < y_width) { + a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width); + } + wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width)); + cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), a_expr.c_str())); + register_reverse_wire_map(y_id, cell->getPort("\\Y")); + continue; + } log_error("Cell type not supported: %s (%s.%s)\n", log_id(cell->type), log_id(module), log_id(cell)); } @@ -494,14 +1019,14 @@ struct FirrtlWorker if (is_valid) { if (make_unconn_id) { wire_decls.push_back(stringf(" wire %s: UInt<1>\n", unconn_id.c_str())); - cell_exprs.push_back(stringf(" %s is invalid\n", unconn_id.c_str())); + wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str())); } wire_exprs.push_back(stringf(" %s <= %s\n", make_id(wire->name), expr.c_str())); } else { if (make_unconn_id) { unconn_id.clear(); } - wire_exprs.push_back(stringf(" %s is invalid\n", make_id(wire->name))); + wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name))); } } @@ -515,6 +1040,24 @@ struct FirrtlWorker f << stringf("\n"); + // If we have any memory definitions, output them. + for (auto kv : memories) { + memory &m = kv.second; + f << stringf(" mem %s:\n", m.name.c_str()); + f << stringf(" data-type => UInt<%d>\n", m.width); + f << stringf(" depth => %d\n", m.size); + for (int i = 0; i < (int) m.read_ports.size(); i += 1) { + f << stringf(" reader => r%d\n", i); + } + for (int i = 0; i < (int) m.write_ports.size(); i += 1) { + f << stringf(" writer => w%d\n", i); + } + f << stringf(" read-latency => %d\n", m.read_latency); + f << stringf(" write-latency => %d\n", m.write_latency); + f << stringf(" read-under-write => undefined\n"); + } + f << stringf("\n"); + for (auto str : cell_exprs) f << str; @@ -527,50 +1070,65 @@ struct FirrtlWorker struct FirrtlBackend : public Backend { FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_firrtl [options] [filename]\n"); log("\n"); log("Write a FIRRTL netlist of the current design.\n"); + log("The following commands are executed by this command:\n"); + log(" pmuxtree\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - size_t argidx; - for (argidx = 1; argidx < args.size(); argidx++) - { - // if (args[argidx] == "-aig") { - // aig_mode = true; - // continue; - // } - break; + size_t argidx = args.size(); // We aren't expecting any arguments. + + // If we weren't explicitly passed a filename, use the last argument (if it isn't a flag). + if (filename == "") { + if (argidx > 0 && args[argidx - 1][0] != '-') { + // extra_args and friends need to see this argument. + argidx -= 1; + filename = args[argidx]; + } } extra_args(f, filename, args, argidx); - log_header(design, "Executing FIRRTL backend.\n"); + if (!design->full_selection()) + log_cmd_error("This command only operates on fully selected designs!\n"); - Module *top = design->top_module(); + log_header(design, "Executing FIRRTL backend.\n"); + log_push(); - if (top == nullptr) - log_error("No top module found!\n"); + Pass::call(design, stringf("pmuxtree")); namecache.clear(); autoid_counter = 0; + // Get the top module, or a reasonable facsimile - we need something for the circuit name. + Module *top = design->top_module(); + Module *last = nullptr; + // Generate module and wire names. for (auto module : design->modules()) { make_id(module->name); + last = module; + if (top == nullptr && module->get_bool_attribute("\\top")) { + top = module; + } for (auto wire : module->wires()) if (wire->port_id) make_id(wire->name); } + if (top == nullptr) + top = last; + *f << stringf("circuit %s:\n", make_id(top->name)); for (auto module : design->modules()) { - FirrtlWorker worker(module, *f); + FirrtlWorker worker(module, *f, design); worker.run(); } diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc index 16d1a97f5..e06786220 100644 --- a/backends/ilang/ilang_backend.cc +++ b/backends/ilang/ilang_backend.cc @@ -40,8 +40,8 @@ void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi for (int i = 0; i < width; i++) { log_assert(offset+i < (int)data.bits.size()); switch (data.bits[offset+i]) { - case RTLIL::S0: break; - case RTLIL::S1: val |= 1 << i; break; + case State::S0: break; + case State::S1: val |= 1 << i; break; default: val = -1; break; } } @@ -54,8 +54,8 @@ void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { - case RTLIL::S0: f << stringf("0"); break; - case RTLIL::S1: f << stringf("1"); break; + case State::S0: f << stringf("0"); break; + case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sz: f << stringf("z"); break; case RTLIL::Sa: f << stringf("-"); break; @@ -160,7 +160,10 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL:: } f << stringf("%s" "cell %s %s\n", indent.c_str(), cell->type.c_str(), cell->name.c_str()); for (auto &it : cell->parameters) { - f << stringf("%s parameter%s %s ", indent.c_str(), (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", it.first.c_str()); + f << stringf("%s parameter%s%s %s ", indent.c_str(), + (it.second.flags & RTLIL::CONST_FLAG_SIGNED) != 0 ? " signed" : "", + (it.second.flags & RTLIL::CONST_FLAG_REAL) != 0 ? " real" : "", + it.first.c_str()); dump_const(f, it.second); f << stringf("\n"); } @@ -201,10 +204,15 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + for (auto ait = (*it)->attributes.begin(); ait != (*it)->attributes.end(); ++ait) { + f << stringf("%s attribute %s ", indent.c_str(), ait->first.c_str()); + dump_const(f, ait->second); + f << stringf("\n"); + } f << stringf("%s case ", indent.c_str()); for (size_t i = 0; i < (*it)->compare.size(); i++) { if (i > 0) - f << stringf(", "); + f << stringf(" , "); dump_sigspec(f, (*it)->compare[i]); } f << stringf("\n"); @@ -219,7 +227,7 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT { f << stringf("%s" "sync ", indent.c_str()); switch (sy->type) { - if (0) case RTLIL::ST0: f << stringf("low "); + case RTLIL::ST0: f << stringf("low "); if (0) case RTLIL::ST1: f << stringf("high "); if (0) case RTLIL::STp: f << stringf("posedge "); if (0) case RTLIL::STn: f << stringf("negedge "); @@ -382,7 +390,7 @@ PRIVATE_NAMESPACE_BEGIN struct IlangBackend : public Backend { IlangBackend() : Backend("ilang", "write design to ilang file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -395,7 +403,7 @@ struct IlangBackend : public Backend { log(" only write selected parts of the design.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool selected = false; @@ -422,7 +430,7 @@ struct IlangBackend : public Backend { struct DumpPass : public Pass { DumpPass() : Pass("dump", "print parts of the design in ilang format") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -445,7 +453,7 @@ struct DumpPass : public Pass { log(" like -outfile but append instead of overwrite\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string filename; bool flag_m = false, flag_n = false, append = false; @@ -480,6 +488,7 @@ struct DumpPass : public Pass { std::stringstream buf; if (!filename.empty()) { + rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), append ? std::ofstream::app : std::ofstream::trunc); if (ff->fail()) { diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc index 34cb52fb4..809a0fa09 100644 --- a/backends/intersynth/intersynth.cc +++ b/backends/intersynth/intersynth.cc @@ -46,7 +46,7 @@ static std::string netname(std::set<std::string> &conntypes_code, std::set<std:: struct IntersynthBackend : public Backend { IntersynthBackend() : Backend("intersynth", "write design to InterSynth netlist file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -71,7 +71,7 @@ struct IntersynthBackend : public Backend { log("http://www.clifford.at/intersynth/\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing INTERSYNTH backend.\n"); log_push(); @@ -108,7 +108,7 @@ struct IntersynthBackend : public Backend { if (f.fail()) log_error("Can't open lib file `%s'.\n", filename.c_str()); RTLIL::Design *lib = new RTLIL::Design; - Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.substr(filename.size()-3) == ".il") ? "ilang" : "verilog"); + Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog")); libs.push_back(lib); } @@ -127,7 +127,7 @@ struct IntersynthBackend : public Backend { RTLIL::Module *module = module_it.second; SigMap sigmap(module); - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; if (module->memories.size() == 0 && module->processes.size() == 0 && module->cells_.size() == 0) continue; @@ -183,7 +183,7 @@ struct IntersynthBackend : public Backend { if (param.second.bits.size() != 32) { node_code += stringf(" %s '", RTLIL::id2cstr(param.first)); for (int i = param.second.bits.size()-1; i >= 0; i--) - node_code += param.second.bits[i] == RTLIL::S1 ? "1" : "0"; + node_code += param.second.bits[i] == State::S1 ? "1" : "0"; } else node_code += stringf(" %s 0x%x", RTLIL::id2cstr(param.first), param.second.as_int()); } diff --git a/backends/json/json.cc b/backends/json/json.cc index d3b7077a2..107009ee4 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -83,18 +83,43 @@ struct JsonWriter return str + " ]"; } + void write_parameter_value(const Const &value) + { + if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) { + string str = value.decode_string(); + int state = 0; + for (char c : str) { + if (state == 0) { + if (c == '0' || c == '1' || c == 'x' || c == 'z') + state = 0; + else if (c == ' ') + state = 1; + else + state = 2; + } else if (state == 1 && c != ' ') + state = 2; + } + if (state < 2) + str += " "; + f << get_string(str); + } else + if (GetSize(value) == 32 && value.is_fully_def()) { + if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) + f << stringf("%d", value.as_int()); + else + f << stringf("%u", value.as_int()); + } else { + f << get_string(value.as_string()); + } + } + void write_parameters(const dict<IdString, Const> ¶meters, bool for_module=false) { bool first = true; for (auto ¶m : parameters) { f << stringf("%s\n", first ? "" : ","); f << stringf(" %s%s: ", for_module ? "" : " ", get_name(param.first).c_str()); - if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) - f << get_string(param.second.decode_string()); - else if (GetSize(param.second.bits) > 32) - f << get_string(param.second.as_string()); - else - f << stringf("%d", param.second.as_int()); + write_parameter_value(param.second); first = false; } } @@ -124,6 +149,10 @@ struct JsonWriter f << stringf("%s\n", first ? "" : ","); f << stringf(" %s: {\n", get_name(n).c_str()); f << stringf(" \"direction\": \"%s\",\n", w->port_input ? w->port_output ? "inout" : "input" : "output"); + if (w->start_offset) + f << stringf(" \"offset\": %d,\n", w->start_offset); + if (w->upto) + f << stringf(" \"upto\": 1,\n"); f << stringf(" \"bits\": %s\n", get_bits(w).c_str()); f << stringf(" }"); first = false; @@ -187,6 +216,10 @@ struct JsonWriter f << stringf(" %s: {\n", get_name(w->name).c_str()); f << stringf(" \"hide_name\": %s,\n", w->name[0] == '$' ? "1" : "0"); f << stringf(" \"bits\": %s,\n", get_bits(w).c_str()); + if (w->start_offset) + f << stringf(" \"offset\": %d,\n", w->start_offset); + if (w->upto) + f << stringf(" \"upto\": 1,\n"); f << stringf(" \"attributes\": {"); write_parameters(w->attributes); f << stringf("\n }\n"); @@ -250,7 +283,7 @@ struct JsonWriter struct JsonBackend : public Backend { JsonBackend() : Backend("json", "write design to a JSON file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -332,12 +365,13 @@ struct JsonBackend : public Backend { log("Module and cell ports and nets can be single bit wide or vectors of multiple\n"); log("bits. Each individual signal bit is assigned a unique integer. The <bit_vector>\n"); log("values referenced above are vectors of this integers. Signal bits that are\n"); - log("connected to a constant driver are denoted as string \"0\" or \"1\" instead of\n"); - log("a number.\n"); + log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n"); + log("\"z\" instead of a number.\n"); log("\n"); - log("Numeric parameter and attribute values up to 32 bits are written as decimal\n"); - log("values. Numbers larger than that are written as string holding the binary\n"); - log("representation of the value.\n"); + log("Numeric 32-bit parameter and attribute values are written as decimal values.\n"); + log("Bit verctors of different sizes, or ones containing 'x' or 'z' bits, are written\n"); + log("as string holding the binary representation of the value. Strings are written\n"); + log("as strings, with an appended blank in cases of strings of the form /[01xz]* */.\n"); log("\n"); log("For example the following Verilog code:\n"); log("\n"); @@ -458,7 +492,7 @@ struct JsonBackend : public Backend { log("format. A program processing this format must ignore all unknown fields.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool aig_mode = false; @@ -482,7 +516,7 @@ struct JsonBackend : public Backend { struct JsonPass : public Pass { JsonPass() : Pass("json", "write design in JSON format") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -499,7 +533,7 @@ struct JsonPass : public Pass { log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } - virtual void execute(std::vector<std::string> args, RTLIL::Design *design) + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string filename; bool aig_mode = false; @@ -523,6 +557,7 @@ struct JsonPass : public Pass { std::stringstream buf; if (!filename.empty()) { + rewrite_filename(filename); std::ofstream *ff = new std::ofstream; ff->open(filename.c_str(), std::ofstream::trunc); if (ff->fail()) { diff --git a/backends/protobuf/.gitignore b/backends/protobuf/.gitignore new file mode 100644 index 000000000..849b38d45 --- /dev/null +++ b/backends/protobuf/.gitignore @@ -0,0 +1,2 @@ +yosys.pb.cc +yosys.pb.h diff --git a/backends/protobuf/Makefile.inc b/backends/protobuf/Makefile.inc new file mode 100644 index 000000000..834cad42c --- /dev/null +++ b/backends/protobuf/Makefile.inc @@ -0,0 +1,8 @@ +ifeq ($(ENABLE_PROTOBUF),1) + +backends/protobuf/yosys.pb.cc backends/protobuf/yosys.pb.h: misc/yosys.proto + $(Q) cd misc && protoc --cpp_out "../backends/protobuf" yosys.proto + +OBJS += backends/protobuf/protobuf.o backends/protobuf/yosys.pb.o + +endif diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc new file mode 100644 index 000000000..671686173 --- /dev/null +++ b/backends/protobuf/protobuf.cc @@ -0,0 +1,371 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <google/protobuf/text_format.h> + +#include "kernel/rtlil.h" +#include "kernel/register.h" +#include "kernel/sigtools.h" +#include "kernel/celltypes.h" +#include "kernel/cellaigs.h" +#include "kernel/log.h" +#include "yosys.pb.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct ProtobufDesignSerializer +{ + bool aig_mode_; + bool use_selection_; + yosys::pb::Design *pb_; + + Design *design_; + Module *module_; + + SigMap sigmap_; + int sigidcounter_; + dict<SigBit, uint64_t> sigids_; + pool<Aig> aig_models_; + + + ProtobufDesignSerializer(bool use_selection, bool aig_mode) : + aig_mode_(aig_mode), use_selection_(use_selection) { } + + string get_name(IdString name) + { + return RTLIL::unescape_id(name); + } + + + void serialize_parameters(google::protobuf::Map<std::string, yosys::pb::Parameter> *out, + const dict<IdString, Const> ¶meters) + { + for (auto ¶m : parameters) { + std::string key = get_name(param.first); + + + yosys::pb::Parameter pb_param; + + if ((param.second.flags & RTLIL::ConstFlags::CONST_FLAG_STRING) != 0) { + pb_param.set_str(param.second.decode_string()); + } else if (GetSize(param.second.bits) > 64) { + pb_param.set_str(param.second.as_string()); + } else { + pb_param.set_int_(param.second.as_int()); + } + + (*out)[key] = pb_param; + } + } + + void get_bits(yosys::pb::BitVector *out, SigSpec sig) + { + for (auto bit : sigmap_(sig)) { + auto sig = out->add_signal(); + + // Constant driver. + if (bit.wire == nullptr) { + if (bit == State::S0) sig->set_constant(sig->CONSTANT_DRIVER_LOW); + else if (bit == State::S1) sig->set_constant(sig->CONSTANT_DRIVER_HIGH); + else if (bit == State::Sz) sig->set_constant(sig->CONSTANT_DRIVER_Z); + else sig->set_constant(sig->CONSTANT_DRIVER_X); + continue; + } + + // Signal - give it a unique identifier. + if (sigids_.count(bit) == 0) { + sigids_[bit] = sigidcounter_++; + } + sig->set_id(sigids_[bit]); + } + } + + void serialize_module(yosys::pb::Module* out, Module *module) + { + module_ = module; + log_assert(module_->design == design_); + sigmap_.set(module_); + sigids_.clear(); + sigidcounter_ = 0; + + serialize_parameters(out->mutable_attribute(), module_->attributes); + + for (auto n : module_->ports) { + Wire *w = module->wire(n); + if (use_selection_ && !module_->selected(w)) + continue; + + yosys::pb::Module::Port pb_port; + pb_port.set_direction(w->port_input ? w->port_output ? + yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT : yosys::pb::DIRECTION_OUTPUT); + get_bits(pb_port.mutable_bits(), w); + (*out->mutable_port())[get_name(n)] = pb_port; + } + + for (auto c : module_->cells()) { + if (use_selection_ && !module_->selected(c)) + continue; + + yosys::pb::Module::Cell pb_cell; + pb_cell.set_hide_name(c->name[0] == '$'); + pb_cell.set_type(get_name(c->type)); + + if (aig_mode_) { + Aig aig(c); + if (aig.name.empty()) + continue; + pb_cell.set_model(aig.name); + aig_models_.insert(aig); + } + serialize_parameters(pb_cell.mutable_parameter(), c->parameters); + serialize_parameters(pb_cell.mutable_attribute(), c->attributes); + + if (c->known()) { + for (auto &conn : c->connections()) { + yosys::pb::Direction direction = yosys::pb::DIRECTION_OUTPUT; + if (c->input(conn.first)) + direction = c->output(conn.first) ? yosys::pb::DIRECTION_INOUT : yosys::pb::DIRECTION_INPUT; + (*pb_cell.mutable_port_direction())[get_name(conn.first)] = direction; + } + } + for (auto &conn : c->connections()) { + yosys::pb::BitVector vec; + get_bits(&vec, conn.second); + (*pb_cell.mutable_connection())[get_name(conn.first)] = vec; + } + + (*out->mutable_cell())[get_name(c->name)] = pb_cell; + } + + for (auto w : module_->wires()) { + if (use_selection_ && !module_->selected(w)) + continue; + + auto netname = out->add_netname(); + netname->set_hide_name(w->name[0] == '$'); + get_bits(netname->mutable_bits(), w); + serialize_parameters(netname->mutable_attributes(), w->attributes); + } + } + + + void serialize_models(google::protobuf::Map<string, yosys::pb::Model> *models) + { + for (auto &aig : aig_models_) { + yosys::pb::Model pb_model; + for (auto &node : aig.nodes) { + auto pb_node = pb_model.add_node(); + if (node.portbit >= 0) { + if (node.inverter) { + pb_node->set_type(pb_node->TYPE_NPORT); + } else { + pb_node->set_type(pb_node->TYPE_PORT); + } + auto port = pb_node->mutable_port(); + port->set_portname(log_id(node.portname)); + port->set_bitindex(node.portbit); + } else if (node.left_parent < 0 && node.right_parent < 0) { + if (node.inverter) { + pb_node->set_type(pb_node->TYPE_TRUE); + } else { + pb_node->set_type(pb_node->TYPE_FALSE); + } + } else { + if (node.inverter) { + pb_node->set_type(pb_node->TYPE_NAND); + } else { + pb_node->set_type(pb_node->TYPE_AND); + } + auto gate = pb_node->mutable_gate(); + gate->set_left(node.left_parent); + gate->set_right(node.right_parent); + } + for (auto &op : node.outports) { + auto pb_op = pb_node->add_out_port(); + pb_op->set_name(log_id(op.first)); + pb_op->set_bit_index(op.second); + } + } + (*models)[aig.name] = pb_model; + } + } + + void serialize_design(yosys::pb::Design *pb, Design *design) + { + GOOGLE_PROTOBUF_VERIFY_VERSION; + pb_ = pb; + pb_->Clear(); + pb_->set_creator(yosys_version_str); + + design_ = design; + design_->sort(); + + auto modules = use_selection_ ? design_->selected_modules() : design_->modules(); + for (auto mod : modules) { + yosys::pb::Module pb_mod; + serialize_module(&pb_mod, mod); + (*pb->mutable_modules())[mod->name.str()] = pb_mod; + } + + serialize_models(pb_->mutable_models()); + } +}; + +struct ProtobufBackend : public Backend { + ProtobufBackend(): Backend("protobuf", "write design to a Protocol Buffer file") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" write_protobuf [options] [filename]\n"); + log("\n"); + log("Write a JSON netlist of the current design.\n"); + log("\n"); + log(" -aig\n"); + log(" include AIG models for the different gate types\n"); + log("\n"); + log(" -text\n"); + log(" output protobuf in Text/ASCII representation\n"); + log("\n"); + log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n"); + log("Yosys source code distribution.\n"); + log("\n"); + } + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + bool aig_mode = false; + bool text_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + if (args[argidx] == "-aig") { + aig_mode = true; + continue; + } + if (args[argidx] == "-text") { + text_mode = true; + continue; + } + break; + } + extra_args(f, filename, args, argidx, !text_mode); + + log_header(design, "Executing Protobuf backend.\n"); + + yosys::pb::Design pb; + ProtobufDesignSerializer serializer(false, aig_mode); + serializer.serialize_design(&pb, design); + + if (text_mode) { + string out; + google::protobuf::TextFormat::PrintToString(pb, &out); + *f << out; + } else { + pb.SerializeToOstream(f); + } + } +} ProtobufBackend; + +struct ProtobufPass : public Pass { + ProtobufPass() : Pass("protobuf", "write design in Protobuf format") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" protobuf [options] [selection]\n"); + log("\n"); + log("Write a JSON netlist of all selected objects.\n"); + log("\n"); + log(" -o <filename>\n"); + log(" write to the specified file.\n"); + log("\n"); + log(" -aig\n"); + log(" include AIG models for the different gate types\n"); + log("\n"); + log(" -text\n"); + log(" output protobuf in Text/ASCII representation\n"); + log("\n"); + log("The schema of the output Protocol Buffer is defined in misc/yosys.pb in the\n"); + log("Yosys source code distribution.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + std::string filename; + bool aig_mode = false; + bool text_mode = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-o" && argidx+1 < args.size()) { + filename = args[++argidx]; + continue; + } + if (args[argidx] == "-aig") { + aig_mode = true; + continue; + } + if (args[argidx] == "-text") { + text_mode = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + std::ostream *f; + std::stringstream buf; + + if (!filename.empty()) { + rewrite_filename(filename); + std::ofstream *ff = new std::ofstream; + ff->open(filename.c_str(), text_mode ? std::ofstream::trunc : (std::ofstream::trunc | std::ofstream::binary)); + if (ff->fail()) { + delete ff; + log_error("Can't open file `%s' for writing: %s\n", filename.c_str(), strerror(errno)); + } + f = ff; + } else { + f = &buf; + } + + yosys::pb::Design pb; + ProtobufDesignSerializer serializer(true, aig_mode); + serializer.serialize_design(&pb, design); + + if (text_mode) { + string out; + google::protobuf::TextFormat::PrintToString(pb, &out); + *f << out; + } else { + pb.SerializeToOstream(f); + } + + if (!filename.empty()) { + delete f; + } else { + log("%s", buf.str().c_str()); + } + } +} ProtobufPass; + +PRIVATE_NAMESPACE_END; diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc index c9656caff..54dbb84af 100644 --- a/backends/simplec/simplec.cc +++ b/backends/simplec/simplec.cc @@ -472,7 +472,7 @@ struct SimplecWorker return; } - if (cell->type == "$_MUX_") + if (cell->type.in("$_MUX_", "$_NMUX_")) { SigBit a = sigmaps.at(work->module)(cell->getPort("\\A")); SigBit b = sigmaps.at(work->module)(cell->getPort("\\B")); @@ -484,7 +484,9 @@ struct SimplecWorker string s_expr = s.wire ? util_get_bit(work->prefix + cid(s.wire->name), s.wire->width, s.offset) : s.data ? "1" : "0"; // casts to bool are a workaround for CBMC bug (https://github.com/diffblue/cbmc/issues/933) - string expr = stringf("%s ? (bool)%s : (bool)%s", s_expr.c_str(), b_expr.c_str(), a_expr.c_str()); + string expr = stringf("%s ? %s(bool)%s : %s(bool)%s", s_expr.c_str(), + cell->type == "$_NMUX_" ? "!" : "", b_expr.c_str(), + cell->type == "$_NMUX_" ? "!" : "", a_expr.c_str()); log_assert(y.wire); funct_declarations.push_back(util_set_bit(work->prefix + cid(y.wire->name), y.wire->width, y.offset, expr) + @@ -742,13 +744,13 @@ struct SimplecWorker struct SimplecBackend : public Backend { SimplecBackend() : Backend("simplec", "convert design to simple C code") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" write_simplec [options] [filename]\n"); log("\n"); - log("Write simple C code for simulating the design. The C code writen can be used to\n"); + log("Write simple C code for simulating the design. The C code written can be used to\n"); log("simulate the design in a C environment, but the purpose of this command is to\n"); log("generate code that works well with C-based formal verification.\n"); log("\n"); @@ -761,7 +763,7 @@ struct SimplecBackend : public Backend { log("THIS COMMAND IS UNDER CONSTRUCTION\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { reserved_cids.clear(); id2cid.clear(); diff --git a/backends/simplec/test00_uut.v b/backends/simplec/test00_uut.v index 744dbe9e3..92329a6f9 100644 --- a/backends/simplec/test00_uut.v +++ b/backends/simplec/test00_uut.v @@ -3,12 +3,12 @@ module test(input [31:0] a, b, c, output [31:0] x, y, z, w); unit_y unit_y_inst (.a(a), .b(b), .c(c), .y(y)); assign z = a ^ b ^ c, w = z; endmodule - + module unit_x(input [31:0] a, b, c, output [31:0] x); assign x = (a & b) | c; endmodule - + module unit_y(input [31:0] a, b, c, output [31:0] y); assign y = a & (b | c); endmodule - + diff --git a/backends/smt2/Makefile.inc b/backends/smt2/Makefile.inc index eacda2734..68394a909 100644 --- a/backends/smt2/Makefile.inc +++ b/backends/smt2/Makefile.inc @@ -3,14 +3,30 @@ OBJS += backends/smt2/smt2.o ifneq ($(CONFIG),mxe) ifneq ($(CONFIG),emcc) + +# MSYS targets support yosys-smtbmc, but require a launcher script +ifeq ($(CONFIG),$(filter $(CONFIG),msys2 msys2-64)) +TARGETS += yosys-smtbmc.exe yosys-smtbmc-script.py +# Needed to find the Python interpreter for yosys-smtbmc scripts. +# Override if necessary, it is only used for msys2 targets. +PYTHON := $(shell cygpath -w -m $(PREFIX)/bin/python3) + +yosys-smtbmc-script.py: backends/smt2/smtbmc.py + $(P) sed -e 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' \ + -e "s|#!/usr/bin/env python3|#!$(PYTHON)|" < $< > $@ + +yosys-smtbmc.exe: misc/launcher.c yosys-smtbmc-script.py + $(P) $(CXX) -DGUI=0 -O -s -o $@ $< +# Other targets +else TARGETS += yosys-smtbmc yosys-smtbmc: backends/smt2/smtbmc.py - $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(__file__) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new + $(P) sed 's|##yosys-sys-path##|sys.path += [os.path.dirname(os.path.realpath(__file__)) + p for p in ["/share/python3", "/../share/yosys/python3"]]|;' < $< > $@.new $(Q) chmod +x $@.new $(Q) mv $@.new $@ +endif $(eval $(call add_share_file,share/python3,backends/smt2/smtio.py)) endif endif - diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc index dce7c25de..081dcda99 100644 --- a/backends/smt2/smt2.cc +++ b/backends/smt2/smt2.cc @@ -32,15 +32,18 @@ struct Smt2Worker CellTypes ct; SigMap sigmap; RTLIL::Module *module; - bool bvmode, memmode, wiresmode, verbose, statebv, statedt; + bool bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode; dict<IdString, int> &mod_stbv_width; - int idcounter, statebv_width; + int idcounter = 0, statebv_width = 0; std::vector<std::string> decls, trans, hier, dtmembers; std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver; std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue; pool<Cell*> recursive_cells, registers; + pool<SigBit> clock_posedge, clock_negedge; + vector<string> ex_state_eq, ex_input_eq; + std::map<RTLIL::SigBit, std::pair<int, int>> fcache; std::map<Cell*, int> memarrays; std::map<int, int> bvsizes; @@ -104,16 +107,24 @@ struct Smt2Worker decls.push_back(decl_str + "\n"); } - Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, dict<IdString, int> &mod_stbv_width) : + Smt2Worker(RTLIL::Module *module, bool bvmode, bool memmode, bool wiresmode, bool verbose, bool statebv, bool statedt, bool forallmode, + dict<IdString, int> &mod_stbv_width, dict<IdString, dict<IdString, pair<bool, bool>>> &mod_clk_cache) : ct(module->design), sigmap(module), module(module), bvmode(bvmode), memmode(memmode), wiresmode(wiresmode), - verbose(verbose), statebv(statebv), statedt(statedt), mod_stbv_width(mod_stbv_width), idcounter(0), statebv_width(0) + verbose(verbose), statebv(statebv), statedt(statedt), forallmode(forallmode), mod_stbv_width(mod_stbv_width) { + pool<SigBit> noclock; + makebits(stringf("%s_is", get_id(module))); for (auto cell : module->cells()) - for (auto &conn : cell->connections()) { + for (auto &conn : cell->connections()) + { + if (GetSize(conn.second) == 0) + continue; + bool is_input = ct.cell_input(cell->type, conn.first); bool is_output = ct.cell_output(cell->type, conn.first); + if (is_output && !is_input) for (auto bit : sigmap(conn.second)) { if (bit_driver.count(bit)) @@ -123,6 +134,66 @@ struct Smt2Worker else if (is_output || !is_input) log_error("Unsupported or unknown directionality on port %s of cell %s.%s (%s).\n", log_id(conn.first), log_id(module), log_id(cell), log_id(cell->type)); + + if (cell->type.in("$mem") && conn.first.in("\\RD_CLK", "\\WR_CLK")) + { + SigSpec clk = sigmap(conn.second); + for (int i = 0; i < GetSize(clk); i++) + { + if (clk[i].wire == nullptr) + continue; + + if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_ENABLE" : "\\WR_CLK_ENABLE")[i] != State::S1) + continue; + + if (cell->getParam(conn.first == "\\RD_CLK" ? "\\RD_CLK_POLARITY" : "\\WR_CLK_POLARITY")[i] == State::S1) + clock_posedge.insert(clk[i]); + else + clock_negedge.insert(clk[i]); + } + } + else + if (cell->type.in("$dff", "$_DFF_P_", "$_DFF_N_") && conn.first.in("\\CLK", "\\C")) + { + bool posedge = (cell->type == "$_DFF_N_") || (cell->type == "$dff" && cell->getParam("\\CLK_POLARITY").as_bool()); + for (auto bit : sigmap(conn.second)) { + if (posedge) + clock_posedge.insert(bit); + else + clock_negedge.insert(bit); + } + } + else + if (mod_clk_cache.count(cell->type) && mod_clk_cache.at(cell->type).count(conn.first)) + { + for (auto bit : sigmap(conn.second)) { + if (mod_clk_cache.at(cell->type).at(conn.first).first) + clock_posedge.insert(bit); + if (mod_clk_cache.at(cell->type).at(conn.first).second) + clock_negedge.insert(bit); + } + } + else + { + for (auto bit : sigmap(conn.second)) + noclock.insert(bit); + } + } + + for (auto bit : noclock) { + clock_posedge.erase(bit); + clock_negedge.erase(bit); + } + + for (auto wire : module->wires()) + { + if (!wire->port_input || GetSize(wire) != 1) + continue; + SigBit bit = sigmap(wire); + if (clock_posedge.count(bit)) + mod_clk_cache[module->name][wire->name].first = true; + if (clock_negedge.count(bit)) + mod_clk_cache[module->name][wire->name].second = true; } } @@ -326,7 +397,8 @@ struct Smt2Worker if (type == 's' || type == 'd' || type == 'b') { width = max(width, GetSize(cell->getPort("\\A"))); - width = max(width, GetSize(cell->getPort("\\B"))); + if (cell->hasPort("\\B")) + width = max(width, GetSize(cell->getPort("\\B"))); } if (cell->hasPort("\\A")) { @@ -344,6 +416,7 @@ struct Smt2Worker for (char ch : expr) { if (ch == 'A') processed_expr += get_bv(sig_a); else if (ch == 'B') processed_expr += get_bv(sig_b); + else if (ch == 'P') processed_expr += get_bv(cell->getPort("\\B")); else if (ch == 'L') processed_expr += is_signed ? "a" : "l"; else if (ch == 'U') processed_expr += is_signed ? "s" : "u"; else processed_expr += ch; @@ -437,6 +510,7 @@ struct Smt2Worker if (cell->type == "$_ANDNOT_") return export_gate(cell, "(and A (not B))"); if (cell->type == "$_ORNOT_") return export_gate(cell, "(or A (not B))"); if (cell->type == "$_MUX_") return export_gate(cell, "(ite S B A)"); + if (cell->type == "$_NMUX_") return export_gate(cell, "(not (ite S B A))"); if (cell->type == "$_AOI3_") return export_gate(cell, "(not (or (and A B) C))"); if (cell->type == "$_OAI3_") return export_gate(cell, "(not (and (or A B) C))"); if (cell->type == "$_AOI4_") return export_gate(cell, "(not (or (and A B) (and C D)))"); @@ -455,14 +529,16 @@ struct Smt2Worker return; } - if (cell->type.in("$anyconst", "$anyseq")) + if (cell->type.in("$anyconst", "$anyseq", "$allconst", "$allseq")) { registers.insert(cell); string infostr = cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell); if (cell->attributes.count("\\reg")) infostr += " " + cell->attributes.at("\\reg").decode_string(); - decls.push_back(stringf("; yosys-smt2-%s %s#%d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, infostr.c_str())); + decls.push_back(stringf("; yosys-smt2-%s %s#%d %d %s\n", cell->type.c_str() + 1, get_id(module), idcounter, GetSize(cell->getPort("\\Y")), infostr.c_str())); makebits(stringf("%s#%d", get_id(module), idcounter), GetSize(cell->getPort("\\Y")), log_signal(cell->getPort("\\Y"))); + if (cell->type == "$anyseq") + ex_input_eq.push_back(stringf(" (= (|%s#%d| state) (|%s#%d| other_state))", get_id(module), idcounter, get_id(module), idcounter)); register_bv(cell->getPort("\\Y"), idcounter++); recursive_cells.erase(cell); return; @@ -480,7 +556,9 @@ struct Smt2Worker if (cell->type.in("$shift", "$shiftx")) { if (cell->getParam("\\B_SIGNED").as_bool()) { - /* FIXME */ + return export_bvop(cell, stringf("(ite (bvsge P #b%0*d) " + "(bvlshr A B) (bvlshr A (bvneg B)))", + GetSize(cell->getPort("\\B")), 0), 's'); } else { return export_bvop(cell, "(bvlshr A B)", 's'); } @@ -506,6 +584,13 @@ struct Smt2Worker if (cell->type == "$div") return export_bvop(cell, "(bvUdiv A B)", 'd'); if (cell->type == "$mod") return export_bvop(cell, "(bvUrem A B)", 'd'); + if (cell->type.in("$reduce_and", "$reduce_or", "$reduce_bool") && + 2*GetSize(cell->getPort("\\A").chunks()) < GetSize(cell->getPort("\\A"))) { + bool is_and = cell->type == "$reduce_and"; + string bits(GetSize(cell->getPort("\\A")), is_and ? '1' : '0'); + return export_bvop(cell, stringf("(%s A #b%s)", is_and ? "=" : "distinct", bits.c_str()), 'b'); + } + if (cell->type == "$reduce_and") return export_reduce(cell, "(and A)", true); if (cell->type == "$reduce_or") return export_reduce(cell, "(or A)", false); if (cell->type == "$reduce_xor") return export_reduce(cell, "(xor A)", false); @@ -516,7 +601,7 @@ struct Smt2Worker if (cell->type == "$logic_and") return export_reduce(cell, "(and (or A) (or B))", false); if (cell->type == "$logic_or") return export_reduce(cell, "(or A B)", false); - if (cell->type == "$mux" || cell->type == "$pmux") + if (cell->type.in("$mux", "$pmux")) { int width = GetSize(cell->getPort("\\Y")); std::string processed_expr = get_bv(cell->getPort("\\A")); @@ -554,17 +639,30 @@ struct Smt2Worker int rd_ports = cell->getParam("\\RD_PORTS").as_int(); int wr_ports = cell->getParam("\\WR_PORTS").as_int(); - decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d\n", get_id(cell), abits, width, rd_ports, wr_ports)); + bool async_read = false; + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) { + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_zero()) + log_error("Memory %s.%s has mixed clocked/nonclocked write ports. This is not supported by \"write_smt2\".\n", log_id(cell), log_id(module)); + async_read = true; + } + + decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(cell), abits, width, rd_ports, wr_ports, async_read ? "async" : "sync")); + + string memstate; + if (async_read) { + memstate = stringf("%s#%d#final", get_id(module), arrayid); + } else { + memstate = stringf("%s#%d#0", get_id(module), arrayid); + } if (statebv) { int mem_size = cell->getParam("\\SIZE").as_int(); int mem_offset = cell->getParam("\\OFFSET").as_int(); - makebits(stringf("%s#%d#0", get_id(module), arrayid), width*mem_size, get_id(cell)); - - decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d#0| state))\n", - get_id(module), get_id(cell), get_id(module), width*mem_size, get_id(module), arrayid)); + makebits(memstate, width*mem_size, get_id(cell)); + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (_ BitVec %d) (|%s| state))\n", + get_id(module), get_id(cell), get_id(module), width*mem_size, memstate.c_str())); for (int i = 0; i < rd_ports; i++) { @@ -584,9 +682,9 @@ struct Smt2Worker read_expr += "0"; for (int k = 0; k < mem_size; k++) - read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s#%d#0| state))\n %s)", + read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)", get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(), - width*(k+1)-1, width*k, get_id(module), arrayid, read_expr.c_str()); + width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str()); decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d)\n %s) ; %s\n", get_id(module), idcounter, get_id(module), width, read_expr.c_str(), log_signal(data_sig))); @@ -600,14 +698,14 @@ struct Smt2Worker else { if (statedt) - dtmembers.push_back(stringf(" (|%s#%d#0| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", - get_id(module), arrayid, abits, width, get_id(cell))); + dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + memstate.c_str(), abits, width, get_id(cell))); else - decls.push_back(stringf("(declare-fun |%s#%d#0| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", - get_id(module), arrayid, get_id(module), abits, width, get_id(cell))); + decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + memstate.c_str(), get_id(module), abits, width, get_id(cell))); - decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s#%d#0| state))\n", - get_id(module), get_id(cell), get_id(module), abits, width, get_id(module), arrayid)); + decls.push_back(stringf("(define-fun |%s_m %s| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) (|%s| state))\n", + get_id(module), get_id(cell), get_id(module), abits, width, memstate.c_str())); for (int i = 0; i < rd_ports; i++) { @@ -622,8 +720,8 @@ struct Smt2Worker decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n", get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig))); - decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s#%d#0| state) (|%s_m:R%dA %s| state))) ; %s\n", - get_id(module), idcounter, get_id(module), width, get_id(module), arrayid, get_id(module), i, get_id(cell), log_signal(data_sig))); + decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n", + get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig))); decls.push_back(stringf("(define-fun |%s_m:R%dD %s| ((state |%s_s|)) (_ BitVec %d) (|%s#%d| state))\n", get_id(module), i, get_id(cell), get_id(module), width, get_id(module), idcounter)); @@ -646,6 +744,9 @@ struct Smt2Worker for (auto &conn : cell->connections()) { + if (GetSize(conn.second) == 0) + continue; + Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); @@ -669,7 +770,7 @@ struct Smt2Worker if (statebv) makebits(stringf("%s_h %s", get_id(module), get_id(cell->name)), mod_stbv_width.at(cell->type)); - if (statedt) + else if (statedt) dtmembers.push_back(stringf(" (|%s_h %s| |%s_s|)\n", get_id(module), get_id(cell->name), get_id(cell->type))); else @@ -713,17 +814,30 @@ struct Smt2Worker decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width)); if (wire->get_bool_attribute("\\keep") || (wiresmode && wire->name[0] == '\\')) decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width)); + if (GetSize(wire) == 1 && (clock_posedge.count(sig) || clock_negedge.count(sig))) + decls.push_back(stringf("; yosys-smt2-clock %s%s%s\n", get_id(wire), + clock_posedge.count(sig) ? " posedge" : "", clock_negedge.count(sig) ? " negedge" : "")); if (bvmode && GetSize(sig) > 1) { decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) (_ BitVec %d) %s)\n", get_id(module), get_id(wire), get_id(module), GetSize(sig), get_bv(sig).c_str())); + if (wire->port_input) + ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", + get_id(module), get_id(wire), get_id(module), get_id(wire))); } else { for (int i = 0; i < GetSize(sig); i++) - if (GetSize(sig) > 1) + if (GetSize(sig) > 1) { decls.push_back(stringf("(define-fun |%s_n %s %d| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), i, get_id(module), get_bool(sig[i]).c_str())); - else + if (wire->port_input) + ex_input_eq.push_back(stringf(" (= (|%s_n %s %d| state) (|%s_n %s %d| other_state))", + get_id(module), get_id(wire), i, get_id(module), get_id(wire), i)); + } else { decls.push_back(stringf("(define-fun |%s_n %s| ((state |%s_s|)) Bool %s)\n", get_id(module), get_id(wire), get_id(module), get_bool(sig[i]).c_str())); + if (wire->port_input) + ex_input_eq.push_back(stringf(" (= (|%s_n %s| state) (|%s_n %s| other_state))", + get_id(module), get_id(wire), get_id(module), get_id(wire))); + } } } } @@ -775,8 +889,8 @@ struct Smt2Worker string name_a = get_bool(cell->getPort("\\A")); string name_en = get_bool(cell->getPort("\\EN")); - decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, - cell->attributes.count("\\src") ? cell->attributes.at("\\src").decode_string().c_str() : get_id(cell))); + string infostr = (cell->name[0] == '$' && cell->attributes.count("\\src")) ? cell->attributes.at("\\src").decode_string() : get_id(cell); + decls.push_back(stringf("; yosys-smt2-%s %d %s\n", cell->type.c_str() + 1, id, infostr.c_str())); if (cell->type == "$cover") decls.push_back(stringf("(define-fun |%s_%c %d| ((state |%s_s|)) Bool (and %s %s)) ; %s\n", @@ -811,8 +925,14 @@ struct Smt2Worker Module *m = module->design->module(cell->type); log_assert(m != nullptr); + hier.push_back(stringf(" (= (|%s_is| state) (|%s_is| %s))\n", + get_id(module), get_id(cell->type), cell_state.c_str())); + for (auto &conn : cell->connections()) { + if (GetSize(conn.second) == 0) + continue; + Wire *w = m->wire(conn.first); SigSpec sig = sigmap(conn.second); @@ -842,6 +962,7 @@ struct Smt2Worker std::string expr_d = get_bool(cell->getPort("\\D")); std::string expr_q = get_bool(cell->getPort("\\Q"), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q")))); + ex_state_eq.push_back(stringf("(= %s %s)", get_bool(cell->getPort("\\Q")).c_str(), get_bool(cell->getPort("\\Q"), "other_state").c_str())); } if (cell->type.in("$ff", "$dff")) @@ -849,13 +970,16 @@ struct Smt2Worker std::string expr_d = get_bv(cell->getPort("\\D")); std::string expr_q = get_bv(cell->getPort("\\Q"), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Q")))); + ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Q")).c_str(), get_bv(cell->getPort("\\Q"), "other_state").c_str())); } - if (cell->type == "$anyconst") + if (cell->type.in("$anyconst", "$allconst")) { std::string expr_d = get_bv(cell->getPort("\\Y")); std::string expr_q = get_bv(cell->getPort("\\Y"), "next_state"); trans.push_back(stringf(" (= %s %s) ; %s %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell), log_signal(cell->getPort("\\Y")))); + if (cell->type == "$anyconst") + ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort("\\Y")).c_str(), get_bv(cell->getPort("\\Y"), "other_state").c_str())); } if (cell->type == "$mem") @@ -866,11 +990,25 @@ struct Smt2Worker int width = cell->getParam("\\WIDTH").as_int(); int wr_ports = cell->getParam("\\WR_PORTS").as_int(); + bool async_read = false; + string initial_memstate, final_memstate; + + if (!cell->getParam("\\WR_CLK_ENABLE").is_fully_ones()) { + log_assert(cell->getParam("\\WR_CLK_ENABLE").is_fully_zero()); + async_read = true; + initial_memstate = stringf("%s#%d#0", get_id(module), arrayid); + final_memstate = stringf("%s#%d#final", get_id(module), arrayid); + } + if (statebv) { int mem_size = cell->getParam("\\SIZE").as_int(); int mem_offset = cell->getParam("\\OFFSET").as_int(); + if (async_read) { + makebits(final_memstate, width*mem_size, get_id(cell)); + } + for (int i = 0; i < wr_ports; i++) { SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits); @@ -909,6 +1047,15 @@ struct Smt2Worker } else { + if (async_read) { + if (statedt) + dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + initial_memstate.c_str(), abits, width, get_id(cell))); + else + decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n", + initial_memstate.c_str(), get_id(module), abits, width, get_id(cell))); + } + for (int i = 0; i < wr_ports; i++) { SigSpec addr_sig = cell->getPort("\\WR_ADDR").extract(abits*i, abits); @@ -944,6 +1091,10 @@ struct Smt2Worker std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, wr_ports); std::string expr_q = stringf("(|%s#%d#0| next_state)", get_id(module), arrayid); trans.push_back(stringf(" (= %s %s) ; %s\n", expr_d.c_str(), expr_q.c_str(), get_id(cell))); + ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid)); + + if (async_read) + hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(cell))); Const init_data = cell->getParam("\\INIT"); int memsize = cell->getParam("\\SIZE").as_int(); @@ -954,20 +1105,27 @@ struct Smt2Worker break; Const initword = init_data.extract(i*width, width, State::Sx); + Const initmask = initword; bool gen_init_constr = false; - for (auto bit : initword.bits) - if (bit == State::S0 || bit == State::S1) + for (int k = 0; k < GetSize(initword); k++) { + if (initword[k] == State::S0 || initword[k] == State::S1) { gen_init_constr = true; + initmask[k] = State::S1; + } else { + initmask[k] = State::S0; + initword[k] = State::S0; + } + } if (gen_init_constr) { if (statebv) /* FIXME */; else - init_list.push_back(stringf("(= (select (|%s#%d#0| state) #b%s) #b%s) ; %s[%d]", + init_list.push_back(stringf("(= (bvand (select (|%s#%d#0| state) #b%s) #b%s) #b%s) ; %s[%d]", get_id(module), arrayid, Const(i, abits).as_string().c_str(), - initword.as_string().c_str(), get_id(cell), i)); + initmask.as_string().c_str(), initword.as_string().c_str(), get_id(cell), i)); } } } @@ -983,6 +1141,37 @@ struct Smt2Worker hier.push_back(stringf(" (|%s_h| (|%s_h %s| state))\n", get_id(c->type), get_id(module), get_id(c->name))); trans.push_back(stringf(" (|%s_t| (|%s_h %s| state) (|%s_h %s| next_state))\n", get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); + ex_state_eq.push_back(stringf("(|%s_ex_state_eq| (|%s_h %s| state) (|%s_h %s| other_state))\n", + get_id(c->type), get_id(module), get_id(c->name), get_id(module), get_id(c->name))); + } + + if (forallmode) + { + string expr = ex_state_eq.empty() ? "true" : "(and"; + if (!ex_state_eq.empty()) { + if (GetSize(ex_state_eq) == 1) { + expr = "\n " + ex_state_eq.front() + "\n"; + } else { + for (auto &str : ex_state_eq) + expr += stringf("\n %s", str.c_str()); + expr += "\n)"; + } + } + decls.push_back(stringf("(define-fun |%s_ex_state_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", + get_id(module), get_id(module), get_id(module), expr.c_str())); + + expr = ex_input_eq.empty() ? "true" : "(and"; + if (!ex_input_eq.empty()) { + if (GetSize(ex_input_eq) == 1) { + expr = "\n " + ex_input_eq.front() + "\n"; + } else { + for (auto &str : ex_input_eq) + expr += stringf("\n %s", str.c_str()); + expr += "\n)"; + } + } + decls.push_back(stringf("(define-fun |%s_ex_input_eq| ((state |%s_s|) (other_state |%s_s|)) Bool %s)\n", + get_id(module), get_id(module), get_id(module), expr.c_str())); } string assert_expr = assert_list.empty() ? "true" : "(and"; @@ -1073,7 +1262,7 @@ struct Smt2Worker struct Smt2Backend : public Backend { Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1229,10 +1418,11 @@ struct Smt2Backend : public Backend { log("from non-zero to zero in the test design.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::ifstream template_f; bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false; + bool forallmode = false; log_header(design, "Executing SMT2 backend.\n"); @@ -1286,7 +1476,7 @@ struct Smt2Backend : public Backend { int indent = 0; while (indent < GetSize(line) && (line[indent] == ' ' || line[indent] == '\t')) indent++; - if (line.substr(indent, 2) == "%%") + if (line.compare(indent, 2, "%%") == 0) break; *f << line << std::endl; } @@ -1336,17 +1526,30 @@ struct Smt2Backend : public Backend { } dict<IdString, int> mod_stbv_width; + dict<IdString, dict<IdString, pair<bool, bool>>> mod_clk_cache; Module *topmod = design->top_module(); std::string topmod_id; for (auto module : sorted_modules) + for (auto cell : module->cells()) + if (cell->type.in("$allconst", "$allseq")) + goto found_forall; + if (0) { + found_forall: + forallmode = true; + *f << stringf("; yosys-smt2-forall\n"); + if (!statebv && !statedt) + log_error("Forall-exists problems are only supported in -stbv or -stdt mode.\n"); + } + + for (auto module : sorted_modules) { - if (module->get_bool_attribute("\\blackbox") || module->has_memories_warn() || module->has_processes_warn()) + if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn()) continue; log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module)); - Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, mod_stbv_width); + Smt2Worker worker(module, bvmode, memmode, wiresmode, verbose, statebv, statedt, forallmode, mod_stbv_width, mod_clk_cache); worker.run(); worker.write(*f); diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py index c8151c266..3d6d3e1b3 100644 --- a/backends/smt2/smtbmc.py +++ b/backends/smt2/smtbmc.py @@ -32,6 +32,7 @@ cexfile = None aimfile = None aiwfile = None aigheader = True +btorwitfile = None vlogtbfile = None vlogtbtop = None inconstr = list() @@ -86,12 +87,15 @@ yosys-smtbmc [options] <yosys_smt2_output> --aig <aim_filename>:<aiw_filename> like above, but for map files and witness files that do not - share a filename prefix (or use differen file extensions). + share a filename prefix (or use different file extensions). --aig-noheader the AIGER witness file does not include the status and properties lines. + --btorwit <btor_witness_filename> + read a BTOR witness. + --noinfo only run the core proof, do not collect and print any additional information (e.g. which assert failed) @@ -99,8 +103,8 @@ yosys-smtbmc [options] <yosys_smt2_output> --presat check if the design with assumptions but without assertions is SAT before checking if assertions are UNSAT. This will - detect if there are contradicting assumtions. In some cases - this will also help to "warmup" the solver, potentially + detect if there are contradicting assumptions. In some cases + this will also help to "warm up" the solver, potentially yielding a speedup. --final-only @@ -145,14 +149,14 @@ yosys-smtbmc [options] <yosys_smt2_output> --append <num_steps> add <num_steps> time steps at the end of the trace when creating a counter example (this additional time - steps will still be constrained by assumtions) + steps will still be constrained by assumptions) """ + so.helpmsg()) sys.exit(1) try: opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:igcm:", so.longopts + - ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "presat", + ["final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "btorwit=", "presat", "dump-vcd=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=", "smtc-init", "smtc-top=", "noinit"]) except: @@ -189,6 +193,8 @@ for o, a in opts: aiwfile = a + ".aiw" elif o == "--aig-noheader": aigheader = False + elif o == "--btorwit": + btorwitfile = a elif o == "--dump-vcd": vcdfile = a elif o == "--dump-vlogtb": @@ -338,7 +344,7 @@ def get_constr_expr(db, state, final=False, getvalues=False): if state not in db: return ([], [], []) if getvalues else "true" - netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*)\](?=[ )]|$)') + netref_regex = re.compile(r'(^|[( ])\[(-?[0-9]+:|)([^\]]*|\S*)\](?=[ )]|$)') def replace_netref(match): state_sel = match.group(2) @@ -575,6 +581,103 @@ if aimfile is not None: num_steps = max(num_steps, step+1) step += 1 +if btorwitfile is not None: + with open(btorwitfile, "r") as f: + step = None + suffix = None + altsuffix = None + header_okay = False + + for line in f: + line = line.strip() + + if line == "sat": + header_okay = True + continue + + if not header_okay: + continue + + if line == "" or line[0] == "b" or line[0] == "j": + continue + + if line == ".": + break + + if line[0] == '#' or line[0] == '@': + step = int(line[1:]) + suffix = line + altsuffix = suffix + if suffix[0] == "@": + altsuffix = "#" + suffix[1:] + else: + altsuffix = "@" + suffix[1:] + continue + + line = line.split() + + if len(line) == 0: + continue + + if line[-1].endswith(suffix): + line[-1] = line[-1][0:len(line[-1]) - len(suffix)] + + if line[-1].endswith(altsuffix): + line[-1] = line[-1][0:len(line[-1]) - len(altsuffix)] + + if line[-1][0] == "$": + continue + + # BV assignments + if len(line) == 3 and line[1][0] != "[": + value = line[1] + name = line[2] + + path = smt.get_path(topmod, name) + + if not smt.net_exists(topmod, path): + continue + + width = smt.net_width(topmod, path) + + if width == 1: + assert value in ["0", "1"] + value = "true" if value == "1" else "false" + else: + value = "#b" + value + + smtexpr = "(= [%s] %s)" % (name, value) + constr_assumes[step].append((btorwitfile, smtexpr)) + + # Array assignments + if len(line) == 4 and line[1][0] == "[": + index = line[1] + value = line[2] + name = line[3] + + path = smt.get_path(topmod, name) + + if not smt.mem_exists(topmod, path): + continue + + meminfo = smt.mem_info(topmod, path) + + if meminfo[1] == 1: + assert value in ["0", "1"] + value = "true" if value == "1" else "false" + else: + value = "#b" + value + + assert index[0] == "[" + assert index[-1] == "]" + index = "#b" + index[1:-1] + + smtexpr = "(= (select [%s] %s) %s)" % (name, index, value) + constr_assumes[step].append((btorwitfile, smtexpr)) + + skip_steps = step + num_steps = step+1 + def write_vcd_trace(steps_start, steps_stop, index): filename = vcdfile.replace("%", index) print_msg("Writing trace to VCD file: %s" % (filename)) @@ -589,12 +692,16 @@ def write_vcd_trace(steps_start, steps_stop, index): if n.startswith("$"): hidden_net = True if not hidden_net: - vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath)) + edge = smt.net_clock(topmod, netpath) + if edge is None: + vcd.add_net([topmod] + netpath, smt.net_width(topmod, netpath)) + else: + vcd.add_clock([topmod] + netpath, edge) path_list.append(netpath) mem_trace_data = dict() for mempath in sorted(smt.hiermems(topmod)): - abits, width, rports, wports = smt.mem_info(topmod, mempath) + abits, width, rports, wports, asyncwr = smt.mem_info(topmod, mempath) expr_id = list() expr_list = list() @@ -644,6 +751,9 @@ def write_vcd_trace(steps_start, steps_stop, index): data = ["x"] * width gotread = False + if len(wdata) == 0 and len(rdata) != 0: + wdata = [[]] * len(rdata) + assert len(rdata) == len(wdata) for i in range(len(wdata)): @@ -663,7 +773,8 @@ def write_vcd_trace(steps_start, steps_stop, index): else: buf[k] = tdata[i][k] - tdata.append(data[:]) + if not asyncwr: + tdata.append(data[:]) for j_data in wdata[i]: if j_data["A"] != addr: @@ -676,6 +787,9 @@ def write_vcd_trace(steps_start, steps_stop, index): if M[k] == "1": data[k] = D[k] + if asyncwr: + tdata.append(data[:]) + assert len(tdata) == len(rdata) netpath = mempath[:] @@ -708,9 +822,12 @@ def write_vlogtb_trace(steps_start, steps_stop, index): if vlogtbtop is not None: for item in vlogtbtop.split("."): - assert item in smt.modinfo[vlogtb_topmod].cells - vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state) - vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item] + if item in smt.modinfo[vlogtb_topmod].cells: + vlogtb_state = "(|%s_h %s| %s)" % (vlogtb_topmod, item, vlogtb_state) + vlogtb_topmod = smt.modinfo[vlogtb_topmod].cells[item] + else: + print_msg("Vlog top module '%s' not found: no cell '%s' in module '%s'" % (vlogtbtop, item, vlogtb_topmod)) + break with open(filename, "w") as f: print("`ifndef VERILATOR", file=f) @@ -782,7 +899,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index): mems = sorted(smt.hiermems(vlogtb_topmod)) for mempath in mems: - abits, width, rports, wports = smt.mem_info(vlogtb_topmod, mempath) + abits, width, rports, wports, asyncwr = smt.mem_info(vlogtb_topmod, mempath) addr_expr_list = list() data_expr_list = list() @@ -885,7 +1002,7 @@ def write_constr_trace(steps_start, steps_stop, index): mems = sorted(smt.hiermems(constr_topmod)) for mempath in mems: - abits, width, rports, wports = smt.mem_info(constr_topmod, mempath) + abits, width, rports, wports, asyncwr = smt.mem_info(constr_topmod, mempath) addr_expr_list = list() data_expr_list = list() @@ -972,7 +1089,7 @@ def print_anyconsts_worker(mod, state, path): for fun, info in smt.modinfo[mod].anyconsts.items(): if info[1] is None: - print_msg("Value for anyconst in %s (%s): %d" % (path, info, smt.bv2int(smt.get("(|%s| %s)" % (fun, state))))) + print_msg("Value for anyconst in %s (%s): %d" % (path, info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state))))) else: print_msg("Value for anyconst %s.%s (%s): %d" % (path, info[1], info[0], smt.bv2int(smt.get("(|%s| %s)" % (fun, state))))) @@ -999,24 +1116,166 @@ def get_cover_list(mod, base): return cover_expr, cover_desc +states = list() +asserts_antecedent_cache = [list()] +asserts_consequent_cache = [list()] +asserts_cache_dirty = False + +def smt_state(step): + smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod)) + states.append("s%d" % step) + +def smt_assert(expr): + if expr == "true": + return + + smt.write("(assert %s)" % expr) + +def smt_assert_antecedent(expr): + if expr == "true": + return + + smt.write("(assert %s)" % expr) + + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_antecedent_cache[-1].append(expr) + +def smt_assert_consequent(expr): + if expr == "true": + return + + smt.write("(assert %s)" % expr) + + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_consequent_cache[-1].append(expr) + +def smt_forall_assert(): + if not smt.forall: + return + + global asserts_cache_dirty + asserts_cache_dirty = False + + def make_assert_expr(asserts_cache): + expr = list() + for lst in asserts_cache: + expr += lst + + assert len(expr) != 0 + + if len(expr) == 1: + expr = expr[0] + else: + expr = "(and %s)" % (" ".join(expr)) + return expr + + antecedent_expr = make_assert_expr(asserts_antecedent_cache) + consequent_expr = make_assert_expr(asserts_consequent_cache) + + states_db = set(states) + used_states_db = set() + new_antecedent_expr = list() + new_consequent_expr = list() + assert_expr = list() + + def make_new_expr(new_expr, expr): + cursor = 0 + while cursor < len(expr): + l = 1 + if expr[cursor] in '|"': + while cursor+l+1 < len(expr) and expr[cursor] != expr[cursor+l]: + l += 1 + l += 1 + elif expr[cursor] not in '() ': + while cursor+l < len(expr) and expr[cursor+l] not in '|"() ': + l += 1 + + word = expr[cursor:cursor+l] + if word in states_db: + used_states_db.add(word) + word += "_" + + new_expr.append(word) + cursor += l + + make_new_expr(new_antecedent_expr, antecedent_expr) + make_new_expr(new_consequent_expr, consequent_expr) + + new_antecedent_expr = ["".join(new_antecedent_expr)] + new_consequent_expr = ["".join(new_consequent_expr)] + + if states[0] in used_states_db: + new_antecedent_expr.append("(|%s_ex_state_eq| %s %s_)" % (topmod, states[0], states[0])) + for s in states: + if s in used_states_db: + new_antecedent_expr.append("(|%s_ex_input_eq| %s %s_)" % (topmod, s, s)) + + if len(new_antecedent_expr) == 0: + new_antecedent_expr = "true" + elif len(new_antecedent_expr) == 1: + new_antecedent_expr = new_antecedent_expr[0] + else: + new_antecedent_expr = "(and %s)" % (" ".join(new_antecedent_expr)) + + if len(new_consequent_expr) == 0: + new_consequent_expr = "true" + elif len(new_consequent_expr) == 1: + new_consequent_expr = new_consequent_expr[0] + else: + new_consequent_expr = "(and %s)" % (" ".join(new_consequent_expr)) + + assert_expr.append("(assert (forall (") + first_state = True + for s in states: + if s in used_states_db: + assert_expr.append("%s(%s_ |%s_s|)" % ("" if first_state else " ", s, topmod)) + first_state = False + assert_expr.append(") (=> %s %s)))" % (new_antecedent_expr, new_consequent_expr)) + + smt.write("".join(assert_expr)) + +def smt_push(): + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_antecedent_cache.append(list()) + asserts_consequent_cache.append(list()) + smt.write("(push 1)") + +def smt_pop(): + global asserts_cache_dirty + asserts_cache_dirty = True + asserts_antecedent_cache.pop() + asserts_consequent_cache.pop() + smt.write("(pop 1)") + +def smt_check_sat(): + if asserts_cache_dirty: + smt_forall_assert() + return smt.check_sat() if tempind: - retstatus = False + retstatus = "FAILED" skip_counter = step_size for step in range(num_steps, -1, -1): - smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod)) - smt.write("(assert (|%s_u| s%d))" % (topmod, step)) - smt.write("(assert (|%s_h| s%d))" % (topmod, step)) - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step)) - smt.write("(assert %s)" % get_constr_expr(constr_assumes, step)) + if smt.forall: + print_msg("Temporal induction not supported for exists-forall problems.") + break + + smt_state(step) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) + smt_assert_consequent(get_constr_expr(constr_assumes, step)) if step == num_steps: - smt.write("(assert (not (and (|%s_a| s%d) %s)))" % (topmod, step, get_constr_expr(constr_asserts, step))) + smt_assert("(not (and (|%s_a| s%d) %s))" % (topmod, step, get_constr_expr(constr_asserts, step))) else: - smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step, step+1)) - smt.write("(assert (|%s_a| s%d))" % (topmod, step)) - smt.write("(assert %s)" % get_constr_expr(constr_asserts, step)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step, step+1)) + smt_assert("(|%s_a| s%d)" % (topmod, step)) + smt_assert(get_constr_expr(constr_asserts, step)) if step > num_steps-skip_steps: print_msg("Skipping induction in step %d.." % (step)) @@ -1030,9 +1289,9 @@ if tempind: skip_counter = 0 print_msg("Trying induction in step %d.." % (step)) - if smt.check_sat() == "sat": + if smt_check_sat() == "sat": if step == 0: - print("%s Temporal induction failed!" % smt.timestamp()) + print_msg("Temporal induction failed!") print_anyconsts(num_steps) print_failed_asserts(num_steps) write_trace(step, num_steps+1, '%') @@ -1043,8 +1302,8 @@ if tempind: write_trace(step, num_steps+1, "%d" % step) else: - print("%s Temporal induction successful." % smt.timestamp()) - retstatus = True + print_msg("Temporal induction successful.") + retstatus = "PASSED" break elif covermode: @@ -1062,48 +1321,52 @@ elif covermode: smt.write("(define-fun covers_0 ((state |%s_s|)) (_ BitVec %d) %s)" % (topmod, len(cover_desc), cover_expr)) step = 0 - retstatus = False + retstatus = "FAILED" found_failed_assert = False assert step_size == 1 while step < num_steps: - smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod)) - smt.write("(assert (|%s_u| s%d))" % (topmod, step)) - smt.write("(assert (|%s_h| s%d))" % (topmod, step)) - smt.write("(assert %s)" % get_constr_expr(constr_assumes, step)) + smt_state(step) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) + smt_assert_consequent(get_constr_expr(constr_assumes, step)) if step == 0: if noinit: - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) else: - smt.write("(assert (|%s_i| s0))" % (topmod)) - smt.write("(assert (|%s_is| s0))" % (topmod)) + smt_assert_antecedent("(|%s_i| s0)" % (topmod)) + smt_assert_antecedent("(|%s_is| s0)" % (topmod)) else: - smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step)) - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) while "1" in cover_mask: print_msg("Checking cover reachability in step %d.." % (step)) - smt.write("(push 1)") - smt.write("(assert (distinct (covers_%d s%d) #b%s))" % (coveridx, step, "0" * len(cover_desc))) + smt_push() + smt_assert("(distinct (covers_%d s%d) #b%s)" % (coveridx, step, "0" * len(cover_desc))) - if smt.check_sat() == "unsat": - smt.write("(pop 1)") + if smt_check_sat() == "unsat": + smt_pop() break if append_steps > 0: for i in range(step+1, step+1+append_steps): print_msg("Appending additional step %d." % i) - smt.write("(declare-fun s%d () |%s_s|)" % (i, topmod)) - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, i)) - smt.write("(assert (|%s_u| s%d))" % (topmod, i)) - smt.write("(assert (|%s_h| s%d))" % (topmod, i)) - smt.write("(assert (|%s_t| s%d s%d))" % (topmod, i-1, i)) - smt.write("(assert %s)" % get_constr_expr(constr_assumes, i)) + smt_state(i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) + smt_assert_consequent(get_constr_expr(constr_assumes, i)) print_msg("Re-solving with appended steps..") - assert smt.check_sat() == "sat" + if smt_check_sat() == "unsat": + print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) + found_failed_assert = True + retstatus = "FAILED" + break reached_covers = smt.bv2bin(smt.get("(covers_%d s%d)" % (coveridx, step))) assert len(reached_covers) == len(cover_desc) @@ -1130,14 +1393,14 @@ elif covermode: break coveridx += 1 - smt.write("(pop 1)") + smt_pop() smt.write("(define-fun covers_%d ((state |%s_s|)) (_ BitVec %d) (bvand (covers_%d state) #b%s))" % (coveridx, topmod, len(cover_desc), coveridx-1, cover_mask)) if found_failed_assert: break if "1" not in cover_mask: - retstatus = True + retstatus = "PASSED" break step += 1 @@ -1149,29 +1412,29 @@ elif covermode: else: # not tempind, covermode step = 0 - retstatus = True + retstatus = "PASSED" while step < num_steps: - smt.write("(declare-fun s%d () |%s_s|)" % (step, topmod)) - smt.write("(assert (|%s_u| s%d))" % (topmod, step)) - smt.write("(assert (|%s_h| s%d))" % (topmod, step)) - smt.write("(assert %s)" % get_constr_expr(constr_assumes, step)) + smt_state(step) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step)) + smt_assert_consequent(get_constr_expr(constr_assumes, step)) if step == 0: if noinit: - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) else: - smt.write("(assert (|%s_i| s0))" % (topmod)) - smt.write("(assert (|%s_is| s0))" % (topmod)) + smt_assert_antecedent("(|%s_i| s0)" % (topmod)) + smt_assert_antecedent("(|%s_is| s0)" % (topmod)) else: - smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step-1, step)) - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step-1, step)) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step)) if step < skip_steps: if assume_skipped is not None and step >= assume_skipped: print_msg("Skipping step %d (and assuming pass).." % (step)) - smt.write("(assert (|%s_a| s%d))" % (topmod, step)) - smt.write("(assert %s)" % get_constr_expr(constr_asserts, step)) + smt_assert("(|%s_a| s%d)" % (topmod, step)) + smt_assert(get_constr_expr(constr_asserts, step)) else: print_msg("Skipping step %d.." % (step)) step += 1 @@ -1180,12 +1443,12 @@ else: # not tempind, covermode last_check_step = step for i in range(1, step_size): if step+i < num_steps: - smt.write("(declare-fun s%d () |%s_s|)" % (step+i, topmod)) - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, step+i)) - smt.write("(assert (|%s_u| s%d))" % (topmod, step+i)) - smt.write("(assert (|%s_h| s%d))" % (topmod, step+i)) - smt.write("(assert (|%s_t| s%d s%d))" % (topmod, step+i-1, step+i)) - smt.write("(assert %s)" % get_constr_expr(constr_assumes, step+i)) + smt_state(step+i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, step+i)) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, step+i)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, step+i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, step+i-1, step+i)) + smt_assert_consequent(get_constr_expr(constr_assumes, step+i)) last_check_step = step+i if not gentrace: @@ -1195,9 +1458,9 @@ else: # not tempind, covermode else: print_msg("Checking assumptions in steps %d to %d.." % (step, last_check_step)) - if smt.check_sat() == "unsat": - print("%s Warmup failed!" % smt.timestamp()) - retstatus = False + if smt_check_sat() == "unsat": + print("%s Assumptions are unsatisfiable!" % smt.timestamp()) + retstatus = "PREUNSAT" break if not final_only: @@ -1205,36 +1468,40 @@ else: # not tempind, covermode print_msg("Checking assertions in step %d.." % (step)) else: print_msg("Checking assertions in steps %d to %d.." % (step, last_check_step)) - smt.write("(push 1)") + smt_push() - smt.write("(assert (not (and %s)))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] + + smt_assert("(not (and %s))" % " ".join(["(|%s_a| s%d)" % (topmod, i) for i in range(step, last_check_step+1)] + [get_constr_expr(constr_asserts, i) for i in range(step, last_check_step+1)])) - if smt.check_sat() == "sat": + if smt_check_sat() == "sat": print("%s BMC failed!" % smt.timestamp()) if append_steps > 0: for i in range(last_check_step+1, last_check_step+1+append_steps): print_msg("Appending additional step %d." % i) - smt.write("(declare-fun s%d () |%s_s|)" % (i, topmod)) - smt.write("(assert (not (|%s_is| s%d)))" % (topmod, i)) - smt.write("(assert (|%s_u| s%d))" % (topmod, i)) - smt.write("(assert (|%s_h| s%d))" % (topmod, i)) - smt.write("(assert (|%s_t| s%d s%d))" % (topmod, i-1, i)) - smt.write("(assert %s)" % get_constr_expr(constr_assumes, i)) - assert smt.check_sat() == "sat" + smt_state(i) + smt_assert_antecedent("(not (|%s_is| s%d))" % (topmod, i)) + smt_assert_consequent("(|%s_u| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_h| s%d)" % (topmod, i)) + smt_assert_antecedent("(|%s_t| s%d s%d)" % (topmod, i-1, i)) + smt_assert_consequent(get_constr_expr(constr_assumes, i)) + print_msg("Re-solving with appended steps..") + if smt_check_sat() == "unsat": + print("%s Cannot appended steps without violating assumptions!" % smt.timestamp()) + retstatus = "FAILED" + break print_anyconsts(step) for i in range(step, last_check_step+1): print_failed_asserts(i) write_trace(0, last_check_step+1+append_steps, '%') - retstatus = False + retstatus = "FAILED" break - smt.write("(pop 1)") + smt_pop() if (constr_final_start is not None) or (last_check_step+1 != num_steps): for i in range(step, last_check_step+1): - smt.write("(assert (|%s_a| s%d))" % (topmod, i)) - smt.write("(assert %s)" % get_constr_expr(constr_asserts, i)) + smt_assert("(|%s_a| s%d)" % (topmod, i)) + smt_assert(get_constr_expr(constr_asserts, i)) if constr_final_start is not None: for i in range(step, last_check_step+1): @@ -1242,32 +1509,32 @@ else: # not tempind, covermode continue print_msg("Checking final constraints in step %d.." % (i)) - smt.write("(push 1)") + smt_push() - smt.write("(assert %s)" % get_constr_expr(constr_assumes, i, final=True)) - smt.write("(assert (not %s))" % get_constr_expr(constr_asserts, i, final=True)) + smt_assert_consequent(get_constr_expr(constr_assumes, i, final=True)) + smt_assert("(not %s)" % get_constr_expr(constr_asserts, i, final=True)) - if smt.check_sat() == "sat": + if smt_check_sat() == "sat": print("%s BMC failed!" % smt.timestamp()) print_anyconsts(i) print_failed_asserts(i, final=True) write_trace(0, i+1, '%') - retstatus = False + retstatus = "FAILED" break - smt.write("(pop 1)") + smt_pop() if not retstatus: break else: # gentrace for i in range(step, last_check_step+1): - smt.write("(assert (|%s_a| s%d))" % (topmod, i)) - smt.write("(assert %s)" % get_constr_expr(constr_asserts, i)) + smt_assert("(|%s_a| s%d)" % (topmod, i)) + smt_assert(get_constr_expr(constr_asserts, i)) print_msg("Solving for step %d.." % (last_check_step)) - if smt.check_sat() != "sat": + if smt_check_sat() != "sat": print("%s No solution found!" % smt.timestamp()) - retstatus = False + retstatus = "FAILED" break elif dumpall: @@ -1276,7 +1543,7 @@ else: # not tempind, covermode step += step_size - if gentrace: + if gentrace and retstatus: print_anyconsts(0) write_trace(0, num_steps, '%') @@ -1284,5 +1551,5 @@ else: # not tempind, covermode smt.write("(exit)") smt.wait() -print_msg("Status: %s" % ("PASSED" if retstatus else "FAILED (!)")) -sys.exit(0 if retstatus else 1) +print_msg("Status: %s" % retstatus) +sys.exit(0 if retstatus == "PASSED" else 1) diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index abf8e812d..34bf7ef38 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -16,10 +16,67 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -import sys, subprocess, re, os +import sys, re, os, signal +import subprocess +if os.name == "posix": + import resource from copy import deepcopy from select import select from time import time +from queue import Queue, Empty +from threading import Thread + + +# This is needed so that the recursive SMT2 S-expression parser +# does not run out of stack frames when parsing large expressions +if os.name == "posix": + smtio_reclimit = 64 * 1024 + if sys.getrecursionlimit() < smtio_reclimit: + sys.setrecursionlimit(smtio_reclimit) + + current_rlimit_stack = resource.getrlimit(resource.RLIMIT_STACK) + if current_rlimit_stack[0] != resource.RLIM_INFINITY: + smtio_stacksize = 128 * 1024 * 1024 + if os.uname().sysname == "Darwin": + # MacOS has rather conservative stack limits + smtio_stacksize = 16 * 1024 * 1024 + if current_rlimit_stack[1] != resource.RLIM_INFINITY: + smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1]) + if current_rlimit_stack[0] < smtio_stacksize: + try: + resource.setrlimit(resource.RLIMIT_STACK, (smtio_stacksize, current_rlimit_stack[1])) + except ValueError: + # couldn't get more stack, just run with what we have + pass + + +# currently running solvers (so we can kill them) +running_solvers = dict() +forced_shutdown = False +solvers_index = 0 + +def force_shutdown(signum, frame): + global forced_shutdown + if not forced_shutdown: + forced_shutdown = True + if signum is not None: + print("<%s>" % signal.Signals(signum).name) + for p in running_solvers.values(): + # os.killpg(os.getpgid(p.pid), signal.SIGTERM) + os.kill(p.pid, signal.SIGTERM) + sys.exit(1) + +if os.name == "posix": + signal.signal(signal.SIGHUP, force_shutdown) +signal.signal(signal.SIGINT, force_shutdown) +signal.signal(signal.SIGTERM, force_shutdown) + +def except_hook(exctype, value, traceback): + if not forced_shutdown: + sys.__excepthook__(exctype, value, traceback) + force_shutdown(None, None) + +sys.excepthook = except_hook hex_dict = { @@ -40,24 +97,33 @@ class SmtModInfo: self.memories = dict() self.wires = set() self.wsize = dict() + self.clocks = dict() self.cells = dict() self.asserts = dict() self.covers = dict() self.anyconsts = dict() self.anyseqs = dict() + self.allconsts = dict() + self.allseqs = dict() + self.asize = dict() class SmtIo: def __init__(self, opts=None): + global solvers_index + self.logic = None self.logic_qf = True self.logic_ax = True self.logic_uf = True self.logic_bv = True self.logic_dt = False + self.forall = False self.produce_models = True self.smt2cache = [list()] self.p = None + self.p_index = solvers_index + solvers_index += 1 if opts is not None: self.logic = opts.logic @@ -91,23 +157,41 @@ class SmtIo: self.topmod = None self.setup_done = False + def __del__(self): + if self.p is not None and not forced_shutdown: + os.killpg(os.getpgid(self.p.pid), signal.SIGTERM) + if running_solvers is not None: + del running_solvers[self.p_index] + def setup(self): assert not self.setup_done + if self.forall: + self.unroll = False + if self.solver == "yices": - self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts + if self.noincr: + self.popen_vargs = ['yices-smt2'] + self.solver_opts + else: + self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts if self.solver == "z3": self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts if self.solver == "cvc4": - self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts + if self.noincr: + self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts + else: + self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts if self.solver == "mathsat": self.popen_vargs = ['mathsat'] + self.solver_opts if self.solver == "boolector": - self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts + if self.noincr: + self.popen_vargs = ['boolector', '--smt2'] + self.solver_opts + else: + self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts self.unroll = True if self.solver == "abc": @@ -126,9 +210,10 @@ class SmtIo: if self.dummy_file is not None: self.dummy_fd = open(self.dummy_file, "w") if not self.noincr: - self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.p_open() if self.unroll: + assert not self.forall self.logic_uf = False self.unroll_idcnt = 0 self.unroll_buffer = "" @@ -148,17 +233,17 @@ class SmtIo: self.setup_done = True + for stmt in self.info_stmts: + self.write(stmt) + if self.produce_models: self.write("(set-option :produce-models true)") self.write("(set-logic %s)" % self.logic) - for stmt in self.info_stmts: - self.write(stmt) - def timestamp(self): secs = int(time() - self.start_time) - return "## %6d %3d:%02d:%02d " % (secs, secs // (60*60), (secs // 60) % 60, secs % 60) + return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60) def replace_in_stmt(self, stmt, pat, repl): if stmt == pat: @@ -209,6 +294,65 @@ class SmtIo: return stmt + def p_thread_main(self): + while True: + data = self.p.stdout.readline().decode("ascii") + if data == "": break + self.p_queue.put(data) + self.p_queue.put("") + self.p_running = False + + def p_open(self): + assert self.p is None + try: + self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except FileNotFoundError: + print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True) + sys.exit(1) + running_solvers[self.p_index] = self.p + self.p_running = True + self.p_next = None + self.p_queue = Queue() + self.p_thread = Thread(target=self.p_thread_main) + self.p_thread.start() + + def p_write(self, data, flush): + assert self.p is not None + self.p.stdin.write(bytes(data, "ascii")) + if flush: self.p.stdin.flush() + + def p_read(self): + assert self.p is not None + if self.p_next is not None: + data = self.p_next + self.p_next = None + return data + if not self.p_running: + return "" + return self.p_queue.get() + + def p_poll(self, timeout=0.1): + assert self.p is not None + assert self.p_running + if self.p_next is not None: + return False + try: + self.p_next = self.p_queue.get(True, timeout) + return False + except Empty: + return True + + def p_close(self): + assert self.p is not None + self.p.stdin.close() + self.p_thread.join() + assert not self.p_running + del running_solvers[self.p_index] + self.p = None + self.p_next = None + self.p_queue = None + self.p_thread = None + def write(self, stmt, unroll=True): if stmt.startswith(";"): self.info(stmt) @@ -281,20 +425,17 @@ class SmtIo: if self.solver != "dummy": if self.noincr: if self.p is not None and not stmt.startswith("(get-"): - self.p.stdin.close() - self.p = None + self.p_close() if stmt == "(push 1)": self.smt2cache.append(list()) elif stmt == "(pop 1)": self.smt2cache.pop() else: if self.p is not None: - self.p.stdin.write(bytes(stmt + "\n", "ascii")) - self.p.stdin.flush() + self.p_write(stmt + "\n", True) self.smt2cache[-1].append(stmt) else: - self.p.stdin.write(bytes(stmt + "\n", "ascii")) - self.p.stdin.flush() + self.p_write(stmt + "\n", True) def info(self, stmt): if not stmt.startswith("; yosys-smt2-"): @@ -314,6 +455,11 @@ class SmtIo: if self.logic is None: self.logic_dt = True + if fields[1] == "yosys-smt2-forall": + if self.logic is None: + self.logic_qf = False + self.forall = True + if fields[1] == "yosys-smt2-module": self.curmod = fields[2] self.modinfo[self.curmod] = SmtModInfo() @@ -337,12 +483,19 @@ class SmtIo: self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3]) if fields[1] == "yosys-smt2-memory": - self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6])) + self.modinfo[self.curmod].memories[fields[2]] = (int(fields[3]), int(fields[4]), int(fields[5]), int(fields[6]), fields[7] == "async") if fields[1] == "yosys-smt2-wire": self.modinfo[self.curmod].wires.add(fields[2]) self.modinfo[self.curmod].wsize[fields[2]] = int(fields[3]) + if fields[1] == "yosys-smt2-clock": + for edge in fields[3:]: + if fields[2] not in self.modinfo[self.curmod].clocks: + self.modinfo[self.curmod].clocks[fields[2]] = edge + elif self.modinfo[self.curmod].clocks[fields[2]] != edge: + self.modinfo[self.curmod].clocks[fields[2]] = "event" + if fields[1] == "yosys-smt2-assert": self.modinfo[self.curmod].asserts["%s_a %s" % (self.curmod, fields[2])] = fields[3] @@ -350,10 +503,20 @@ class SmtIo: self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3] if fields[1] == "yosys-smt2-anyconst": - self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[3], None if len(fields) <= 4 else fields[4]) + self.modinfo[self.curmod].anyconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) if fields[1] == "yosys-smt2-anyseq": - self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[3], None if len(fields) <= 4 else fields[4]) + self.modinfo[self.curmod].anyseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + + if fields[1] == "yosys-smt2-allconst": + self.modinfo[self.curmod].allconsts[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) + + if fields[1] == "yosys-smt2-allseq": + self.modinfo[self.curmod].allseqs[fields[2]] = (fields[4], None if len(fields) <= 5 else fields[5]) + self.modinfo[self.curmod].asize[fields[2]] = int(fields[3]) def hiernets(self, top, regs_only=False): def hiernets_worker(nets, mod, cursor): @@ -370,7 +533,8 @@ class SmtIo: def hieranyconsts(self, top): def worker(results, mod, cursor): for name, value in sorted(self.modinfo[mod].anyconsts.items()): - results.append((cursor, name, value[0], value[1])) + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) for cellname, celltype in sorted(self.modinfo[mod].cells.items()): worker(results, celltype, cursor + [cellname]) @@ -381,7 +545,32 @@ class SmtIo: def hieranyseqs(self, top): def worker(results, mod, cursor): for name, value in sorted(self.modinfo[mod].anyseqs.items()): - results.append((cursor, name, value[0], value[1])) + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(results, celltype, cursor + [cellname]) + + results = list() + worker(results, top, []) + return results + + def hierallconsts(self, top): + def worker(results, mod, cursor): + for name, value in sorted(self.modinfo[mod].allconsts.items()): + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) + for cellname, celltype in sorted(self.modinfo[mod].cells.items()): + worker(results, celltype, cursor + [cellname]) + + results = list() + worker(results, top, []) + return results + + def hierallseqs(self, top): + def worker(results, mod, cursor): + for name, value in sorted(self.modinfo[mod].allseqs.items()): + width = self.modinfo[mod].asize[name] + results.append((cursor, name, value[0], value[1], width)) for cellname, celltype in sorted(self.modinfo[mod].cells.items()): worker(results, celltype, cursor + [cellname]) @@ -408,7 +597,7 @@ class SmtIo: if self.solver == "dummy": line = self.dummy_fd.readline().strip() else: - line = self.p.stdout.readline().decode("ascii").strip() + line = self.p_read().strip() if self.dummy_file is not None: self.dummy_fd.write(line + "\n") @@ -421,12 +610,14 @@ class SmtIo: if count_brackets == 0: break if self.solver != "dummy" and self.p.poll(): - print("SMT Solver terminated unexpectedly: %s" % "".join(stmt)) + print("%s Solver terminated unexpectedly: %s" % (self.timestamp(), "".join(stmt)), flush=True) sys.exit(1) stmt = "".join(stmt) if stmt.startswith("(error"): - print("SMT Solver Error: %s" % stmt, file=sys.stderr) + print("%s Solver Error: %s" % (self.timestamp(), stmt), flush=True) + if self.solver != "dummy": + self.p_close() sys.exit(1) return stmt @@ -441,15 +632,13 @@ class SmtIo: if self.solver != "dummy": if self.noincr: if self.p is not None: - self.p.stdin.close() - self.p = None - self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + self.p_close() + self.p_open() for cache_ctx in self.smt2cache: for cache_stmt in cache_ctx: - self.p.stdin.write(bytes(cache_stmt + "\n", "ascii")) + self.p_write(cache_stmt + "\n", False) - self.p.stdin.write(bytes("(check-sat)\n", "ascii")) - self.p.stdin.flush() + self.p_write("(check-sat)\n", True) if self.timeinfo: i = 0 @@ -457,7 +646,7 @@ class SmtIo: count = 0 num_bs = 0 - while select([self.p.stdout], [], [], 0.1) == ([], [], []): + while self.p_poll(): count += 1 if count < 25: @@ -486,11 +675,43 @@ class SmtIo: print("\b \b" * num_bs, end="", file=sys.stderr) sys.stderr.flush() + else: + count = 0 + while self.p_poll(60): + count += 1 + msg = None + + if count == 1: + msg = "1 minute" + + elif count in [5, 10, 15, 30]: + msg = "%d minutes" % count + + elif count == 60: + msg = "1 hour" + + elif count % 60 == 0: + msg = "%d hours" % (count // 60) + + if msg is not None: + print("%s waiting for solver (%s)" % (self.timestamp(), msg), flush=True) + result = self.read() + if self.debug_file: print("(set-info :status %s)" % result, file=self.debug_file) print("(check-sat)", file=self.debug_file) self.debug_file.flush() + + if result not in ["sat", "unsat"]: + if result == "": + print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True) + else: + print("%s Unexpected response from solver: %s" % (self.timestamp(), result), flush=True) + if self.solver != "dummy": + self.p_close() + sys.exit(1) + return result def parse(self, stmt): @@ -545,6 +766,9 @@ class SmtIo: return h def bv2bin(self, v): + if type(v) is list and len(v) == 3 and v[0] == "_" and v[1].startswith("bv"): + x, n = int(v[1][2:]), int(v[2]) + return "".join("1" if (x & (1 << i)) else "0" for i in range(n-1, -1, -1)) if v == "true": return "1" if v == "false": return "0" if v.startswith("#b"): @@ -568,7 +792,7 @@ class SmtIo: def get_path(self, mod, path): assert mod in self.modinfo - path = path.split(".") + path = path.replace("\\", "/").split(".") for i in range(len(path)-1): first = ".".join(path[0:i+1]) @@ -613,6 +837,17 @@ class SmtIo: assert net_path[-1] in self.modinfo[mod].wsize return self.modinfo[mod].wsize[net_path[-1]] + def net_clock(self, mod, net_path): + for i in range(len(net_path)-1): + assert mod in self.modinfo + assert net_path[i] in self.modinfo[mod].cells + mod = self.modinfo[mod].cells[net_path[i]] + + assert mod in self.modinfo + if net_path[-1] not in self.modinfo[mod].clocks: + return None + return self.modinfo[mod].clocks[net_path[-1]] + def net_exists(self, mod, net_path): for i in range(len(net_path)-1): if mod not in self.modinfo: return False @@ -672,6 +907,7 @@ class SmtIo: def wait(self): if self.p is not None: self.p.wait() + self.p_close() class SmtOpts: @@ -763,6 +999,7 @@ class MkVcd: self.f = f self.t = -1 self.nets = dict() + self.clocks = dict() def add_net(self, path, width): path = tuple(path) @@ -770,34 +1007,86 @@ class MkVcd: key = "n%d" % len(self.nets) self.nets[path] = (key, width) + def add_clock(self, path, edge): + path = tuple(path) + assert self.t == -1 + key = "n%d" % len(self.nets) + self.nets[path] = (key, 1) + self.clocks[path] = (key, edge) + def set_net(self, path, bits): path = tuple(path) assert self.t >= 0 assert path in self.nets - print("b%s %s" % (bits, self.nets[path][0]), file=self.f) + if path not in self.clocks: + print("b%s %s" % (bits, self.nets[path][0]), file=self.f) + + def escape_name(self, name): + name = re.sub(r"\[([0-9a-zA-Z_]*[a-zA-Z_][0-9a-zA-Z_]*)\]", r"<\1>", name) + if re.match("[\[\]]", name) and name[0] != "\\": + name = "\\" + name + return name def set_time(self, t): assert t >= self.t if t != self.t: if self.t == -1: + print("$version Generated by Yosys-SMTBMC $end", file=self.f) + print("$timescale 1ns $end", file=self.f) print("$var integer 32 t smt_step $end", file=self.f) print("$var event 1 ! smt_clock $end", file=self.f) + + def vcdescape(n): + if n.startswith("$") or ":" in n: + return "\\" + n + return n + scope = [] for path in sorted(self.nets): - while len(scope)+1 > len(path) or (len(scope) > 0 and scope[-1] != path[len(scope)-1]): + key, width = self.nets[path] + + uipath = list(path) + if "." in uipath[-1] and not uipath[-1].startswith("$"): + uipath = uipath[0:-1] + uipath[-1].split(".") + for i in range(len(uipath)): + uipath[i] = re.sub(r"\[([^\]]*)\]", r"<\1>", uipath[i]) + + while uipath[:len(scope)] != scope: print("$upscope $end", file=self.f) scope = scope[:-1] - while len(scope)+1 < len(path): - print("$scope module %s $end" % path[len(scope)], file=self.f) - scope.append(path[len(scope)-1]) - key, width = self.nets[path] - print("$var wire %d %s %s $end" % (width, key, path[-1]), file=self.f) + + while uipath[:-1] != scope: + scopename = uipath[len(scope)] + print("$scope module %s $end" % vcdescape(scopename), file=self.f) + scope.append(uipath[len(scope)]) + + if path in self.clocks and self.clocks[path][1] == "event": + print("$var event 1 %s %s $end" % (key, vcdescape(uipath[-1])), file=self.f) + else: + print("$var wire %d %s %s $end" % (width, key, vcdescape(uipath[-1])), file=self.f) + for i in range(len(scope)): print("$upscope $end", file=self.f) + print("$enddefinitions $end", file=self.f) + self.t = t assert self.t >= 0 + + if self.t > 0: + print("#%d" % (10 * self.t - 5), file=self.f) + for path in sorted(self.clocks.keys()): + if self.clocks[path][1] == "posedge": + print("b0 %s" % self.nets[path][0], file=self.f) + elif self.clocks[path][1] == "negedge": + print("b1 %s" % self.nets[path][0], file=self.f) + print("#%d" % (10 * self.t), file=self.f) print("1!", file=self.f) print("b%s t" % format(self.t, "032b"), file=self.f) + for path in sorted(self.clocks.keys()): + if self.clocks[path][1] == "negedge": + print("b0 %s" % self.nets[path][0], file=self.f) + else: + print("b1 %s" % self.nets[path][0], file=self.f) diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 768969e6b..f755307bf 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -42,7 +42,7 @@ struct SmvWorker pool<Wire*> partial_assignment_wires; dict<SigBit, std::pair<const char*, int>> partial_assignment_bits; - vector<string> assignments, invarspecs; + vector<string> inputvars, vars, definitions, assignments, invarspecs; const char *cid() { @@ -61,7 +61,7 @@ struct SmvWorker { string name = stringf("_%s", id.c_str()); - if (name.substr(0, 2) == "_\\") + if (name.compare(0, 2, "_\\") == 0) name = "_" + name.substr(2); for (auto &c : name) { @@ -195,7 +195,7 @@ struct SmvWorker return rvalue(sig); const char *temp_id = cid(); - f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig)); +// f << stringf(" %s : unsigned word[%d]; -- %s\n", temp_id, GetSize(sig), log_signal(sig)); int offset = 0; for (auto bit : sig) { @@ -210,14 +210,14 @@ struct SmvWorker void run() { f << stringf("MODULE %s\n", cid(module->name)); - f << stringf(" VAR\n"); for (auto wire : module->wires()) { if (SigSpec(wire) != sigmap(wire)) partial_assignment_wires.insert(wire); - f << stringf(" %s : unsigned word[%d]; -- %s\n", cid(wire->name), wire->width, log_id(wire)); + if (wire->port_input) + inputvars.push_back(stringf("%s : unsigned word[%d]; -- %s", cid(wire->name), wire->width, log_id(wire))); if (wire->attributes.count("\\init")) assignments.push_back(stringf("init(%s) := %s;", lvalue(wire), rvalue(wire->attributes.at("\\init")))); @@ -275,8 +275,8 @@ struct SmvWorker const char *b_shr = rvalue_u(sig_b); const char *b_shl = cid(); - f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b)); - assignments.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b))); +// f << stringf(" %s : unsigned word[%d]; -- neg(%s)\n", b_shl, GetSize(sig_b), log_signal(sig_b)); + definitions.push_back(stringf("%s := unsigned(-%s);", b_shl, rvalue_s(sig_b))); string expr_shl = stringf("resize(%s << %s[%d:0], %d)", expr_a.c_str(), b_shl, shift_b_width-1, width_y); string expr_shr = stringf("resize(%s >> %s[%d:0], %d)", expr_a.c_str(), b_shr, shift_b_width-1, width_y); @@ -303,7 +303,7 @@ struct SmvWorker GetSize(sig_b)-shift_b_width, width_y, expr.c_str()); } - assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); + definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); continue; } @@ -319,12 +319,12 @@ struct SmvWorker if (cell->getParam("\\A_SIGNED").as_bool()) { - assignments.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := unsigned(%s%s);", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue_s(cell->getPort("\\A"), width))); } else { - assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue_u(cell->getPort("\\A"), width))); } @@ -346,12 +346,12 @@ struct SmvWorker if (cell->getParam("\\A_SIGNED").as_bool()) { - assignments.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := unsigned(%s %s %s);", lvalue(cell->getPort("\\Y")), rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width))); } else { - assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width))); } @@ -370,12 +370,12 @@ struct SmvWorker if (cell->getParam("\\A_SIGNED").as_bool()) { - assignments.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := resize(unsigned(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), rvalue_s(cell->getPort("\\A"), width), op.c_str(), rvalue_s(cell->getPort("\\B"), width), width_y)); } else { - assignments.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := resize(%s %s %s, %d);", lvalue(cell->getPort("\\Y")), rvalue_u(cell->getPort("\\A"), width), op.c_str(), rvalue_u(cell->getPort("\\B"), width), width_y)); } @@ -407,7 +407,7 @@ struct SmvWorker expr_b = stringf("resize(%s, %d)", rvalue(cell->getPort("\\B")), width); } - assignments.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := resize(word1(%s %s %s), %d);", lvalue(cell->getPort("\\Y")), expr_a.c_str(), op.c_str(), expr_b.c_str(), GetSize(cell->getPort("\\Y")))); continue; @@ -425,7 +425,7 @@ struct SmvWorker if (cell->type == "$reduce_or") expr = stringf("%s != 0ub%d_0", expr_a, width_a); if (cell->type == "$reduce_bool") expr = stringf("%s != 0ub%d_0", expr_a, width_a); - assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); continue; } @@ -444,7 +444,7 @@ struct SmvWorker if (cell->type == "$reduce_xnor") expr = "!(" + expr + ")"; - assignments.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(%s, %d);", expr_y, expr.c_str(), width_y)); continue; } @@ -462,7 +462,7 @@ struct SmvWorker if (cell->type == "$logic_and") expr = expr_a + " & " + expr_b; if (cell->type == "$logic_or") expr = expr_a + " | " + expr_b; - assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr.c_str(), width_y)); continue; } @@ -474,7 +474,7 @@ struct SmvWorker string expr_a = stringf("(%s = 0ub%d_0)", rvalue(cell->getPort("\\A")), width_a); const char *expr_y = lvalue(cell->getPort("\\Y")); - assignments.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y)); + definitions.push_back(stringf("%s := resize(word1(%s), %d);", expr_y, expr_a.c_str(), width_y)); continue; } @@ -490,12 +490,13 @@ struct SmvWorker expr += stringf("bool(%s) ? %s : ", rvalue(sig_s[i]), rvalue(sig_b.extract(i*width, width))); expr += rvalue(sig_a); - assignments.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); + definitions.push_back(stringf("%s := %s;", lvalue(cell->getPort("\\Y")), expr.c_str())); continue; } if (cell->type == "$dff") { + vars.push_back(stringf("%s : unsigned word[%d]; -- %s", lvalue(cell->getPort("\\Q")), GetSize(cell->getPort("\\Q")), log_signal(cell->getPort("\\Q")))); assignments.push_back(stringf("next(%s) := %s;", lvalue(cell->getPort("\\Q")), rvalue(cell->getPort("\\D")))); continue; } @@ -503,7 +504,7 @@ struct SmvWorker if (cell->type.in("$_BUF_", "$_NOT_")) { string op = cell->type == "$_NOT_" ? "!" : ""; - assignments.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A")))); + definitions.push_back(stringf("%s := %s%s;", lvalue(cell->getPort("\\Y")), op.c_str(), rvalue(cell->getPort("\\A")))); continue; } @@ -517,49 +518,56 @@ struct SmvWorker if (cell->type.in("$_XNOR_")) op = "xnor"; if (cell->type.in("$_ANDNOT_", "$_ORNOT_")) - assignments.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := %s %s (!%s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); else if (cell->type.in("$_NAND_", "$_NOR_")) - assignments.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !(%s %s %s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); else - assignments.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := %s %s %s;", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), op.c_str(), rvalue(cell->getPort("\\B")))); continue; } if (cell->type == "$_MUX_") { - assignments.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := bool(%s) ? %s : %s;", lvalue(cell->getPort("\\Y")), + rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A")))); + continue; + } + + if (cell->type == "$_NMUX_") + { + definitions.push_back(stringf("%s := !(bool(%s) ? %s : %s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\S")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\A")))); continue; } if (cell->type == "$_AOI3_") { - assignments.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s & %s) | %s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")))); continue; } if (cell->type == "$_OAI3_") { - assignments.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s | %s) & %s);", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")))); continue; } if (cell->type == "$_AOI4_") { - assignments.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s & %s) | (%s & %s));", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D")))); continue; } if (cell->type == "$_OAI4_") { - assignments.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")), + definitions.push_back(stringf("%s := !((%s | %s) & (%s | %s));", lvalue(cell->getPort("\\Y")), rvalue(cell->getPort("\\A")), rvalue(cell->getPort("\\B")), rvalue(cell->getPort("\\C")), rvalue(cell->getPort("\\D")))); continue; } @@ -567,13 +575,13 @@ struct SmvWorker if (cell->type[0] == '$') log_error("Found currently unsupported cell type %s (%s.%s).\n", log_id(cell->type), log_id(module), log_id(cell)); - f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); +// f << stringf(" %s : %s;\n", cid(cell->name), cid(cell->type)); for (auto &conn : cell->connections()) if (cell->output(conn.first)) - assignments.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first))); + definitions.push_back(stringf("%s := %s.%s;", lvalue(conn.second), cid(cell->name), cid(conn.first))); else - assignments.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second))); + definitions.push_back(stringf("%s.%s := %s;", cid(cell->name), cid(conn.first), rvalue(conn.second))); } for (Wire *wire : partial_assignment_wires) @@ -657,7 +665,25 @@ struct SmvWorker } } - assignments.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str())); + definitions.push_back(stringf("%s := %s;", cid(wire->name), expr.c_str())); + } + + if (!inputvars.empty()) { + f << stringf(" IVAR\n"); + for (const string &line : inputvars) + f << stringf(" %s\n", line.c_str()); + } + + if (!vars.empty()) { + f << stringf(" VAR\n"); + for (const string &line : vars) + f << stringf(" %s\n", line.c_str()); + } + + if (!definitions.empty()) { + f << stringf(" DEFINE\n"); + for (const string &line : definitions) + f << stringf(" %s\n", line.c_str()); } if (!assignments.empty()) { @@ -675,7 +701,7 @@ struct SmvWorker struct SmvBackend : public Backend { SmvBackend() : Backend("smv", "write design to SMV file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -693,7 +719,7 @@ struct SmvBackend : public Backend { log("THIS COMMAND IS UNDER CONSTRUCTION\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::ifstream template_f; bool verbose = false; @@ -720,7 +746,7 @@ struct SmvBackend : public Backend { pool<Module*> modules; for (auto module : design->modules()) - if (!module->get_bool_attribute("\\blackbox") && !module->has_memories_warn() && !module->has_processes_warn()) + if (!module->get_blackbox_attribute() && !module->has_memories_warn() && !module->has_processes_warn()) modules.insert(module); if (template_f.is_open()) diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc index 4101cbf98..6738a4bbd 100644 --- a/backends/spice/spice.cc +++ b/backends/spice/spice.cc @@ -132,7 +132,7 @@ static void print_spice_module(std::ostream &f, RTLIL::Module *module, RTLIL::De struct SpiceBackend : public Backend { SpiceBackend() : Backend("spice", "write design to SPICE netlist file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -161,7 +161,7 @@ struct SpiceBackend : public Backend { log(" set the specified module as design top module\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { std::string top_module_name; RTLIL::Module *top_module = NULL; @@ -212,7 +212,7 @@ struct SpiceBackend : public Backend { for (auto module_it : design->modules_) { RTLIL::Module *module = module_it.second; - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; if (module->processes.size() != 0) diff --git a/backends/table/table.cc b/backends/table/table.cc index 27b7edfff..796f18059 100644 --- a/backends/table/table.cc +++ b/backends/table/table.cc @@ -29,7 +29,7 @@ PRIVATE_NAMESPACE_BEGIN struct TableBackend : public Backend { TableBackend() : Backend("table", "write design as connectivity table") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -48,7 +48,7 @@ struct TableBackend : public Backend { log("module inputs and outputs are output using cell type and port '-' and with\n"); log("'pi' (primary input) or 'po' (primary output) or 'pio' as direction.\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing TABLE backend.\n"); @@ -67,7 +67,7 @@ struct TableBackend : public Backend { for (auto module : design->modules()) { - if (module->get_bool_attribute("\\blackbox")) + if (module->get_blackbox_attribute()) continue; SigMap sigmap(module); @@ -109,7 +109,7 @@ struct TableBackend : public Backend { else if (cell->output(conn.first)) *f << "out" << "\t"; else - *f << "unkown" << "\t"; + *f << "unknown" << "\t"; *f << log_signal(sigmap(conn.second)) << "\n"; } diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index bdf705056..54d0f6148 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -33,11 +33,11 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, defparam, decimal; -int auto_name_counter, auto_name_offset, auto_name_digits; +bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit; +int auto_name_counter, auto_name_offset, auto_name_digits, extmem_counter; std::map<RTLIL::IdString, int> auto_name_map; std::set<RTLIL::IdString> reg_wires, reg_ct; -std::string auto_prefix; +std::string auto_prefix, extmem_prefix; RTLIL::Module *active_module; dict<RTLIL::SigBit, RTLIL::State> active_initdata; @@ -126,6 +126,33 @@ std::string id(RTLIL::IdString internal_id, bool may_rename = true) break; } + const pool<string> keywords = { + // IEEE 1800-2017 Annex B + "accept_on", "alias", "always", "always_comb", "always_ff", "always_latch", "and", "assert", "assign", "assume", "automatic", "before", + "begin", "bind", "bins", "binsof", "bit", "break", "buf", "bufif0", "bufif1", "byte", "case", "casex", "casez", "cell", "chandle", + "checker", "class", "clocking", "cmos", "config", "const", "constraint", "context", "continue", "cover", "covergroup", "coverpoint", + "cross", "deassign", "default", "defparam", "design", "disable", "dist", "do", "edge", "else", "end", "endcase", "endchecker", + "endclass", "endclocking", "endconfig", "endfunction", "endgenerate", "endgroup", "endinterface", "endmodule", "endpackage", + "endprimitive", "endprogram", "endproperty", "endsequence", "endspecify", "endtable", "endtask", "enum", "event", "eventually", + "expect", "export", "extends", "extern", "final", "first_match", "for", "force", "foreach", "forever", "fork", "forkjoin", "function", + "generate", "genvar", "global", "highz0", "highz1", "if", "iff", "ifnone", "ignore_bins", "illegal_bins", "implements", "implies", + "import", "incdir", "include", "initial", "inout", "input", "inside", "instance", "int", "integer", "interconnect", "interface", + "intersect", "join", "join_any", "join_none", "large", "let", "liblist", "library", "local", "localparam", "logic", "longint", + "macromodule", "matches", "medium", "modport", "module", "nand", "negedge", "nettype", "new", "nexttime", "nmos", "nor", + "noshowcancelled", "not", "notif0", "notif1", "null", "or", "output", "package", "packed", "parameter", "pmos", "posedge", "primitive", + "priority", "program", "property", "protected", "pull0", "pull1", "pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent", + "pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real", "realtime", "ref", "reg", "reject_on", "release", "repeat", + "restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until", + "s_until_with", "scalared", "sequence", "shortint", "shortreal", "showcancelled", "signed", "small", "soft", "solve", "specify", + "specparam", "static", "string", "strong", "strong0", "strong1", "struct", "super", "supply0", "supply1", "sync_accept_on", + "sync_reject_on", "table", "tagged", "task", "this", "throughout", "time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1", + "tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef", "union", "unique", "unique0", "unsigned", "until", "until_with", + "untyped", "use", "uwire", "var", "vectored", "virtual", "void", "wait", "wait_order", "wand", "weak", "weak0", "weak1", "while", + "wildcard", "wire", "with", "within", "wor", "xnor", "xor", + }; + if (keywords.count(str)) + do_escape = true; + if (do_escape) return "\\" + std::string(str) + " "; return std::string(str); @@ -156,10 +183,16 @@ bool is_reg_wire(RTLIL::SigSpec sig, std::string ®_name) return true; } -void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool set_signed = false, bool escape_comment = false) +void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool no_decimal = false, bool escape_comment = false) { + bool set_signed = (data.flags & RTLIL::CONST_FLAG_SIGNED) != 0; if (width < 0) width = data.bits.size() - offset; + if (width == 0) { + // See IEEE 1364-2005 Clause 5.1.14. + f << "{0{1'b0}}"; + return; + } if (nostr) goto dump_hex; if ((data.flags & RTLIL::CONST_FLAG_STRING) == 0 || width != (int)data.bits.size()) { @@ -167,9 +200,9 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o int32_t val = 0; for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); - if (data.bits[i] != RTLIL::S0 && data.bits[i] != RTLIL::S1) + if (data.bits[i] != State::S0 && data.bits[i] != State::S1) goto dump_hex; - if (data.bits[i] == RTLIL::S1) + if (data.bits[i] == State::S1) val |= 1 << (i - offset); } if (decimal) @@ -186,11 +219,11 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o for (int i = offset; i < offset+width; i++) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { - case RTLIL::S0: bin_digits.push_back('0'); break; - case RTLIL::S1: bin_digits.push_back('1'); break; + case State::S0: bin_digits.push_back('0'); break; + case State::S1: bin_digits.push_back('1'); break; case RTLIL::Sx: bin_digits.push_back('x'); break; case RTLIL::Sz: bin_digits.push_back('z'); break; - case RTLIL::Sa: bin_digits.push_back('z'); break; + case RTLIL::Sa: bin_digits.push_back('?'); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } @@ -219,6 +252,12 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o hex_digits.push_back('z'); continue; } + if (bit_3 == '?' || bit_2 == '?' || bit_1 == '?' || bit_0 == '?') { + if (bit_3 != '?' || bit_2 != '?' || bit_1 != '?' || bit_0 != '?') + goto dump_bin; + hex_digits.push_back('?'); + continue; + } int val = 8*(bit_3 - '0') + 4*(bit_2 - '0') + 2*(bit_1 - '0') + (bit_0 - '0'); hex_digits.push_back(val < 10 ? '0' + val : 'a' + val - 10); } @@ -234,17 +273,18 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o for (int i = offset+width-1; i >= offset; i--) { log_assert(i < (int)data.bits.size()); switch (data.bits[i]) { - case RTLIL::S0: f << stringf("0"); break; - case RTLIL::S1: f << stringf("1"); break; + case State::S0: f << stringf("0"); break; + case State::S1: f << stringf("1"); break; case RTLIL::Sx: f << stringf("x"); break; case RTLIL::Sz: f << stringf("z"); break; - case RTLIL::Sa: f << stringf("z"); break; + case RTLIL::Sa: f << stringf("?"); break; case RTLIL::Sm: log_error("Found marker state in final netlist."); } } } } else { - f << stringf("\""); + if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) + f << stringf("\""); std::string str = data.decode_string(); for (size_t i = 0; i < str.size(); i++) { if (str[i] == '\n') @@ -262,7 +302,8 @@ void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int o else f << str[i]; } - f << stringf("\""); + if ((data.flags & RTLIL::CONST_FLAG_REAL) == 0) + f << stringf("\""); } } @@ -313,6 +354,10 @@ void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool no_decima void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) { + if (GetSize(sig) == 0) { + f << "\"\""; + return; + } if (sig.is_chunk()) { dump_sigchunk(f, sig.as_chunk()); } else { @@ -326,26 +371,29 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig) } } -void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false) +void dump_attributes(std::ostream &f, std::string indent, dict<RTLIL::IdString, RTLIL::Const> &attributes, char term = '\n', bool modattr = false, bool regattr = false, bool as_comment = false) { if (noattr) return; + if (attr2comment) + as_comment = true; for (auto it = attributes.begin(); it != attributes.end(); ++it) { - f << stringf("%s" "%s %s", indent.c_str(), attr2comment ? "/*" : "(*", id(it->first).c_str()); + if (it->first == "\\init" && regattr) continue; + f << stringf("%s" "%s %s", indent.c_str(), as_comment ? "/*" : "(*", id(it->first).c_str()); f << stringf(" = "); - if (modattr && (it->second == Const(0, 1) || it->second == Const(0))) + if (modattr && (it->second == State::S0 || it->second == Const(0))) f << stringf(" 0 "); - else if (modattr && (it->second == Const(1, 1) || it->second == Const(1))) + else if (modattr && (it->second == State::S1 || it->second == Const(1))) f << stringf(" 1 "); else - dump_const(f, it->second, -1, 0, false, false, attr2comment); - f << stringf(" %s%c", attr2comment ? "*/" : "*)", term); + dump_const(f, it->second, -1, 0, false, as_comment); + f << stringf(" %s%c", as_comment ? "*/" : "*)", term); } } void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) { - dump_attributes(f, indent, wire->attributes); + dump_attributes(f, indent, wire->attributes, '\n', /*modattr=*/false, /*regattr=*/reg_wires.count(wire->name)); #if 0 if (wire->port_input && !wire->port_output) f << stringf("%s" "input %s", indent.c_str(), reg_wires.count(wire->name) ? "reg " : ""); @@ -388,7 +436,7 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire) void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory) { dump_attributes(f, indent, memory->attributes); - f << stringf("%s" "reg [%d:0] %s [%d:0];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size-1); + f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), memory->width-1, id(memory->name).c_str(), memory->size+memory->start_offset-1, memory->start_offset); } void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true) @@ -511,6 +559,20 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$_NMUX_") { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = !("); + dump_cell_expr_port(f, cell, "S", false); + f << stringf(" ? "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", false); + f << stringf(" : "); + dump_cell_expr_port(f, cell, "A", false); + f << stringf(");\n"); + return true; + } + if (cell->type.in("$_AOI3_", "$_OAI3_")) { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort("\\Y")); @@ -543,7 +605,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type.substr(0, 6) == "$_DFF_") + if (cell->type.begins_with("$_DFF_")) { std::string reg_name = cellname(cell); bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); @@ -584,7 +646,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type.substr(0, 8) == "$_DFFSR_") + if (cell->type.begins_with("$_DFFSR_")) { char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10]; @@ -678,13 +740,45 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) #undef HANDLE_UNIOP #undef HANDLE_BINOP - if (cell->type == "$shiftx") + if (cell->type == "$shift") { f << stringf("%s" "assign ", indent.c_str()); dump_sigspec(f, cell->getPort("\\Y")); f << stringf(" = "); + if (cell->getParam("\\B_SIGNED").as_bool()) + { + f << stringf("$signed("); + dump_sigspec(f, cell->getPort("\\B")); + f << stringf(")"); + f << stringf(" < 0 ? "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" << - "); + dump_sigspec(f, cell->getPort("\\B")); + f << stringf(" : "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" >> "); + dump_sigspec(f, cell->getPort("\\B")); + } + else + { + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" >> "); + dump_sigspec(f, cell->getPort("\\B")); + } + f << stringf(";\n"); + return true; + } + + if (cell->type == "$shiftx") + { + std::string temp_id = next_auto_id(); + f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort("\\A"))-1, temp_id.c_str()); dump_sigspec(f, cell->getPort("\\A")); - f << stringf("["); + f << stringf(";\n"); + + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = %s[", temp_id.c_str()); if (cell->getParam("\\B_SIGNED").as_bool()) f << stringf("$signed("); dump_sigspec(f, cell->getPort("\\B")); @@ -710,7 +804,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type == "$pmux" || cell->type == "$pmux_safe") + if (cell->type == "$pmux") { int width = cell->parameters["\\WIDTH"].as_int(); int s_width = cell->getPort("\\S").size(); @@ -722,18 +816,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) f << stringf("%s" " input [%d:0] s;\n", indent.c_str(), s_width-1); dump_attributes(f, indent + " ", cell->attributes); - if (cell->type != "$pmux_safe" && !noattr) + if (!noattr) f << stringf("%s" " (* parallel_case *)\n", indent.c_str()); f << stringf("%s" " casez (s)", indent.c_str()); - if (cell->type != "$pmux_safe") - f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); + f << stringf(noattr ? " // synopsys parallel_case\n" : "\n"); for (int i = 0; i < s_width; i++) { f << stringf("%s" " %d'b", indent.c_str(), s_width); for (int j = s_width-1; j >= 0; j--) - f << stringf("%c", j == i ? '1' : cell->type == "$pmux_safe" ? '0' : '?'); + f << stringf("%c", j == i ? '1' : '?'); f << stringf(":\n"); f << stringf("%s" " %s = b[%d:%d];\n", indent.c_str(), func_name.c_str(), (i+1)*width-1, i*width); @@ -757,6 +850,18 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$tribuf") + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = "); + dump_sigspec(f, cell->getPort("\\EN")); + f << stringf(" ? "); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(" : %d'bz;\n", cell->parameters.at("\\WIDTH").as_int()); + return true; + } + if (cell->type == "$slice") { f << stringf("%s" "assign ", indent.c_str()); @@ -779,6 +884,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$lut") + { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Y")); + f << stringf(" = "); + dump_const(f, cell->parameters.at("\\LUT")); + f << stringf(" >> "); + dump_attributes(f, "", cell->attributes, ' '); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(";\n"); + return true; + } + if (cell->type == "$dffsr") { SigSpec sig_clk = cell->getPort("\\CLK"); @@ -832,7 +950,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } - if (cell->type == "$dff" || cell->type == "$adff" || cell->type == "$dffe") + if (cell->type.in("$dff", "$adff", "$dffe")) { RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst; bool pol_clk, pol_arst = false, pol_en = false; @@ -897,12 +1015,49 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type == "$dlatch") + { + RTLIL::SigSpec sig_en; + bool pol_en = false; + + sig_en = cell->getPort("\\EN"); + pol_en = cell->parameters["\\EN_POLARITY"].as_bool(); + + std::string reg_name = cellname(cell); + bool out_is_reg_wire = is_reg_wire(cell->getPort("\\Q"), reg_name); + + if (!out_is_reg_wire) { + f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters["\\WIDTH"].as_int()-1, reg_name.c_str()); + dump_reg_init(f, cell->getPort("\\Q")); + f << ";\n"; + } + + f << stringf("%s" "always @*\n", indent.c_str()); + + f << stringf("%s" " if (%s", indent.c_str(), pol_en ? "" : "!"); + dump_sigspec(f, sig_en); + f << stringf(")\n"); + + f << stringf("%s" " %s = ", indent.c_str(), reg_name.c_str()); + dump_cell_expr_port(f, cell, "D", false); + f << stringf(";\n"); + + if (!out_is_reg_wire) { + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort("\\Q")); + f << stringf(" = %s;\n", reg_name.c_str()); + } + + return true; + } + if (cell->type == "$mem") { RTLIL::IdString memid = cell->parameters["\\MEMID"].decode_string(); std::string mem_id = id(cell->parameters["\\MEMID"].decode_string()); int abits = cell->parameters["\\ABITS"].as_int(); int size = cell->parameters["\\SIZE"].as_int(); + int offset = cell->parameters["\\OFFSET"].as_int(); int width = cell->parameters["\\WIDTH"].as_int(); bool use_init = !(RTLIL::SigSpec(cell->parameters["\\INIT"]).is_fully_undef()); @@ -911,17 +1066,67 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) // initial begin // memid[0] = ... // end - f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size-1, 0); + f << stringf("%s" "reg [%d:%d] %s [%d:%d];\n", indent.c_str(), width-1, 0, mem_id.c_str(), size+offset-1, offset); if (use_init) { - f << stringf("%s" "initial begin\n", indent.c_str()); - for (int i=0; i<size; i++) + if (extmem) + { + std::string extmem_filename = stringf("%s-%d.mem", extmem_prefix.c_str(), extmem_counter++); + + std::string extmem_filename_esc; + for (auto c : extmem_filename) + { + if (c == '\n') + extmem_filename_esc += "\\n"; + else if (c == '\t') + extmem_filename_esc += "\\t"; + else if (c < 32) + extmem_filename_esc += stringf("\\%03o", c); + else if (c == '"') + extmem_filename_esc += "\\\""; + else if (c == '\\') + extmem_filename_esc += "\\\\"; + else + extmem_filename_esc += c; + } + f << stringf("%s" "initial $readmemb(\"%s\", %s);\n", indent.c_str(), extmem_filename_esc.c_str(), mem_id.c_str()); + + std::ofstream extmem_f(extmem_filename, std::ofstream::trunc); + if (extmem_f.fail()) + log_error("Can't open file `%s' for writing: %s\n", extmem_filename.c_str(), strerror(errno)); + else + { + for (int i=0; i<size; i++) + { + RTLIL::Const element = cell->parameters["\\INIT"].extract(i*width, width); + for (int j=0; j<element.size(); j++) + { + switch (element[element.size()-j-1]) + { + case State::S0: extmem_f << '0'; break; + case State::S1: extmem_f << '1'; break; + case State::Sx: extmem_f << 'x'; break; + case State::Sz: extmem_f << 'z'; break; + case State::Sa: extmem_f << '_'; break; + case State::Sm: log_error("Found marker state in final netlist."); + } + } + extmem_f << '\n'; + } + } + + } + else { - f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i); - dump_const(f, cell->parameters["\\INIT"].extract(i*width, width)); - f << stringf(";\n"); + f << stringf("%s" "initial begin\n", indent.c_str()); + for (int i=0; i<size; i++) + { + f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i); + dump_const(f, cell->parameters["\\INIT"].extract(i*width, width)); + f << stringf(";\n"); + } + f << stringf("%s" "end\n", indent.c_str()); } - f << stringf("%s" "end\n", indent.c_str()); } // create a map : "edge clk" -> expressions within that clock domain @@ -944,43 +1149,46 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) use_rd_clk = cell->parameters["\\RD_CLK_ENABLE"].extract(i).as_bool(); rd_clk_posedge = cell->parameters["\\RD_CLK_POLARITY"].extract(i).as_bool(); rd_transparent = cell->parameters["\\RD_TRANSPARENT"].extract(i).as_bool(); + if (use_rd_clk) { - std::ostringstream os; - dump_sigspec(os, sig_rd_clk); - clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str()); - if( clk_to_lof_body.count(clk_domain_str) == 0 ) - clk_to_lof_body[clk_domain_str] = std::vector<std::string>(); - } - if (use_rd_clk && !rd_transparent) - { - // for clocked read ports make something like: - // reg [..] temp_id; - // always @(posedge clk) - // if (rd_en) temp_id <= array_reg[r_addr]; - // assign r_data = temp_id; - std::string temp_id = next_auto_id(); - lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) ); { std::ostringstream os; - if (sig_rd_en != RTLIL::SigBit(true)) + dump_sigspec(os, sig_rd_clk); + clk_domain_str = stringf("%sedge %s", rd_clk_posedge ? "pos" : "neg", os.str().c_str()); + if( clk_to_lof_body.count(clk_domain_str) == 0 ) + clk_to_lof_body[clk_domain_str] = std::vector<std::string>(); + } + if (!rd_transparent) + { + // for clocked read ports make something like: + // reg [..] temp_id; + // always @(posedge clk) + // if (rd_en) temp_id <= array_reg[r_addr]; + // assign r_data = temp_id; + std::string temp_id = next_auto_id(); + lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_data.size() - 1, temp_id.c_str()) ); { - os << stringf("if ("); - dump_sigspec(os, sig_rd_en); - os << stringf(") "); + std::ostringstream os; + if (sig_rd_en != RTLIL::SigBit(true)) + { + os << stringf("if ("); + dump_sigspec(os, sig_rd_en); + os << stringf(") "); + } + os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str()); + dump_sigspec(os, sig_rd_addr); + os << stringf("];\n"); + clk_to_lof_body[clk_domain_str].push_back(os.str()); + } + { + std::ostringstream os; + dump_sigspec(os, sig_rd_data); + std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str()); + clk_to_lof_body[""].push_back(line); } - os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str()); - dump_sigspec(os, sig_rd_addr); - os << stringf("];\n"); - clk_to_lof_body[clk_domain_str].push_back(os.str()); } + else { - std::ostringstream os; - dump_sigspec(os, sig_rd_data); - std::string line = stringf("assign %s = %s;\n", os.str().c_str(), temp_id.c_str()); - clk_to_lof_body[""].push_back(line); - } - } else { - if (rd_transparent) { // for rd-transparent read-ports make something like: // reg [..] temp_id; // always @(posedge clk) @@ -1000,15 +1208,15 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), temp_id.c_str()); clk_to_lof_body[""].push_back(line); } - } else { - // for non-clocked read-ports make something like: - // assign r_data = array_reg[r_addr]; - std::ostringstream os, os2; - dump_sigspec(os, sig_rd_data); - dump_sigspec(os2, sig_rd_addr); - std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); - clk_to_lof_body[""].push_back(line); } + } else { + // for non-clocked read-ports make something like: + // assign r_data = array_reg[r_addr]; + std::ostringstream os, os2; + dump_sigspec(os, sig_rd_data); + dump_sigspec(os2, sig_rd_addr); + std::string line = stringf("assign %s = %s[%s];\n", os.str().c_str(), mem_id.c_str(), os2.str().c_str()); + clk_to_lof_body[""].push_back(line); } } @@ -1110,6 +1318,118 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) return true; } + if (cell->type.in("$assert", "$assume", "$cover")) + { + f << stringf("%s" "always @* if (", indent.c_str()); + dump_sigspec(f, cell->getPort("\\EN")); + f << stringf(") %s(", cell->type.c_str()+1); + dump_sigspec(f, cell->getPort("\\A")); + f << stringf(");\n"); + return true; + } + + if (cell->type.in("$specify2", "$specify3")) + { + f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + + SigSpec en = cell->getPort("\\EN"); + if (en != State::S1) { + f << stringf("if ("); + dump_sigspec(f, cell->getPort("\\EN")); + f << stringf(") "); + } + + f << "("; + if (cell->type == "$specify3" && cell->getParam("\\EDGE_EN").as_bool()) + f << (cell->getParam("\\EDGE_POL").as_bool() ? "posedge ": "negedge "); + + dump_sigspec(f, cell->getPort("\\SRC")); + + f << " "; + if (cell->getParam("\\SRC_DST_PEN").as_bool()) + f << (cell->getParam("\\SRC_DST_POL").as_bool() ? "+": "-"); + f << (cell->getParam("\\FULL").as_bool() ? "*> ": "=> "); + + if (cell->type == "$specify3") { + f << "("; + dump_sigspec(f, cell->getPort("\\DST")); + f << " "; + if (cell->getParam("\\DAT_DST_PEN").as_bool()) + f << (cell->getParam("\\DAT_DST_POL").as_bool() ? "+": "-"); + f << ": "; + dump_sigspec(f, cell->getPort("\\DAT")); + f << ")"; + } else { + dump_sigspec(f, cell->getPort("\\DST")); + } + + bool bak_decimal = decimal; + decimal = 1; + + f << ") = ("; + dump_const(f, cell->getParam("\\T_RISE_MIN")); + f << ":"; + dump_const(f, cell->getParam("\\T_RISE_TYP")); + f << ":"; + dump_const(f, cell->getParam("\\T_RISE_MAX")); + f << ", "; + dump_const(f, cell->getParam("\\T_FALL_MIN")); + f << ":"; + dump_const(f, cell->getParam("\\T_FALL_TYP")); + f << ":"; + dump_const(f, cell->getParam("\\T_FALL_MAX")); + f << ");\n"; + + decimal = bak_decimal; + + f << stringf("%s" "endspecify\n", indent.c_str()); + return true; + } + + if (cell->type == "$specrule") + { + f << stringf("%s" "specify\n%s ", indent.c_str(), indent.c_str()); + + string spec_type = cell->getParam("\\TYPE").decode_string(); + f << stringf("%s(", spec_type.c_str()); + + if (cell->getParam("\\SRC_PEN").as_bool()) + f << (cell->getParam("\\SRC_POL").as_bool() ? "posedge ": "negedge "); + dump_sigspec(f, cell->getPort("\\SRC")); + + if (cell->getPort("\\SRC_EN") != State::S1) { + f << " &&& "; + dump_sigspec(f, cell->getPort("\\SRC_EN")); + } + + f << ", "; + if (cell->getParam("\\DST_PEN").as_bool()) + f << (cell->getParam("\\DST_POL").as_bool() ? "posedge ": "negedge "); + dump_sigspec(f, cell->getPort("\\DST")); + + if (cell->getPort("\\DST_EN") != State::S1) { + f << " &&& "; + dump_sigspec(f, cell->getPort("\\DST_EN")); + } + + bool bak_decimal = decimal; + decimal = 1; + + f << ", "; + dump_const(f, cell->getParam("\\T_LIMIT")); + + if (spec_type == "$setuphold" || spec_type == "$recrem" || spec_type == "$fullskew") { + f << ", "; + dump_const(f, cell->getParam("\\T_LIMIT2")); + } + + f << ");\n"; + decimal = bak_decimal; + + f << stringf("%s" "endspecify\n", indent.c_str()); + return true; + } + // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_ // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm @@ -1132,8 +1452,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (it != cell->parameters.begin()) f << stringf(","); f << stringf("\n%s .%s(", indent.c_str(), id(it->first).c_str()); - bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0; - dump_const(f, it->second, -1, 0, false, is_signed); + dump_const(f, it->second); f << stringf(")"); } f << stringf("\n%s" ")", indent.c_str()); @@ -1180,12 +1499,20 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell) if (defparam && cell->parameters.size() > 0) { for (auto it = cell->parameters.begin(); it != cell->parameters.end(); ++it) { f << stringf("%sdefparam %s.%s = ", indent.c_str(), cell_name.c_str(), id(it->first).c_str()); - bool is_signed = (it->second.flags & RTLIL::CONST_FLAG_SIGNED) != 0; - dump_const(f, it->second, -1, 0, false, is_signed); + dump_const(f, it->second); f << stringf(";\n"); } } + if (siminit && reg_ct.count(cell->type) && cell->hasPort("\\Q")) { + std::stringstream ss; + dump_reg_init(ss, cell->getPort("\\Q")); + if (!ss.str().empty()) { + f << stringf("%sinitial %s.Q", indent.c_str(), cell_name.c_str()); + f << ss.str(); + f << ";\n"; + } + } } void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) @@ -1238,12 +1565,14 @@ void dump_proc_switch(std::ostream &f, std::string indent, RTLIL::SwitchRule *sw return; } + dump_attributes(f, indent, sw->attributes); f << stringf("%s" "casez (", indent.c_str()); dump_sigspec(f, sw->signal); f << stringf(")\n"); bool got_default = false; for (auto it = sw->cases.begin(); it != sw->cases.end(); ++it) { + dump_attributes(f, indent + " ", (*it)->attributes, '\n', /*modattr=*/false, /*regattr=*/false, /*as_comment=*/true); if ((*it)->compare.size() == 0) { if (got_default) continue; @@ -1302,6 +1631,8 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo if (sync->type == RTLIL::STa) { f << stringf("%s" "always @* begin\n", indent.c_str()); + } else if (sync->type == RTLIL::STi) { + f << stringf("%s" "initial begin\n", indent.c_str()); } else { f << stringf("%s" "always @(", indent.c_str()); if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1) @@ -1362,14 +1693,15 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) SigSpec sig = active_sigmap(wire); Const val = wire->attributes.at("\\init"); for (int i = 0; i < GetSize(sig) && i < GetSize(val); i++) - active_initdata[sig[i]] = val.bits.at(i); + if (val[i] == State::S0 || val[i] == State::S1) + active_initdata[sig[i]] = val[i]; } if (!module->processes.empty()) - log_warning("Module %s contains unmapped RTLIL proccesses. RTLIL processes\n" + log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" "can't always be mapped directly to Verilog always blocks. Unintended\n" "changes in simulation behavior are possible! Use \"proc\" to convert\n" - "processes to logic networks and registers.", log_id(module)); + "processes to logic networks and registers.\n", log_id(module)); f << stringf("\n"); for (auto it = module->processes.begin(); it != module->processes.end(); ++it) @@ -1405,7 +1737,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) } } - dump_attributes(f, indent, module->attributes, '\n', true); + dump_attributes(f, indent, module->attributes, '\n', /*modattr=*/true); f << stringf("%s" "module %s(", indent.c_str(), id(module->name, false).c_str()); bool keep_running = true; for (int port_id = 1; keep_running; port_id++) { @@ -1446,7 +1778,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module) struct VerilogBackend : public Backend { VerilogBackend() : Backend("verilog", "write design to Verilog file") { } - virtual void help() + void help() YS_OVERRIDE { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); @@ -1472,9 +1804,13 @@ struct VerilogBackend : public Backend { log(" without this option all internal cells are converted to Verilog\n"); log(" expressions.\n"); log("\n"); + log(" -siminit\n"); + log(" add initial statements with hierarchical refs to initialize FFs when\n"); + log(" in -noexpr mode.\n"); + log("\n"); log(" -nodec\n"); log(" 32-bit constant values are by default dumped as decimal numbers,\n"); - log(" not bit pattern. This option decativates this feature and instead\n"); + log(" not bit pattern. This option deactivates this feature and instead\n"); log(" will write out all constants in binary.\n"); log("\n"); log(" -decimal\n"); @@ -1482,17 +1818,25 @@ struct VerilogBackend : public Backend { log("\n"); log(" -nohex\n"); log(" constant values that are compatible with hex output are usually\n"); - log(" dumped as hex values. This option decativates this feature and\n"); + log(" dumped as hex values. This option deactivates this feature and\n"); log(" instead will write out all constants in binary.\n"); log("\n"); log(" -nostr\n"); log(" Parameters and attributes that are specified as strings in the\n"); log(" original input will be output as strings by this back-end. This\n"); - log(" decativates this feature and instead will write string constants\n"); + log(" deactivates this feature and instead will write string constants\n"); log(" as binary numbers.\n"); log("\n"); + log(" -extmem\n"); + log(" instead of initializing memories using assignments to individual\n"); + log(" elements, use the '$readmemh' function to read initialization data\n"); + log(" from a file. This data is written to a file named by appending\n"); + log(" a sequential index to the Verilog filename and replacing the extension\n"); + log(" with '.mem', e.g. 'write_verilog -extmem foo.v' writes 'foo-1.mem',\n"); + log(" 'foo-2.mem' and so on.\n"); + log("\n"); log(" -defparam\n"); - log(" Use 'defparam' statements instead of the Verilog-2001 syntax for\n"); + log(" use 'defparam' statements instead of the Verilog-2001 syntax for\n"); log(" cell parameters.\n"); log("\n"); log(" -blackboxes\n"); @@ -1514,7 +1858,7 @@ struct VerilogBackend : public Backend { log("this command is called on a design with RTLIL processes.\n"); log("\n"); } - virtual void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) + void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { log_header(design, "Executing Verilog backend.\n"); @@ -1526,17 +1870,23 @@ struct VerilogBackend : public Backend { nodec = false; nohex = false; nostr = false; + extmem = false; defparam = false; decimal = false; + siminit = false; auto_prefix = ""; bool blackboxes = false; bool selected = false; + auto_name_map.clear(); + reg_wires.clear(); reg_ct.clear(); reg_ct.insert("$dff"); reg_ct.insert("$adff"); + reg_ct.insert("$dffe"); + reg_ct.insert("$dlatch"); reg_ct.insert("$_DFF_N_"); reg_ct.insert("$_DFF_P_"); @@ -1594,6 +1944,11 @@ struct VerilogBackend : public Backend { nostr = true; continue; } + if (arg == "-extmem") { + extmem = true; + extmem_counter = 1; + continue; + } if (arg == "-defparam") { defparam = true; continue; @@ -1602,6 +1957,10 @@ struct VerilogBackend : public Backend { decimal = true; continue; } + if (arg == "-siminit") { + siminit = true; + continue; + } if (arg == "-blackboxes") { blackboxes = true; continue; @@ -1617,12 +1976,18 @@ struct VerilogBackend : public Backend { break; } extra_args(f, filename, args, argidx); + if (extmem) + { + if (filename.empty()) + log_cmd_error("Option -extmem must be used with a filename.\n"); + extmem_prefix = filename.substr(0, filename.rfind('.')); + } design->sort(); *f << stringf("/* Generated by %s */\n", yosys_version_str); for (auto it = design->modules_.begin(); it != design->modules_.end(); ++it) { - if (it->second->get_bool_attribute("\\blackbox") != blackboxes) + if (it->second->get_blackbox_attribute() != blackboxes) continue; if (selected && !design->selected_whole_module(it->first)) { if (design->selected_module(it->first)) @@ -1633,6 +1998,8 @@ struct VerilogBackend : public Backend { dump_module(*f, "", it->second); } + auto_name_map.clear(); + reg_wires.clear(); reg_ct.clear(); } } VerilogBackend; |