aboutsummaryrefslogtreecommitdiffstats
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/aiger/aiger.cc12
-rw-r--r--backends/aiger/xaiger.cc193
-rw-r--r--backends/btor/btor.cc14
-rw-r--r--backends/btor/test_cells.sh2
-rw-r--r--backends/cxxrtl/cxxrtl.cc139
-rw-r--r--backends/firrtl/firrtl.cc68
-rw-r--r--backends/smt2/smt2.cc10
-rw-r--r--backends/smt2/smtbmc.py11
-rw-r--r--backends/smt2/smtio.py37
-rw-r--r--backends/smv/smv.cc5
-rw-r--r--backends/smv/test_cells.sh4
-rw-r--r--backends/verilog/verilog_backend.cc89
12 files changed, 379 insertions, 205 deletions
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index cac32a8da..e5a41b5c5 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -629,30 +629,30 @@ struct AigerWriter
int a = aig_map.at(sig[i]);
if (verbose_map)
- wire_lines[a] += stringf("wire %d %d %s\n", a, i, log_id(wire));
+ wire_lines[a] += stringf("wire %d %d %s\n", a, wire->start_offset+i, log_id(wire));
if (wire->port_input) {
log_assert((a & 1) == 0);
- input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, i, log_id(wire));
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));
}
if (wire->port_output) {
int o = ordered_outputs.at(sig[i]);
- output_lines[o] += stringf("output %d %d %s\n", o, i, log_id(wire));
+ output_lines[o] += stringf("output %d %d %s\n", o, wire->start_offset+i, log_id(wire));
}
if (init_inputs.count(sig[i])) {
int a = init_inputs.at(sig[i]);
log_assert((a & 1) == 0);
- init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, i, log_id(wire));
+ init_lines[a] += stringf("init %d %d %s\n", (a >> 1)-1, wire->start_offset+i, log_id(wire));
}
if (ordered_latches.count(sig[i])) {
int l = ordered_latches.at(sig[i]);
if (zinit_mode && (aig_latchinit.at(l) == 1))
- latch_lines[l] += stringf("invlatch %d %d %s\n", l, i, log_id(wire));
+ latch_lines[l] += stringf("invlatch %d %d %s\n", l, wire->start_offset+i, log_id(wire));
else
- latch_lines[l] += stringf("latch %d %d %s\n", l, i, log_id(wire));
+ latch_lines[l] += stringf("latch %d %d %s\n", l, wire->start_offset+i, log_id(wire));
}
}
}
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 3c7c745fe..4a8f54c4d 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -76,9 +76,11 @@ void aiger_encode(std::ostream &f, int x)
struct XAigerWriter
{
+ Design *design;
Module *module;
SigMap sigmap;
+ dict<SigBit, State> init_map;
pool<SigBit> input_bits, output_bits;
dict<SigBit, SigBit> not_map, alias_map;
dict<SigBit, pair<SigBit, SigBit>> and_map;
@@ -137,7 +139,7 @@ struct XAigerWriter
return a;
}
- XAigerWriter(Module *module, bool holes_mode=false) : module(module), sigmap(module)
+ XAigerWriter(Module *module, bool dff_mode) : design(module->design), module(module), sigmap(module)
{
pool<SigBit> undriven_bits;
pool<SigBit> unused_bits;
@@ -154,10 +156,11 @@ struct XAigerWriter
// promote keep wires
for (auto wire : module->wires())
- if (wire->get_bool_attribute(ID::keep))
+ if (wire->get_bool_attribute(ID::keep) || wire->get_bool_attribute(ID::abc9_keep))
sigmap.add(wire);
- for (auto wire : module->wires())
+ for (auto wire : module->wires()) {
+ auto it = wire->attributes.find(ID::init);
for (int i = 0; i < GetSize(wire); i++)
{
SigBit wirebit(wire, i);
@@ -174,17 +177,27 @@ struct XAigerWriter
undriven_bits.insert(bit);
unused_bits.insert(bit);
- bool scc = wire->attributes.count(ID::abc9_scc);
- if (wire->port_input || scc)
+ bool keep = wire->get_bool_attribute(ID::abc9_keep);
+ if (wire->port_input || keep)
input_bits.insert(bit);
- bool keep = wire->get_bool_attribute(ID::keep);
- if (wire->port_output || keep || scc) {
+ keep = keep || wire->get_bool_attribute(ID::keep);
+ if (wire->port_output || keep) {
if (bit != wirebit)
alias_map[wirebit] = bit;
output_bits.insert(wirebit);
}
+
+ if (it != wire->attributes.end()) {
+ auto s = it->second[i];
+ if (s != State::Sx) {
+ auto r = init_map.insert(std::make_pair(bit, it->second[i]));
+ if (!r.second && r.first->second != it->second[i])
+ log_error("Bit '%s' has a conflicting (* init *) value.\n", log_signal(bit));
+ }
+ }
}
+ }
TimingInfo timing;
@@ -212,10 +225,7 @@ struct XAigerWriter
continue;
}
- if (cell->type == ID($__ABC9_FF_) &&
- // The presence of an abc9_mergeability attribute indicates
- // that we do want to pass this flop to ABC
- cell->attributes.count(ID::abc9_mergeability))
+ if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep))
{
SigBit D = sigmap(cell->getPort(ID::D).as_bit());
SigBit Q = sigmap(cell->getPort(ID::Q).as_bit());
@@ -231,31 +241,29 @@ struct XAigerWriter
continue;
}
- RTLIL::Module* inst_module = module->design->module(cell->type);
- if (inst_module) {
- IdString derived_type = inst_module->derive(module->design, cell->parameters);
- inst_module = module->design->module(derived_type);
- log_assert(inst_module);
-
+ RTLIL::Module* inst_module = design->module(cell->type);
+ if (inst_module && inst_module->get_blackbox_attribute()) {
bool abc9_flop = false;
- if (!cell->has_keep_attr()) {
- auto it = cell->attributes.find(ID::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
- // (all others are combinatorial)
- abc9_flop = inst_module->get_bool_attribute(ID::abc9_flop);
- if (!abc9_flop)
- continue;
- }
+
+ auto it = cell->attributes.find(ID::abc9_box_seq);
+ if (it != cell->attributes.end()) {
+ log_assert(!cell->has_keep_attr());
+ log_assert(cell->parameters.empty());
+ 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
+ // (all others are combinatorial)
+ log_assert(cell->parameters.empty());
+ abc9_flop = inst_module->get_bool_attribute(ID::abc9_flop);
+ if (!abc9_flop)
+ continue;
}
- if (!timing.count(derived_type))
+ if (!timing.count(inst_module->name))
timing.setup_module(inst_module);
- auto &t = timing.at(derived_type).arrival;
+ auto &t = timing.at(inst_module->name).arrival;
for (const auto &conn : cell->connections()) {
auto port_wire = inst_module->wire(conn.first);
if (!port_wire->port_output)
@@ -269,7 +277,7 @@ struct XAigerWriter
#ifndef NDEBUG
if (ys_debug(1)) {
static std::set<std::tuple<IdString,IdString,int>> seen;
- if (seen.emplace(derived_type, conn.first, i).second) log("%s.%s[%d] abc9_arrival = %d\n",
+ if (seen.emplace(inst_module->name, conn.first, i).second) log("%s.%s[%d] abc9_arrival = %d\n",
log_id(cell->type), log_id(conn.first), i, d);
}
#endif
@@ -280,10 +288,6 @@ struct XAigerWriter
if (abc9_flop)
continue;
}
- else {
- if (cell->type == ID($__ABC9_DELAY))
- log_error("Cell type '%s' not recognised. Check that '+/abc9_model.v' has been read.\n", cell->type.c_str());
- }
bool cell_known = inst_module || cell->known();
for (const auto &c : cell->connections()) {
@@ -317,9 +321,9 @@ struct XAigerWriter
for (auto cell : box_list) {
log_assert(cell);
- RTLIL::Module* box_module = module->design->module(cell->type);
+ RTLIL::Module* box_module = design->module(cell->type);
log_assert(box_module);
- log_assert(box_module->attributes.count(ID::abc9_box_id) || box_module->get_bool_attribute(ID::abc9_flop));
+ log_assert(box_module->has_attribute(ID::abc9_box_id));
auto r = box_ports.insert(cell->type);
if (r.second) {
@@ -383,27 +387,6 @@ struct XAigerWriter
undriven_bits.erase(O);
}
}
-
- // Connect <cell>.abc9_ff.Q (inserted by abc9_map.v) as the last input to the flop box
- if (box_module->get_bool_attribute(ID::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)
@@ -419,16 +402,14 @@ struct XAigerWriter
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());
- }
+ 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;
@@ -589,17 +570,14 @@ struct XAigerWriter
int box_count = 0;
for (auto cell : box_list) {
log_assert(cell);
+ log_assert(cell->parameters.empty());
- RTLIL::Module* box_module = module->design->module(cell->type);
- log_assert(box_module);
-
- IdString derived_type = box_module->derive(box_module->design, cell->parameters);
- box_module = box_module->design->module(derived_type);
- log_assert(box_module);
-
- auto r = cell_cache.insert(derived_type);
+ auto r = cell_cache.insert(cell->type);
auto &v = r.first->second;
if (r.second) {
+ RTLIL::Module* box_module = design->module(cell->type);
+ log_assert(box_module);
+
int box_inputs = 0, box_outputs = 0;
for (auto port_name : box_module->ports) {
RTLIL::Wire *w = box_module->wire(port_name);
@@ -610,11 +588,6 @@ struct XAigerWriter
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(ID::abc9_flop))
- box_inputs++;
-
std::get<0>(v) = box_inputs;
std::get<1>(v) = box_outputs;
std::get<2>(v) = box_module->attributes.at(ID::abc9_box_id).as_int();
@@ -635,23 +608,27 @@ struct XAigerWriter
auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);
write_s_buffer(ff_bits.size());
+ dict<SigSpec, int> clk_to_mergeability;
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();
+ SigSpec clk_and_pol{sigmap(cell->getPort(ID::C)), cell->type[6] == 'P' ? State::S1 : State::S0};
+ auto r = clk_to_mergeability.insert(std::make_pair(clk_and_pol, clk_to_mergeability.size()+1));
+ int mergeability = r.first->second;
log_assert(mergeability > 0);
write_r_buffer(mergeability);
- Const init = cell->attributes.at(ID::abc9_init, State::Sx);
- log_assert(GetSize(init) == 1);
+ SigBit Q = sigmap(cell->getPort(ID::Q));
+ State init = init_map.at(Q, State::Sx);
+ log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init));
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_s_buffer(2);
}
// Use arrival time from output of flop box
@@ -671,10 +648,16 @@ struct XAigerWriter
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()));
+ RTLIL::Design *holes_design;
+ auto it = saved_designs.find("$abc9_holes");
+ if (it != saved_designs.end())
+ holes_design = it->second;
+ else
+ holes_design = nullptr;
+ RTLIL::Module *holes_module = holes_design ? holes_design->module(module->name) : nullptr;
if (holes_module) {
std::stringstream a_buffer;
- XAigerWriter writer(holes_module, true /* holes_mode */);
+ XAigerWriter writer(holes_module, false /* dff_mode */);
writer.write_aiger(a_buffer, false /*ascii_mode*/);
f << "a";
@@ -704,10 +687,10 @@ struct XAigerWriter
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());
+ design->scratchpad_set_int("write_xaiger.num_ands", and_map.size());
+ design->scratchpad_set_int("write_xaiger.num_wires", aig_map.size());
+ design->scratchpad_set_int("write_xaiger.num_inputs", input_bits.size());
+ design->scratchpad_set_int("write_xaiger.num_outputs", output_bits.size());
}
void write_map(std::ostream &f)
@@ -725,13 +708,12 @@ struct XAigerWriter
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));
+ input_lines[a] += stringf("input %d %d %s\n", (a >> 1)-1, wire->start_offset+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);
+ output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire));
continue;
}
}
@@ -762,10 +744,10 @@ struct XAigerBackend : public Backend {
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("is currently selected) to an XAIGER file. Any non $_NOT_, $_AND_, (optionally\n");
+ log("$_DFF_N_, $_DFF_P_), or non (* abc9_box *) cells will be converted into psuedo-\n");
+ log("inputs and pseudo-outputs. Whitebox contents will be taken from the equivalent\n");
+ log("module in the '$abc9_holes' design, if it exists.\n");
log("\n");
log(" -ascii\n");
log(" write ASCII version of AIGER format\n");
@@ -773,10 +755,13 @@ struct XAigerBackend : public Backend {
log(" -map <filename>\n");
log(" write an extra file with port and box symbols\n");
log("\n");
+ log(" -dff\n");
+ log(" write $_DFF_[NP]_ cells\n");
+ log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
- bool ascii_mode = false;
+ bool ascii_mode = false, dff_mode = false;
std::string map_filename;
log_header(design, "Executing XAIGER backend.\n");
@@ -792,6 +777,10 @@ struct XAigerBackend : public Backend {
map_filename = args[++argidx];
continue;
}
+ if (args[argidx] == "-dff") {
+ dff_mode = true;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx, !ascii_mode);
@@ -809,7 +798,7 @@ struct XAigerBackend : public Backend {
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);
+ XAigerWriter writer(top_module, dff_mode);
writer.write_aiger(*f, ascii_mode);
if (!map_filename.empty()) {
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 14c8484e8..2816d3246 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -266,20 +266,26 @@ struct BtorWorker
goto okay;
}
- if (cell->type.in(ID($div), ID($mod)))
+ if (cell->type.in(ID($div), ID($mod), ID($modfloor)))
{
+ bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
+ bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
+
string btor_op;
if (cell->type == ID($div)) btor_op = "div";
+ // "rem" = truncating modulo
if (cell->type == ID($mod)) btor_op = "rem";
+ // "mod" = flooring modulo
+ if (cell->type == ID($modfloor)) {
+ // "umod" doesn't exist because it's the same as "urem"
+ btor_op = a_signed || b_signed ? "mod" : "rem";
+ }
log_assert(!btor_op.empty());
int width = GetSize(cell->getPort(ID::Y));
width = std::max(width, GetSize(cell->getPort(ID::A)));
width = std::max(width, GetSize(cell->getPort(ID::B)));
- bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
- bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
-
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh
index e0f1a0514..3f077201a 100644
--- a/backends/btor/test_cells.sh
+++ b/backends/btor/test_cells.sh
@@ -6,7 +6,7 @@ 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'
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor'
for fn in test_*.il; do
../../../yosys -p "
diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc
index 237700b29..0cceecbba 100644
--- a/backends/cxxrtl/cxxrtl.cc
+++ b/backends/cxxrtl/cxxrtl.cc
@@ -212,14 +212,14 @@ bool is_ff_cell(RTLIL::IdString type)
bool is_internal_cell(RTLIL::IdString type)
{
- return type[0] == '$' && !type.begins_with("$paramod\\");
+ return type[0] == '$' && !type.begins_with("$paramod");
}
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
{
RTLIL::Module *cell_module = cell->module->design->module(cell->type);
log_assert(cell_module != nullptr);
- return cell_module->get_bool_attribute(ID(cxxrtl.blackbox));
+ return cell_module->get_bool_attribute(ID(cxxrtl_blackbox));
}
enum class CxxrtlPortType {
@@ -231,14 +231,14 @@ enum class CxxrtlPortType {
CxxrtlPortType cxxrtl_port_type(const RTLIL::Cell *cell, RTLIL::IdString port)
{
RTLIL::Module *cell_module = cell->module->design->module(cell->type);
- if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ if (cell_module == nullptr || !cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
return CxxrtlPortType::UNKNOWN;
RTLIL::Wire *cell_output_wire = cell_module->wire(port);
log_assert(cell_output_wire != nullptr);
- bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl.comb));
- bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl.sync));
+ bool is_comb = cell_output_wire->get_bool_attribute(ID(cxxrtl_comb));
+ bool is_sync = cell_output_wire->get_bool_attribute(ID(cxxrtl_sync));
if (is_comb && is_sync)
- log_cmd_error("Port `%s.%s' is marked as both `cxxrtl.comb` and `cxxrtl.sync`.\n",
+ log_cmd_error("Port `%s.%s' is marked as both `cxxrtl_comb` and `cxxrtl_sync`.\n",
log_id(cell_module), log_signal(cell_output_wire));
else if (is_comb)
return CxxrtlPortType::COMB;
@@ -513,7 +513,6 @@ struct CxxrtlWorker {
bool elide_public = false;
bool localize_internal = false;
bool localize_public = false;
- bool run_opt_clean_purge = false;
bool run_proc_flatten = false;
bool max_opt_level = false;
@@ -606,7 +605,7 @@ struct CxxrtlWorker {
std::string mangle(const RTLIL::Module *module)
{
- return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl.blackbox)));
+ return mangle_module_name(module->name, /*is_blackbox=*/module->get_bool_attribute(ID(cxxrtl_blackbox)));
}
std::string mangle(const RTLIL::Memory *memory)
@@ -634,19 +633,19 @@ struct CxxrtlWorker {
std::vector<std::string> template_param_names(const RTLIL::Module *module)
{
- if (!module->has_attribute(ID(cxxrtl.template)))
+ if (!module->has_attribute(ID(cxxrtl_template)))
return {};
- if (module->attributes.at(ID(cxxrtl.template)).flags != RTLIL::CONST_FLAG_STRING)
- log_cmd_error("Attribute `cxxrtl.template' of module `%s' is not a string.\n", log_id(module));
+ if (module->attributes.at(ID(cxxrtl_template)).flags != RTLIL::CONST_FLAG_STRING)
+ log_cmd_error("Attribute `cxxrtl_template' of module `%s' is not a string.\n", log_id(module));
- std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl.template)), " \t");
+ std::vector<std::string> param_names = split_by(module->get_string_attribute(ID(cxxrtl_template)), " \t");
for (const auto &param_name : param_names) {
// Various lowercase prefixes (p_, i_, cell_, ...) are used for member variables, so require
// parameters to start with an uppercase letter to avoid name conflicts. (This is the convention
// in both Verilog and C++, anyway.)
if (!isupper(param_name[0]))
- log_cmd_error("Attribute `cxxrtl.template' of module `%s' includes a parameter `%s', "
+ log_cmd_error("Attribute `cxxrtl_template' of module `%s' includes a parameter `%s', "
"which does not start with an uppercase letter.\n",
log_id(module), param_name.c_str());
}
@@ -677,7 +676,7 @@ struct CxxrtlWorker {
{
RTLIL::Module *cell_module = cell->module->design->module(cell->type);
log_assert(cell_module != nullptr);
- if (!cell_module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ if (!cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
return "";
std::vector<std::string> param_names = template_param_names(cell_module);
@@ -726,12 +725,13 @@ struct CxxrtlWorker {
void dump_const_init(const RTLIL::Const &data, int width, int offset = 0, bool fixed_width = false)
{
+ const int CHUNK_SIZE = 32;
f << "{";
while (width > 0) {
- const int CHUNK_SIZE = 32;
- uint32_t chunk = data.extract(offset, width > CHUNK_SIZE ? CHUNK_SIZE : width).as_int();
+ int chunk_width = min(width, CHUNK_SIZE);
+ uint32_t chunk = data.extract(offset, chunk_width).as_int();
if (fixed_width)
- f << stringf("0x%08xu", chunk);
+ f << stringf("0x%.*xu", (3 + chunk_width) / 4, chunk);
else
f << stringf("%#xu", chunk);
if (width > CHUNK_SIZE)
@@ -1418,8 +1418,8 @@ struct CxxrtlWorker {
f << indent << "value<" << wire->width << "> " << mangle(wire) << ";\n";
} else {
std::string width;
- if (wire->module->has_attribute(ID(cxxrtl.blackbox)) && wire->has_attribute(ID(cxxrtl.width))) {
- width = wire->get_string_attribute(ID(cxxrtl.width));
+ if (wire->module->has_attribute(ID(cxxrtl_blackbox)) && wire->has_attribute(ID(cxxrtl_width))) {
+ width = wire->get_string_attribute(ID(cxxrtl_width));
} else {
width = std::to_string(wire->width);
}
@@ -1521,7 +1521,7 @@ struct CxxrtlWorker {
{
inc_indent();
f << indent << "bool converged = " << (eval_converges.at(module) ? "true" : "false") << ";\n";
- if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto wire : module->wires()) {
if (edge_wires[wire]) {
for (auto edge_type : edge_types) {
@@ -1573,10 +1573,10 @@ struct CxxrtlWorker {
f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
continue;
}
- if (!module->get_bool_attribute(ID(cxxrtl.blackbox)) || wire->port_id != 0)
+ if (!module->get_bool_attribute(ID(cxxrtl_blackbox)) || wire->port_id != 0)
f << indent << "changed |= " << mangle(wire) << ".commit();\n";
}
- if (!module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto memory : module->memories) {
if (!writable_memories[memory.second])
continue;
@@ -1623,8 +1623,8 @@ struct CxxrtlWorker {
void dump_module_intf(RTLIL::Module *module)
{
dump_attrs(module);
- if (module->get_bool_attribute(ID(cxxrtl.blackbox))) {
- if (module->has_attribute(ID(cxxrtl.template)))
+ if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {
+ if (module->has_attribute(ID(cxxrtl_template)))
f << indent << "template" << template_params(module, /*is_decl=*/true) << "\n";
f << indent << "struct " << mangle(module) << " : public module {\n";
inc_indent();
@@ -1686,7 +1686,7 @@ struct CxxrtlWorker {
dump_attrs(cell);
RTLIL::Module *cell_module = module->design->module(cell->type);
log_assert(cell_module != nullptr);
- if (cell_module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> ";
f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell);
f << "::create(" << escape_cxx_string(cell->name.str()) << ", ";
@@ -1711,7 +1711,7 @@ struct CxxrtlWorker {
void dump_module_impl(RTLIL::Module *module)
{
- if (module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
return;
f << indent << "bool " << mangle(module) << "::eval() {\n";
dump_eval_method(module);
@@ -1730,9 +1730,9 @@ struct CxxrtlWorker {
for (auto module : design->modules()) {
if (!design->selected_module(module))
continue;
- if (module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ if (module->get_bool_attribute(ID(cxxrtl_blackbox)))
modules.push_back(module); // cxxrtl blackboxes first
- if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox)))
continue;
topo_design.node(module);
@@ -1821,16 +1821,16 @@ struct CxxrtlWorker {
SigMap &sigmap = sigmaps[module];
sigmap.set(module);
- if (module->get_bool_attribute(ID(cxxrtl.blackbox))) {
+ if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto port : module->ports) {
RTLIL::Wire *wire = module->wire(port);
- if (wire->has_attribute(ID(cxxrtl.edge))) {
- RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl.edge)];
+ if (wire->has_attribute(ID(cxxrtl_edge))) {
+ RTLIL::Const edge_attr = wire->attributes[ID(cxxrtl_edge)];
if (!(edge_attr.flags & RTLIL::CONST_FLAG_STRING) || (int)edge_attr.decode_string().size() != GetSize(wire))
- log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' is not a string with one character per bit.\n",
+ log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' is not a string with one character per bit.\n",
log_id(module), log_signal(wire));
- std::string edges = wire->get_string_attribute(ID(cxxrtl.edge));
+ std::string edges = wire->get_string_attribute(ID(cxxrtl_edge));
for (int i = 0; i < GetSize(wire); i++) {
RTLIL::SigSpec wire_sig = wire;
switch (edges[i]) {
@@ -1839,7 +1839,7 @@ struct CxxrtlWorker {
case 'n': register_edge_signal(sigmap, wire_sig[i], RTLIL::STn); break;
case 'a': register_edge_signal(sigmap, wire_sig[i], RTLIL::STe); break;
default:
- log_cmd_error("Attribute `cxxrtl.edge' of port `%s.%s' contains specifiers "
+ log_cmd_error("Attribute `cxxrtl_edge' of port `%s.%s' contains specifiers "
"other than '-', 'p', 'n', or 'a'.\n",
log_id(module), log_signal(wire));
}
@@ -1868,12 +1868,12 @@ struct CxxrtlWorker {
RTLIL::Module *cell_module = design->module(cell->type);
if (cell_module &&
cell_module->get_blackbox_attribute() &&
- !cell_module->get_bool_attribute(ID(cxxrtl.blackbox)))
+ !cell_module->get_bool_attribute(ID(cxxrtl_blackbox)))
log_cmd_error("External blackbox cell `%s' is not marked as a CXXRTL blackbox.\n", log_id(cell->type));
if (cell_module &&
- cell_module->get_bool_attribute(ID(cxxrtl.blackbox)) &&
- cell_module->get_bool_attribute(ID(cxxrtl.template)))
+ cell_module->get_bool_attribute(ID(cxxrtl_blackbox)) &&
+ cell_module->get_bool_attribute(ID(cxxrtl_template)))
blackbox_specializations[cell_module].insert(template_args(cell));
FlowGraph::Node *node = flow.add_node(cell);
@@ -1942,13 +1942,13 @@ struct CxxrtlWorker {
case RTLIL::STa:
break;
+ case RTLIL::STg:
+ log_cmd_error("Global clock is not supported.\n");
+
// Handling of init-type sync rules is delegated to the `proc_init` pass, so we can use the wire
// attribute regardless of input.
case RTLIL::STi:
log_assert(false);
-
- case RTLIL::STg:
- log_cmd_error("Global clock is not supported.\n");
}
}
@@ -2008,6 +2008,7 @@ struct CxxrtlWorker {
log("Module `%s' contains feedback arcs through wires:\n", log_id(module));
for (auto wire : feedback_wires)
log(" %s\n", log_id(wire));
+ log("\n");
}
for (auto wire : module->wires()) {
@@ -2039,20 +2040,20 @@ struct CxxrtlWorker {
log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));
for (auto wire : buffered_wires)
log(" %s\n", log_id(wire));
+ log("\n");
}
eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
}
if (has_feedback_arcs || has_buffered_wires) {
// Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
- // by optimizing the design, if after `opt_clean -purge` there are any feedback wires remaining, it is very
+ // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very
// likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message.
const char *why_pessimistic = nullptr;
if (has_feedback_arcs)
why_pessimistic = "feedback wires";
else if (has_buffered_wires)
why_pessimistic = "buffered combinatorial wires";
- log("\n");
log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic);
if (!max_opt_level)
log("Increasing the optimization level may eliminate %s from the design.\n", why_pessimistic);
@@ -2064,7 +2065,7 @@ struct CxxrtlWorker {
has_sync_init = has_packed_mem = false;
for (auto module : design->modules()) {
- if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl.blackbox)))
+ if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox)))
continue;
if (!design->selected_whole_module(module))
@@ -2086,34 +2087,39 @@ struct CxxrtlWorker {
void prepare_design(RTLIL::Design *design)
{
+ bool did_anything = false;
bool has_sync_init, has_packed_mem;
log_push();
check_design(design, has_sync_init, has_packed_mem);
if (run_proc_flatten) {
Pass::call(design, "proc");
Pass::call(design, "flatten");
+ did_anything = true;
} else if (has_sync_init) {
// We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those
// in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.)
Pass::call(design, "proc_prune");
Pass::call(design, "proc_clean");
Pass::call(design, "proc_init");
+ did_anything = true;
}
- if (has_packed_mem)
+ if (has_packed_mem) {
Pass::call(design, "memory_unpack");
+ did_anything = true;
+ }
// Recheck the design if it was modified.
if (has_sync_init || has_packed_mem)
check_design(design, has_sync_init, has_packed_mem);
log_assert(!(has_sync_init || has_packed_mem));
- if (run_opt_clean_purge)
- Pass::call(design, "opt_clean -purge");
log_pop();
+ if (did_anything)
+ log_spacer();
analyze_design(design);
}
};
struct CxxrtlBackend : public Backend {
- static const int DEFAULT_OPT_LEVEL = 6;
+ static const int DEFAULT_OPT_LEVEL = 5;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
void help() YS_OVERRIDE
@@ -2155,12 +2161,12 @@ struct CxxrtlBackend : public Backend {
log("For example, the following Verilog code defines a CXXRTL black box interface for\n");
log("a synchronous debug sink:\n");
log("\n");
- log(" (* cxxrtl.blackbox *)\n");
+ log(" (* cxxrtl_blackbox *)\n");
log(" module debug(...);\n");
- log(" (* cxxrtl.edge = \"p\" *) input clk;\n");
+ log(" (* cxxrtl_edge = \"p\" *) input clk;\n");
log(" input en;\n");
log(" input [7:0] i_data;\n");
- log(" (* cxxrtl.sync *) output [7:0] o_data;\n");
+ log(" (* cxxrtl_sync *) output [7:0] o_data;\n");
log(" endmodule\n");
log("\n");
log("For this HDL interface, this backend will generate the following C++ interface:\n");
@@ -2205,13 +2211,13 @@ struct CxxrtlBackend : public Backend {
log("port widths. For example, the following Verilog code defines a CXXRTL black box\n");
log("interface for a configurable width debug sink:\n");
log("\n");
- log(" (* cxxrtl.blackbox, cxxrtl.template = \"WIDTH\" *)\n");
+ log(" (* cxxrtl_blackbox, cxxrtl_template = \"WIDTH\" *)\n");
log(" module debug(...);\n");
log(" parameter WIDTH = 8;\n");
- log(" (* cxxrtl.edge = \"p\" *) input clk;\n");
+ log(" (* cxxrtl_edge = \"p\" *) input clk;\n");
log(" input en;\n");
- log(" (* cxxrtl.width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n");
- log(" (* cxxrtl.width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n");
+ log(" (* cxxrtl_width = \"WIDTH\" *) input [WIDTH - 1:0] i_data;\n");
+ log(" (* cxxrtl_width = \"WIDTH\" *) output [WIDTH - 1:0] o_data;\n");
log(" endmodule\n");
log("\n");
log("For this parametric HDL interface, this backend will generate the following C++\n");
@@ -2245,27 +2251,27 @@ struct CxxrtlBackend : public Backend {
log("\n");
log("The following attributes are recognized by this backend:\n");
log("\n");
- log(" cxxrtl.blackbox\n");
+ log(" cxxrtl_blackbox\n");
log(" only valid on modules. if specified, the module contents are ignored,\n");
log(" and the generated code includes only the module interface and a factory\n");
log(" function, which will be called to instantiate the module.\n");
log("\n");
- log(" cxxrtl.edge\n");
+ log(" cxxrtl_edge\n");
log(" only valid on inputs of black boxes. must be one of \"p\", \"n\", \"a\".\n");
log(" if specified on signal `clk`, the generated code includes edge detectors\n");
log(" `posedge_p_clk()` (if \"p\"), `negedge_p_clk()` (if \"n\"), or both (if\n");
log(" \"a\"), simplifying implementation of clocked black boxes.\n");
log("\n");
- log(" cxxrtl.template\n");
+ log(" cxxrtl_template\n");
log(" only valid on black boxes. must contain a space separated sequence of\n");
log(" identifiers that have a corresponding black box parameters. for each\n");
log(" of them, the generated code includes a `size_t` template parameter.\n");
log("\n");
- log(" cxxrtl.width\n");
+ log(" cxxrtl_width\n");
log(" only valid on ports of black boxes. must be a constant expression, which\n");
log(" is directly inserted into generated code.\n");
log("\n");
- log(" cxxrtl.comb, cxxrtl.sync\n");
+ log(" cxxrtl_comb, cxxrtl_sync\n");
log(" only valid on outputs of black boxes. if specified, indicates that every\n");
log(" bit of the output port is driven, correspondingly, by combinatorial or\n");
log(" synchronous logic. this knowledge is used for scheduling optimizations.\n");
@@ -2305,10 +2311,7 @@ struct CxxrtlBackend : public Backend {
log(" like -O3, and localize public wires not marked (*keep*) if possible.\n");
log("\n");
log(" -O5\n");
- log(" like -O4, and run `opt_clean -purge` first.\n");
- log("\n");
- log(" -O6\n");
- log(" like -O5, and run `proc; flatten` first.\n");
+ log(" like -O4, and run `proc; flatten` first.\n");
log("\n");
}
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -2342,19 +2345,23 @@ struct CxxrtlBackend : public Backend {
extra_args(f, filename, args, argidx);
switch (opt_level) {
- case 6:
+ // the highest level here must match DEFAULT_OPT_LEVEL
+ case 5:
worker.max_opt_level = true;
worker.run_proc_flatten = true;
- case 5:
- worker.run_opt_clean_purge = true;
+ YS_FALLTHROUGH
case 4:
worker.localize_public = true;
+ YS_FALLTHROUGH
case 3:
worker.elide_public = true;
+ YS_FALLTHROUGH
case 2:
worker.localize_internal = true;
+ YS_FALLTHROUGH
case 1:
worker.elide_internal = true;
+ YS_FALLTHROUGH
case 0:
break;
default:
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index 40d05a036..b1d8500bb 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -343,7 +343,7 @@ struct FirrtlWorker
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 */
+ YS_FALLTHROUGH
case FD_OUT:
sourceExpr = firstName;
sinkExpr = secondExpr;
@@ -351,7 +351,7 @@ struct FirrtlWorker
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 */
+ YS_FALLTHROUGH
case FD_IN:
sourceExpr = secondExpr;
sinkExpr = firstName;
@@ -392,7 +392,34 @@ struct FirrtlWorker
return result;
}
- void run()
+ void emit_extmodule()
+ {
+ std::string moduleFileinfo = getFileinfo(module);
+ f << stringf(" extmodule %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
+ vector<std::string> port_decls;
+
+ for (auto wire : module->wires())
+ {
+ const auto wireName = make_id(wire->name);
+ std::string wireFileinfo = getFileinfo(wire);
+
+ 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> %s\n", wire->port_input ? "input" : "output",
+ wireName, wire->width, wireFileinfo.c_str()));
+ }
+
+ for (auto &str : port_decls)
+ {
+ f << str;
+ }
+
+ f << stringf("\n");
+ }
+
+ void emit_module()
{
std::string moduleFileinfo = getFileinfo(module);
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
@@ -446,7 +473,7 @@ struct FirrtlWorker
string y_id = make_id(cell->name);
std::string cellFileinfo = getFileinfo(cell);
- if (cell->type.in(ID($not), ID($logic_not), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))
+ if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))
{
string a_expr = make_expr(cell->getPort(ID::A));
wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str()));
@@ -462,7 +489,7 @@ struct FirrtlWorker
// Assume the FIRRTL width is a single bit.
firrtl_width = 1;
- if (cell->type == ID($not)) primop = "not";
+ if (cell->type.in(ID($not), ID($_NOT_))) primop = "not";
else if (cell->type == ID($neg)) {
primop = "neg";
firrtl_is_signed = true; // Result of "neg" is signed (an SInt).
@@ -494,7 +521,7 @@ struct FirrtlWorker
continue;
}
- if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or), ID($eq), ID($eqx),
+ if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_), ID($eq), ID($eqx),
ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl),
ID($logic_and), ID($logic_or), ID($pow)))
{
@@ -524,7 +551,7 @@ struct FirrtlWorker
// 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(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or)))
+ if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_)))
{
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
@@ -558,19 +585,20 @@ struct FirrtlWorker
firrtl_is_signed = a_signed | b_signed;
firrtl_width = a_width;
} else if (cell->type == ID($mod)) {
+ // "rem" = truncating modulo
primop = "rem";
firrtl_width = min(a_width, b_width);
- } else if (cell->type == ID($and)) {
+ } else if (cell->type.in(ID($and), ID($_AND_))) {
primop = "and";
always_uint = true;
firrtl_width = max(a_width, b_width);
}
- else if (cell->type == ID($or) ) {
+ else if (cell->type.in(ID($or), ID($_OR_))) {
primop = "or";
always_uint = true;
firrtl_width = max(a_width, b_width);
}
- else if (cell->type == ID($xor)) {
+ else if (cell->type.in(ID($xor), ID($_XOR_))) {
primop = "xor";
always_uint = true;
firrtl_width = max(a_width, b_width);
@@ -694,7 +722,8 @@ struct FirrtlWorker
}
}
- if (!cell->parameters.at(ID::B_SIGNED).as_bool()) {
+ auto it = cell->parameters.find(ID::B_SIGNED);
+ if (it == cell->parameters.end() || !it->second.as_bool()) {
b_expr = "asUInt(" + b_expr + ")";
}
@@ -723,9 +752,10 @@ struct FirrtlWorker
continue;
}
- if (cell->type.in(ID($mux)))
+ if (cell->type.in(ID($mux), ID($_MUX_)))
{
- int width = cell->parameters.at(ID::WIDTH).as_int();
+ auto it = cell->parameters.find(ID::WIDTH);
+ int width = it == cell->parameters.end()? 1 : it->second.as_int();
string a_expr = make_expr(cell->getPort(ID::A));
string b_expr = make_expr(cell->getPort(ID::B));
string s_expr = make_expr(cell->getPort(ID::S));
@@ -1076,6 +1106,18 @@ struct FirrtlWorker
for (auto str : wire_exprs)
f << str;
+
+ f << stringf("\n");
+ }
+
+ void run()
+ {
+ // Blackboxes should be emitted as `extmodule`s in firrtl. Only ports are
+ // emitted in such a case.
+ if (module->get_blackbox_attribute())
+ emit_extmodule();
+ else
+ emit_module();
}
};
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index 3e67e55f2..26f17bcb3 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -590,7 +590,17 @@ struct Smt2Worker
if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)");
if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)");
if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd');
+ // "rem" = truncating modulo
if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd');
+ // "mod" = flooring modulo
+ if (cell->type == ID($modfloor)) {
+ // bvumod doesn't exist because it's the same as bvurem
+ if (cell->getParam(ID::A_SIGNED).as_bool()) {
+ return export_bvop(cell, "(bvsmod A B)", 'd');
+ } else {
+ return export_bvop(cell, "(bvurem A B)", 'd');
+ }
+ }
if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) &&
2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) {
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index d3015b066..03f001bfd 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -1511,7 +1511,7 @@ else: # not tempind, covermode
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())
+ print("%s Cannot append steps without violating assumptions!" % smt.timestamp())
retstatus = "FAILED"
break
print_anyconsts(step)
@@ -1548,7 +1548,7 @@ else: # not tempind, covermode
break
smt_pop()
- if not retstatus:
+ if retstatus == "FAILED" or retstatus == "PREUNSAT":
break
else: # gentrace
@@ -1557,8 +1557,9 @@ else: # not tempind, covermode
smt_assert(get_constr_expr(constr_asserts, i))
print_msg("Solving for step %d.." % (last_check_step))
- if smt_check_sat() != "sat":
- print("%s No solution found!" % smt.timestamp())
+ status = smt_check_sat()
+ if status != "sat":
+ print("%s No solution found! (%s)" % (smt.timestamp(), status))
retstatus = "FAILED"
break
@@ -1568,7 +1569,7 @@ else: # not tempind, covermode
step += step_size
- if gentrace and retstatus:
+ if gentrace and retstatus == "PASSED":
print_anyconsts(0)
write_trace(0, num_steps, '%')
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 69f59df79..9f7c8c6d9 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -121,6 +121,7 @@ class SmtIo:
self.logic_bv = True
self.logic_dt = False
self.forall = False
+ self.timeout = 0
self.produce_models = True
self.smt2cache = [list()]
self.p = None
@@ -135,6 +136,7 @@ class SmtIo:
self.debug_file = opts.debug_file
self.dummy_file = opts.dummy_file
self.timeinfo = opts.timeinfo
+ self.timeout = opts.timeout
self.unroll = opts.unroll
self.noincr = opts.noincr
self.info_stmts = opts.info_stmts
@@ -147,6 +149,7 @@ class SmtIo:
self.debug_file = None
self.dummy_file = None
self.timeinfo = os.name != "nt"
+ self.timeout = 0
self.unroll = False
self.noincr = False
self.info_stmts = list()
@@ -172,22 +175,32 @@ class SmtIo:
self.unroll = False
if self.solver == "yices":
- if self.noincr:
+ if self.noincr or self.forall:
self.popen_vargs = ['yices-smt2'] + self.solver_opts
else:
self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
+ if self.timeout != 0:
+ self.popen_vargs.append('-t')
+ self.popen_vargs.append('%d' % self.timeout);
if self.solver == "z3":
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
+ if self.timeout != 0:
+ self.popen_vargs.append('-T:%d' % self.timeout);
if self.solver == "cvc4":
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.timeout != 0:
+ self.popen_vargs.append('--tlimit=%d000' % self.timeout);
if self.solver == "mathsat":
self.popen_vargs = ['mathsat'] + self.solver_opts
+ if self.timeout != 0:
+ print('timeout option is not supported for mathsat.')
+ sys.exit(1)
if self.solver == "boolector":
if self.noincr:
@@ -195,6 +208,9 @@ class SmtIo:
else:
self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
self.unroll = True
+ if self.timeout != 0:
+ print('timeout option is not supported for boolector.')
+ sys.exit(1)
if self.solver == "abc":
if len(self.solver_opts) > 0:
@@ -204,6 +220,9 @@ class SmtIo:
self.logic_ax = False
self.unroll = True
self.noincr = True
+ if self.timeout != 0:
+ print('timeout option is not supported for abc.')
+ sys.exit(1)
if self.solver == "dummy":
assert self.dummy_file is not None
@@ -232,12 +251,16 @@ class SmtIo:
if self.logic_uf: self.logic += "UF"
if self.logic_bv: self.logic += "BV"
if self.logic_dt: self.logic = "ALL"
+ if self.solver == "yices" and self.forall: self.logic = "BV"
self.setup_done = True
for stmt in self.info_stmts:
self.write(stmt)
+ if self.forall and self.solver == "yices":
+ self.write("(set-option :yices-ef-max-iters 1000000000)")
+
if self.produce_models:
self.write("(set-option :produce-models true)")
@@ -706,7 +729,7 @@ class SmtIo:
if self.forall:
result = self.read()
- while result not in ["sat", "unsat", "unknown"]:
+ while result not in ["sat", "unsat", "unknown", "timeout", "interrupted", ""]:
print("%s %s: %s" % (self.timestamp(), self.solver, result))
result = self.read()
else:
@@ -717,7 +740,7 @@ class SmtIo:
print("(check-sat)", file=self.debug_file)
self.debug_file.flush()
- if result not in ["sat", "unsat"]:
+ if result not in ["sat", "unsat", "unknown", "timeout", "interrupted"]:
if result == "":
print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
else:
@@ -927,7 +950,7 @@ class SmtIo:
class SmtOpts:
def __init__(self):
self.shortopts = "s:S:v"
- self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
+ self.longopts = ["unroll", "noincr", "noprogress", "timeout=", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
self.solver = "yices"
self.solver_opts = list()
self.debug_print = False
@@ -936,6 +959,7 @@ class SmtOpts:
self.unroll = False
self.noincr = False
self.timeinfo = os.name != "nt"
+ self.timeout = 0
self.logic = None
self.info_stmts = list()
self.nocomments = False
@@ -945,6 +969,8 @@ class SmtOpts:
self.solver = a
elif o == "-S":
self.solver_opts.append(a)
+ elif o == "--timeout":
+ self.timeout = int(a)
elif o == "-v":
self.debug_print = True
elif o == "--unroll":
@@ -976,6 +1002,9 @@ class SmtOpts:
-S <opt>
pass <opt> as command line argument to the solver
+ --timeout <value>
+ set the solver timeout to the specified value (in seconds).
+
--logic <smt2_logic>
use the specified SMT2 logic (e.g. QF_AUFBV)
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index 7113ebc97..2fc7099f4 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -358,7 +358,8 @@ struct SmvWorker
continue;
}
- if (cell->type.in(ID($div), ID($mod)))
+ // SMV has a "mod" operator, but its semantics don't seem to be well-defined - to be safe, don't generate it at all
+ if (cell->type.in(ID($div)/*, ID($mod), ID($modfloor)*/))
{
int width_y = GetSize(cell->getPort(ID::Y));
int width = max(width_y, GetSize(cell->getPort(ID::A)));
@@ -366,7 +367,7 @@ struct SmvWorker
string expr_a, expr_b, op;
if (cell->type == ID($div)) op = "/";
- if (cell->type == ID($mod)) op = "mod";
+ //if (cell->type == ID($mod)) op = "mod";
if (cell->getParam(ID::A_SIGNED).as_bool())
{
diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh
index 63de465c0..145b9c33b 100644
--- a/backends/smv/test_cells.sh
+++ b/backends/smv/test_cells.sh
@@ -7,8 +7,8 @@ mkdir -p test_cells.tmp
cd test_cells.tmp
# don't test $mul to reduce runtime
-# don't test $div and $mod to reduce runtime and avoid "div by zero" message
-../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod'
+# don't test $div/$mod/$divfloor/$modfloor to reduce runtime and avoid "div by zero" message
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$divfloor /$modfloor'
cat > template.txt << "EOT"
%module main
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 11b2ae10f..4f44a053a 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -740,6 +740,95 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
#undef HANDLE_UNIOP
#undef HANDLE_BINOP
+ if (cell->type == ID($divfloor))
+ {
+ // wire [MAXLEN+1:0] _0_, _1_, _2_;
+ // assign _0_ = $signed(A);
+ // assign _1_ = $signed(B);
+ // assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1));
+ // assign Y = $signed(_2_) / $signed(_1_);
+
+ if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+ SigSpec sig_a = cell->getPort(ID::A);
+ SigSpec sig_b = cell->getPort(ID::B);
+
+ std::string buf_a = next_auto_id();
+ std::string buf_b = next_auto_id();
+ std::string buf_num = next_auto_id();
+ int size_a = GetSize(sig_a);
+ int size_b = GetSize(sig_b);
+ int size_y = GetSize(cell->getPort(ID::Y));
+ int size_max = std::max(size_a, std::max(size_b, size_y));
+
+ // intentionally one wider than maximum width
+ f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str());
+ f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str());
+ dump_cell_expr_port(f, cell, "A", true);
+ f << stringf(";\n");
+ f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str());
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(";\n");
+
+ f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str());
+ f << stringf("(");
+ dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+ f << stringf(" == ");
+ dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+ f << stringf(") || ");
+ dump_sigspec(f, sig_a);
+ f << stringf(" == 0 ? %s : ", buf_a.c_str());
+ f << stringf("$signed(%s - (", buf_a.c_str());
+ dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+ f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str());
+
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort(ID::Y));
+ f << stringf(" = $signed(%s) / ", buf_num.c_str());
+ dump_attributes(f, "", cell->attributes, ' ');
+ f << stringf("$signed(%s);\n", buf_b.c_str());
+ return true;
+ } else {
+ // same as truncating division
+ dump_cell_expr_binop(f, indent, cell, "/");
+ return true;
+ }
+ }
+
+ if (cell->type == ID($modfloor))
+ {
+ // wire truncated = $signed(A) % $signed(B);
+ // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated);
+
+ if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+ SigSpec sig_a = cell->getPort(ID::A);
+ SigSpec sig_b = cell->getPort(ID::B);
+
+ std::string temp_id = next_auto_id();
+ f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
+ dump_cell_expr_port(f, cell, "A", true);
+ f << stringf(" %% ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(";\n");
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort(ID::Y));
+ f << stringf(" = (");
+ dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+ f << stringf(" == ");
+ dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+ f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str());
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(" + $signed(%s);\n", temp_id.c_str());
+ return true;
+ } else {
+ // same as truncating modulo
+ dump_cell_expr_binop(f, indent, cell, "%");
+ return true;
+ }
+ }
+
if (cell->type == ID($shift))
{
f << stringf("%s" "assign ", indent.c_str());