aboutsummaryrefslogtreecommitdiffstats
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/aiger/aiger.cc6
-rw-r--r--backends/aiger/xaiger.cc6
-rw-r--r--backends/blif/blif.cc114
-rw-r--r--backends/btor/.gitignore1
-rw-r--r--backends/btor/btor.cc225
-rwxr-xr-x[-rw-r--r--]backends/btor/test_cells.sh4
-rw-r--r--backends/cxxrtl/cxxrtl.h365
-rw-r--r--backends/cxxrtl/cxxrtl_backend.cc498
-rw-r--r--backends/cxxrtl/cxxrtl_capi.cc25
-rw-r--r--backends/cxxrtl/cxxrtl_capi.h129
-rw-r--r--backends/cxxrtl/cxxrtl_vcd.h46
-rw-r--r--backends/cxxrtl/cxxrtl_vcd_capi.h4
-rw-r--r--backends/edif/edif.cc12
-rw-r--r--backends/firrtl/firrtl.cc356
-rw-r--r--backends/ilang/Makefile.inc3
-rw-r--r--backends/intersynth/intersynth.cc8
-rw-r--r--backends/json/json.cc8
-rw-r--r--backends/protobuf/protobuf.cc8
-rw-r--r--backends/rtlil/Makefile.inc3
-rw-r--r--backends/rtlil/rtlil_backend.cc (renamed from backends/ilang/ilang_backend.cc)75
-rw-r--r--backends/rtlil/rtlil_backend.h (renamed from backends/ilang/ilang_backend.h)8
-rw-r--r--backends/simplec/simplec.cc4
-rw-r--r--backends/smt2/smt2.cc482
-rw-r--r--backends/smt2/smtbmc.py44
-rw-r--r--backends/smt2/smtio.py24
-rw-r--r--backends/smv/.gitignore1
-rw-r--r--backends/smv/smv.cc4
-rw-r--r--backends/spice/spice.cc4
-rw-r--r--backends/table/table.cc4
-rw-r--r--backends/verilog/verilog_backend.cc963
30 files changed, 2135 insertions, 1299 deletions
diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc
index e5a41b5c5..476b30488 100644
--- a/backends/aiger/aiger.cc
+++ b/backends/aiger/aiger.cc
@@ -111,7 +111,7 @@ struct AigerWriter
// promote public wires
for (auto wire : module->wires())
- if (wire->name[0] == '\\')
+ if (wire->name.isPublic())
sigmap.add(wire);
// promote input wires
@@ -681,7 +681,7 @@ struct AigerWriter
struct AigerBackend : public Backend {
AigerBackend() : Backend("aiger", "write design to AIGER file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -719,7 +719,7 @@ struct AigerBackend : public Backend {
log(" AIGER file happy.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool ascii_mode = false;
bool zinit_mode = false;
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 8bbadbc91..27499b64a 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -146,7 +146,7 @@ struct XAigerWriter
// promote public wires
for (auto wire : module->wires())
- if (wire->name[0] == '\\')
+ if (wire->name.isPublic())
sigmap.add(wire);
// promote input wires
@@ -731,7 +731,7 @@ struct XAigerWriter
struct XAigerBackend : public Backend {
XAigerBackend() : Backend("xaiger", "write design to XAIGER file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -753,7 +753,7 @@ struct XAigerBackend : public Backend {
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
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool ascii_mode = false, dff_mode = false;
std::string map_filename;
diff --git a/backends/blif/blif.cc b/backends/blif/blif.cc
index b028df848..088819042 100644
--- a/backends/blif/blif.cc
+++ b/backends/blif/blif.cc
@@ -86,20 +86,18 @@ struct BlifDumper
}
}
- vector<shared_str> cstr_buf;
pool<SigBit> cstr_bits_seen;
- const char *cstr(RTLIL::IdString id)
+ const std::string str(RTLIL::IdString id)
{
std::string str = RTLIL::unescape_id(id);
for (size_t i = 0; i < str.size(); i++)
if (str[i] == '#' || str[i] == '=' || str[i] == '<' || str[i] == '>')
str[i] = '?';
- cstr_buf.push_back(str);
- return cstr_buf.back().c_str();
+ return str;
}
- const char *cstr(RTLIL::SigBit sig)
+ const std::string str(RTLIL::SigBit sig)
{
cstr_bits_seen.insert(sig);
@@ -117,11 +115,10 @@ struct BlifDumper
if (sig.wire->width != 1)
str += stringf("[%d]", sig.wire->upto ? sig.wire->start_offset+sig.wire->width-sig.offset-1 : sig.wire->start_offset+sig.offset);
- cstr_buf.push_back(str);
- return cstr_buf.back().c_str();
+ return str;
}
- const char *cstr_init(RTLIL::SigBit sig)
+ const std::string str_init(RTLIL::SigBit sig)
{
sigmap.apply(sig);
@@ -130,8 +127,7 @@ struct BlifDumper
string str = stringf(" %d", init_bits.at(sig));
- cstr_buf.push_back(str);
- return cstr_buf.back().c_str();
+ return str;
}
const char *subckt_or_gate(std::string cell_type)
@@ -168,7 +164,7 @@ struct BlifDumper
void dump()
{
f << stringf("\n");
- f << stringf(".model %s\n", cstr(module->name));
+ f << stringf(".model %s\n", str(module->name).c_str());
std::map<int, RTLIL::Wire*> inputs, outputs;
@@ -183,7 +179,7 @@ struct BlifDumper
for (auto &it : inputs) {
RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++)
- f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
+ f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str());
}
f << stringf("\n");
@@ -191,7 +187,7 @@ struct BlifDumper
for (auto &it : outputs) {
RTLIL::Wire *wire = it.second;
for (int i = 0; i < wire->width; i++)
- f << stringf(" %s", cstr(RTLIL::SigSpec(wire, i)));
+ f << stringf(" %s", str(RTLIL::SigSpec(wire, i)).c_str());
}
f << stringf("\n");
@@ -233,131 +229,131 @@ struct BlifDumper
if (config->unbuf_types.count(cell->type)) {
auto portnames = config->unbuf_types.at(cell->type);
f << stringf(".names %s %s\n1 1\n",
- cstr(cell->getPort(portnames.first)), cstr(cell->getPort(portnames.second)));
+ str(cell->getPort(portnames.first)).c_str(), str(cell->getPort(portnames.second)).c_str());
continue;
}
if (!config->icells_mode && cell->type == ID($_NOT_)) {
f << stringf(".names %s %s\n0 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_AND_)) {
f << stringf(".names %s %s %s\n11 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_OR_)) {
f << stringf(".names %s %s %s\n1- 1\n-1 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_XOR_)) {
f << stringf(".names %s %s %s\n10 1\n01 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_NAND_)) {
f << stringf(".names %s %s %s\n0- 1\n-0 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_NOR_)) {
f << stringf(".names %s %s %s\n00 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_XNOR_)) {
f << stringf(".names %s %s %s\n11 1\n00 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_ANDNOT_)) {
f << stringf(".names %s %s %s\n10 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_ORNOT_)) {
f << stringf(".names %s %s %s\n1- 1\n-0 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_AOI3_)) {
f << stringf(".names %s %s %s %s\n-00 1\n0-0 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_OAI3_)) {
f << stringf(".names %s %s %s %s\n00- 1\n--0 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)), cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(), str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_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(ID::A)), cstr(cell->getPort(ID::B)),
- cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(),
+ str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_OAI4_)) {
f << stringf(".names %s %s %s %s %s\n00-- 1\n--00 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)),
- cstr(cell->getPort(ID::C)), cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(),
+ str(cell->getPort(ID::C)).c_str(), str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_MUX_)) {
f << stringf(".names %s %s %s %s\n1-0 1\n-11 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)),
- cstr(cell->getPort(ID::S)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(),
+ str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_NMUX_)) {
f << stringf(".names %s %s %s %s\n0-0 1\n-01 1\n",
- cstr(cell->getPort(ID::A)), cstr(cell->getPort(ID::B)),
- cstr(cell->getPort(ID::S)), cstr(cell->getPort(ID::Y)));
+ str(cell->getPort(ID::A)).c_str(), str(cell->getPort(ID::B)).c_str(),
+ str(cell->getPort(ID::S)).c_str(), str(cell->getPort(ID::Y)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_FF_)) {
- f << stringf(".latch %s %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)),
- cstr_init(cell->getPort(ID::Q)));
+ f << stringf(".latch %s %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
+ str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DFF_N_)) {
- f << stringf(".latch %s %s fe %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)),
- cstr(cell->getPort(ID::C)), cstr_init(cell->getPort(ID::Q)));
+ f << stringf(".latch %s %s fe %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
+ str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DFF_P_)) {
- f << stringf(".latch %s %s re %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)),
- cstr(cell->getPort(ID::C)), cstr_init(cell->getPort(ID::Q)));
+ f << stringf(".latch %s %s re %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
+ str(cell->getPort(ID::C)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DLATCH_N_)) {
- f << stringf(".latch %s %s al %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)),
- cstr(cell->getPort(ID::E)), cstr_init(cell->getPort(ID::Q)));
+ f << stringf(".latch %s %s al %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
+ str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
if (!config->icells_mode && cell->type == ID($_DLATCH_P_)) {
- f << stringf(".latch %s %s ah %s%s\n", cstr(cell->getPort(ID::D)), cstr(cell->getPort(ID::Q)),
- cstr(cell->getPort(ID::E)), cstr_init(cell->getPort(ID::Q)));
+ f << stringf(".latch %s %s ah %s%s\n", str(cell->getPort(ID::D)).c_str(), str(cell->getPort(ID::Q)).c_str(),
+ str(cell->getPort(ID::E)).c_str(), str_init(cell->getPort(ID::Q)).c_str());
goto internal_cell;
}
@@ -367,10 +363,10 @@ struct BlifDumper
auto width = cell->parameters.at(ID::WIDTH).as_int();
log_assert(inputs.size() == width);
for (int i = width-1; i >= 0; i--)
- f << stringf(" %s", cstr(inputs.extract(i, 1)));
+ f << stringf(" %s", str(inputs.extract(i, 1)).c_str());
auto &output = cell->getPort(ID::Y);
log_assert(output.size() == 1);
- f << stringf(" %s", cstr(output));
+ f << stringf(" %s", str(output).c_str());
f << stringf("\n");
RTLIL::SigSpec mask = cell->parameters.at(ID::LUT);
for (int i = 0; i < (1 << width); i++)
@@ -393,10 +389,10 @@ struct BlifDumper
table.push_back(State::S0);
log_assert(inputs.size() == width);
for (int i = 0; i < width; i++)
- f << stringf(" %s", cstr(inputs.extract(i, 1)));
+ f << stringf(" %s", str(inputs.extract(i, 1)).c_str());
auto &output = cell->getPort(ID::Y);
log_assert(output.size() == 1);
- f << stringf(" %s", cstr(output));
+ f << stringf(" %s", str(output).c_str());
f << stringf("\n");
for (int i = 0; i < depth; i++) {
for (int j = 0; j < width; j++) {
@@ -411,11 +407,11 @@ struct BlifDumper
goto internal_cell;
}
- f << stringf(".%s %s", subckt_or_gate(cell->type.str()), cstr(cell->type));
+ f << stringf(".%s %s", subckt_or_gate(cell->type.str()), str(cell->type).c_str());
for (auto &conn : cell->connections())
{
if (conn.second.size() == 1) {
- f << stringf(" %s=%s", cstr(conn.first), cstr(conn.second[0]));
+ f << stringf(" %s=%s", str(conn.first).c_str(), str(conn.second[0]).c_str());
continue;
}
@@ -424,20 +420,20 @@ struct BlifDumper
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]));
+ f << stringf(" %s[%d]=%s", str(conn.first).c_str(), i, str(conn.second[i]).c_str());
} 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 ?
+ f << stringf(" %s[%d]=%s", str(conn.first).c_str(), sig.wire->upto ?
sig.wire->start_offset+sig.wire->width-sig.offset-1 :
- sig.wire->start_offset+sig.offset, cstr(conn.second[i]));
+ sig.wire->start_offset+sig.offset, str(conn.second[i]).c_str());
}
}
}
f << stringf("\n");
if (config->cname_mode)
- f << stringf(".cname %s\n", cstr(cell->name));
+ f << stringf(".cname %s\n", str(cell->name).c_str());
if (config->attr_mode)
dump_params(".attr", cell->attributes);
if (config->param_mode)
@@ -446,7 +442,7 @@ struct BlifDumper
if (0) {
internal_cell:
if (config->iname_mode)
- f << stringf(".cname %s\n", cstr(cell->name));
+ f << stringf(".cname %s\n", str(cell->name).c_str());
if (config->iattr_mode)
dump_params(".attr", cell->attributes);
}
@@ -462,12 +458,12 @@ struct BlifDumper
continue;
if (config->conn_mode)
- f << stringf(".conn %s %s\n", cstr(rhs_bit), cstr(lhs_bit));
+ f << stringf(".conn %s %s\n", str(rhs_bit).c_str(), str(lhs_bit).c_str());
else if (!config->buf_type.empty())
f << stringf(".%s %s %s=%s %s=%s\n", subckt_or_gate(config->buf_type), config->buf_type.c_str(),
- config->buf_in.c_str(), cstr(rhs_bit), config->buf_out.c_str(), cstr(lhs_bit));
+ config->buf_in.c_str(), str(rhs_bit).c_str(), config->buf_out.c_str(), str(lhs_bit).c_str());
else
- f << stringf(".names %s %s\n1 1\n", cstr(rhs_bit), cstr(lhs_bit));
+ f << stringf(".names %s %s\n1 1\n", str(rhs_bit).c_str(), str(lhs_bit).c_str());
}
f << stringf(".end\n");
@@ -482,7 +478,7 @@ struct BlifDumper
struct BlifBackend : public Backend {
BlifBackend() : Backend("blif", "write design to BLIF file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -552,7 +548,7 @@ struct BlifBackend : public Backend {
log(" do not write definitions for the $true, $false and $undef wires.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
std::string top_module_name;
std::string buf_type, buf_in, buf_out;
diff --git a/backends/btor/.gitignore b/backends/btor/.gitignore
new file mode 100644
index 000000000..d23d492d7
--- /dev/null
+++ b/backends/btor/.gitignore
@@ -0,0 +1 @@
+/test_cells.tmp/
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 9ac312480..639c6f129 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -27,6 +27,7 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
+#include "kernel/mem.h"
#include <string>
USING_YOSYS_NAMESPACE
@@ -68,12 +69,15 @@ struct BtorWorker
// ff inputs that need to be evaluated (<nid>, <ff_cell>)
vector<pair<int, Cell*>> ff_todo;
+ vector<pair<int, Mem*>> mem_todo;
pool<Cell*> cell_recursion_guard;
vector<int> bad_properties;
dict<SigBit, bool> initbits;
pool<Wire*> statewires;
pool<string> srcsymbols;
+ vector<Mem> memories;
+ dict<Cell*, Mem*> mem_cells;
string indent, info_filename;
vector<string> info_lines;
@@ -205,9 +209,8 @@ struct BtorWorker
if (cell->type.in(ID($xnor), ID($_XNOR_))) btor_op = "xnor";
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)));
+ int width_ay = std::max(GetSize(cell->getPort(ID::A)), GetSize(cell->getPort(ID::Y)));
+ int width = std::max(width_ay, 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;
@@ -224,11 +227,23 @@ struct BtorWorker
int sid = get_bv_sid(width);
int nid;
+ int nid_a;
+ if (cell->type.in(ID($shl), ID($shr), ID($shift), ID($shiftx)) && a_signed && width_ay < width) {
+ // sign-extend A up to the width of Y
+ int nid_a_padded = get_sig_nid(cell->getPort(ID::A), width_ay, a_signed);
+
+ // zero-extend the rest
+ int zeroes = get_sig_nid(Const(0, width-width_ay));
+ nid_a = next_nid++;
+ btorf("%d concat %d %d %d\n", nid_a, sid, zeroes, nid_a_padded);
+ } else {
+ 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);
+
if (btor_op == "shift")
{
- int nid_a = get_sig_nid(cell->getPort(ID::A), width, false);
- int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
-
int nid_r = next_nid++;
btorf("%d srl %d %d %d\n", nid_r, sid, nid_a, nid_b);
@@ -248,9 +263,6 @@ struct BtorWorker
}
else
{
- 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);
-
nid = next_nid++;
btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
}
@@ -696,49 +708,45 @@ struct BtorWorker
goto okay;
}
- if (cell->type == ID($mem))
+ if (cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit)))
{
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
- int nwords = cell->getParam(ID::SIZE).as_int();
- int rdports = cell->getParam(ID::RD_PORTS).as_int();
- int wrports = cell->getParam(ID::WR_PORTS).as_int();
+ Mem *mem = mem_cells[cell];
- Const wr_clk_en = cell->getParam(ID::WR_CLK_ENABLE);
- Const rd_clk_en = cell->getParam(ID::RD_CLK_ENABLE);
+ int abits = ceil_log2(mem->size);
- bool asyncwr = wr_clk_en.is_fully_zero();
+ bool asyncwr = false;
+ bool syncwr = false;
- 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));
+ for (auto &port : mem->wr_ports) {
+ if (port.clk_enable)
+ syncwr = true;
+ else
+ asyncwr = true;
+ }
- SigSpec sig_rd_addr = sigmap(cell->getPort(ID::RD_ADDR));
- SigSpec sig_rd_data = sigmap(cell->getPort(ID::RD_DATA));
+ if (asyncwr && syncwr)
+ log_error("Memory %s.%s has mixed async/sync write ports.\n",
+ log_id(module), log_id(mem->memid));
- SigSpec sig_wr_addr = sigmap(cell->getPort(ID::WR_ADDR));
- SigSpec sig_wr_data = sigmap(cell->getPort(ID::WR_DATA));
- SigSpec sig_wr_en = sigmap(cell->getPort(ID::WR_EN));
+ for (auto &port : mem->rd_ports)
+ if (port.clk_enable)
+ log_error("Memory %s.%s has sync read ports.\n",
+ log_id(module), log_id(mem->memid));
- int data_sid = get_bv_sid(width);
+ int data_sid = get_bv_sid(mem->width);
int bool_sid = get_bv_sid(1);
- int sid = get_mem_sid(abits, width);
+ int sid = get_mem_sid(abits, mem->width);
- Const initdata = cell->getParam(ID::INIT);
- initdata.exts(nwords*width);
int nid_init_val = -1;
- if (!initdata.is_fully_undef())
+ if (!mem->inits.empty())
{
+ Const initdata = mem->get_init_data();
bool constword = true;
- Const firstword = initdata.extract(0, width);
+ Const firstword = initdata.extract(0, mem->width);
- for (int i = 1; i < nwords; i++) {
- Const thisword = initdata.extract(i*width, width);
+ for (int i = 1; i < mem->size; i++) {
+ Const thisword = initdata.extract(i*mem->width, mem->width);
if (thisword != firstword) {
constword = false;
break;
@@ -756,8 +764,8 @@ struct BtorWorker
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);
+ for (int i = 0; i < mem->size; i++) {
+ Const thisword = initdata.extract(i*mem->width, mem->width);
if (thisword.is_fully_undef())
continue;
Const thisaddr(i, abits);
@@ -776,10 +784,10 @@ struct BtorWorker
int nid = next_nid++;
int nid_head = nid;
- if (cell->name[0] == '$')
+ if (mem->memid[0] == '$')
btorf("%d state %d\n", nid, sid);
else
- btorf("%d state %d %s\n", nid, sid, log_id(cell));
+ btorf("%d state %d %s\n", nid, sid, log_id(mem->memid));
if (nid_init_val >= 0)
{
@@ -789,15 +797,14 @@ struct BtorWorker
if (asyncwr)
{
- for (int port = 0; port < wrports; port++)
+ for (auto &port : mem->wr_ports)
{
- 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);
+ SigSpec wa = port.addr;
+ wa.extend_u0(abits);
int wa_nid = get_sig_nid(wa);
- int wd_nid = get_sig_nid(wd);
- int we_nid = get_sig_nid(we);
+ int wd_nid = get_sig_nid(port.data);
+ int we_nid = get_sig_nid(port.en);
int nid2 = next_nid++;
btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid);
@@ -827,22 +834,22 @@ struct BtorWorker
}
}
- for (int port = 0; port < rdports; port++)
+ for (auto &port : mem->rd_ports)
{
- SigSpec ra = sig_rd_addr.extract(port*abits, abits);
- SigSpec rd = sig_rd_data.extract(port*width, width);
+ SigSpec ra = port.addr;
+ ra.extend_u0(abits);
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);
+ add_nid_sig(rd_nid, port.data);
}
if (!asyncwr)
{
- ff_todo.push_back(make_pair(nid, cell));
+ mem_todo.push_back(make_pair(nid, mem));
}
else
{
@@ -1057,6 +1064,15 @@ struct BtorWorker
if (!info_filename.empty())
infof("name %s\n", log_id(module));
+ memories = Mem::get_all_memories(module);
+
+ dict<IdString, Mem*> mem_dict;
+ for (auto &mem : memories)
+ mem_dict[mem.memid] = &mem;
+ for (auto cell : module->cells())
+ if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit)))
+ mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()];
+
btorf_push("inputs");
for (auto wire : module->wires())
@@ -1193,7 +1209,7 @@ struct BtorWorker
continue;
}
- while (!ff_todo.empty())
+ while (!ff_todo.empty() || !mem_todo.empty())
{
vector<pair<int, Cell*>> todo;
todo.swap(ff_todo);
@@ -1205,70 +1221,71 @@ struct BtorWorker
btorf_push(stringf("next %s", log_id(cell)));
- if (cell->type == ID($mem))
- {
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
- int wrports = cell->getParam(ID::WR_PORTS).as_int();
+ SigSpec sig = sigmap(cell->getPort(ID::D));
+ int nid_q = get_sig_nid(sig);
+ int sid = get_bv_sid(GetSize(sig));
+ btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str());
- SigSpec sig_wr_addr = sigmap(cell->getPort(ID::WR_ADDR));
- SigSpec sig_wr_data = sigmap(cell->getPort(ID::WR_DATA));
- SigSpec sig_wr_en = sigmap(cell->getPort(ID::WR_EN));
+ btorf_pop(stringf("next %s", log_id(cell)));
+ }
- 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;
+ vector<pair<int, Mem*>> mtodo;
+ mtodo.swap(mem_todo);
- 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);
+ for (auto &it : mtodo)
+ {
+ int nid = it.first;
+ Mem *mem = it.second;
+
+ btorf_push(stringf("next %s", log_id(mem->memid)));
+
+ int abits = ceil_log2(mem->size);
- int wa_nid = get_sig_nid(wa);
- int wd_nid = get_sig_nid(wd);
- int we_nid = get_sig_nid(we);
+ int data_sid = get_bv_sid(mem->width);
+ int bool_sid = get_bv_sid(1);
+ int sid = get_mem_sid(abits, mem->width);
+ int nid_head = nid;
- int nid2 = next_nid++;
- btorf("%d read %d %d %d\n", nid2, data_sid, nid_head, wa_nid);
+ for (auto &port : mem->wr_ports)
+ {
+ SigSpec wa = port.addr;
+ wa.extend_u0(abits);
- int nid3 = next_nid++;
- btorf("%d not %d %d\n", nid3, data_sid, we_nid);
+ int wa_nid = get_sig_nid(wa);
+ int wd_nid = get_sig_nid(port.data);
+ int we_nid = get_sig_nid(port.en);
- int nid4 = next_nid++;
- btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3);
+ 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 nid5 = next_nid++;
- btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid);
+ int nid4 = next_nid++;
+ btorf("%d and %d %d %d\n", nid4, data_sid, nid2, nid3);
- int nid6 = next_nid++;
- btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4);
+ int nid5 = next_nid++;
+ btorf("%d and %d %d %d\n", nid5, data_sid, wd_nid, we_nid);
- int nid7 = next_nid++;
- btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6);
+ int nid6 = next_nid++;
+ btorf("%d or %d %d %d\n", nid6, data_sid, nid5, nid4);
- int nid8 = next_nid++;
- btorf("%d redor %d %d\n", nid8, bool_sid, we_nid);
+ int nid7 = next_nid++;
+ btorf("%d write %d %d %d %d\n", nid7, sid, nid_head, wa_nid, nid6);
- int nid9 = next_nid++;
- btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head);
+ int nid8 = next_nid++;
+ btorf("%d redor %d %d\n", nid8, bool_sid, we_nid);
- nid_head = nid9;
- }
+ int nid9 = next_nid++;
+ btorf("%d ite %d %d %d %d\n", nid9, sid, nid8, nid7, nid_head);
- int nid2 = next_nid++;
- btorf("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, getinfo(cell).c_str());
- }
- else
- {
- SigSpec sig = sigmap(cell->getPort(ID::D));
- int nid_q = get_sig_nid(sig);
- int sid = get_bv_sid(GetSize(sig));
- btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str());
+ nid_head = nid9;
}
- btorf_pop(stringf("next %s", log_id(cell)));
+ int nid2 = next_nid++;
+ btorf("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, (mem->cell ? getinfo(mem->cell) : getinfo(mem->mem)).c_str());
+
+ btorf_pop(stringf("next %s", log_id(mem->memid)));
}
}
@@ -1335,7 +1352,7 @@ struct BtorWorker
struct BtorBackend : public Backend {
BtorBackend() : Backend("btor", "write design to BTOR file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1359,7 +1376,7 @@ struct BtorBackend : public Backend {
log(" Output symbols for internal netnames (starting with '$')\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool verbose = false, single_bad = false, cover_mode = false, print_internal_names = false;
string info_filename;
diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh
index 3f077201a..0a011932d 100644..100755
--- 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 /$divfloor /$modfloor'
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor /$shiftx'
for fn in test_*.il; do
../../../yosys -p "
@@ -19,7 +19,7 @@ for fn in test_*.il; do
hierarchy -top main
write_btor ${fn%.il}.btor
"
- boolectormc -kmax 1 --trace-gen --stop-first -v ${fn%.il}.btor > ${fn%.il}.out
+ btormc -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
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index c988c9e80..41089a153 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -17,6 +17,11 @@
*/
// This file is included by the designs generated with `write_cxxrtl`. It is not used in Yosys itself.
+//
+// The CXXRTL support library implements compile time specialized arbitrary width arithmetics, as well as provides
+// composite lvalues made out of bit slices and concatenations of lvalues. This allows the `write_cxxrtl` pass
+// to perform a straightforward translation of RTLIL structures to readable C++, relying on the C++ compiler
+// to unwrap the abstraction and generate efficient code.
#ifndef CXXRTL_H
#define CXXRTL_H
@@ -35,10 +40,19 @@
#include <backends/cxxrtl/cxxrtl_capi.h>
-// The CXXRTL support library implements compile time specialized arbitrary width arithmetics, as well as provides
-// composite lvalues made out of bit slices and concatenations of lvalues. This allows the `write_cxxrtl` pass
-// to perform a straightforward translation of RTLIL structures to readable C++, relying on the C++ compiler
-// to unwrap the abstraction and generate efficient code.
+// CXXRTL essentially uses the C++ compiler as a hygienic macro engine that feeds an instruction selector.
+// It generates a lot of specialized template functions with relatively large bodies that, when inlined
+// into the caller and (for those with loops) unrolled, often expose many new optimization opportunities.
+// Because of this, most of the CXXRTL runtime must be always inlined for best performance.
+#ifndef __has_attribute
+# define __has_attribute(x) 0
+#endif
+#if __has_attribute(always_inline)
+#define CXXRTL_ALWAYS_INLINE inline __attribute__((__always_inline__))
+#else
+#define CXXRTL_ALWAYS_INLINE inline
+#endif
+
namespace cxxrtl {
// All arbitrary-width values in CXXRTL are backed by arrays of unsigned integers called chunks. The chunk size
@@ -52,6 +66,7 @@ namespace cxxrtl {
// Therefore, using relatively wide chunks and clearing the high bits explicitly and only when we know they may be
// clobbered results in simpler generated code.
typedef uint32_t chunk_t;
+typedef uint64_t wide_chunk_t;
template<typename T>
struct chunk_traits {
@@ -85,6 +100,7 @@ struct value : public expr_base<value<Bits>> {
value<Bits> &operator=(const value<Bits> &) = default;
// A (no-op) helper that forces the cast to value<>.
+ CXXRTL_ALWAYS_INLINE
const value<Bits> &val() const {
return *this;
}
@@ -95,12 +111,42 @@ struct value : public expr_base<value<Bits>> {
return ss.str();
}
+ // Conversion operations.
+ //
+ // These functions ensure that a conversion is never out of range, and should be always used, if at all
+ // possible, instead of direct manipulation of the `data` member. For very large types, .slice() and
+ // .concat() can be used to split them into more manageable parts.
+ template<class IntegerT>
+ CXXRTL_ALWAYS_INLINE
+ IntegerT get() const {
+ static_assert(std::numeric_limits<IntegerT>::is_integer && !std::numeric_limits<IntegerT>::is_signed,
+ "get<T>() requires T to be an unsigned integral type");
+ static_assert(std::numeric_limits<IntegerT>::digits >= Bits,
+ "get<T>() requires T to be at least as wide as the value is");
+ IntegerT result = 0;
+ for (size_t n = 0; n < chunks; n++)
+ result |= IntegerT(data[n]) << (n * chunk::bits);
+ return result;
+ }
+
+ template<class IntegerT>
+ CXXRTL_ALWAYS_INLINE
+ void set(IntegerT other) {
+ static_assert(std::numeric_limits<IntegerT>::is_integer && !std::numeric_limits<IntegerT>::is_signed,
+ "set<T>() requires T to be an unsigned integral type");
+ static_assert(std::numeric_limits<IntegerT>::digits >= Bits,
+ "set<T>() requires the value to be at least as wide as T is");
+ for (size_t n = 0; n < chunks; n++)
+ data[n] = (other >> (n * chunk::bits)) & chunk::mask;
+ }
+
// Operations with compile-time parameters.
//
// These operations are used to implement slicing, concatenation, and blitting.
// The trunc, zext and sext operations add or remove most significant bits (i.e. on the left);
// the rtrunc and rzext operations add or remove least significant bits (i.e. on the right).
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> trunc() const {
static_assert(NewBits <= Bits, "trunc() may not increase width");
value<NewBits> result;
@@ -111,6 +157,7 @@ struct value : public expr_base<value<Bits>> {
}
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> zext() const {
static_assert(NewBits >= Bits, "zext() may not decrease width");
value<NewBits> result;
@@ -120,6 +167,7 @@ struct value : public expr_base<value<Bits>> {
}
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> sext() const {
static_assert(NewBits >= Bits, "sext() may not decrease width");
value<NewBits> result;
@@ -135,6 +183,7 @@ struct value : public expr_base<value<Bits>> {
}
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> rtrunc() const {
static_assert(NewBits <= Bits, "rtrunc() may not increase width");
value<NewBits> result;
@@ -154,6 +203,7 @@ struct value : public expr_base<value<Bits>> {
}
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> rzext() const {
static_assert(NewBits >= Bits, "rzext() may not decrease width");
value<NewBits> result;
@@ -165,13 +215,14 @@ struct value : public expr_base<value<Bits>> {
carry = (shift_bits == 0) ? 0
: data[n] >> (chunk::bits - shift_bits);
}
- if (carry != 0)
- result.data[result.chunks - 1] = carry;
+ if (shift_chunks + chunks < result.chunks)
+ result.data[shift_chunks + chunks] = carry;
return result;
}
// Bit blit operation, i.e. a partial read-modify-write.
template<size_t Stop, size_t Start>
+ CXXRTL_ALWAYS_INLINE
value<Bits> blit(const value<Stop - Start + 1> &source) const {
static_assert(Stop >= Start, "blit() may not reverse bit order");
constexpr chunk::type start_mask = ~(chunk::mask << (Start % chunk::bits));
@@ -196,6 +247,7 @@ struct value : public expr_base<value<Bits>> {
// than the operand. In C++17 these can be replaced with `if constexpr`.
template<size_t NewBits, typename = void>
struct zext_cast {
+ CXXRTL_ALWAYS_INLINE
value<NewBits> operator()(const value<Bits> &val) {
return val.template zext<NewBits>();
}
@@ -203,6 +255,7 @@ struct value : public expr_base<value<Bits>> {
template<size_t NewBits>
struct zext_cast<NewBits, typename std::enable_if<(NewBits < Bits)>::type> {
+ CXXRTL_ALWAYS_INLINE
value<NewBits> operator()(const value<Bits> &val) {
return val.template trunc<NewBits>();
}
@@ -210,6 +263,7 @@ struct value : public expr_base<value<Bits>> {
template<size_t NewBits, typename = void>
struct sext_cast {
+ CXXRTL_ALWAYS_INLINE
value<NewBits> operator()(const value<Bits> &val) {
return val.template sext<NewBits>();
}
@@ -217,17 +271,20 @@ struct value : public expr_base<value<Bits>> {
template<size_t NewBits>
struct sext_cast<NewBits, typename std::enable_if<(NewBits < Bits)>::type> {
+ CXXRTL_ALWAYS_INLINE
value<NewBits> operator()(const value<Bits> &val) {
return val.template trunc<NewBits>();
}
};
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> zcast() const {
return zext_cast<NewBits>()(*this);
}
template<size_t NewBits>
+ CXXRTL_ALWAYS_INLINE
value<NewBits> scast() const {
return sext_cast<NewBits>()(*this);
}
@@ -246,6 +303,10 @@ struct value : public expr_base<value<Bits>> {
data[offset_chunks] |= value ? 1 << offset_bits : 0;
}
+ explicit operator bool() const {
+ return !is_zero();
+ }
+
bool is_zero() const {
for (size_t n = 0; n < chunks; n++)
if (data[n] != 0)
@@ -253,10 +314,6 @@ struct value : public expr_base<value<Bits>> {
return true;
}
- explicit operator bool() const {
- return !is_zero();
- }
-
bool is_neg() const {
return data[chunks - 1] & (1 << ((Bits - 1) % chunk::bits));
}
@@ -349,10 +406,12 @@ struct value : public expr_base<value<Bits>> {
: data[chunks - 1 - n] << (chunk::bits - shift_bits);
}
if (Signed && is_neg()) {
- for (size_t n = chunks - shift_chunks; n < chunks; n++)
+ size_t top_chunk_idx = (Bits - shift_bits) / chunk::bits;
+ size_t top_chunk_bits = (Bits - shift_bits) % chunk::bits;
+ for (size_t n = top_chunk_idx + 1; n < chunks; n++)
result.data[n] = chunk::mask;
if (shift_bits != 0)
- result.data[chunks - shift_chunks] |= chunk::mask << (chunk::bits - shift_bits);
+ result.data[top_chunk_idx] |= chunk::mask << top_chunk_bits;
}
return result;
}
@@ -393,10 +452,11 @@ struct value : public expr_base<value<Bits>> {
bool carry = CarryIn;
for (size_t n = 0; n < result.chunks; n++) {
result.data[n] = data[n] + (Invert ? ~other.data[n] : other.data[n]) + carry;
+ if (result.chunks - 1 == n)
+ result.data[result.chunks - 1] &= result.msb_mask;
carry = (result.data[n] < data[n]) ||
(result.data[n] == data[n] && carry);
}
- result.data[result.chunks - 1] &= result.msb_mask;
return {result, carry};
}
@@ -425,6 +485,24 @@ struct value : public expr_base<value<Bits>> {
bool overflow = (is_neg() == !other.is_neg()) && (is_neg() != result.is_neg());
return result.is_neg() ^ overflow; // a.scmp(b) ≡ a s< b
}
+
+ template<size_t ResultBits>
+ value<ResultBits> mul(const value<Bits> &other) const {
+ value<ResultBits> result;
+ wide_chunk_t wide_result[result.chunks + 1] = {};
+ for (size_t n = 0; n < chunks; n++) {
+ for (size_t m = 0; m < chunks && n + m < result.chunks; m++) {
+ wide_result[n + m] += wide_chunk_t(data[n]) * wide_chunk_t(other.data[m]);
+ wide_result[n + m + 1] += wide_result[n + m] >> chunk::bits;
+ wide_result[n + m] &= chunk::mask;
+ }
+ }
+ for (size_t n = 0; n < result.chunks; n++) {
+ result.data[n] = wide_result[n];
+ }
+ result.data[result.chunks - 1] &= result.msb_mask;
+ return result;
+ }
};
// Expression template for a slice, usable as lvalue or rvalue, and composable with other expression templates here.
@@ -439,12 +517,14 @@ struct slice_expr : public expr_base<slice_expr<T, Stop, Start>> {
slice_expr(T &expr) : expr(expr) {}
slice_expr(const slice_expr<T, Stop, Start> &) = delete;
+ CXXRTL_ALWAYS_INLINE
operator value<bits>() const {
return static_cast<const value<T::bits> &>(expr)
.template rtrunc<T::bits - Start>()
.template trunc<bits>();
}
+ CXXRTL_ALWAYS_INLINE
slice_expr<T, Stop, Start> &operator=(const value<bits> &rhs) {
// Generic partial assignment implemented using a read-modify-write operation on the sliced expression.
expr = static_cast<const value<T::bits> &>(expr)
@@ -453,6 +533,7 @@ struct slice_expr : public expr_base<slice_expr<T, Stop, Start>> {
}
// A helper that forces the cast to value<>, which allows deduction to work.
+ CXXRTL_ALWAYS_INLINE
value<bits> val() const {
return static_cast<const value<bits> &>(*this);
}
@@ -469,6 +550,7 @@ struct concat_expr : public expr_base<concat_expr<T, U>> {
concat_expr(T &ms_expr, U &ls_expr) : ms_expr(ms_expr), ls_expr(ls_expr) {}
concat_expr(const concat_expr<T, U> &) = delete;
+ CXXRTL_ALWAYS_INLINE
operator value<bits>() const {
value<bits> ms_shifted = static_cast<const value<T::bits> &>(ms_expr)
.template rzext<bits>();
@@ -477,6 +559,7 @@ struct concat_expr : public expr_base<concat_expr<T, U>> {
return ms_shifted.bit_or(ls_extended);
}
+ CXXRTL_ALWAYS_INLINE
concat_expr<T, U> &operator=(const value<bits> &rhs) {
ms_expr = rhs.template rtrunc<T::bits>();
ls_expr = rhs.template trunc<U::bits>();
@@ -484,6 +567,7 @@ struct concat_expr : public expr_base<concat_expr<T, U>> {
}
// A helper that forces the cast to value<>, which allows deduction to work.
+ CXXRTL_ALWAYS_INLINE
value<bits> val() const {
return static_cast<const value<bits> &>(*this);
}
@@ -508,21 +592,25 @@ struct concat_expr : public expr_base<concat_expr<T, U>> {
template<class T>
struct expr_base {
template<size_t Stop, size_t Start = Stop>
+ CXXRTL_ALWAYS_INLINE
slice_expr<const T, Stop, Start> slice() const {
return {*static_cast<const T *>(this)};
}
template<size_t Stop, size_t Start = Stop>
+ CXXRTL_ALWAYS_INLINE
slice_expr<T, Stop, Start> slice() {
return {*static_cast<T *>(this)};
}
template<class U>
+ CXXRTL_ALWAYS_INLINE
concat_expr<const T, typename std::remove_reference<const U>::type> concat(const U &other) const {
return {*static_cast<const T *>(this), other};
}
template<class U>
+ CXXRTL_ALWAYS_INLINE
concat_expr<T, typename std::remove_reference<U>::type> concat(U &&other) {
return {*static_cast<T *>(this), other};
}
@@ -563,6 +651,18 @@ struct wire {
wire(wire<Bits> &&) = default;
wire<Bits> &operator=(const wire<Bits> &) = delete;
+ template<class IntegerT>
+ CXXRTL_ALWAYS_INLINE
+ IntegerT get() const {
+ return curr.template get<IntegerT>();
+ }
+
+ template<class IntegerT>
+ CXXRTL_ALWAYS_INLINE
+ void set(IntegerT other) {
+ next.template set<IntegerT>(other);
+ }
+
bool commit() {
if (curr != next) {
curr = next;
@@ -608,6 +708,7 @@ struct memory {
// This utterly reprehensible construct is the most reasonable way to apply a function to every element
// of a parameter pack, if the elements all have different types and so cannot be cast to an initializer list.
auto _ = {std::move(std::begin(init.data), std::end(init.data), data.begin() + init.offset)...};
+ (void)_;
}
// An operator for direct memory reads. May be used at any time during the simulation.
@@ -676,10 +777,8 @@ struct metadata {
// In debug mode, using the wrong .as_*() function will assert.
// In release mode, using the wrong .as_*() function will safely return a default value.
- union {
- const unsigned uint_value = 0;
- const signed sint_value;
- };
+ const unsigned uint_value = 0;
+ const signed sint_value = 0;
const std::string string_value = "";
const double double_value = 0.0;
@@ -716,68 +815,155 @@ struct metadata {
typedef std::map<std::string, metadata> metadata_map;
+// Helper class to disambiguate values/wires and their aliases.
+struct debug_alias {};
+
// This structure is intended for consumption via foreign function interfaces, like Python's ctypes.
// Because of this it uses a C-style layout that is easy to parse rather than more idiomatic C++.
//
// To avoid violating strict aliasing rules, this structure has to be a subclass of the one used
// in the C API, or it would not be possible to cast between the pointers to these.
struct debug_item : ::cxxrtl_object {
+ // Object types.
enum : uint32_t {
VALUE = CXXRTL_VALUE,
WIRE = CXXRTL_WIRE,
MEMORY = CXXRTL_MEMORY,
+ ALIAS = CXXRTL_ALIAS,
+ };
+
+ // Object flags.
+ enum : uint32_t {
+ INPUT = CXXRTL_INPUT,
+ OUTPUT = CXXRTL_OUTPUT,
+ INOUT = CXXRTL_INOUT,
+ DRIVEN_SYNC = CXXRTL_DRIVEN_SYNC,
+ DRIVEN_COMB = CXXRTL_DRIVEN_COMB,
+ UNDRIVEN = CXXRTL_UNDRIVEN,
};
debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {}
template<size_t Bits>
- debug_item(value<Bits> &item) {
+ debug_item(value<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout");
- type = VALUE;
- width = Bits;
- depth = 1;
- curr = item.data;
- next = item.data;
+ type = VALUE;
+ flags = flags_;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = item.data;
+ next = item.data;
}
template<size_t Bits>
- debug_item(const value<Bits> &item) {
+ debug_item(const value<Bits> &item, size_t lsb_offset = 0) {
static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
"value<Bits> is not compatible with C layout");
- type = VALUE;
- width = Bits;
- depth = 1;
- curr = const_cast<uint32_t*>(item.data);
- next = nullptr;
+ type = VALUE;
+ flags = DRIVEN_COMB;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = const_cast<chunk_t*>(item.data);
+ next = nullptr;
}
template<size_t Bits>
- debug_item(wire<Bits> &item) {
+ debug_item(wire<Bits> &item, size_t lsb_offset = 0, uint32_t flags_ = 0) {
static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
"wire<Bits> is not compatible with C layout");
- type = WIRE;
- width = Bits;
- depth = 1;
- curr = item.curr.data;
- next = item.next.data;
+ type = WIRE;
+ flags = flags_;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = item.curr.data;
+ next = item.next.data;
}
template<size_t Width>
- debug_item(memory<Width> &item) {
+ debug_item(memory<Width> &item, size_t zero_offset = 0) {
static_assert(sizeof(item.data[0]) == value<Width>::chunks * sizeof(chunk_t),
"memory<Width> is not compatible with C layout");
- type = MEMORY;
- width = Width;
- depth = item.data.size();
- curr = item.data.empty() ? nullptr : item.data[0].data;
- next = nullptr;
+ type = MEMORY;
+ flags = 0;
+ width = Width;
+ lsb_at = 0;
+ depth = item.data.size();
+ zero_at = zero_offset;
+ curr = item.data.empty() ? nullptr : item.data[0].data;
+ next = nullptr;
+ }
+
+ template<size_t Bits>
+ debug_item(debug_alias, const value<Bits> &item, size_t lsb_offset = 0) {
+ static_assert(sizeof(item) == value<Bits>::chunks * sizeof(chunk_t),
+ "value<Bits> is not compatible with C layout");
+ type = ALIAS;
+ flags = DRIVEN_COMB;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = const_cast<chunk_t*>(item.data);
+ next = nullptr;
+ }
+
+ template<size_t Bits>
+ debug_item(debug_alias, const wire<Bits> &item, size_t lsb_offset = 0) {
+ static_assert(sizeof(item.curr) == value<Bits>::chunks * sizeof(chunk_t) &&
+ sizeof(item.next) == value<Bits>::chunks * sizeof(chunk_t),
+ "wire<Bits> is not compatible with C layout");
+ type = ALIAS;
+ flags = DRIVEN_COMB;
+ width = Bits;
+ lsb_at = lsb_offset;
+ depth = 1;
+ zero_at = 0;
+ curr = const_cast<chunk_t*>(item.curr.data);
+ next = nullptr;
}
};
static_assert(std::is_standard_layout<debug_item>::value, "debug_item is not compatible with C layout");
-typedef std::map<std::string, debug_item> debug_items;
+struct debug_items {
+ std::map<std::string, std::vector<debug_item>> table;
+
+ void add(const std::string &name, debug_item &&item) {
+ std::vector<debug_item> &parts = table[name];
+ parts.emplace_back(item);
+ std::sort(parts.begin(), parts.end(),
+ [](const debug_item &a, const debug_item &b) {
+ return a.lsb_at < b.lsb_at;
+ });
+ }
+
+ size_t count(const std::string &name) const {
+ if (table.count(name) == 0)
+ return 0;
+ return table.at(name).size();
+ }
+
+ const std::vector<debug_item> &parts_at(const std::string &name) const {
+ return table.at(name);
+ }
+
+ const debug_item &at(const std::string &name) const {
+ const std::vector<debug_item> &parts = table.at(name);
+ assert(parts.size() == 1);
+ return parts.at(0);
+ }
+
+ const debug_item &operator [](const std::string &name) const {
+ return at(name);
+ }
+};
struct module {
module() {}
@@ -799,7 +985,9 @@ struct module {
return deltas;
}
- virtual void debug_info(debug_items &items, std::string path = "") {}
+ virtual void debug_info(debug_items &items, std::string path = "") {
+ (void)items, (void)path;
+ }
};
} // namespace cxxrtl
@@ -823,271 +1011,322 @@ using namespace cxxrtl;
// std::max isn't constexpr until C++14 for no particular reason (it's an oversight), so we define our own.
template<class T>
+CXXRTL_ALWAYS_INLINE
constexpr T max(const T &a, const T &b) {
return a > b ? a : b;
}
// Logic operations
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> logic_not(const value<BitsA> &a) {
return value<BitsY> { a ? 0u : 1u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> logic_and(const value<BitsA> &a, const value<BitsB> &b) {
- return value<BitsY> { (bool(a) & bool(b)) ? 1u : 0u };
+ return value<BitsY> { (bool(a) && bool(b)) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> logic_or(const value<BitsA> &a, const value<BitsB> &b) {
- return value<BitsY> { (bool(a) | bool(b)) ? 1u : 0u };
+ return value<BitsY> { (bool(a) || bool(b)) ? 1u : 0u };
}
// Reduction operations
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> reduce_and(const value<BitsA> &a) {
return value<BitsY> { a.bit_not().is_zero() ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> reduce_or(const value<BitsA> &a) {
return value<BitsY> { a ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> reduce_xor(const value<BitsA> &a) {
return value<BitsY> { (a.ctpop() % 2) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> reduce_xnor(const value<BitsA> &a) {
return value<BitsY> { (a.ctpop() % 2) ? 0u : 1u };
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> reduce_bool(const value<BitsA> &a) {
return value<BitsY> { a ? 1u : 0u };
}
// Bitwise operations
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> not_u(const value<BitsA> &a) {
return a.template zcast<BitsY>().bit_not();
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> not_s(const value<BitsA> &a) {
return a.template scast<BitsY>().bit_not();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> and_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().bit_and(b.template zcast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> and_ss(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().bit_and(b.template scast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> or_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().bit_or(b.template zcast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> or_ss(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().bit_or(b.template scast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> xor_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().bit_xor(b.template zcast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> xor_ss(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().bit_xor(b.template scast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> xnor_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().bit_xor(b.template zcast<BitsY>()).bit_not();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> xnor_ss(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().bit_xor(b.template scast<BitsY>()).bit_not();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shl_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().template shl(b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shl_su(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().template shl(b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> sshl_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().template shl(b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> sshl_su(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().template shl(b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shr_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template shr(b).template zcast<BitsY>();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shr_su(const value<BitsA> &a, const value<BitsB> &b) {
return a.template shr(b).template scast<BitsY>();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> sshr_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template shr(b).template zcast<BitsY>();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> sshr_su(const value<BitsA> &a, const value<BitsB> &b) {
return a.template sshr(b).template scast<BitsY>();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shift_uu(const value<BitsA> &a, const value<BitsB> &b) {
return shr_uu<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shift_su(const value<BitsA> &a, const value<BitsB> &b) {
return shr_su<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shift_us(const value<BitsA> &a, const value<BitsB> &b) {
return b.is_neg() ? shl_uu<BitsY>(a, b.template sext<BitsB + 1>().neg()) : shr_uu<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shift_ss(const value<BitsA> &a, const value<BitsB> &b) {
return b.is_neg() ? shl_su<BitsY>(a, b.template sext<BitsB + 1>().neg()) : shr_su<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shiftx_uu(const value<BitsA> &a, const value<BitsB> &b) {
return shift_uu<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shiftx_su(const value<BitsA> &a, const value<BitsB> &b) {
return shift_su<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shiftx_us(const value<BitsA> &a, const value<BitsB> &b) {
return shift_us<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> shiftx_ss(const value<BitsA> &a, const value<BitsB> &b) {
return shift_ss<BitsY>(a, b);
}
// Comparison operations
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> eq_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY>{ a.template zext<BitsExt>() == b.template zext<BitsExt>() ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> eq_ss(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY>{ a.template sext<BitsExt>() == b.template sext<BitsExt>() ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> ne_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY>{ a.template zext<BitsExt>() != b.template zext<BitsExt>() ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> ne_ss(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY>{ a.template sext<BitsExt>() != b.template sext<BitsExt>() ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> eqx_uu(const value<BitsA> &a, const value<BitsB> &b) {
return eq_uu<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> eqx_ss(const value<BitsA> &a, const value<BitsB> &b) {
return eq_ss<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> nex_uu(const value<BitsA> &a, const value<BitsB> &b) {
return ne_uu<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> nex_ss(const value<BitsA> &a, const value<BitsB> &b) {
return ne_ss<BitsY>(a, b);
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> gt_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { b.template zext<BitsExt>().ucmp(a.template zext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> gt_ss(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { b.template sext<BitsExt>().scmp(a.template sext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> ge_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { !a.template zext<BitsExt>().ucmp(b.template zext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> ge_ss(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { !a.template sext<BitsExt>().scmp(b.template sext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> lt_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { a.template zext<BitsExt>().ucmp(b.template zext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> lt_ss(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { a.template sext<BitsExt>().scmp(b.template sext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> le_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { !b.template zext<BitsExt>().ucmp(a.template zext<BitsExt>()) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> le_ss(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t BitsExt = max(BitsA, BitsB);
return value<BitsY> { !b.template sext<BitsExt>().scmp(a.template sext<BitsExt>()) ? 1u : 0u };
@@ -1095,71 +1334,68 @@ value<BitsY> le_ss(const value<BitsA> &a, const value<BitsB> &b) {
// Arithmetic operations
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> pos_u(const value<BitsA> &a) {
return a.template zcast<BitsY>();
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> pos_s(const value<BitsA> &a) {
return a.template scast<BitsY>();
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> neg_u(const value<BitsA> &a) {
return a.template zcast<BitsY>().neg();
}
template<size_t BitsY, size_t BitsA>
+CXXRTL_ALWAYS_INLINE
value<BitsY> neg_s(const value<BitsA> &a) {
return a.template scast<BitsY>().neg();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> add_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().add(b.template zcast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> add_ss(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().add(b.template scast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> sub_uu(const value<BitsA> &a, const value<BitsB> &b) {
return a.template zcast<BitsY>().sub(b.template zcast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> sub_ss(const value<BitsA> &a, const value<BitsB> &b) {
return a.template scast<BitsY>().sub(b.template scast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> mul_uu(const value<BitsA> &a, const value<BitsB> &b) {
- value<BitsY> product;
- value<BitsY> multiplicand = a.template zcast<BitsY>();
- const value<BitsB> &multiplier = b;
- uint32_t multiplicand_shift = 0;
- for (size_t step = 0; step < BitsB; step++) {
- if (multiplier.bit(step)) {
- multiplicand = multiplicand.shl(value<32> { multiplicand_shift });
- product = product.add(multiplicand);
- multiplicand_shift = 0;
- }
- multiplicand_shift++;
- }
- return product;
+ constexpr size_t BitsM = BitsA >= BitsB ? BitsA : BitsB;
+ return a.template zcast<BitsM>().template mul<BitsY>(b.template zcast<BitsM>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> mul_ss(const value<BitsA> &a, const value<BitsB> &b) {
- value<BitsB + 1> ub = b.template sext<BitsB + 1>();
- if (ub.is_neg()) ub = ub.neg();
- value<BitsY> y = mul_uu<BitsY>(a.template scast<BitsY>(), ub);
- return b.is_neg() ? y.neg() : y;
+ return a.template scast<BitsY>().template mul<BitsY>(b.template scast<BitsY>());
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
std::pair<value<BitsY>, value<BitsY>> divmod_uu(const value<BitsA> &a, const value<BitsB> &b) {
constexpr size_t Bits = max(BitsY, max(BitsA, BitsB));
value<Bits> quotient;
@@ -1181,6 +1417,7 @@ std::pair<value<BitsY>, value<BitsY>> divmod_uu(const value<BitsA> &a, const val
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
std::pair<value<BitsY>, value<BitsY>> divmod_ss(const value<BitsA> &a, const value<BitsB> &b) {
value<BitsA + 1> ua = a.template sext<BitsA + 1>();
value<BitsB + 1> ub = b.template sext<BitsB + 1>();
@@ -1194,21 +1431,25 @@ std::pair<value<BitsY>, value<BitsY>> divmod_ss(const value<BitsA> &a, const val
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> div_uu(const value<BitsA> &a, const value<BitsB> &b) {
return divmod_uu<BitsY>(a, b).first;
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> div_ss(const value<BitsA> &a, const value<BitsB> &b) {
return divmod_ss<BitsY>(a, b).first;
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> mod_uu(const value<BitsA> &a, const value<BitsB> &b) {
return divmod_uu<BitsY>(a, b).second;
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
+CXXRTL_ALWAYS_INLINE
value<BitsY> mod_ss(const value<BitsA> &a, const value<BitsB> &b) {
return divmod_ss<BitsY>(a, b).second;
}
diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc
index 4c04a2f14..a48ea5b23 100644
--- a/backends/cxxrtl/cxxrtl_backend.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -22,6 +22,7 @@
#include "kernel/sigtools.h"
#include "kernel/utils.h"
#include "kernel/celltypes.h"
+#include "kernel/mem.h"
#include "kernel/log.h"
USING_YOSYS_NAMESPACE
@@ -171,11 +172,6 @@ struct Scheduler {
}
};
-bool is_input_wire(const RTLIL::Wire *wire)
-{
- return wire->port_input && !wire->port_output;
-}
-
bool is_unary_cell(RTLIL::IdString type)
{
return type.in(
@@ -202,19 +198,15 @@ bool is_extending_cell(RTLIL::IdString type)
bool is_elidable_cell(RTLIL::IdString type)
{
return is_unary_cell(type) || is_binary_cell(type) || type.in(
- ID($mux), ID($concat), ID($slice));
-}
-
-bool is_sync_ff_cell(RTLIL::IdString type)
-{
- return type.in(
- ID($dff), ID($dffe));
+ ID($mux), ID($concat), ID($slice), ID($pmux));
}
bool is_ff_cell(RTLIL::IdString type)
{
- return is_sync_ff_cell(type) || type.in(
- ID($adff), ID($dffsr), ID($dlatch), ID($dlatchsr), ID($sr));
+ return type.in(
+ ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce),
+ ID($adff), ID($adffe), ID($dffsr), ID($dffsre),
+ ID($dlatch), ID($adlatch), ID($dlatchsr), ID($sr));
}
bool is_internal_cell(RTLIL::IdString type)
@@ -282,6 +274,7 @@ struct FlowGraph {
std::vector<Node*> nodes;
dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<const RTLIL::Wire*, bool> wire_def_elidable, wire_use_elidable;
+ dict<RTLIL::SigBit, bool> bit_has_state;
~FlowGraph()
{
@@ -289,17 +282,24 @@ struct FlowGraph {
delete node;
}
- void add_defs(Node *node, const RTLIL::SigSpec &sig, bool fully_sync, bool elidable)
+ void add_defs(Node *node, const RTLIL::SigSpec &sig, bool is_ff, bool elidable)
{
for (auto chunk : sig.chunks())
if (chunk.wire) {
- if (fully_sync)
+ if (is_ff) {
+ // A sync def means that a wire holds design state because it is driven directly by
+ // a flip-flop output. Such a wire can never be unbuffered.
wire_sync_defs[chunk.wire].insert(node);
- else
+ } else {
+ // A comb def means that a wire doesn't hold design state. It might still be connected,
+ // indirectly, to a flip-flop output.
wire_comb_defs[chunk.wire].insert(node);
+ }
}
+ for (auto bit : sig.bits())
+ bit_has_state[bit] |= is_ff;
// Only comb defs of an entire wire in the right order can be elided.
- if (!fully_sync && sig.is_wire())
+ if (!is_ff && sig.is_wire())
wire_def_elidable[sig.as_wire()] = elidable;
}
@@ -327,7 +327,7 @@ struct FlowGraph {
// Connections
void add_connect_defs_uses(Node *node, const RTLIL::SigSig &conn)
{
- add_defs(node, conn.first, /*fully_sync=*/false, /*elidable=*/true);
+ add_defs(node, conn.first, /*is_ff=*/false, /*elidable=*/true);
add_uses(node, conn.second);
}
@@ -374,7 +374,7 @@ struct FlowGraph {
if (cell->output(conn.first))
if (is_cxxrtl_sync_port(cell, conn.first)) {
// See note regarding elidability below.
- add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/false);
+ add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/false);
}
}
@@ -383,18 +383,18 @@ struct FlowGraph {
for (auto conn : cell->connections()) {
if (cell->output(conn.first)) {
if (is_elidable_cell(cell->type))
- add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/true);
- else if (is_sync_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool()))
- add_defs(node, conn.second, /*fully_sync=*/true, /*elidable=*/false);
+ add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/true);
+ else if (is_ff_cell(cell->type) || (cell->type == ID($memrd) && cell->getParam(ID::CLK_ENABLE).as_bool()))
+ add_defs(node, conn.second, /*is_ff=*/true, /*elidable=*/false);
else if (is_internal_cell(cell->type))
- add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/false);
+ add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/false);
else if (!is_cxxrtl_sync_port(cell, conn.first)) {
// Although at first it looks like outputs of user-defined cells may always be elided, the reality is
// more complex. Fully sync outputs produce no defs and so don't participate in elision. Fully comb
// outputs are assigned in a different way depending on whether the cell's eval() immediately converged.
// Unknown/mixed outputs could be elided, but should be rare in practical designs and don't justify
// the infrastructure required to elide outputs of cells with many of them.
- add_defs(node, conn.second, /*fully_sync=*/false, /*elidable=*/false);
+ add_defs(node, conn.second, /*is_ff=*/false, /*elidable=*/false);
}
}
if (cell->input(conn.first))
@@ -432,7 +432,7 @@ struct FlowGraph {
void add_case_defs_uses(Node *node, const RTLIL::CaseRule *case_)
{
for (auto &action : case_->actions) {
- add_defs(node, action.first, /*is_sync=*/false, /*elidable=*/false);
+ add_defs(node, action.first, /*is_ff=*/false, /*elidable=*/false);
add_uses(node, action.second);
}
for (auto sub_switch : case_->switches) {
@@ -451,9 +451,9 @@ struct FlowGraph {
for (auto sync : process->syncs)
for (auto action : sync->actions) {
if (sync->type == RTLIL::STp || sync->type == RTLIL::STn || sync->type == RTLIL::STe)
- add_defs(node, action.first, /*is_sync=*/true, /*elidable=*/false);
+ add_defs(node, action.first, /*is_ff=*/true, /*elidable=*/false);
else
- add_defs(node, action.first, /*is_sync=*/false, /*elidable=*/false);
+ add_defs(node, action.first, /*is_ff=*/false, /*elidable=*/false);
add_uses(node, action.second);
}
}
@@ -527,12 +527,16 @@ struct CxxrtlWorker {
std::ostream *impl_f = nullptr;
std::ostream *intf_f = nullptr;
- bool elide_internal = false;
- bool elide_public = false;
+ bool run_hierarchy = false;
+ bool run_flatten = false;
+ bool run_proc = false;
+
+ bool unbuffer_internal = false;
+ bool unbuffer_public = false;
bool localize_internal = false;
bool localize_public = false;
- bool run_proc_flatten = false;
- bool max_opt_level = false;
+ bool elide_internal = false;
+ bool elide_public = false;
bool debug_info = false;
@@ -547,9 +551,11 @@ struct CxxrtlWorker {
dict<const RTLIL::Cell*, pool<const RTLIL::Cell*>> transparent_for;
dict<const RTLIL::Wire*, FlowGraph::Node> elided_wires;
dict<const RTLIL::Module*, std::vector<FlowGraph::Node>> schedule;
+ pool<const RTLIL::Wire*> unbuffered_wires;
pool<const RTLIL::Wire*> localized_wires;
dict<const RTLIL::Wire*, const RTLIL::Wire*> debug_alias_wires;
dict<const RTLIL::Wire*, RTLIL::Const> debug_const_wires;
+ dict<RTLIL::SigBit, bool> bit_has_state;
dict<const RTLIL::Module*, pool<std::string>> blackbox_specializations;
dict<const RTLIL::Module*, bool> eval_converges;
@@ -786,7 +792,8 @@ struct CxxrtlWorker {
dump_const(chunk.data, chunk.width, chunk.offset);
return false;
} else {
- if (!is_lhs && elided_wires.count(chunk.wire)) {
+ if (elided_wires.count(chunk.wire)) {
+ log_assert(!is_lhs);
const FlowGraph::Node &node = elided_wires[chunk.wire];
switch (node.type) {
case FlowGraph::Node::Type::CONNECT:
@@ -799,7 +806,7 @@ struct CxxrtlWorker {
default:
log_assert(false);
}
- } else if (localized_wires[chunk.wire] || is_input_wire(chunk.wire)) {
+ } else if (unbuffered_wires[chunk.wire]) {
f << mangle(chunk.wire);
} else {
f << mangle(chunk.wire) << (is_lhs ? ".next" : ".curr");
@@ -942,6 +949,21 @@ struct CxxrtlWorker {
f << " : ";
dump_sigspec_rhs(cell->getPort(ID::A));
f << ")";
+ // Parallel (one-hot) muxes
+ } else if (cell->type == ID($pmux)) {
+ int width = cell->getParam(ID::WIDTH).as_int();
+ int s_width = cell->getParam(ID::S_WIDTH).as_int();
+ for (int part = 0; part < s_width; part++) {
+ f << "(";
+ dump_sigspec_rhs(cell->getPort(ID::S).extract(part));
+ f << " ? ";
+ dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width));
+ f << " : ";
+ }
+ dump_sigspec_rhs(cell->getPort(ID::A));
+ for (int part = 0; part < s_width; part++) {
+ f << ")";
+ }
// Concats
} else if (cell->type == ID($concat)) {
dump_sigspec_rhs(cell->getPort(ID::B));
@@ -1008,35 +1030,6 @@ struct CxxrtlWorker {
f << " = ";
dump_cell_elided(cell);
f << ";\n";
- // Parallel (one-hot) muxes
- } else if (cell->type == ID($pmux)) {
- int width = cell->getParam(ID::WIDTH).as_int();
- int s_width = cell->getParam(ID::S_WIDTH).as_int();
- bool first = true;
- for (int part = 0; part < s_width; part++) {
- f << (first ? indent : " else ");
- first = false;
- f << "if (";
- dump_sigspec_rhs(cell->getPort(ID::S).extract(part));
- f << ") {\n";
- inc_indent();
- f << indent;
- dump_sigspec_lhs(cell->getPort(ID::Y));
- f << " = ";
- dump_sigspec_rhs(cell->getPort(ID::B).extract(part * width, width));
- f << ";\n";
- dec_indent();
- f << indent << "}";
- }
- f << " else {\n";
- inc_indent();
- f << indent;
- dump_sigspec_lhs(cell->getPort(ID::Y));
- f << " = ";
- dump_sigspec_rhs(cell->getPort(ID::A));
- f << ";\n";
- dec_indent();
- f << indent << "}\n";
// Flip-flops
} else if (is_ff_cell(cell->type)) {
if (cell->hasPort(ID::CLK) && cell->getPort(ID::CLK).is_wire()) {
@@ -1046,7 +1039,7 @@ struct CxxrtlWorker {
f << indent << "if (" << (cell->getParam(ID::CLK_POLARITY).as_bool() ? "posedge_" : "negedge_")
<< mangle(clk_bit) << ") {\n";
inc_indent();
- if (cell->type == ID($dffe)) {
+ if (cell->hasPort(ID::EN)) {
f << indent << "if (";
dump_sigspec_rhs(cell->getPort(ID::EN));
f << " == value<1> {" << cell->getParam(ID::EN_POLARITY).as_bool() << "u}) {\n";
@@ -1057,7 +1050,24 @@ struct CxxrtlWorker {
f << " = ";
dump_sigspec_rhs(cell->getPort(ID::D));
f << ";\n";
- if (cell->type == ID($dffe)) {
+ if (cell->hasPort(ID::EN) && cell->type != ID($sdffce)) {
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (cell->hasPort(ID::SRST)) {
+ f << indent << "if (";
+ dump_sigspec_rhs(cell->getPort(ID::SRST));
+ f << " == value<1> {" << cell->getParam(ID::SRST_POLARITY).as_bool() << "u}) {\n";
+ inc_indent();
+ f << indent;
+ dump_sigspec_lhs(cell->getPort(ID::Q));
+ f << " = ";
+ dump_const(cell->getParam(ID::SRST_VALUE));
+ f << ";\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
+ if (cell->hasPort(ID::EN) && cell->type == ID($sdffce)) {
dec_indent();
f << indent << "}\n";
}
@@ -1139,7 +1149,7 @@ struct CxxrtlWorker {
}
// The generated code has two bounds checks; one in an assertion, and another that guards the read.
// This is done so that the code does not invoke undefined behavior under any conditions, but nevertheless
- // loudly crashes if an illegal condition is encountered. The assert may be turned off with -NDEBUG not
+ // loudly crashes if an illegal condition is encountered. The assert may be turned off with -DNDEBUG not
// just for release builds, but also to make sure the simulator (which is presumably embedded in some
// larger program) will never crash the code that calls into it.
//
@@ -1148,31 +1158,33 @@ struct CxxrtlWorker {
f << indent << "if(" << valid_index_temp << ".valid) {\n";
inc_indent();
if (writable_memories[memory]) {
- std::string addr_temp = fresh_temporary();
- f << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = ";
- dump_sigspec_rhs(cell->getPort(ID::ADDR));
- f << ";\n";
std::string lhs_temp = fresh_temporary();
f << indent << "value<" << memory->width << "> " << lhs_temp << " = "
<< mangle(memory) << "[" << valid_index_temp << ".index];\n";
std::vector<const RTLIL::Cell*> memwr_cells(transparent_for[cell].begin(), transparent_for[cell].end());
- std::sort(memwr_cells.begin(), memwr_cells.end(),
- [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
- return a->getParam(ID::PRIORITY).as_int() < b->getParam(ID::PRIORITY).as_int();
- });
- for (auto memwr_cell : memwr_cells) {
- f << indent << "if (" << addr_temp << " == ";
- dump_sigspec_rhs(memwr_cell->getPort(ID::ADDR));
- f << ") {\n";
- inc_indent();
- f << indent << lhs_temp << " = " << lhs_temp;
- f << ".update(";
- dump_sigspec_rhs(memwr_cell->getPort(ID::DATA));
- f << ", ";
- dump_sigspec_rhs(memwr_cell->getPort(ID::EN));
- f << ");\n";
- dec_indent();
- f << indent << "}\n";
+ if (!memwr_cells.empty()) {
+ std::string addr_temp = fresh_temporary();
+ f << indent << "const value<" << cell->getPort(ID::ADDR).size() << "> &" << addr_temp << " = ";
+ dump_sigspec_rhs(cell->getPort(ID::ADDR));
+ f << ";\n";
+ std::sort(memwr_cells.begin(), memwr_cells.end(),
+ [](const RTLIL::Cell *a, const RTLIL::Cell *b) {
+ return a->getParam(ID::PRIORITY).as_int() < b->getParam(ID::PRIORITY).as_int();
+ });
+ for (auto memwr_cell : memwr_cells) {
+ f << indent << "if (" << addr_temp << " == ";
+ dump_sigspec_rhs(memwr_cell->getPort(ID::ADDR));
+ f << ") {\n";
+ inc_indent();
+ f << indent << lhs_temp << " = " << lhs_temp;
+ f << ".update(";
+ dump_sigspec_rhs(memwr_cell->getPort(ID::DATA));
+ f << ", ";
+ dump_sigspec_rhs(memwr_cell->getPort(ID::EN));
+ f << ");\n";
+ dec_indent();
+ f << indent << "}\n";
+ }
}
f << indent;
dump_sigspec_lhs(cell->getPort(ID::DATA));
@@ -1434,13 +1446,12 @@ struct CxxrtlWorker {
{
if (elided_wires.count(wire))
return;
- if (localized_wires.count(wire) != is_local_context)
- return;
- if (is_local_context) {
+ if (localized_wires[wire] && is_local_context) {
dump_attrs(wire);
f << indent << "value<" << wire->width << "> " << mangle(wire) << ";\n";
- } else {
+ }
+ if (!localized_wires[wire] && !is_local_context) {
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));
@@ -1449,14 +1460,21 @@ struct CxxrtlWorker {
}
dump_attrs(wire);
- f << indent << (is_input_wire(wire) ? "value" : "wire") << "<" << width << "> " << mangle(wire);
+ f << indent;
+ if (wire->port_input && wire->port_output)
+ f << "/*inout*/ ";
+ else if (wire->port_input)
+ f << "/*input*/ ";
+ else if (wire->port_output)
+ f << "/*output*/ ";
+ f << (unbuffered_wires[wire] ? "value" : "wire") << "<" << width << "> " << mangle(wire);
if (wire->has_attribute(ID::init)) {
f << " ";
dump_const_init(wire->attributes.at(ID::init));
}
f << ";\n";
if (edge_wires[wire]) {
- if (is_input_wire(wire)) {
+ if (unbuffered_wires[wire]) {
f << indent << "value<" << width << "> prev_" << mangle(wire);
if (wire->has_attribute(ID::init)) {
f << " ";
@@ -1467,7 +1485,7 @@ struct CxxrtlWorker {
for (auto edge_type : edge_types) {
if (edge_type.first.wire == wire) {
std::string prev, next;
- if (is_input_wire(wire)) {
+ if (unbuffered_wires[wire]) {
prev = "prev_" + mangle(edge_type.first.wire);
next = mangle(edge_type.first.wire);
} else {
@@ -1590,9 +1608,9 @@ struct CxxrtlWorker {
inc_indent();
f << indent << "bool changed = false;\n";
for (auto wire : module->wires()) {
- if (elided_wires.count(wire) || localized_wires.count(wire))
+ if (elided_wires.count(wire))
continue;
- if (is_input_wire(wire)) {
+ if (unbuffered_wires[wire]) {
if (edge_wires[wire])
f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
continue;
@@ -1619,57 +1637,122 @@ struct CxxrtlWorker {
void dump_debug_info_method(RTLIL::Module *module)
{
+ size_t count_public_wires = 0;
size_t count_const_wires = 0;
size_t count_alias_wires = 0;
size_t count_member_wires = 0;
size_t count_skipped_wires = 0;
+ size_t count_driven_sync = 0;
+ size_t count_driven_comb = 0;
+ size_t count_undriven = 0;
+ size_t count_mixed_driver = 0;
inc_indent();
f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
for (auto wire : module->wires()) {
if (wire->name[0] != '\\')
continue;
+ if (module->get_bool_attribute(ID(cxxrtl_blackbox)) && (wire->port_id == 0))
+ continue;
+ count_public_wires++;
if (debug_const_wires.count(wire)) {
// Wire tied to a constant
f << indent << "static const value<" << wire->width << "> const_" << mangle(wire) << " = ";
dump_const(debug_const_wires[wire]);
f << ";\n";
- f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
- f << ", debug_item(const_" << mangle(wire) << "));\n";
+ f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
+ f << ", debug_item(const_" << mangle(wire) << ", ";
+ f << wire->start_offset << "));\n";
count_const_wires++;
} else if (debug_alias_wires.count(wire)) {
// Alias of a member wire
- f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
- f << ", debug_item(" << mangle(debug_alias_wires[wire]) << "));\n";
+ f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
+ f << ", debug_item(debug_alias(), " << mangle(debug_alias_wires[wire]) << ", ";
+ f << wire->start_offset << "));\n";
count_alias_wires++;
} else if (!localized_wires.count(wire)) {
// Member wire
- f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
- f << ", debug_item(" << mangle(wire) << "));\n";
+ std::vector<std::string> flags;
+
+ if (wire->port_input && wire->port_output)
+ flags.push_back("INOUT");
+ else if (wire->port_input)
+ flags.push_back("INPUT");
+ else if (wire->port_output)
+ flags.push_back("OUTPUT");
+
+ bool has_driven_sync = false;
+ bool has_driven_comb = false;
+ bool has_undriven = false;
+ SigSpec sig(wire);
+ for (auto bit : sig.bits())
+ if (!bit_has_state.count(bit))
+ has_undriven = true;
+ else if (bit_has_state[bit])
+ has_driven_sync = true;
+ else
+ has_driven_comb = true;
+ if (has_driven_sync)
+ flags.push_back("DRIVEN_SYNC");
+ if (has_driven_sync && !has_driven_comb && !has_undriven)
+ count_driven_sync++;
+ if (has_driven_comb)
+ flags.push_back("DRIVEN_COMB");
+ if (!has_driven_sync && has_driven_comb && !has_undriven)
+ count_driven_comb++;
+ if (has_undriven)
+ flags.push_back("UNDRIVEN");
+ if (!has_driven_sync && !has_driven_comb && has_undriven)
+ count_undriven++;
+ if (has_driven_sync + has_driven_comb + has_undriven > 1)
+ count_mixed_driver++;
+
+ f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(wire));
+ f << ", debug_item(" << mangle(wire) << ", ";
+ f << wire->start_offset;
+ bool first = true;
+ for (auto flag : flags) {
+ if (first) {
+ first = false;
+ f << ", ";
+ } else {
+ f << "|";
+ }
+ f << "debug_item::" << flag;
+ }
+ f << "));\n";
count_member_wires++;
} else {
count_skipped_wires++;
}
}
- for (auto &memory_it : module->memories) {
- if (memory_it.first[0] != '\\')
- continue;
- f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(memory_it.second));
- f << ", debug_item(" << mangle(memory_it.second) << "));\n";
- }
- for (auto cell : module->cells()) {
- if (is_internal_cell(cell->type))
- continue;
- const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
- f << indent << mangle(cell) << access << "debug_info(items, ";
- f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n";
+ if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
+ for (auto &memory_it : module->memories) {
+ if (memory_it.first[0] != '\\')
+ continue;
+ f << indent << "items.add(path + " << escape_cxx_string(get_hdl_name(memory_it.second));
+ f << ", debug_item(" << mangle(memory_it.second) << ", ";
+ f << memory_it.second->start_offset << "));\n";
+ }
+ for (auto cell : module->cells()) {
+ if (is_internal_cell(cell->type))
+ continue;
+ const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
+ f << indent << mangle(cell) << access << "debug_info(items, ";
+ f << "path + " << escape_cxx_string(get_hdl_name(cell) + ' ') << ");\n";
+ }
}
dec_indent();
- log_debug("Debug information statistics for module %s:\n", log_id(module));
- log_debug(" Const wires: %zu\n", count_const_wires);
- log_debug(" Alias wires: %zu\n", count_alias_wires);
- log_debug(" Member wires: %zu\n", count_member_wires);
- log_debug(" Other wires: %zu (no debug information)\n", count_skipped_wires);
+ log_debug("Debug information statistics for module `%s':\n", log_id(module));
+ log_debug(" Public wires: %zu, of which:\n", count_public_wires);
+ log_debug(" Const wires: %zu\n", count_const_wires);
+ log_debug(" Alias wires: %zu\n", count_alias_wires);
+ log_debug(" Member wires: %zu, of which:\n", count_member_wires);
+ log_debug(" Driven sync: %zu\n", count_driven_sync);
+ log_debug(" Driven comb: %zu\n", count_driven_comb);
+ log_debug(" Undriven: %zu\n", count_undriven);
+ log_debug(" Mixed driver: %zu\n", count_mixed_driver);
+ log_debug(" Other wires: %zu (no debug information)\n", count_skipped_wires);
}
void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
@@ -1840,7 +1923,8 @@ struct CxxrtlWorker {
topo_design.edge(cell_module, module);
}
}
- log_assert(topo_design.sort());
+ bool no_loops = topo_design.sort();
+ log_assert(no_loops);
modules.insert(modules.end(), topo_design.sorted.begin(), topo_design.sorted.end());
if (split_intf) {
@@ -1912,10 +1996,12 @@ struct CxxrtlWorker {
f << "} // namespace " << design_ns << "\n";
f << "\n";
if (top_module != nullptr && debug_info) {
+ f << "extern \"C\"\n";
f << "cxxrtl_toplevel " << design_ns << "_create() {\n";
inc_indent();
+ std::string top_type = design_ns + "::" + mangle(top_module);
f << indent << "return new _cxxrtl_toplevel { ";
- f << "std::make_unique<" << design_ns << "::" << mangle(top_module) << ">()";
+ f << "std::unique_ptr<" << top_type << ">(new " + top_type + ")";
f << " };\n";
dec_indent();
f << "}\n";
@@ -1949,7 +2035,7 @@ struct CxxrtlWorker {
void analyze_design(RTLIL::Design *design)
{
bool has_feedback_arcs = false;
- bool has_buffered_wires = false;
+ bool has_buffered_comb_wires = false;
for (auto module : design->modules()) {
if (!design->selected_module(module))
@@ -1961,6 +2047,8 @@ struct CxxrtlWorker {
if (module->get_bool_attribute(ID(cxxrtl_blackbox))) {
for (auto port : module->ports) {
RTLIL::Wire *wire = module->wire(port);
+ if (wire->port_input && !wire->port_output)
+ unbuffered_wires.insert(wire);
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))
@@ -2016,7 +2104,7 @@ struct CxxrtlWorker {
FlowGraph::Node *node = flow.add_node(cell);
// Various DFF cells are treated like posedge/negedge processes, see above for details.
- if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($dffsr))) {
+ if (cell->type.in(ID($dff), ID($dffe), ID($adff), ID($adffe), ID($dffsr), ID($dffsre), ID($sdff), ID($sdffe), ID($sdffce))) {
if (cell->getPort(ID::CLK).is_wire())
register_edge_signal(sigmap, cell->getPort(ID::CLK),
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
@@ -2096,6 +2184,8 @@ struct CxxrtlWorker {
if (wire->name.begins_with("$") && !elide_internal) continue;
if (wire->name.begins_with("\\") && !elide_public) continue;
if (edge_wires[wire]) continue;
+ if (flow.wire_comb_defs[wire].size() > 1)
+ log_cmd_error("Wire %s.%s has multiple drivers.\n", log_id(module), log_id(wire));
log_assert(flow.wire_comb_defs[wire].size() == 1);
elided_wires[wire] = **flow.wire_comb_defs[wire].begin();
}
@@ -2145,17 +2235,20 @@ 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()) {
if (feedback_wires[wire]) continue;
- if (wire->port_id != 0) continue;
+ if (wire->port_output && !module->get_bool_attribute(ID::top)) continue;
+ if (wire->name.begins_with("$") && !unbuffer_internal) continue;
+ if (wire->name.begins_with("\\") && !unbuffer_public) continue;
+ if (flow.wire_sync_defs.count(wire) > 0) continue;
+ unbuffered_wires.insert(wire);
+ if (edge_wires[wire]) continue;
if (wire->get_bool_attribute(ID::keep)) continue;
+ if (wire->port_input || wire->port_output) continue;
if (wire->name.begins_with("$") && !localize_internal) continue;
if (wire->name.begins_with("\\") && !localize_public) continue;
- if (edge_wires[wire]) continue;
- if (flow.wire_sync_defs.count(wire) > 0) continue;
localized_wires.insert(wire);
}
@@ -2165,22 +2258,22 @@ struct CxxrtlWorker {
// it is possible that a design with no feedback arcs would end up with doubly buffered wires in such cases
// as a wire with multiple drivers where one of them is combinatorial and the other is synchronous. Such designs
// also require more than one delta cycle to converge.
- pool<const RTLIL::Wire*> buffered_wires;
+ pool<const RTLIL::Wire*> buffered_comb_wires;
for (auto wire : module->wires()) {
- if (flow.wire_comb_defs[wire].size() > 0 && !elided_wires.count(wire) && !localized_wires[wire]) {
- if (!feedback_wires[wire])
- buffered_wires.insert(wire);
- }
+ if (flow.wire_comb_defs[wire].size() > 0 && !unbuffered_wires[wire] && !feedback_wires[wire])
+ buffered_comb_wires.insert(wire);
}
- if (!buffered_wires.empty()) {
- has_buffered_wires = true;
+ if (!buffered_comb_wires.empty()) {
+ has_buffered_comb_wires = true;
log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));
- for (auto wire : buffered_wires)
+ for (auto wire : buffered_comb_wires)
log(" %s\n", log_id(wire));
- log("\n");
}
- eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
+ eval_converges[module] = feedback_wires.empty() && buffered_comb_wires.empty();
+
+ for (auto item : flow.bit_has_state)
+ bit_has_state.insert(item);
if (debug_info) {
// Find wires that alias other wires or are tied to a constant; debug information can be enriched with these
@@ -2191,7 +2284,7 @@ struct CxxrtlWorker {
for (auto wire : module->wires()) {
if (wire->name[0] != '\\')
continue;
- if (!localized_wires[wire])
+ if (!unbuffered_wires[wire])
continue;
const RTLIL::Wire *wire_it = wire;
while (1) {
@@ -2204,7 +2297,7 @@ struct CxxrtlWorker {
RTLIL::SigSpec rhs_sig = node->connect.second;
if (rhs_sig.is_wire()) {
RTLIL::Wire *rhs_wire = rhs_sig.as_wire();
- if (localized_wires[rhs_wire]) {
+ if (unbuffered_wires[rhs_wire]) {
wire_it = rhs_wire; // maybe an alias
} else {
debug_alias_wires[wire] = rhs_wire; // is an alias
@@ -2220,24 +2313,26 @@ struct CxxrtlWorker {
}
}
}
- if (has_feedback_arcs || has_buffered_wires) {
+ if (has_feedback_arcs || has_buffered_comb_wires) {
// Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
// 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)
+ else if (has_buffered_comb_wires)
why_pessimistic = "buffered combinatorial wires";
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);
+ if (!run_flatten)
+ log("Flattening may eliminate %s from the design.\n", why_pessimistic);
+ if (!run_proc)
+ log("Converting processes to netlists may eliminate %s from the design.\n", why_pessimistic);
}
}
- void check_design(RTLIL::Design *design, bool &has_sync_init, bool &has_packed_mem)
+ void check_design(RTLIL::Design *design, bool &has_top, bool &has_sync_init, bool &has_packed_mem)
{
- has_sync_init = has_packed_mem = false;
+ has_sync_init = has_packed_mem = has_top = false;
for (auto module : design->modules()) {
if (module->get_blackbox_attribute() && !module->has_attribute(ID(cxxrtl_blackbox)))
@@ -2249,13 +2344,17 @@ struct CxxrtlWorker {
if (!design->selected_module(module))
continue;
+ if (module->get_bool_attribute(ID::top))
+ has_top = true;
+
for (auto proc : module->processes)
for (auto sync : proc.second->syncs)
if (sync->type == RTLIL::STi)
has_sync_init = true;
- for (auto cell : module->cells())
- if (cell->type == ID($mem))
+ // The Mem constructor also checks for well-formedness of $meminit cells, if any.
+ for (auto &mem : Mem::get_all_memories(module))
+ if (mem.packed)
has_packed_mem = true;
}
}
@@ -2263,13 +2362,20 @@ struct CxxrtlWorker {
void prepare_design(RTLIL::Design *design)
{
bool did_anything = false;
- bool has_sync_init, has_packed_mem;
+ bool has_top, 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");
+ check_design(design, has_top, has_sync_init, has_packed_mem);
+ if (run_hierarchy && !has_top) {
+ Pass::call(design, "hierarchy -auto-top");
+ did_anything = true;
+ }
+ if (run_flatten) {
Pass::call(design, "flatten");
did_anything = true;
+ }
+ if (run_proc) {
+ Pass::call(design, "proc");
+ 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.)
@@ -2283,9 +2389,9 @@ struct CxxrtlWorker {
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 (did_anything)
+ check_design(design, has_top, has_sync_init, has_packed_mem);
+ log_assert(has_top && !has_sync_init && !has_packed_mem);
log_pop();
if (did_anything)
log_spacer();
@@ -2294,11 +2400,12 @@ struct CxxrtlWorker {
};
struct CxxrtlBackend : public Backend {
- static const int DEFAULT_OPT_LEVEL = 5;
+ static const int DEFAULT_OPT_LEVEL = 6;
+ static const int OPT_LEVEL_DEBUG = 4;
static const int DEFAULT_DEBUG_LEVEL = 1;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -2317,9 +2424,9 @@ struct CxxrtlBackend : public Backend {
log(" top.step();\n");
log(" while (1) {\n");
log(" /* user logic */\n");
- log(" top.p_clk = value<1> {0u};\n");
+ log(" top.p_clk.set(false);\n");
log(" top.step();\n");
- log(" top.p_clk = value<1> {1u};\n");
+ log(" top.p_clk.set(true);\n");
log(" top.step();\n");
log(" }\n");
log(" }\n");
@@ -2466,6 +2573,22 @@ struct CxxrtlBackend : public Backend {
log(" place the generated code into namespace <ns-name>. if not specified,\n");
log(" \"cxxrtl_design\" is used.\n");
log("\n");
+ log(" -nohierarchy\n");
+ log(" use design hierarchy as-is. in most designs, a top module should be\n");
+ log(" present as it is exposed through the C API and has unbuffered outputs\n");
+ log(" for improved performance; it will be determined automatically if absent.\n");
+ log("\n");
+ log(" -noflatten\n");
+ log(" don't flatten the design. fully flattened designs can evaluate within\n");
+ log(" one delta cycle if they have no combinatorial feedback.\n");
+ log(" note that the debug interface and waveform dumps use full hierarchical\n");
+ log(" names for all wires even in flattened designs.\n");
+ log("\n");
+ log(" -noproc\n");
+ log(" don't convert processes to netlists. in most designs, converting\n");
+ log(" processes significantly improves evaluation performance at the cost of\n");
+ log(" slight increase in compilation time.\n");
+ log("\n");
log(" -O <level>\n");
log(" set the optimization level. the default is -O%d. higher optimization\n", DEFAULT_OPT_LEVEL);
log(" levels dramatically decrease compile and run time, and highest level\n");
@@ -2475,19 +2598,26 @@ struct CxxrtlBackend : public Backend {
log(" no optimization.\n");
log("\n");
log(" -O1\n");
- log(" elide internal wires if possible.\n");
+ log(" localize internal wires if possible.\n");
log("\n");
log(" -O2\n");
- log(" like -O1, and localize internal wires if possible.\n");
+ log(" like -O1, and unbuffer internal wires if possible.\n");
log("\n");
log(" -O3\n");
- log(" like -O2, and elide public wires not marked (*keep*) if possible.\n");
+ log(" like -O2, and elide internal wires if possible.\n");
log("\n");
log(" -O4\n");
- log(" like -O3, and localize public wires not marked (*keep*) if possible.\n");
+ log(" like -O3, and unbuffer public wires not marked (*keep*) if possible.\n");
log("\n");
log(" -O5\n");
- log(" like -O4, and run `proc; flatten` first.\n");
+ log(" like -O4, and localize public wires not marked (*keep*) if possible.\n");
+ log("\n");
+ log(" -O6\n");
+ log(" like -O5, and elide public wires not marked (*keep*) if possible.\n");
+ log("\n");
+ log(" -Og\n");
+ log(" highest optimization level that provides debug information for all\n");
+ log(" public wires. currently, alias for -O%d.\n", OPT_LEVEL_DEBUG);
log("\n");
log(" -g <level>\n");
log(" set the debug level. the default is -g%d. higher debug levels provide\n", DEFAULT_DEBUG_LEVEL);
@@ -2502,8 +2632,11 @@ struct CxxrtlBackend : public Backend {
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
+ bool nohierarchy = false;
+ bool noflatten = false;
+ bool noproc = false;
int opt_level = DEFAULT_OPT_LEVEL;
int debug_level = DEFAULT_DEBUG_LEVEL;
CxxrtlWorker worker;
@@ -2513,6 +2646,27 @@ struct CxxrtlBackend : public Backend {
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
+ if (args[argidx] == "-nohierarchy") {
+ nohierarchy = true;
+ continue;
+ }
+ if (args[argidx] == "-noflatten") {
+ noflatten = true;
+ continue;
+ }
+ if (args[argidx] == "-noproc") {
+ noproc = true;
+ continue;
+ }
+ if (args[argidx] == "-Og") {
+ opt_level = OPT_LEVEL_DEBUG;
+ continue;
+ }
+ if (args[argidx] == "-O" && argidx+1 < args.size() && args[argidx+1] == "g") {
+ argidx++;
+ opt_level = OPT_LEVEL_DEBUG;
+ continue;
+ }
if (args[argidx] == "-O" && argidx+1 < args.size()) {
opt_level = std::stoi(args[++argidx]);
continue;
@@ -2541,30 +2695,34 @@ struct CxxrtlBackend : public Backend {
}
extra_args(f, filename, args, argidx);
+ worker.run_hierarchy = !nohierarchy;
+ worker.run_flatten = !noflatten;
+ worker.run_proc = !noproc;
switch (opt_level) {
// the highest level here must match DEFAULT_OPT_LEVEL
+ case 6:
+ worker.elide_public = true;
+ YS_FALLTHROUGH
case 5:
- worker.max_opt_level = true;
- worker.run_proc_flatten = true;
+ worker.localize_public = true;
YS_FALLTHROUGH
case 4:
- worker.localize_public = true;
+ worker.unbuffer_public = true;
YS_FALLTHROUGH
case 3:
- worker.elide_public = true;
+ worker.elide_internal = true;
YS_FALLTHROUGH
case 2:
worker.localize_internal = true;
YS_FALLTHROUGH
case 1:
- worker.elide_internal = true;
+ worker.unbuffer_internal = true;
YS_FALLTHROUGH
case 0:
break;
default:
log_cmd_error("Invalid optimization level %d.\n", opt_level);
}
-
switch (debug_level) {
// the highest level here must match DEFAULT_DEBUG_LEVEL
case 1:
diff --git a/backends/cxxrtl/cxxrtl_capi.cc b/backends/cxxrtl/cxxrtl_capi.cc
index 489d72da5..b77e4c491 100644
--- a/backends/cxxrtl/cxxrtl_capi.cc
+++ b/backends/cxxrtl/cxxrtl_capi.cc
@@ -43,18 +43,29 @@ void cxxrtl_destroy(cxxrtl_handle handle) {
delete handle;
}
+int cxxrtl_eval(cxxrtl_handle handle) {
+ return handle->module->eval();
+}
+
+int cxxrtl_commit(cxxrtl_handle handle) {
+ return handle->module->commit();
+}
+
size_t cxxrtl_step(cxxrtl_handle handle) {
return handle->module->step();
}
-cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) {
- if (handle->objects.count(name) > 0)
- return static_cast<cxxrtl_object*>(&handle->objects.at(name));
- return nullptr;
+struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts) {
+ auto it = handle->objects.table.find(name);
+ if (it == handle->objects.table.end())
+ return nullptr;
+ *parts = it->second.size();
+ return static_cast<cxxrtl_object*>(&it->second[0]);
}
void cxxrtl_enum(cxxrtl_handle handle, void *data,
- void (*callback)(void *data, const char *name, cxxrtl_object *object)) {
- for (auto &it : handle->objects)
- callback(data, it.first.c_str(), static_cast<cxxrtl_object*>(&it.second));
+ void (*callback)(void *data, const char *name,
+ cxxrtl_object *object, size_t parts)) {
+ for (auto &it : handle->objects.table)
+ callback(data, it.first.c_str(), static_cast<cxxrtl_object*>(&it.second[0]), it.second.size());
}
diff --git a/backends/cxxrtl/cxxrtl_capi.h b/backends/cxxrtl/cxxrtl_capi.h
index 46aa662b2..385d6dcf3 100644
--- a/backends/cxxrtl/cxxrtl_capi.h
+++ b/backends/cxxrtl/cxxrtl_capi.h
@@ -26,6 +26,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <assert.h>
#ifdef __cplusplus
extern "C" {
@@ -54,12 +55,28 @@ cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design);
// Release all resources used by a design and its handle.
void cxxrtl_destroy(cxxrtl_handle handle);
+// Evaluate the design, propagating changes on inputs to the `next` value of internal state and
+// output wires.
+//
+// Returns 1 if the design is known to immediately converge, 0 otherwise.
+int cxxrtl_eval(cxxrtl_handle handle);
+
+// Commit the design, replacing the `curr` value of internal state and output wires with the `next`
+// value.
+//
+// Return 1 if any of the `curr` values were updated, 0 otherwise.
+int cxxrtl_commit(cxxrtl_handle handle);
+
// Simulate the design to a fixed point.
//
// Returns the number of delta cycles.
size_t cxxrtl_step(cxxrtl_handle handle);
// Type of a simulated object.
+//
+// The type of a simulated object indicates the way it is stored and the operations that are legal
+// to perform on it (i.e. won't crash the simulation). It says very little about object semantics,
+// which is specified through flags.
enum cxxrtl_type {
// Values correspond to singly buffered netlist nodes, i.e. nodes driven exclusively by
// combinatorial cells, or toplevel input nodes.
@@ -73,7 +90,8 @@ enum cxxrtl_type {
CXXRTL_VALUE = 0,
// Wires correspond to doubly buffered netlist nodes, i.e. nodes driven, at least in part, by
- // storage cells, or by combinatorial cells that are a part of a feedback path.
+ // storage cells, or by combinatorial cells that are a part of a feedback path. They are also
+ // present in non-optimized builds.
//
// Wires can be inspected via the `curr` pointer and modified via the `next` pointer (which are
// distinct for wires). Note that changes to the bits driven by combinatorial cells will be
@@ -89,7 +107,74 @@ enum cxxrtl_type {
// always NULL.
CXXRTL_MEMORY = 2,
- // More object types will be added in the future, but the existing ones will never change.
+ // Aliases correspond to netlist nodes driven by another node such that their value is always
+ // exactly equal.
+ //
+ // Aliases can be inspected via the `curr` pointer. They cannot be modified, and the `next`
+ // pointer is always NULL.
+ CXXRTL_ALIAS = 3,
+
+ // More object types may be added in the future, but the existing ones will never change.
+};
+
+// Flags of a simulated object.
+//
+// The flags of a simulated object indicate its role in the netlist:
+// * The flags `CXXRTL_INPUT` and `CXXRTL_OUTPUT` designate module ports.
+// * The flags `CXXRTL_DRIVEN_SYNC`, `CXXRTL_DRIVEN_COMB`, and `CXXRTL_UNDRIVEN` specify
+// the semantics of node state. An object with several of these flags set has different bits
+// follow different semantics.
+enum cxxrtl_flag {
+ // Node is a module input port.
+ //
+ // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined
+ // with `CXXRTL_OUTPUT`, as well as other flags.
+ CXXRTL_INPUT = 1 << 0,
+
+ // Node is a module output port.
+ //
+ // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with `CXXRTL_INPUT`,
+ // as well as other flags.
+ CXXRTL_OUTPUT = 1 << 1,
+
+ // Node is a module inout port.
+ //
+ // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with other flags.
+ CXXRTL_INOUT = (CXXRTL_INPUT|CXXRTL_OUTPUT),
+
+ // Node has bits that are driven by a storage cell.
+ //
+ // This flag can be set on objects of type `CXXRTL_WIRE`. It may be combined with
+ // `CXXRTL_DRIVEN_COMB` and `CXXRTL_UNDRIVEN`, as well as other flags.
+ //
+ // This flag is set on wires that have bits connected directly to the output of a flip-flop or
+ // a latch, and hold its state. Many `CXXRTL_WIRE` objects may not have the `CXXRTL_DRIVEN_SYNC`
+ // flag set; for example, output ports and feedback wires generally won't. Writing to the `next`
+ // pointer of these wires updates stored state, and for designs without combinatorial loops,
+ // capturing the value from every of these wires through the `curr` pointer creates a complete
+ // snapshot of the design state.
+ CXXRTL_DRIVEN_SYNC = 1 << 2,
+
+ // Node has bits that are driven by a combinatorial cell or another node.
+ //
+ // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined
+ // with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_UNDRIVEN`, as well as other flags.
+ //
+ // This flag is set on objects that have bits connected to the output of a combinatorial cell,
+ // or directly to another node. For designs without combinatorial loops, writing to such bits
+ // through the `next` pointer (if it is not NULL) has no effect.
+ CXXRTL_DRIVEN_COMB = 1 << 3,
+
+ // Node has bits that are not driven.
+ //
+ // This flag can be set on objects of type `CXXRTL_VALUE` and `CXXRTL_WIRE`. It may be combined
+ // with `CXXRTL_DRIVEN_SYNC` and `CXXRTL_DRIVEN_COMB`, as well as other flags.
+ //
+ // This flag is set on objects that have bits not driven by an output of any cell or by another
+ // node, such as inputs and dangling wires.
+ CXXRTL_UNDRIVEN = 1 << 4,
+
+ // More object flags may be added in the future, but the existing ones will never change.
};
// Description of a simulated object.
@@ -103,12 +188,21 @@ struct cxxrtl_object {
// determines all other properties of the object.
uint32_t type; // actually `enum cxxrtl_type`
+ // Flags of the object.
+ uint32_t flags; // actually bit mask of `enum cxxrtl_flags`
+
// Width of the object in bits.
size_t width;
+ // Index of the least significant bit.
+ size_t lsb_at;
+
// Depth of the object. Only meaningful for memories; for other objects, always 1.
size_t depth;
+ // Index of the first word. Only meaningful for memories; for other objects, always 0;
+ size_t zero_at;
+
// Bits stored in the object, as 32-bit chunks, least significant bits first.
//
// The width is rounded up to a multiple of 32; the padding bits are always set to 0 by
@@ -123,7 +217,7 @@ struct cxxrtl_object {
uint32_t *curr;
uint32_t *next;
- // More description fields will be added in the future, but the existing ones will never change.
+ // More description fields may be added in the future, but the existing ones will never change.
};
// Retrieve description of a simulated object.
@@ -133,17 +227,36 @@ struct cxxrtl_object {
// the top-level module instantiates a module `foo`, which in turn contains a wire `bar`, the full
// hierarchical name is `\foo \bar`.
//
-// Returns the object if it was found, NULL otherwise. The returned value is valid until the design
-// is destroyed.
-struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name);
+// The storage of a single abstract object may be split (usually with the `splitnets` pass) into
+// many physical parts, all of which correspond to the same hierarchical name. To handle such cases,
+// this function returns an array and writes its length to `parts`. The array is sorted by `lsb_at`.
+//
+// Returns the object parts if it was found, NULL otherwise. The returned parts are valid until
+// the design is destroyed.
+struct cxxrtl_object *cxxrtl_get_parts(cxxrtl_handle handle, const char *name, size_t *parts);
+
+// Retrieve description of a single part simulated object.
+//
+// This function is a shortcut for the most common use of `cxxrtl_get_parts`. It asserts that,
+// if the object exists, it consists of a single part. If assertions are disabled, it returns NULL
+// for multi-part objects.
+inline struct cxxrtl_object *cxxrtl_get(cxxrtl_handle handle, const char *name) {
+ size_t parts = 0;
+ struct cxxrtl_object *object = cxxrtl_get_parts(handle, name, &parts);
+ assert(object == NULL || parts == 1);
+ if (object == NULL || parts == 1)
+ return object;
+ return NULL;
+}
// Enumerate simulated objects.
//
// For every object in the simulation, `callback` is called with the provided `data`, the full
-// hierarchical name of the object (see `cxxrtl_get` for details), and the object description.
+// hierarchical name of the object (see `cxxrtl_get` for details), and the object parts.
// The provided `name` and `object` values are valid until the design is destroyed.
void cxxrtl_enum(cxxrtl_handle handle, void *data,
- void (*callback)(void *data, const char *name, struct cxxrtl_object *object));
+ void (*callback)(void *data, const char *name,
+ struct cxxrtl_object *object, size_t parts));
#ifdef __cplusplus
}
diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h
index f6b78bbf7..dbeabbaf2 100644
--- a/backends/cxxrtl/cxxrtl_vcd.h
+++ b/backends/cxxrtl/cxxrtl_vcd.h
@@ -66,11 +66,19 @@ class vcd_writer {
} while (ident != 0);
}
- void emit_var(const variable &var, const std::string &type, const std::string &name) {
+ void emit_var(const variable &var, const std::string &type, const std::string &name,
+ size_t lsb_at, bool multipart) {
assert(!streaming);
buffer += "$var " + type + " " + std::to_string(var.width) + " ";
emit_ident(var.ident);
- buffer += " " + name + " $end\n";
+ buffer += " " + name;
+ if (multipart || name.back() == ']' || lsb_at != 0) {
+ if (var.width == 1)
+ buffer += " [" + std::to_string(lsb_at) + "]";
+ else
+ buffer += " [" + std::to_string(lsb_at + var.width - 1) + ":" + std::to_string(lsb_at) + "]";
+ }
+ buffer += " $end\n";
}
void emit_enddefinitions() {
@@ -104,13 +112,13 @@ class vcd_writer {
buffer += '\n';
}
- const variable &register_variable(size_t width, chunk_t *curr, bool immutable = false) {
+ const variable &register_variable(size_t width, chunk_t *curr, bool constant = false) {
if (aliases.count(curr)) {
return variables[aliases[curr]];
} else {
const size_t chunks = (width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
aliases[curr] = variables.size();
- if (immutable) {
+ if (constant) {
variables.emplace_back(variable { variables.size(), width, curr, (size_t)-1 });
} else {
variables.emplace_back(variable { variables.size(), width, curr, cache.size() });
@@ -122,7 +130,7 @@ class vcd_writer {
bool test_variable(const variable &var) {
if (var.prev_off == (size_t)-1)
- return false; // immutable
+ return false; // constant
const size_t chunks = (var.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
if (std::equal(&var.curr[0], &var.curr[chunks], &cache[var.prev_off])) {
return false;
@@ -155,7 +163,7 @@ public:
emit_timescale(number, unit);
}
- void add(const std::string &hier_name, const debug_item &item) {
+ void add(const std::string &hier_name, const debug_item &item, bool multipart = false) {
std::vector<std::string> scope = split_hierarchy(hier_name);
std::string name = scope.back();
scope.pop_back();
@@ -164,20 +172,31 @@ public:
switch (item.type) {
// Not the best naming but oh well...
case debug_item::VALUE:
- emit_var(register_variable(item.width, item.curr, /*immutable=*/item.next == nullptr), "wire", name);
+ emit_var(register_variable(item.width, item.curr, /*constant=*/item.next == nullptr),
+ "wire", name, item.lsb_at, multipart);
break;
case debug_item::WIRE:
- emit_var(register_variable(item.width, item.curr), "reg", name);
+ emit_var(register_variable(item.width, item.curr),
+ "reg", name, item.lsb_at, multipart);
break;
case debug_item::MEMORY: {
const size_t stride = (item.width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
for (size_t index = 0; index < item.depth; index++) {
chunk_t *nth_curr = &item.curr[stride * index];
std::string nth_name = name + '[' + std::to_string(index) + ']';
- emit_var(register_variable(item.width, nth_curr), "reg", nth_name);
+ emit_var(register_variable(item.width, nth_curr),
+ "reg", nth_name, item.lsb_at, multipart);
}
break;
}
+ case debug_item::ALIAS:
+ // Like VALUE, but, even though `item.next == nullptr` always holds, the underlying value
+ // can actually change, and must be tracked. In most cases the VCD identifier will be
+ // unified with the aliased reg, but we should handle the case where only the alias is
+ // added to the VCD writer, too.
+ emit_var(register_variable(item.width, item.curr),
+ "wire", name, item.lsb_at, multipart);
+ break;
}
}
@@ -185,9 +204,10 @@ public:
void add(const debug_items &items, const Filter &filter) {
// `debug_items` is a map, so the items are already sorted in an order optimal for emitting
// VCD scope sections.
- for (auto &it : items)
- if (filter(it.first, it.second))
- add(it.first, it.second);
+ for (auto &it : items.table)
+ for (auto &part : it.second)
+ if (filter(it.first, part))
+ add(it.first, part, it.second.size() > 1);
}
void add(const debug_items &items) {
@@ -198,7 +218,7 @@ public:
void add_without_memories(const debug_items &items) {
this->template add(items, [](const std::string &, const debug_item &item) {
- return item.type == debug_item::VALUE || item.type == debug_item::WIRE;
+ return item.type != debug_item::MEMORY;
});
}
diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.h b/backends/cxxrtl/cxxrtl_vcd_capi.h
index 6a7fb9f47..d55afe223 100644
--- a/backends/cxxrtl/cxxrtl_vcd_capi.h
+++ b/backends/cxxrtl/cxxrtl_vcd_capi.h
@@ -75,8 +75,8 @@ void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle);
//
// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data,
- int (*filter)(void *data, const char *name,
- const struct cxxrtl_object *object));
+ int (*filter)(void *data, const char *name,
+ const struct cxxrtl_object *object));
// Schedule all CXXRTL objects in a simulation except for memories.
//
diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc
index 7e24468c0..e0013238c 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") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -126,7 +126,7 @@ struct EdifBackend : public Backend {
log("is targeted.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing EDIF backend.\n");
std::string top_module_name;
@@ -330,7 +330,7 @@ struct EdifBackend : public Backend {
}
*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_blackbox_attribute())
@@ -373,8 +373,8 @@ struct EdifBackend : public Backend {
}
{
- int c1 = w1->name[0] == '\\';
- int c2 = w2->name[0] == '\\';
+ int c1 = w1->name.isPublic();
+ int c2 = w2->name.isPublic();
if (c1 > c2) goto promote;
if (c1 < c2) goto nopromote;
@@ -524,7 +524,7 @@ struct EdifBackend : public Backend {
*f << stringf(" (portRef %c (instanceRef GND))\n", gndvccy ? 'Y' : 'G');
if (sig == RTLIL::State::S1)
*f << stringf(" (portRef %c (instanceRef VCC))\n", gndvccy ? 'Y' : 'P');
- }
+ }
*f << stringf(" )");
if (attr_properties && sig.wire != NULL)
for (auto &p : sig.wire->attributes)
diff --git a/backends/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index b1d8500bb..44c3397da 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -102,6 +102,260 @@ const char *make_id(IdString id)
return namecache.at(id).c_str();
}
+std::string dump_const_string(const RTLIL::Const &data)
+{
+ std::string res_str;
+
+ std::string str = data.decode_string();
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ if (str[i] == '\n')
+ res_str += "\\n";
+ else if (str[i] == '\t')
+ res_str += "\\t";
+ else if (str[i] < 32)
+ res_str += stringf("\\%03o", str[i]);
+ else if (str[i] == '"')
+ res_str += "\\\"";
+ else if (str[i] == '\\')
+ res_str += "\\\\";
+ else
+ res_str += str[i];
+ }
+
+ return res_str;
+}
+
+std::string dump_const(const RTLIL::Const &data)
+{
+ std::string res_str;
+
+ // // For debugging purposes to find out how Yosys encodes flags.
+ // res_str += stringf("flags_%x --> ", data.flags);
+
+ // Real-valued parameter.
+ if (data.flags & RTLIL::CONST_FLAG_REAL)
+ {
+ // Yosys stores real values as strings, so we call the string dumping code.
+ res_str += dump_const_string(data);
+ }
+ // String parameter.
+ else if (data.flags & RTLIL::CONST_FLAG_STRING)
+ {
+ res_str += "\"";
+ res_str += dump_const_string(data);
+ res_str += "\"";
+ }
+ // Numeric (non-real) parameter.
+ else
+ {
+ int width = data.bits.size();
+
+ // If a standard 32-bit int, then emit standard int value like "56" or
+ // "-56". Firrtl supports negative-valued int literals.
+ //
+ // SignedInt
+ // : ( '+' | '-' ) PosInt
+ // ;
+ if (width <= 32)
+ {
+ int32_t int_val = 0;
+
+ for (int i = 0; i < width; i++)
+ {
+ switch (data.bits[i])
+ {
+ case State::S0: break;
+ case State::S1: int_val |= (1 << i); break;
+ default:
+ log_error("Unexpected int value\n");
+ break;
+ }
+ }
+
+ res_str += stringf("%d", int_val);
+ }
+ else
+ {
+ // If value is larger than 32 bits, then emit a binary representation of
+ // the number as integers are not large enough to contain the result.
+ // There is a caveat to this approach though:
+ //
+ // Note that parameter may be defined as having a fixed width as follows:
+ //
+ // parameter signed [26:0] test_signed;
+ // parameter [26:0] test_unsigned;
+ // parameter signed [40:0] test_signed_large;
+ //
+ // However, if you assign a value on the RHS without specifying the
+ // precision, then yosys considers the value you used as an int and
+ // assigns it a width of 32 bits regardless of the type of the parameter.
+ //
+ // defparam <inst_name> .test_signed = 49; (width = 32, though should be 27 based on definition)
+ // defparam <inst_name> .test_unsigned = 40'd35; (width = 40, though should be 27 based on definition)
+ // defparam <inst_name> .test_signed_large = 40'd12; (width = 40)
+ //
+ // We therefore may lose the precision of the original verilog literal if
+ // it was written without its bitwidth specifier.
+
+ // Emit binary prefix for string.
+ res_str += "\"b";
+
+ // Emit bits.
+ for (int i = width - 1; i >= 0; i--)
+ {
+ log_assert(i < width);
+ switch (data.bits[i])
+ {
+ case State::S0: res_str += "0"; break;
+ case State::S1: res_str += "1"; break;
+ case State::Sx: res_str += "x"; break;
+ case State::Sz: res_str += "z"; break;
+ case State::Sa: res_str += "-"; break;
+ case State::Sm: res_str += "m"; break;
+ }
+ }
+
+ res_str += "\"";
+ }
+ }
+
+ return res_str;
+}
+
+std::string extmodule_name(RTLIL::Cell *cell, RTLIL::Module *mod_instance)
+{
+ // Since we are creating a custom extmodule for every cell that instantiates
+ // this blackbox, we need to create a custom name for it. We just use the
+ // name of the blackbox itself followed by the name of the cell.
+ const std::string cell_name = std::string(make_id(cell->name));
+ const std::string blackbox_name = std::string(make_id(mod_instance->name));
+ const std::string extmodule_name = blackbox_name + "_" + cell_name;
+ return extmodule_name;
+}
+
+/**
+ * Emits a parameterized extmodule. Instance parameters are obtained from
+ * ''cell'' as it represents the instantiation of the blackbox defined by
+ * ''mod_instance'' and therefore contains all its instance parameters.
+ */
+void emit_extmodule(RTLIL::Cell *cell, RTLIL::Module *mod_instance, std::ostream &f)
+{
+ const std::string indent = " ";
+
+ const std::string blackbox_name = std::string(make_id(mod_instance->name));
+ const std::string exported_name = extmodule_name(cell, mod_instance);
+
+ // We use the cell's fileinfo for this extmodule as its parameters come from
+ // the cell and not from the module itself (the module contains default
+ // parameters, not the instance-specific ones we're using to emit the
+ // extmodule).
+ const std::string extmoduleFileinfo = getFileinfo(cell);
+
+ // Emit extmodule header.
+ f << stringf(" extmodule %s: %s\n", exported_name.c_str(), extmoduleFileinfo.c_str());
+
+ // Emit extmodule ports.
+ for (auto wire : mod_instance->wires())
+ {
+ const auto wireName = make_id(wire->name);
+ const std::string wireFileinfo = getFileinfo(wire);
+
+ if (wire->port_input && wire->port_output)
+ {
+ log_error("Module port %s.%s is inout!\n", log_id(mod_instance), log_id(wire));
+ }
+
+ const std::string portDecl = stringf("%s%s %s: UInt<%d> %s\n",
+ indent.c_str(),
+ wire->port_input ? "input" : "output",
+ wireName,
+ wire->width,
+ wireFileinfo.c_str()
+ );
+
+ f << portDecl;
+ }
+
+ // Emit extmodule "defname" field. This is the name of the verilog blackbox
+ // that is used when verilog is emitted, so we use the name of mod_instance
+ // here.
+ f << stringf("%sdefname = %s\n", indent.c_str(), blackbox_name.c_str());
+
+ // Emit extmodule generic parameters.
+ for (const auto &p : cell->parameters)
+ {
+ const RTLIL::IdString p_id = p.first;
+ const RTLIL::Const p_value = p.second;
+
+ std::string param_name(p_id.c_str());
+ const std::string param_value = dump_const(p_value);
+
+ // Remove backslashes from parameters as these come from the internal RTLIL
+ // naming scheme, but should not exist in the emitted firrtl blackboxes.
+ // When firrtl is converted to verilog and given to downstream synthesis
+ // tools, these tools expect to find blackbox names and parameters as they
+ // were originally defined, i.e. without the extra RTLIL naming conventions.
+ param_name.erase(
+ std::remove(param_name.begin(), param_name.end(), '\\'),
+ param_name.end()
+ );
+
+ f << stringf("%sparameter %s = %s\n", indent.c_str(), param_name.c_str(), param_value.c_str());
+ }
+
+ f << "\n";
+}
+
+/**
+ * Emits extmodules for every instantiated blackbox in the design.
+ *
+ * RTLIL stores instance parameters at the cell's instantiation location.
+ * However, firrtl does not support module parameterization (everything is
+ * already elaborated). Firrtl instead supports external modules (extmodule),
+ * i.e. blackboxes that are defined by verilog and which have no body in
+ * firrtl itself other than the declaration of the blackboxes ports and
+ * parameters.
+ *
+ * Furthermore, firrtl does not support parameterization (even of extmodules)
+ * at a module's instantiation location and users must instead declare
+ * different extmodules with different instance parameters in the extmodule
+ * definition itself.
+ *
+ * This function goes through the design to identify all RTLIL blackboxes
+ * and emit parameterized extmodules with a unique name for each of them. The
+ * name that's given to the extmodule is
+ *
+ * <blackbox_name>_<instance_name>
+ *
+ * Beware that it is therefore necessary for users to replace "parameterized"
+ * instances in the RTLIL sense with these custom extmodules for the firrtl to
+ * be valid.
+ */
+void emit_elaborated_extmodules(RTLIL::Design *design, std::ostream &f)
+{
+ for (auto module : design->modules())
+ {
+ for (auto cell : module->cells())
+ {
+ // Is this cell a module instance?
+ bool cellIsModuleInstance = cell->type[0] != '$';
+
+ if (cellIsModuleInstance)
+ {
+ // Find the module corresponding to this instance.
+ auto modInstance = design->module(cell->type);
+ bool modIsBlackbox = modInstance->get_blackbox_attribute();
+
+ if (modIsBlackbox)
+ {
+ emit_extmodule(cell, modInstance, f);
+ }
+ }
+ }
+ }
+}
+
struct FirrtlWorker
{
Module *module;
@@ -328,8 +582,16 @@ struct FirrtlWorker
log_warning("No instance for %s.%s\n", cell_type.c_str(), cell_name.c_str());
return;
}
+
+ // If the instance is that of a blackbox, use the modified extmodule name
+ // that contains per-instance parameterizations. These instances were
+ // emitted earlier in the firrtl backend.
+ const std::string instanceName = instModule->get_blackbox_attribute() ?
+ extmodule_name(cell, instModule) :
+ instanceOf;
+
std::string cellFileinfo = getFileinfo(cell);
- wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceOf.c_str(), cellFileinfo.c_str()));
+ wire_exprs.push_back(stringf("%s" "inst %s%s of %s %s", indent.c_str(), cell_name.c_str(), cell_name_comment.c_str(), instanceName.c_str(), cellFileinfo.c_str()));
for (auto it = cell->connections().begin(); it != cell->connections().end(); ++it) {
if (it->second.size() > 0) {
@@ -392,33 +654,6 @@ struct FirrtlWorker
return result;
}
- 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);
@@ -440,12 +675,12 @@ struct FirrtlWorker
{
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",
+ port_decls.push_back(stringf("%s%s %s: UInt<%d> %s\n", indent.c_str(), wire->port_input ? "input" : "output",
wireName, wire->width, wireFileinfo.c_str()));
}
else
{
- wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", wireName, wire->width, wireFileinfo.c_str()));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), wireName, wire->width, wireFileinfo.c_str()));
}
}
@@ -476,7 +711,7 @@ struct FirrtlWorker
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()));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));
if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
@@ -516,7 +751,7 @@ struct FirrtlWorker
if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
- cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
+ cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@@ -528,7 +763,7 @@ struct FirrtlWorker
string a_expr = make_expr(cell->getPort(ID::A));
string b_expr = make_expr(cell->getPort(ID::B));
std::string cellFileinfo = getFileinfo(cell);
- wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str()));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), y_width, cellFileinfo.c_str()));
if (a_signed) {
a_expr = "asSInt(" + a_expr + ")";
@@ -746,7 +981,7 @@ struct FirrtlWorker
if ((firrtl_is_signed && !always_uint))
expr = stringf("asUInt(%s)", expr.c_str());
- cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
+ cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@@ -759,11 +994,11 @@ struct FirrtlWorker
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));
- wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), width, cellFileinfo.c_str()));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d> %s\n", indent.c_str(), y_id.c_str(), width, cellFileinfo.c_str()));
string expr = stringf("mux(%s, %s, %s)", s_expr.c_str(), b_expr.c_str(), a_expr.c_str());
- cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
+ cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
@@ -902,9 +1137,9 @@ struct FirrtlWorker
string expr = make_expr(cell->getPort(ID::D));
string clk_expr = "asClock(" + make_expr(cell->getPort(ID::CLK)) + ")";
- wire_decls.push_back(stringf(" reg %s: UInt<%d>, %s %s\n", y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str()));
+ wire_decls.push_back(stringf("%sreg %s: UInt<%d>, %s %s\n", indent.c_str(), y_id.c_str(), width, clk_expr.c_str(), cellFileinfo.c_str()));
- cell_exprs.push_back(stringf(" %s <= %s %s\n", y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
+ cell_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), y_id.c_str(), expr.c_str(), cellFileinfo.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Q));
continue;
@@ -923,7 +1158,7 @@ struct FirrtlWorker
string a_expr = make_expr(cell->getPort(ID::A));
// Get the initial bit selector
string b_expr = make_expr(cell->getPort(ID::B));
- wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
if (cell->getParam(ID::B_SIGNED).as_bool()) {
// Use validif to constrain the selection (test the sign bit)
@@ -933,7 +1168,7 @@ struct FirrtlWorker
}
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()));
+ cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
@@ -945,7 +1180,7 @@ struct FirrtlWorker
string b_expr = make_expr(cell->getPort(ID::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));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
if (cell->getParam(ID::B_SIGNED).as_bool()) {
// We generate a left or right shift based on the sign of b.
@@ -959,7 +1194,7 @@ struct FirrtlWorker
} 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()));
+ cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
@@ -972,8 +1207,8 @@ struct FirrtlWorker
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()));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
+ cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), a_expr.c_str()));
register_reverse_wire_map(y_id, cell->getPort(ID::Y));
continue;
}
@@ -986,8 +1221,8 @@ struct FirrtlWorker
int y_width = GetSize(conn.first);
string expr = make_expr(conn.second);
- wire_decls.push_back(stringf(" wire %s: UInt<%d>\n", y_id.c_str(), y_width));
- cell_exprs.push_back(stringf(" %s <= %s\n", y_id.c_str(), expr.c_str()));
+ wire_decls.push_back(stringf("%swire %s: UInt<%d>\n", indent.c_str(), y_id.c_str(), y_width));
+ cell_exprs.push_back(stringf("%s%s <= %s\n", indent.c_str(), y_id.c_str(), expr.c_str()));
register_reverse_wire_map(y_id, conn.first);
}
@@ -1053,13 +1288,13 @@ struct FirrtlWorker
if (is_valid) {
if (make_unconn_id) {
- wire_decls.push_back(stringf(" wire %s: UInt<1> %s\n", unconn_id.c_str(), wireFileinfo.c_str()));
+ wire_decls.push_back(stringf("%swire %s: UInt<1> %s\n", indent.c_str(), unconn_id.c_str(), wireFileinfo.c_str()));
// `invalid` is a firrtl construction for simulation so we will not
// tag it with a @[fileinfo] tag as it doesn't directly correspond to
// a specific line of verilog code.
- wire_decls.push_back(stringf(" %s is invalid\n", unconn_id.c_str()));
+ wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), unconn_id.c_str()));
}
- wire_exprs.push_back(stringf(" %s <= %s %s\n", make_id(wire->name), expr.c_str(), wireFileinfo.c_str()));
+ wire_exprs.push_back(stringf("%s%s <= %s %s\n", indent.c_str(), make_id(wire->name), expr.c_str(), wireFileinfo.c_str()));
} else {
if (make_unconn_id) {
unconn_id.clear();
@@ -1067,7 +1302,7 @@ struct FirrtlWorker
// `invalid` is a firrtl construction for simulation so we will not
// tag it with a @[fileinfo] tag as it doesn't directly correspond to
// a specific line of verilog code.
- wire_decls.push_back(stringf(" %s is invalid\n", make_id(wire->name)));
+ wire_decls.push_back(stringf("%s%s is invalid\n", indent.c_str(), make_id(wire->name)));
}
}
@@ -1112,18 +1347,13 @@ struct FirrtlWorker
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();
+ emit_module();
}
};
struct FirrtlBackend : public Backend {
FirrtlBackend() : Backend("firrtl", "write design to a FIRRTL file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1134,7 +1364,7 @@ struct FirrtlBackend : public Backend {
log(" pmuxtree\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
size_t argidx = args.size(); // We aren't expecting any arguments.
@@ -1180,10 +1410,16 @@ struct FirrtlBackend : public Backend {
std::string circuitFileinfo = getFileinfo(top);
*f << stringf("circuit %s: %s\n", make_id(top->name), circuitFileinfo.c_str());
+ emit_elaborated_extmodules(design, *f);
+
+ // Emit non-blackbox modules.
for (auto module : design->modules())
{
- FirrtlWorker worker(module, *f, design);
- worker.run();
+ if (!module->get_blackbox_attribute())
+ {
+ FirrtlWorker worker(module, *f, design);
+ worker.run();
+ }
}
namecache.clear();
diff --git a/backends/ilang/Makefile.inc b/backends/ilang/Makefile.inc
deleted file mode 100644
index 52fc2b891..000000000
--- a/backends/ilang/Makefile.inc
+++ /dev/null
@@ -1,3 +0,0 @@
-
-OBJS += backends/ilang/ilang_backend.o
-
diff --git a/backends/intersynth/intersynth.cc b/backends/intersynth/intersynth.cc
index 31dce1cca..a6b36de6c 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") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -59,7 +59,7 @@ struct IntersynthBackend : public Backend {
log(" do not generate celltypes and conntypes commands. i.e. just output\n");
log(" the netlists. this is used for postsilicon synthesis.\n");
log("\n");
- log(" -lib <verilog_or_ilang_file>\n");
+ log(" -lib <verilog_or_rtlil_file>\n");
log(" Use the specified library file for determining whether cell ports are\n");
log(" inputs or outputs. This option can be used multiple times to specify\n");
log(" more than one library.\n");
@@ -71,7 +71,7 @@ struct IntersynthBackend : public Backend {
log("http://www.clifford.at/intersynth/\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) 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.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "ilang" : "verilog"));
+ Frontend::frontend_call(lib, &f, filename, (filename.size() > 3 && filename.compare(filename.size()-3, std::string::npos, ".il") == 0 ? "rtlil" : "verilog"));
libs.push_back(lib);
}
diff --git a/backends/json/json.cc b/backends/json/json.cc
index 5edc50f60..eeadc1b89 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -294,7 +294,7 @@ struct JsonWriter
struct JsonBackend : public Backend {
JsonBackend() : Backend("json", "write design to a JSON file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -530,7 +530,7 @@ struct JsonBackend : public Backend {
log("format. A program processing this format must ignore all unknown fields.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool aig_mode = false;
bool compat_int_mode = false;
@@ -559,7 +559,7 @@ struct JsonBackend : public Backend {
struct JsonPass : public Pass {
JsonPass() : Pass("json", "write design in JSON format") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -580,7 +580,7 @@ struct JsonPass : public Pass {
log("See 'help write_json' for a description of the JSON format used.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::string filename;
bool aig_mode = false;
diff --git a/backends/protobuf/protobuf.cc b/backends/protobuf/protobuf.cc
index 671686173..f6623a382 100644
--- a/backends/protobuf/protobuf.cc
+++ b/backends/protobuf/protobuf.cc
@@ -231,7 +231,7 @@ struct ProtobufDesignSerializer
struct ProtobufBackend : public Backend {
ProtobufBackend(): Backend("protobuf", "write design to a Protocol Buffer file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -249,7 +249,7 @@ struct ProtobufBackend : public Backend {
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
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool aig_mode = false;
bool text_mode = false;
@@ -286,7 +286,7 @@ struct ProtobufBackend : public Backend {
struct ProtobufPass : public Pass {
ProtobufPass() : Pass("protobuf", "write design in Protobuf format") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -307,7 +307,7 @@ struct ProtobufPass : public Pass {
log("Yosys source code distribution.\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::string filename;
bool aig_mode = false;
diff --git a/backends/rtlil/Makefile.inc b/backends/rtlil/Makefile.inc
new file mode 100644
index 000000000..f691282ca
--- /dev/null
+++ b/backends/rtlil/Makefile.inc
@@ -0,0 +1,3 @@
+
+OBJS += backends/rtlil/rtlil_backend.o
+
diff --git a/backends/ilang/ilang_backend.cc b/backends/rtlil/rtlil_backend.cc
index 3a418de3c..01b4bde53 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/rtlil/rtlil_backend.cc
@@ -18,19 +18,19 @@
* ---
*
* A very simple and straightforward backend for the RTLIL text
- * representation (as understood by the 'ilang' frontend).
+ * representation.
*
*/
-#include "ilang_backend.h"
+#include "rtlil_backend.h"
#include "kernel/yosys.h"
#include <errno.h>
USING_YOSYS_NAMESPACE
-using namespace ILANG_BACKEND;
+using namespace RTLIL_BACKEND;
YOSYS_NAMESPACE_BEGIN
-void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint)
+void RTLIL_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int width, int offset, bool autoint)
{
if (width < 0)
width = data.bits.size() - offset;
@@ -83,7 +83,7 @@ void ILANG_BACKEND::dump_const(std::ostream &f, const RTLIL::Const &data, int wi
}
}
-void ILANG_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint)
+void RTLIL_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint)
{
if (chunk.wire == NULL) {
dump_const(f, chunk.data, chunk.width, chunk.offset, autoint);
@@ -97,7 +97,7 @@ void ILANG_BACKEND::dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk,
}
}
-void ILANG_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint)
+void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint)
{
if (sig.is_chunk()) {
dump_sigchunk(f, sig.as_chunk(), autoint);
@@ -111,7 +111,7 @@ void ILANG_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo
}
}
-void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire)
+void RTLIL_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire)
{
for (auto &it : wire->attributes) {
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
@@ -136,7 +136,7 @@ void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::
f << stringf("%s\n", wire->name.c_str());
}
-void ILANG_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory)
+void RTLIL_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL::Memory *memory)
{
for (auto &it : memory->attributes) {
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
@@ -153,7 +153,7 @@ void ILANG_BACKEND::dump_memory(std::ostream &f, std::string indent, const RTLIL
f << stringf("%s\n", memory->name.c_str());
}
-void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
+void RTLIL_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::Cell *cell)
{
for (auto &it : cell->attributes) {
f << stringf("%s" "attribute %s ", indent.c_str(), it.first.c_str());
@@ -177,7 +177,7 @@ void ILANG_BACKEND::dump_cell(std::ostream &f, std::string indent, const RTLIL::
f << stringf("%s" "end\n", indent.c_str());
}
-void ILANG_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs)
+void RTLIL_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, const RTLIL::CaseRule *cs)
{
for (auto it = cs->actions.begin(); it != cs->actions.end(); ++it)
{
@@ -192,7 +192,7 @@ void ILANG_BACKEND::dump_proc_case_body(std::ostream &f, std::string indent, con
dump_proc_switch(f, indent, *it);
}
-void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw)
+void RTLIL_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const RTLIL::SwitchRule *sw)
{
for (auto it = sw->attributes.begin(); it != sw->attributes.end(); ++it) {
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
@@ -225,7 +225,7 @@ void ILANG_BACKEND::dump_proc_switch(std::ostream &f, std::string indent, const
f << stringf("%s" "end\n", indent.c_str());
}
-void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy)
+void RTLIL_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RTLIL::SyncRule *sy)
{
f << stringf("%s" "sync ", indent.c_str());
switch (sy->type) {
@@ -251,7 +251,7 @@ void ILANG_BACKEND::dump_proc_sync(std::ostream &f, std::string indent, const RT
}
}
-void ILANG_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc)
+void RTLIL_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::Process *proc)
{
for (auto it = proc->attributes.begin(); it != proc->attributes.end(); ++it) {
f << stringf("%s" "attribute %s ", indent.c_str(), it->first.c_str());
@@ -265,7 +265,7 @@ void ILANG_BACKEND::dump_proc(std::ostream &f, std::string indent, const RTLIL::
f << stringf("%s" "end\n", indent.c_str());
}
-void ILANG_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
+void RTLIL_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
{
f << stringf("%s" "connect ", indent.c_str());
dump_sigspec(f, left);
@@ -274,7 +274,7 @@ void ILANG_BACKEND::dump_conn(std::ostream &f, std::string indent, const RTLIL::
f << stringf("\n");
}
-void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
+void RTLIL_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Module *module, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
{
bool print_header = flag_m || design->selected_whole_module(module->name);
bool print_body = !flag_n || !design->selected_whole_module(module->name);
@@ -360,11 +360,9 @@ void ILANG_BACKEND::dump_module(std::ostream &f, std::string indent, RTLIL::Modu
f << stringf("%s" "end\n", indent.c_str());
}
-void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
+void RTLIL_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool only_selected, bool flag_m, bool flag_n)
{
-#ifndef NDEBUG
int init_autoidx = autoidx;
-#endif
if (!flag_m) {
int count_selected_mods = 0;
@@ -398,26 +396,26 @@ void ILANG_BACKEND::dump_design(std::ostream &f, RTLIL::Design *design, bool onl
YOSYS_NAMESPACE_END
PRIVATE_NAMESPACE_BEGIN
-struct IlangBackend : public Backend {
- IlangBackend() : Backend("ilang", "write design to ilang file") { }
- void help() YS_OVERRIDE
+struct RTLILBackend : public Backend {
+ RTLILBackend() : Backend("rtlil", "write design to RTLIL file") { }
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
- log(" write_ilang [filename]\n");
+ log(" write_rtlil [filename]\n");
log("\n");
- log("Write the current design to an 'ilang' file. (ilang is a text representation\n");
+ log("Write the current design to an RTLIL file. (RTLIL is a text representation\n");
log("of a design in yosys's internal format.)\n");
log("\n");
log(" -selected\n");
log(" only write selected parts of the design.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
bool selected = false;
- log_header(design, "Executing ILANG backend.\n");
+ log_header(design, "Executing RTLIL backend.\n");
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
@@ -434,20 +432,35 @@ struct IlangBackend : public Backend {
log("Output filename: %s\n", filename.c_str());
*f << stringf("# Generated by %s\n", yosys_version_str);
- ILANG_BACKEND::dump_design(*f, design, selected, true, false);
+ RTLIL_BACKEND::dump_design(*f, design, selected, true, false);
+ }
+} RTLILBackend;
+
+struct IlangBackend : public Backend {
+ IlangBackend() : Backend("ilang", "(deprecated) alias of write_rtlil") { }
+ void help() override
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log("See `help write_rtlil`.\n");
+ log("\n");
+ }
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
+ {
+ RTLILBackend.execute(f, filename, args, design);
}
} IlangBackend;
struct DumpPass : public Pass {
- DumpPass() : Pass("dump", "print parts of the design in ilang format") { }
- void help() YS_OVERRIDE
+ DumpPass() : Pass("dump", "print parts of the design in RTLIL format") { }
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" dump [options] [selection]\n");
log("\n");
log("Write the selected parts of the design to the console or specified file in\n");
- log("ilang format.\n");
+ log("RTLIL format.\n");
log("\n");
log(" -m\n");
log(" also dump the module headers, even if only parts of a single\n");
@@ -463,7 +476,7 @@ struct DumpPass : public Pass {
log(" like -outfile but append instead of overwrite\n");
log("\n");
}
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::string filename;
bool flag_m = false, flag_n = false, append = false;
@@ -510,7 +523,7 @@ struct DumpPass : public Pass {
f = &buf;
}
- ILANG_BACKEND::dump_design(*f, design, true, flag_m, flag_n);
+ RTLIL_BACKEND::dump_design(*f, design, true, flag_m, flag_n);
if (!filename.empty()) {
delete f;
diff --git a/backends/ilang/ilang_backend.h b/backends/rtlil/rtlil_backend.h
index 97dcbb628..77eea353c 100644
--- a/backends/ilang/ilang_backend.h
+++ b/backends/rtlil/rtlil_backend.h
@@ -18,19 +18,19 @@
* ---
*
* A very simple and straightforward backend for the RTLIL text
- * representation (as understood by the 'ilang' frontend).
+ * representation.
*
*/
-#ifndef ILANG_BACKEND_H
-#define ILANG_BACKEND_H
+#ifndef RTLIL_BACKEND_H
+#define RTLIL_BACKEND_H
#include "kernel/yosys.h"
#include <stdio.h>
YOSYS_NAMESPACE_BEGIN
-namespace ILANG_BACKEND {
+namespace RTLIL_BACKEND {
void dump_const(std::ostream &f, const RTLIL::Const &data, int width = -1, int offset = 0, bool autoint = true);
void dump_sigchunk(std::ostream &f, const RTLIL::SigChunk &chunk, bool autoint = true);
void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, bool autoint = true);
diff --git a/backends/simplec/simplec.cc b/backends/simplec/simplec.cc
index 83ed5e6e0..3adeaa6c0 100644
--- a/backends/simplec/simplec.cc
+++ b/backends/simplec/simplec.cc
@@ -744,7 +744,7 @@ struct SimplecWorker
struct SimplecBackend : public Backend {
SimplecBackend() : Backend("simplec", "convert design to simple C code") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -763,7 +763,7 @@ struct SimplecBackend : public Backend {
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
reserved_cids.clear();
id2cid.clear();
diff --git a/backends/smt2/smt2.cc b/backends/smt2/smt2.cc
index 26f17bcb3..a185fdd74 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -22,6 +22,7 @@
#include "kernel/sigtools.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
+#include "kernel/mem.h"
#include <string>
USING_YOSYS_NAMESPACE
@@ -40,12 +41,15 @@ struct Smt2Worker
std::map<RTLIL::SigBit, RTLIL::Cell*> bit_driver;
std::set<RTLIL::Cell*> exported_cells, hiercells, hiercells_queue;
pool<Cell*> recursive_cells, registers;
+ std::vector<Mem> memories;
+ dict<Cell*, Mem*> mem_cells;
+ std::set<Mem*> memory_queue;
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<Mem*, int> memarrays;
std::map<int, int> bvsizes;
dict<IdString, char*> ids;
@@ -116,12 +120,73 @@ struct Smt2Worker
makebits(stringf("%s_is", get_id(module)));
+ dict<IdString, Mem*> mem_dict;
+ memories = Mem::get_all_memories(module);
+ for (auto &mem : memories)
+ {
+ mem_dict[mem.memid] = &mem;
+ for (auto &port : mem.wr_ports)
+ {
+ if (port.clk_enable) {
+ SigSpec clk = sigmap(port.clk);
+ for (int i = 0; i < GetSize(clk); i++)
+ {
+ if (clk[i].wire == nullptr)
+ continue;
+ if (port.clk_polarity)
+ clock_posedge.insert(clk[i]);
+ else
+ clock_negedge.insert(clk[i]);
+ }
+ }
+ for (auto bit : sigmap(port.en))
+ noclock.insert(bit);
+ for (auto bit : sigmap(port.addr))
+ noclock.insert(bit);
+ for (auto bit : sigmap(port.data))
+ noclock.insert(bit);
+ }
+ for (auto &port : mem.rd_ports)
+ {
+ if (port.clk_enable) {
+ SigSpec clk = sigmap(port.clk);
+ for (int i = 0; i < GetSize(clk); i++)
+ {
+ if (clk[i].wire == nullptr)
+ continue;
+ if (port.clk_polarity)
+ clock_posedge.insert(clk[i]);
+ else
+ clock_negedge.insert(clk[i]);
+ }
+ }
+ for (auto bit : sigmap(port.en))
+ noclock.insert(bit);
+ for (auto bit : sigmap(port.addr))
+ noclock.insert(bit);
+ for (auto bit : sigmap(port.data))
+ noclock.insert(bit);
+ Cell *driver = port.cell ? port.cell : mem.cell;
+ for (auto bit : sigmap(port.data)) {
+ if (bit_driver.count(bit))
+ log_error("Found multiple drivers for %s.\n", log_signal(bit));
+ bit_driver[bit] = driver;
+ }
+ }
+ }
+
for (auto cell : module->cells())
for (auto &conn : cell->connections())
{
if (GetSize(conn.second) == 0)
continue;
+ // Handled above.
+ if (cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit))) {
+ mem_cells[cell] = mem_dict[cell->parameters.at(ID::MEMID).decode_string()];
+ continue;
+ }
+
bool is_input = ct.cell_input(cell->type, conn.first);
bool is_output = ct.cell_output(cell->type, conn.first);
@@ -135,24 +200,6 @@ struct Smt2Worker
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(ID($mem)) && conn.first.in(ID::RD_CLK, ID::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 == ID::RD_CLK ? ID::RD_CLK_ENABLE : ID::WR_CLK_ENABLE)[i] != State::S1)
- continue;
-
- if (cell->getParam(conn.first == ID::RD_CLK ? ID::RD_CLK_POLARITY : ID::WR_CLK_POLARITY)[i] == State::S1)
- clock_posedge.insert(clk[i]);
- else
- clock_negedge.insert(clk[i]);
- }
- }
- else
if (cell->type.in(ID($dff), ID($_DFF_P_), ID($_DFF_N_)) && conn.first.in(ID::CLK, ID::C))
{
bool posedge = (cell->type == ID($_DFF_N_)) || (cell->type == ID($dff) && cell->getParam(ID::CLK_POLARITY).as_bool());
@@ -647,27 +694,35 @@ struct Smt2Worker
// FIXME: $slice $concat
}
- if (memmode && cell->type == ID($mem))
+ if (memmode && cell->type.in(ID($mem), ID($memrd), ID($memwr), ID($meminit)))
{
+ Mem *mem = mem_cells[cell];
+
+ if (memarrays.count(mem)) {
+ recursive_cells.erase(cell);
+ return;
+ }
+
int arrayid = idcounter++;
- memarrays[cell] = arrayid;
-
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
- int rd_ports = cell->getParam(ID::RD_PORTS).as_int();
- int wr_ports = cell->getParam(ID::WR_PORTS).as_int();
-
- bool async_read = false;
- if (!cell->getParam(ID::WR_CLK_ENABLE).is_fully_ones()) {
- if (!cell->getParam(ID::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;
+ memarrays[mem] = arrayid;
+
+ int abits = ceil_log2(mem->size);
+
+ bool has_sync_wr = false;
+ bool has_async_wr = false;
+ for (auto &port : mem->wr_ports) {
+ if (port.clk_enable)
+ has_sync_wr = true;
+ else
+ has_async_wr = true;
}
+ if (has_async_wr && has_sync_wr)
+ 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));
- 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"));
+ decls.push_back(stringf("; yosys-smt2-memory %s %d %d %d %d %s\n", get_id(mem->memid), abits, mem->width, GetSize(mem->rd_ports), GetSize(mem->wr_ports), has_async_wr ? "async" : "sync"));
string memstate;
- if (async_read) {
+ if (has_async_wr) {
memstate = stringf("%s#%d#final", get_id(module), arrayid);
} else {
memstate = stringf("%s#%d#0", get_id(module), arrayid);
@@ -675,80 +730,79 @@ struct Smt2Worker
if (statebv)
{
- int mem_size = cell->getParam(ID::SIZE).as_int();
- int mem_offset = cell->getParam(ID::OFFSET).as_int();
-
- makebits(memstate, width*mem_size, get_id(cell));
+ makebits(memstate, mem->width*mem->size, get_id(mem->memid));
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()));
+ get_id(module), get_id(mem->memid), get_id(module), mem->width*mem->size, memstate.c_str()));
- for (int i = 0; i < rd_ports; i++)
+ for (int i = 0; i < GetSize(mem->rd_ports); i++)
{
- SigSpec addr_sig = cell->getPort(ID::RD_ADDR).extract(abits*i, abits);
- SigSpec data_sig = cell->getPort(ID::RD_DATA).extract(width*i, width);
+ auto &port = mem->rd_ports[i];
+ SigSpec addr_sig = port.addr;
+ addr_sig.extend_u0(abits);
std::string addr = get_bv(addr_sig);
- if (cell->getParam(ID::RD_CLK_ENABLE).extract(i).as_bool())
+ if (port.clk_enable)
log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
- "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module));
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
+ get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
std::string read_expr = "#b";
- for (int k = 0; k < width; k++)
+ for (int k = 0; k < mem->width; k++)
read_expr += "0";
- for (int k = 0; k < mem_size; k++)
+ for (int k = 0; k < mem->size; k++)
read_expr = stringf("(ite (= (|%s_m:R%dA %s| state) #b%s) ((_ extract %d %d) (|%s| state))\n %s)",
- get_id(module), i, get_id(cell), Const(k+mem_offset, abits).as_string().c_str(),
- width*(k+1)-1, width*k, memstate.c_str(), read_expr.c_str());
+ get_id(module), i, get_id(mem->memid), Const(k+mem->start_offset, abits).as_string().c_str(),
+ mem->width*(k+1)-1, mem->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)));
+ get_id(module), idcounter, get_id(module), mem->width, read_expr.c_str(), log_signal(port.data)));
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));
+ get_id(module), i, get_id(mem->memid), get_id(module), mem->width, get_id(module), idcounter));
- register_bv(data_sig, idcounter++);
+ register_bv(port.data, idcounter++);
}
}
else
{
if (statedt)
dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
- memstate.c_str(), abits, width, get_id(cell)));
+ memstate.c_str(), abits, mem->width, get_id(mem->memid)));
else
decls.push_back(stringf("(declare-fun |%s| (|%s_s|) (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
- memstate.c_str(), get_id(module), abits, width, get_id(cell)));
+ memstate.c_str(), get_id(module), abits, mem->width, get_id(mem->memid)));
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()));
+ get_id(module), get_id(mem->memid), get_id(module), abits, mem->width, memstate.c_str()));
- for (int i = 0; i < rd_ports; i++)
+ for (int i = 0; i < GetSize(mem->rd_ports); i++)
{
- SigSpec addr_sig = cell->getPort(ID::RD_ADDR).extract(abits*i, abits);
- SigSpec data_sig = cell->getPort(ID::RD_DATA).extract(width*i, width);
+ auto &port = mem->rd_ports[i];
+ SigSpec addr_sig = port.addr;
+ addr_sig.extend_u0(abits);
std::string addr = get_bv(addr_sig);
- if (cell->getParam(ID::RD_CLK_ENABLE).extract(i).as_bool())
+ if (port.clk_enable)
log_error("Read port %d (%s) of memory %s.%s is clocked. This is not supported by \"write_smt2\"! "
- "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(data_sig), log_id(cell), log_id(module));
+ "Call \"memory\" with -nordff to avoid this error.\n", i, log_signal(port.data), log_id(mem->memid), log_id(module));
decls.push_back(stringf("(define-fun |%s_m:R%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
+ get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
decls.push_back(stringf("(define-fun |%s#%d| ((state |%s_s|)) (_ BitVec %d) (select (|%s| state) (|%s_m:R%dA %s| state))) ; %s\n",
- get_id(module), idcounter, get_id(module), width, memstate.c_str(), get_id(module), i, get_id(cell), log_signal(data_sig)));
+ get_id(module), idcounter, get_id(module), mem->width, memstate.c_str(), get_id(module), i, get_id(mem->memid), log_signal(port.data)));
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));
+ get_id(module), i, get_id(mem->memid), get_id(module), mem->width, get_id(module), idcounter));
- register_bv(data_sig, idcounter++);
+ register_bv(port.data, idcounter++);
}
}
- registers.insert(cell);
+ memory_queue.insert(mem);
recursive_cells.erase(cell);
return;
}
@@ -822,40 +876,51 @@ struct Smt2Worker
for (auto bit : SigSpec(wire))
if (reg_bits.count(bit))
is_register = true;
- if (wire->port_id || is_register || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name[0] == '\\')) {
+ if (wire->port_id || is_register || wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic())) {
RTLIL::SigSpec sig = sigmap(wire);
+ std::vector<std::string> comments;
if (wire->port_input)
- decls.push_back(stringf("; yosys-smt2-input %s %d\n", get_id(wire), wire->width));
+ comments.push_back(stringf("; yosys-smt2-input %s %d\n", get_id(wire), wire->width));
if (wire->port_output)
- decls.push_back(stringf("; yosys-smt2-output %s %d\n", get_id(wire), wire->width));
+ comments.push_back(stringf("; yosys-smt2-output %s %d\n", get_id(wire), wire->width));
if (is_register)
- decls.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
- if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name[0] == '\\'))
- decls.push_back(stringf("; yosys-smt2-wire %s %d\n", get_id(wire), wire->width));
+ comments.push_back(stringf("; yosys-smt2-register %s %d\n", get_id(wire), wire->width));
+ if (wire->get_bool_attribute(ID::keep) || (wiresmode && wire->name.isPublic()))
+ comments.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),
+ comments.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) {
+ std::string sig_bv = get_bv(sig);
+ if (!comments.empty())
+ decls.insert(decls.end(), comments.begin(), comments.end());
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()));
+ get_id(module), get_id(wire), get_id(module), GetSize(sig), sig_bv.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++)
+ std::vector<std::string> sig_bool;
+ for (int i = 0; i < GetSize(sig); i++) {
+ sig_bool.push_back(get_bool(sig[i]));
+ }
+ if (!comments.empty())
+ decls.insert(decls.end(), comments.begin(), comments.end());
+ for (int i = 0; i < GetSize(sig); i++) {
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()));
+ get_id(module), get_id(wire), i, get_id(module), sig_bool[i].c_str()));
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()));
+ get_id(module), get_id(wire), get_id(module), sig_bool[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)));
}
+ }
}
}
}
@@ -966,7 +1031,7 @@ struct Smt2Worker
}
}
- for (int iter = 1; !registers.empty(); iter++)
+ for (int iter = 1; !registers.empty() || !memory_queue.empty(); iter++)
{
pool<Cell*> this_regs;
this_regs.swap(registers);
@@ -999,152 +1064,156 @@ struct Smt2Worker
if (cell->type == ID($anyconst))
ex_state_eq.push_back(stringf("(= %s %s)", get_bv(cell->getPort(ID::Y)).c_str(), get_bv(cell->getPort(ID::Y), "other_state").c_str()));
}
+ }
- if (cell->type == ID($mem))
- {
- int arrayid = memarrays.at(cell);
+ std::set<Mem*> this_mems;
+ this_mems.swap(memory_queue);
+
+ for (auto mem : this_mems)
+ {
+ int arrayid = memarrays.at(mem);
+
+ int abits = ceil_log2(mem->size);;
+
+ bool has_sync_wr = false;
+ bool has_async_wr = false;
+ for (auto &port : mem->wr_ports) {
+ if (port.clk_enable)
+ has_sync_wr = true;
+ else
+ has_async_wr = true;
+ }
- int abits = cell->getParam(ID::ABITS).as_int();
- int width = cell->getParam(ID::WIDTH).as_int();
- int wr_ports = cell->getParam(ID::WR_PORTS).as_int();
+ string initial_memstate, final_memstate;
- bool async_read = false;
- string initial_memstate, final_memstate;
+ if (has_async_wr) {
+ log_assert(!has_sync_wr);
+ initial_memstate = stringf("%s#%d#0", get_id(module), arrayid);
+ final_memstate = stringf("%s#%d#final", get_id(module), arrayid);
+ }
- if (!cell->getParam(ID::WR_CLK_ENABLE).is_fully_ones()) {
- log_assert(cell->getParam(ID::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)
+ {
+ if (has_async_wr) {
+ makebits(final_memstate, mem->width*mem->size, get_id(mem->memid));
}
- if (statebv)
+ for (int i = 0; i < GetSize(mem->wr_ports); i++)
{
- int mem_size = cell->getParam(ID::SIZE).as_int();
- int mem_offset = cell->getParam(ID::OFFSET).as_int();
-
- if (async_read) {
- makebits(final_memstate, width*mem_size, get_id(cell));
+ auto &port = mem->wr_ports[i];
+ SigSpec addr_sig = port.addr;
+ addr_sig.extend_u0(abits);
+
+ std::string addr = get_bv(addr_sig);
+ std::string data = get_bv(port.data);
+ std::string mask = get_bv(port.en);
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
+ addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(mem->memid));
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(mem->memid), get_id(module), mem->width, data.c_str(), log_signal(port.data)));
+ data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(mem->memid));
+
+ decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(mem->memid), get_id(module), mem->width, mask.c_str(), log_signal(port.en)));
+ mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(mem->memid));
+
+ std::string data_expr;
+
+ for (int k = mem->size-1; k >= 0; k--) {
+ std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))",
+ data.c_str(), mask.c_str(), mem->width*(k+1)-1, mem->width*k, get_id(module), arrayid, i, mask.c_str());
+ data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))",
+ addr.c_str(), Const(k+mem->start_offset, abits).as_string().c_str(), new_data.c_str(),
+ mem->width*(k+1)-1, mem->width*k, get_id(module), arrayid, i);
}
- for (int i = 0; i < wr_ports; i++)
- {
- SigSpec addr_sig = cell->getPort(ID::WR_ADDR).extract(abits*i, abits);
- SigSpec data_sig = cell->getPort(ID::WR_DATA).extract(width*i, width);
- SigSpec mask_sig = cell->getPort(ID::WR_EN).extract(width*i, width);
+ decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n",
+ get_id(module), arrayid, i+1, get_id(module), mem->width*mem->size, data_expr.c_str(), get_id(mem->memid)));
+ }
+ }
+ else
+ {
+ if (has_async_wr) {
+ if (statedt)
+ dtmembers.push_back(stringf(" (|%s| (Array (_ BitVec %d) (_ BitVec %d))) ; %s\n",
+ initial_memstate.c_str(), abits, mem->width, get_id(mem->memid)));
+ 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, mem->width, get_id(mem->memid)));
+ }
- std::string addr = get_bv(addr_sig);
- std::string data = get_bv(data_sig);
- std::string mask = get_bv(mask_sig);
+ for (int i = 0; i < GetSize(mem->wr_ports); i++)
+ {
+ auto &port = mem->wr_ports[i];
+ SigSpec addr_sig = port.addr;
+ addr_sig.extend_u0(abits);
- decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
- addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell));
+ std::string addr = get_bv(addr_sig);
+ std::string data = get_bv(port.data);
+ std::string mask = get_bv(port.en);
- decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig)));
- data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell));
+ decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(mem->memid), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
+ addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(mem->memid));
- decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig)));
- mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell));
+ decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(mem->memid), get_id(module), mem->width, data.c_str(), log_signal(port.data)));
+ data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(mem->memid));
- std::string data_expr;
+ decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
+ get_id(module), i, get_id(mem->memid), get_id(module), mem->width, mask.c_str(), log_signal(port.en)));
+ mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(mem->memid));
- for (int k = mem_size-1; k >= 0; k--) {
- std::string new_data = stringf("(bvor (bvand %s %s) (bvand ((_ extract %d %d) (|%s#%d#%d| state)) (bvnot %s)))",
- data.c_str(), mask.c_str(), width*(k+1)-1, width*k, get_id(module), arrayid, i, mask.c_str());
- data_expr += stringf("\n (ite (= %s #b%s) %s ((_ extract %d %d) (|%s#%d#%d| state)))",
- addr.c_str(), Const(k+mem_offset, abits).as_string().c_str(), new_data.c_str(),
- width*(k+1)-1, width*k, get_id(module), arrayid, i);
- }
+ data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))",
+ data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str());
- decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (_ BitVec %d) (concat%s)) ; %s\n",
- get_id(module), arrayid, i+1, get_id(module), width*mem_size, data_expr.c_str(), get_id(cell)));
- }
+ decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) "
+ "(store (|%s#%d#%d| state) %s %s)) ; %s\n",
+ get_id(module), arrayid, i+1, get_id(module), abits, mem->width,
+ get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(mem->memid)));
}
- 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(ID::WR_ADDR).extract(abits*i, abits);
- SigSpec data_sig = cell->getPort(ID::WR_DATA).extract(width*i, width);
- SigSpec mask_sig = cell->getPort(ID::WR_EN).extract(width*i, width);
+ }
- std::string addr = get_bv(addr_sig);
- std::string data = get_bv(data_sig);
- std::string mask = get_bv(mask_sig);
+ std::string expr_d = stringf("(|%s#%d#%d| state)", get_id(module), arrayid, GetSize(mem->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(mem->memid)));
+ ex_state_eq.push_back(stringf("(= (|%s#%d#0| state) (|%s#%d#0| other_state))", get_id(module), arrayid, get_id(module), arrayid));
- decls.push_back(stringf("(define-fun |%s_m:W%dA %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), abits, addr.c_str(), log_signal(addr_sig)));
- addr = stringf("(|%s_m:W%dA %s| state)", get_id(module), i, get_id(cell));
+ if (has_async_wr)
+ hier.push_back(stringf(" (= %s (|%s| state)) ; %s\n", expr_d.c_str(), final_memstate.c_str(), get_id(mem->memid)));
- decls.push_back(stringf("(define-fun |%s_m:W%dD %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), width, data.c_str(), log_signal(data_sig)));
- data = stringf("(|%s_m:W%dD %s| state)", get_id(module), i, get_id(cell));
+ Const init_data = mem->get_init_data();
- decls.push_back(stringf("(define-fun |%s_m:W%dM %s| ((state |%s_s|)) (_ BitVec %d) %s) ; %s\n",
- get_id(module), i, get_id(cell), get_id(module), width, mask.c_str(), log_signal(mask_sig)));
- mask = stringf("(|%s_m:W%dM %s| state)", get_id(module), i, get_id(cell));
+ for (int i = 0; i < mem->size; i++)
+ {
+ if (i*mem->width >= GetSize(init_data))
+ break;
- data = stringf("(bvor (bvand %s %s) (bvand (select (|%s#%d#%d| state) %s) (bvnot %s)))",
- data.c_str(), mask.c_str(), get_id(module), arrayid, i, addr.c_str(), mask.c_str());
+ Const initword = init_data.extract(i*mem->width, mem->width, State::Sx);
+ Const initmask = initword;
+ bool gen_init_constr = false;
- decls.push_back(stringf("(define-fun |%s#%d#%d| ((state |%s_s|)) (Array (_ BitVec %d) (_ BitVec %d)) "
- "(store (|%s#%d#%d| state) %s %s)) ; %s\n",
- get_id(module), arrayid, i+1, get_id(module), abits, width,
- get_id(module), arrayid, i, addr.c_str(), data.c_str(), get_id(cell)));
+ 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;
}
}
- 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(ID::INIT);
- int memsize = cell->getParam(ID::SIZE).as_int();
-
- for (int i = 0; i < memsize; i++)
+ if (gen_init_constr)
{
- if (i*width >= GetSize(init_data))
- break;
-
- Const initword = init_data.extract(i*width, width, State::Sx);
- Const initmask = initword;
- bool gen_init_constr = false;
-
- 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("(= (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(),
- initmask.as_string().c_str(), initword.as_string().c_str(), get_id(cell), i));
- }
+ if (statebv)
+ /* FIXME */;
+ else
+ 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(),
+ initmask.as_string().c_str(), initword.as_string().c_str(), get_id(mem->memid), i));
}
}
}
@@ -1280,7 +1349,7 @@ struct Smt2Worker
struct Smt2Backend : public Backend {
Smt2Backend() : Backend("smt2", "write design to SMT-LIBv2 file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1387,8 +1456,12 @@ struct Smt2Backend : public Backend {
log(" use the given template file. the line containing only the token '%%%%'\n");
log(" is replaced with the regular output of this command.\n");
log("\n");
+ log(" -solver-option <option> <value>\n");
+ log(" emit a `; yosys-smt2-solver-option` directive for yosys-smtbmc to write\n");
+ log(" the given option as a `(set-option ...)` command in the SMT-LIBv2.\n");
+ log("\n");
log("[1] For more information on SMT-LIBv2 visit http://smt-lib.org/ or read David\n");
- log("R. Cok's tutorial: http://www.grammatech.com/resources/smt/SMTLIBTutorial.pdf\n");
+ log("R. Cok's tutorial: https://smtlib.github.io/jSMTLIB/SMTLIBTutorial.pdf\n");
log("\n");
log("---------------------------------------------------------------------------\n");
log("\n");
@@ -1436,11 +1509,12 @@ struct Smt2Backend : public Backend {
log("from non-zero to zero in the test design.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
std::ifstream template_f;
bool bvmode = true, memmode = true, wiresmode = false, verbose = false, statebv = false, statedt = false;
bool forallmode = false;
+ dict<std::string, std::string> solver_options;
log_header(design, "Executing SMT2 backend.\n");
@@ -1484,6 +1558,11 @@ struct Smt2Backend : public Backend {
verbose = true;
continue;
}
+ if (args[argidx] == "-solver-option" && argidx+2 < args.size()) {
+ solver_options.emplace(args[argidx+1], args[argidx+2]);
+ argidx += 2;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
@@ -1514,6 +1593,9 @@ struct Smt2Backend : public Backend {
if (statedt)
*f << stringf("; yosys-smt2-stdt\n");
+ for (auto &it : solver_options)
+ *f << stringf("; yosys-smt2-solver-option %s %s\n", it.first.c_str(), it.second.c_str());
+
std::vector<RTLIL::Module*> sorted_modules;
// extract module dependencies
@@ -1562,7 +1644,7 @@ struct Smt2Backend : public Backend {
for (auto module : sorted_modules)
{
- if (module->get_blackbox_attribute() || module->has_memories_warn() || module->has_processes_warn())
+ if (module->get_blackbox_attribute() || module->has_processes_warn())
continue;
log("Creating SMT-LIBv2 representation of module %s.\n", log_id(module));
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index 03f001bfd..da5a7f57e 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -817,6 +817,24 @@ def write_vcd_trace(steps_start, steps_stop, index):
vcd.set_time(steps_stop)
+def char_ok_in_verilog(c,i):
+ if ('A' <= c <= 'Z'): return True
+ if ('a' <= c <= 'z'): return True
+ if ('0' <= c <= '9' and i>0): return True
+ if (c == '_'): return True
+ if (c == '$'): return True
+ return False
+
+def escape_identifier(identifier):
+ if type(identifier) is list:
+ return map(escape_identifier, identifier)
+ if "." in identifier:
+ return ".".join(escape_identifier(identifier.split(".")))
+ if (all(char_ok_in_verilog(identifier[i],i) for i in range(0, len(identifier)))):
+ return identifier
+ return "\\"+identifier+" "
+
+
def write_vlogtb_trace(steps_start, steps_stop, index):
filename = vlogtbfile.replace("%", index)
@@ -858,12 +876,12 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
for name, width in primary_inputs:
if name in clock_inputs:
- print(" wire [%d:0] PI_%s = clock;" % (width-1, name), file=f)
+ print(" wire [%d:0] %s = clock;" % (width-1, escape_identifier("PI_"+name)), file=f)
else:
- print(" reg [%d:0] PI_%s;" % (width-1, name), file=f)
+ print(" reg [%d:0] %s;" % (width-1, escape_identifier("PI_"+name)), file=f)
- print(" %s UUT (" % vlogtb_topmod, file=f)
- print(",\n".join(" .{name}(PI_{name})".format(name=name) for name, _ in primary_inputs), file=f)
+ print(" %s UUT (" % escape_identifier(vlogtb_topmod), file=f)
+ print(",\n".join(" .%s(%s)" % (escape_identifier(name), escape_identifier("PI_"+name)) for name, _ in primary_inputs), file=f)
print(" );", file=f)
print("`ifndef VERILATOR", file=f)
@@ -893,14 +911,14 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
for n in reg:
if n.startswith("$"):
hidden_net = True
- print(" %sUUT.%s = %d'b%s;" % ("// " if hidden_net else "", ".".join(reg), len(val), val), file=f)
+ print(" %sUUT.%s = %d'b%s;" % ("// " if hidden_net else "", ".".join(escape_identifier(reg)), len(val), val), file=f)
anyconsts = sorted(smt.hieranyconsts(vlogtb_topmod))
for info in anyconsts:
if info[3] is not None:
modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps_start)), info[0])
value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate)))
- print(" UUT.%s = %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f);
+ print(" UUT.%s = %d'b%s;" % (".".join(escape_identifier(info[0] + [info[3]])), len(value), value), file=f);
mems = sorted(smt.hiermems(vlogtb_topmod))
for mempath in mems:
@@ -924,7 +942,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
addr_data[addr] = data
for addr, data in addr_data.items():
- print(" UUT.%s[%d'b%s] = %d'b%s;" % (".".join(mempath), len(addr), addr, len(data), data), file=f)
+ print(" UUT.%s[%d'b%s] = %d'b%s;" % (".".join(escape_identifier(mempath)), len(addr), addr, len(data), data), file=f)
print("", file=f)
anyseqs = sorted(smt.hieranyseqs(vlogtb_topmod))
@@ -940,18 +958,18 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
for name, val in zip(pi_names, pi_values):
if i > 0:
- print(" PI_%s <= %d'b%s;" % (".".join(name), len(val), val), file=f)
+ print(" %s <= %d'b%s;" % (escape_identifier("PI_"+".".join(name)), len(val), val), file=f)
else:
- print(" PI_%s = %d'b%s;" % (".".join(name), len(val), val), file=f)
+ print(" %s = %d'b%s;" % (escape_identifier("PI_"+".".join(name)), len(val), val), file=f)
for info in anyseqs:
if info[3] is not None:
modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), info[0])
value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate)))
if i > 0:
- print(" UUT.%s <= %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f);
+ print(" UUT.%s <= %d'b%s;" % (".".join(escape_identifier(info[0] + [info[3]])), len(value), value), file=f);
else:
- print(" UUT.%s = %d'b%s;" % (".".join(info[0] + [info[3]]), len(value), value), file=f);
+ print(" UUT.%s = %d'b%s;" % (".".join(escape_identifier(info[0] + [info[3]])), len(value), value), file=f);
if i > 0:
print(" end", file=f)
@@ -1275,10 +1293,10 @@ def smt_pop():
asserts_consequent_cache.pop()
smt.write("(pop 1)")
-def smt_check_sat():
+def smt_check_sat(expected=["sat", "unsat"]):
if asserts_cache_dirty:
smt_forall_assert()
- return smt.check_sat()
+ return smt.check_sat(expected=expected)
if tempind:
retstatus = "FAILED"
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 72ab39d39..516091011 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -124,6 +124,7 @@ class SmtIo:
self.timeout = 0
self.produce_models = True
self.smt2cache = [list()]
+ self.smt2_options = dict()
self.p = None
self.p_index = solvers_index
solvers_index += 1
@@ -258,14 +259,24 @@ class SmtIo:
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)")
+ #See the SMT-LIB Standard, Section 4.1.7
+ modestart_options = [":global-declarations", ":interactive-mode", ":produce-assertions", ":produce-assignments", ":produce-models", ":produce-proofs", ":produce-unsat-assumptions", ":produce-unsat-cores", ":random-seed"]
+ for key, val in self.smt2_options.items():
+ if key in modestart_options:
+ self.write("(set-option {} {})".format(key, val))
+
self.write("(set-logic %s)" % self.logic)
+ if self.forall and self.solver == "yices":
+ self.write("(set-option :yices-ef-max-iters 1000000000)")
+
+ for key, val in self.smt2_options.items():
+ if key not in modestart_options:
+ self.write("(set-option {} {})".format(key, val))
+
def timestamp(self):
secs = int(time() - self.start_time)
return "## %3d:%02d:%02d " % (secs // (60*60), (secs // 60) % 60, secs % 60)
@@ -468,6 +479,9 @@ class SmtIo:
fields = stmt.split()
+ if fields[1] == "yosys-smt2-solver-option":
+ self.smt2_options[fields[2]] = fields[3]
+
if fields[1] == "yosys-smt2-nomem":
if self.logic is None:
self.logic_ax = False
@@ -653,7 +667,7 @@ class SmtIo:
return stmt
- def check_sat(self):
+ def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted"]):
if self.debug_print:
print("> (check-sat)")
if self.debug_file and not self.nocomments:
@@ -740,7 +754,7 @@ class SmtIo:
print("(check-sat)", file=self.debug_file)
self.debug_file.flush()
- if result not in ["sat", "unsat", "unknown", "timeout", "interrupted"]:
+ if result not in expected:
if result == "":
print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
else:
diff --git a/backends/smv/.gitignore b/backends/smv/.gitignore
new file mode 100644
index 000000000..d23d492d7
--- /dev/null
+++ b/backends/smv/.gitignore
@@ -0,0 +1 @@
+/test_cells.tmp/
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index 2fc7099f4..4e5c6050d 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -702,7 +702,7 @@ struct SmvWorker
struct SmvBackend : public Backend {
SmvBackend() : Backend("smv", "write design to SMV file") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -720,7 +720,7 @@ struct SmvBackend : public Backend {
log("THIS COMMAND IS UNDER CONSTRUCTION\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
std::ifstream template_f;
bool verbose = false;
diff --git a/backends/spice/spice.cc b/backends/spice/spice.cc
index 84e93b61b..aa20f106a 100644
--- a/backends/spice/spice.cc
+++ b/backends/spice/spice.cc
@@ -130,7 +130,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") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -159,7 +159,7 @@ struct SpiceBackend : public Backend {
log(" set the specified module as design top module\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
std::string top_module_name;
RTLIL::Module *top_module = NULL;
diff --git a/backends/table/table.cc b/backends/table/table.cc
index 796f18059..77642ccbd 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") { }
- void help() YS_OVERRIDE
+ void help() 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");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing TABLE backend.\n");
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 4f44a053a..9523f4a52 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -25,6 +25,8 @@
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/sigtools.h"
+#include "kernel/ff.h"
+#include "kernel/mem.h"
#include <string>
#include <sstream>
#include <set>
@@ -33,10 +35,10 @@
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
-bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit;
+bool verbose, norename, noattr, attr2comment, noexpr, nodec, nohex, nostr, extmem, defparam, decimal, siminit, systemverilog;
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::set<RTLIL::IdString> reg_wires;
std::string auto_prefix, extmem_prefix;
RTLIL::Module *active_module;
@@ -433,10 +435,250 @@ void dump_wire(std::ostream &f, std::string indent, RTLIL::Wire *wire)
#endif
}
-void dump_memory(std::ostream &f, std::string indent, RTLIL::Memory *memory)
+void dump_memory(std::ostream &f, std::string indent, Mem &mem)
{
- dump_attributes(f, indent, memory->attributes);
- 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);
+ std::string mem_id = id(mem.memid);
+
+ dump_attributes(f, indent, mem.attributes);
+ f << stringf("%s" "reg [%d:0] %s [%d:%d];\n", indent.c_str(), mem.width-1, mem_id.c_str(), mem.size+mem.start_offset-1, mem.start_offset);
+
+ // for memory block make something like:
+ // reg [7:0] memid [3:0];
+ // initial begin
+ // memid[0] = ...
+ // end
+ if (!mem.inits.empty())
+ {
+ 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
+ {
+ Const data = mem.get_init_data();
+ for (int i=0; i<mem.size; i++)
+ {
+ RTLIL::Const element = data.extract(i*mem.width, mem.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" "initial begin\n", indent.c_str());
+ for (auto &init : mem.inits) {
+ int words = GetSize(init.data) / mem.width;
+ int start = init.addr.as_int();
+ for (int i=0; i<words; i++)
+ {
+ f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i + start);
+ dump_const(f, init.data.extract(i*mem.width, mem.width));
+ f << stringf(";\n");
+ }
+ }
+ f << stringf("%s" "end\n", indent.c_str());
+ }
+ }
+
+ // create a map : "edge clk" -> expressions within that clock domain
+ dict<std::string, std::vector<std::string>> clk_to_lof_body;
+ clk_to_lof_body[""] = std::vector<std::string>();
+ std::string clk_domain_str;
+ // create a list of reg declarations
+ std::vector<std::string> lof_reg_declarations;
+
+ // read ports
+ for (auto &port : mem.rd_ports)
+ {
+ if (port.clk_enable)
+ {
+ {
+ std::ostringstream os;
+ dump_sigspec(os, port.clk);
+ clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "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 (!port.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", port.data.size() - 1, temp_id.c_str()) );
+ {
+ std::ostringstream os;
+ if (port.en != RTLIL::SigBit(true))
+ {
+ os << stringf("if (");
+ dump_sigspec(os, port.en);
+ os << stringf(") ");
+ }
+ os << stringf("%s <= %s[", temp_id.c_str(), mem_id.c_str());
+ dump_sigspec(os, port.addr);
+ os << stringf("];\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ }
+ {
+ std::ostringstream os;
+ dump_sigspec(os, port.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
+ {
+ // for rd-transparent read-ports make something like:
+ // reg [..] temp_id;
+ // always @(posedge clk)
+ // temp_id <= r_addr;
+ // assign r_data = array_reg[temp_id];
+ std::string temp_id = next_auto_id();
+ lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", port.addr.size() - 1, temp_id.c_str()) );
+ {
+ std::ostringstream os;
+ dump_sigspec(os, port.addr);
+ std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
+ clk_to_lof_body[clk_domain_str].push_back(line);
+ }
+ {
+ std::ostringstream os;
+ dump_sigspec(os, port.data);
+ 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, port.data);
+ dump_sigspec(os2, port.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);
+ }
+ }
+
+ // write ports
+ for (auto &port : mem.wr_ports)
+ {
+ {
+ std::ostringstream os;
+ dump_sigspec(os, port.clk);
+ clk_domain_str = stringf("%sedge %s", port.clk_polarity ? "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>();
+ }
+ // make something like:
+ // always @(posedge clk)
+ // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
+ // ...
+ for (int i = 0; i < GetSize(port.en); i++)
+ {
+ int start_i = i, width = 1;
+ SigBit wen_bit = port.en[i];
+
+ while (i+1 < GetSize(port.en) && active_sigmap(port.en[i+1]) == active_sigmap(wen_bit))
+ i++, width++;
+
+ if (wen_bit == State::S0)
+ continue;
+
+ std::ostringstream os;
+ if (wen_bit != State::S1)
+ {
+ os << stringf("if (");
+ dump_sigspec(os, wen_bit);
+ os << stringf(") ");
+ }
+ os << stringf("%s[", mem_id.c_str());
+ dump_sigspec(os, port.addr);
+ if (width == GetSize(port.en))
+ os << stringf("] <= ");
+ else
+ os << stringf("][%d:%d] <= ", i, start_i);
+ dump_sigspec(os, port.data.extract(start_i, width));
+ os << stringf(";\n");
+ clk_to_lof_body[clk_domain_str].push_back(os.str());
+ }
+ }
+ // Output Verilog that looks something like this:
+ // reg [..] _3_;
+ // always @(posedge CLK2) begin
+ // _3_ <= memory[D1ADDR];
+ // if (A1EN)
+ // memory[A1ADDR] <= A1DATA;
+ // if (A2EN)
+ // memory[A2ADDR] <= A2DATA;
+ // ...
+ // end
+ // always @(negedge CLK1) begin
+ // if (C1EN)
+ // memory[C1ADDR] <= C1DATA;
+ // end
+ // ...
+ // assign D1DATA = _3_;
+ // assign D2DATA <= memory[D2ADDR];
+
+ // the reg ... definitions
+ for(auto &reg : lof_reg_declarations)
+ {
+ f << stringf("%s" "%s", indent.c_str(), reg.c_str());
+ }
+ // the block of expressions by clock domain
+ for(auto &pair : clk_to_lof_body)
+ {
+ std::string clk_domain = pair.first;
+ std::vector<std::string> lof_lines = pair.second;
+ if( clk_domain != "")
+ {
+ f << stringf("%s" "always%s @(%s) begin\n", indent.c_str(), systemverilog ? "_ff" : "", clk_domain.c_str());
+ for(auto &line : lof_lines)
+ f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
+ f << stringf("%s" "end\n", indent.c_str());
+ }
+ else
+ {
+ // the non-clocked assignments
+ for(auto &line : lof_lines)
+ f << stringf("%s" "%s", indent.c_str(), line.c_str());
+ }
+ }
}
void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, bool gen_signed = true)
@@ -451,7 +693,7 @@ void dump_cell_expr_port(std::ostream &f, RTLIL::Cell *cell, std::string port, b
std::string cellname(RTLIL::Cell *cell)
{
- if (!norename && cell->name[0] == '$' && reg_ct.count(cell->type) && cell->hasPort(ID::Q))
+ if (!norename && cell->name[0] == '$' && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_)))
{
RTLIL::SigSpec sig = cell->getPort(ID::Q);
if (GetSize(sig) != 1 || sig.is_fully_const())
@@ -605,93 +847,6 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type.begins_with("$_DFF_"))
- {
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
- f << ";\n";
- }
-
- dump_attributes(f, indent, cell->attributes);
- f << stringf("%s" "always @(%sedge ", indent.c_str(), cell->type[6] == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::C));
- if (cell->type[7] != '_') {
- f << stringf(" or %sedge ", cell->type[7] == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::R));
- }
- f << stringf(")\n");
-
- if (cell->type[7] != '_') {
- f << stringf("%s" " if (%s", indent.c_str(), cell->type[7] == 'P' ? "" : "!");
- dump_sigspec(f, cell->getPort(ID::R));
- f << stringf(")\n");
- f << stringf("%s" " %s <= %c;\n", indent.c_str(), reg_name.c_str(), cell->type[8]);
- f << stringf("%s" " else\n", indent.c_str());
- }
-
- 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(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
- if (cell->type.begins_with("$_DFFSR_"))
- {
- char pol_c = cell->type[8], pol_s = cell->type[9], pol_r = cell->type[10];
-
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
- f << ";\n";
- }
-
- dump_attributes(f, indent, cell->attributes);
- f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_c == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::C));
- f << stringf(" or %sedge ", pol_s == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::S));
- f << stringf(" or %sedge ", pol_r == 'P' ? "pos" : "neg");
- dump_sigspec(f, cell->getPort(ID::R));
- f << stringf(")\n");
-
- f << stringf("%s" " if (%s", indent.c_str(), pol_r == 'P' ? "" : "!");
- dump_sigspec(f, cell->getPort(ID::R));
- f << stringf(")\n");
- f << stringf("%s" " %s <= 0;\n", indent.c_str(), reg_name.c_str());
-
- f << stringf("%s" " else if (%s", indent.c_str(), pol_s == 'P' ? "" : "!");
- dump_sigspec(f, cell->getPort(ID::S));
- f << stringf(")\n");
- f << stringf("%s" " %s <= 1;\n", indent.c_str(), reg_name.c_str());
-
- f << stringf("%s" " else\n", indent.c_str());
- 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(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
#define HANDLE_UNIOP(_type, _operator) \
if (cell->type ==_type) { dump_cell_expr_uniop(f, indent, cell, _operator); return true; }
#define HANDLE_BINOP(_type, _operator) \
@@ -836,21 +991,19 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(" = ");
if (cell->getParam(ID::B_SIGNED).as_bool())
{
- f << stringf("$signed(");
- dump_sigspec(f, cell->getPort(ID::B));
- f << stringf(")");
+ dump_cell_expr_port(f, cell, "B", true);
f << stringf(" < 0 ? ");
- dump_sigspec(f, cell->getPort(ID::A));
+ dump_cell_expr_port(f, cell, "A", true);
f << stringf(" << - ");
dump_sigspec(f, cell->getPort(ID::B));
f << stringf(" : ");
- dump_sigspec(f, cell->getPort(ID::A));
+ dump_cell_expr_port(f, cell, "A", true);
f << stringf(" >> ");
dump_sigspec(f, cell->getPort(ID::B));
}
else
{
- dump_sigspec(f, cell->getPort(ID::A));
+ dump_cell_expr_port(f, cell, "A", true);
f << stringf(" >> ");
dump_sigspec(f, cell->getPort(ID::B));
}
@@ -986,423 +1139,198 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- if (cell->type == ID($dffsr))
+ if (RTLIL::builtin_ff_cell_types().count(cell->type))
{
- SigSpec sig_clk = cell->getPort(ID::CLK);
- SigSpec sig_set = cell->getPort(ID::SET);
- SigSpec sig_clr = cell->getPort(ID::CLR);
- SigSpec sig_d = cell->getPort(ID::D);
- SigSpec sig_q = cell->getPort(ID::Q);
+ FfData ff(nullptr, cell);
- int width = cell->parameters[ID::WIDTH].as_int();
- bool pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool();
- bool pol_set = cell->parameters[ID::SET_POLARITY].as_bool();
- bool pol_clr = cell->parameters[ID::CLR_POLARITY].as_bool();
+ // $ff / $_FF_ cell: not supported.
+ if (ff.has_d && !ff.has_clk && !ff.has_en)
+ return false;
std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(sig_q, reg_name);
+ bool out_is_reg_wire = is_reg_wire(ff.sig_q, reg_name);
if (!out_is_reg_wire) {
- f << stringf("%s" "reg [%d:0] %s", indent.c_str(), width-1, reg_name.c_str());
- dump_reg_init(f, sig_q);
- f << ";\n";
- }
-
- for (int i = 0; i < width; i++) {
- f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
- dump_sigspec(f, sig_clk);
- f << stringf(", %sedge ", pol_set ? "pos" : "neg");
- dump_sigspec(f, sig_set);
- f << stringf(", %sedge ", pol_clr ? "pos" : "neg");
- dump_sigspec(f, sig_clr);
- f << stringf(")\n");
-
- f << stringf("%s" " if (%s", indent.c_str(), pol_clr ? "" : "!");
- dump_sigspec(f, sig_clr);
- f << stringf(") %s[%d] <= 1'b0;\n", reg_name.c_str(), i);
-
- f << stringf("%s" " else if (%s", indent.c_str(), pol_set ? "" : "!");
- dump_sigspec(f, sig_set);
- f << stringf(") %s[%d] <= 1'b1;\n", reg_name.c_str(), i);
-
- f << stringf("%s" " else %s[%d] <= ", indent.c_str(), reg_name.c_str(), i);
- dump_sigspec(f, sig_d[i]);
- f << stringf(";\n");
- }
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "assign ", indent.c_str());
- dump_sigspec(f, sig_q);
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
- if (cell->type.in(ID($dff), ID($adff), ID($dffe)))
- {
- RTLIL::SigSpec sig_clk, sig_arst, sig_en, val_arst;
- bool pol_clk, pol_arst = false, pol_en = false;
-
- sig_clk = cell->getPort(ID::CLK);
- pol_clk = cell->parameters[ID::CLK_POLARITY].as_bool();
-
- if (cell->type == ID($adff)) {
- sig_arst = cell->getPort(ID::ARST);
- pol_arst = cell->parameters[ID::ARST_POLARITY].as_bool();
- val_arst = RTLIL::SigSpec(cell->parameters[ID::ARST_VALUE]);
- }
-
- if (cell->type == ID($dffe)) {
- sig_en = cell->getPort(ID::EN);
- pol_en = cell->parameters[ID::EN_POLARITY].as_bool();
- }
-
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
- f << ";\n";
- }
-
- f << stringf("%s" "always @(%sedge ", indent.c_str(), pol_clk ? "pos" : "neg");
- dump_sigspec(f, sig_clk);
- if (cell->type == ID($adff)) {
- f << stringf(" or %sedge ", pol_arst ? "pos" : "neg");
- dump_sigspec(f, sig_arst);
- }
- f << stringf(")\n");
-
- if (cell->type == ID($adff)) {
- f << stringf("%s" " if (%s", indent.c_str(), pol_arst ? "" : "!");
- dump_sigspec(f, sig_arst);
- f << stringf(")\n");
- f << stringf("%s" " %s <= ", indent.c_str(), reg_name.c_str());
- dump_sigspec(f, val_arst);
- f << stringf(";\n");
- f << stringf("%s" " else\n", indent.c_str());
- }
-
- if (cell->type == ID($dffe)) {
- 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(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
-
- return true;
- }
-
- if (cell->type == ID($dlatch))
- {
- RTLIL::SigSpec sig_en;
- bool pol_en = false;
-
- sig_en = cell->getPort(ID::EN);
- pol_en = cell->parameters[ID::EN_POLARITY].as_bool();
-
- std::string reg_name = cellname(cell);
- bool out_is_reg_wire = is_reg_wire(cell->getPort(ID::Q), reg_name);
-
- if (!out_is_reg_wire) {
- f << stringf("%s" "reg [%d:0] %s", indent.c_str(), cell->parameters[ID::WIDTH].as_int()-1, reg_name.c_str());
- dump_reg_init(f, cell->getPort(ID::Q));
+ if (ff.width == 1)
+ f << stringf("%s" "reg %s", indent.c_str(), reg_name.c_str());
+ else
+ f << stringf("%s" "reg [%d:0] %s", indent.c_str(), ff.width-1, reg_name.c_str());
+ dump_reg_init(f, ff.sig_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(ID::Q));
- f << stringf(" = %s;\n", reg_name.c_str());
- }
+ // If the FF has CLR/SET inputs, emit every bit slice separately.
+ int chunks = ff.has_sr ? ff.width : 1;
+ bool chunky = ff.has_sr && ff.width != 1;
- return true;
- }
-
- if (cell->type == ID($mem))
- {
- RTLIL::IdString memid = cell->parameters[ID::MEMID].decode_string();
- std::string mem_id = id(cell->parameters[ID::MEMID].decode_string());
- int abits = cell->parameters[ID::ABITS].as_int();
- int size = cell->parameters[ID::SIZE].as_int();
- int offset = cell->parameters[ID::OFFSET].as_int();
- int width = cell->parameters[ID::WIDTH].as_int();
- bool use_init = !(RTLIL::SigSpec(cell->parameters[ID::INIT]).is_fully_undef());
-
- // for memory block make something like:
- // reg [7:0] memid [3:0];
- // initial begin
- // memid[0] = ...
- // end
- dump_attributes(f, indent.c_str(), cell->attributes);
- 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)
+ for (int i = 0; i < chunks; 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)
+ SigSpec sig_d;
+ Const val_arst, val_srst;
+ std::string reg_bit_name, sig_set_name, sig_clr_name, sig_arst_name;
+ if (chunky) {
+ reg_bit_name = stringf("%s[%d]", reg_name.c_str(), i);
+ if (ff.has_d)
+ sig_d = ff.sig_d[i];
+ } else {
+ reg_bit_name = reg_name;
+ if (ff.has_d)
+ sig_d = ff.sig_d;
+ }
+ if (ff.has_arst)
+ val_arst = chunky ? ff.val_arst[i] : ff.val_arst;
+ if (ff.has_srst)
+ val_srst = chunky ? ff.val_srst[i] : ff.val_srst;
+
+ // If there are constants in the sensitivity list, replace them with an intermediate wire
+ if (ff.has_sr) {
+ if (ff.sig_set[i].wire == NULL)
{
- 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;
+ sig_set_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_set_name.c_str());
+ dump_const(f, ff.sig_set[i].data);
+ f << stringf(";\n");
}
- 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
+ if (ff.sig_clr[i].wire == NULL)
{
- for (int i=0; i<size; i++)
- {
- RTLIL::Const element = cell->parameters[ID::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';
- }
+ sig_clr_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_clr_name.c_str());
+ dump_const(f, ff.sig_clr[i].data);
+ f << stringf(";\n");
}
-
- }
- else
- {
- f << stringf("%s" "initial begin\n", indent.c_str());
- for (int i=0; i<size; i++)
+ } else if (ff.has_arst) {
+ if (ff.sig_arst[i].wire == NULL)
{
- f << stringf("%s" " %s[%d] = ", indent.c_str(), mem_id.c_str(), i);
- dump_const(f, cell->parameters[ID::INIT].extract(i*width, width));
+ sig_arst_name = next_auto_id();
+ f << stringf("%s" "wire %s = ", indent.c_str(), sig_arst_name.c_str());
+ dump_const(f, ff.sig_arst[i].data);
f << stringf(";\n");
}
- f << stringf("%s" "end\n", indent.c_str());
}
- }
- // create a map : "edge clk" -> expressions within that clock domain
- dict<std::string, std::vector<std::string>> clk_to_lof_body;
- clk_to_lof_body[""] = std::vector<std::string>();
- std::string clk_domain_str;
- // create a list of reg declarations
- std::vector<std::string> lof_reg_declarations;
-
- int nread_ports = cell->parameters[ID::RD_PORTS].as_int();
- RTLIL::SigSpec sig_rd_clk, sig_rd_en, sig_rd_data, sig_rd_addr;
- bool use_rd_clk, rd_clk_posedge, rd_transparent;
- // read ports
- for (int i=0; i < nread_ports; i++)
- {
- sig_rd_clk = cell->getPort(ID::RD_CLK).extract(i);
- sig_rd_en = cell->getPort(ID::RD_EN).extract(i);
- sig_rd_data = cell->getPort(ID::RD_DATA).extract(i*width, width);
- sig_rd_addr = cell->getPort(ID::RD_ADDR).extract(i*abits, abits);
- use_rd_clk = cell->parameters[ID::RD_CLK_ENABLE].extract(i).as_bool();
- rd_clk_posedge = cell->parameters[ID::RD_CLK_POLARITY].extract(i).as_bool();
- rd_transparent = cell->parameters[ID::RD_TRANSPARENT].extract(i).as_bool();
- if (use_rd_clk)
+ dump_attributes(f, indent, cell->attributes);
+ if (ff.has_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>();
+ // FFs.
+ f << stringf("%s" "always%s @(%sedge ", indent.c_str(), systemverilog ? "_ff" : "", ff.pol_clk ? "pos" : "neg");
+ dump_sigspec(f, ff.sig_clk);
+ if (ff.has_sr) {
+ f << stringf(", %sedge ", ff.pol_set ? "pos" : "neg");
+ if (ff.sig_set[i].wire == NULL)
+ f << stringf("%s", sig_set_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_set[i]);
+
+ f << stringf(", %sedge ", ff.pol_clr ? "pos" : "neg");
+ if (ff.sig_clr[i].wire == NULL)
+ f << stringf("%s", sig_clr_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_clr[i]);
+
+ } else if (ff.has_arst) {
+ f << stringf(", %sedge ", ff.pol_arst ? "pos" : "neg");
+ if (ff.sig_arst[i].wire == NULL)
+ f << stringf("%s", sig_arst_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_arst);
}
- 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()) );
- {
- 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);
- }
+ f << stringf(")\n");
+
+ f << stringf("%s" " ", indent.c_str());
+ if (ff.has_sr) {
+ f << stringf("if (%s", ff.pol_clr ? "" : "!");
+ if (ff.sig_clr[i].wire == NULL)
+ f << stringf("%s", sig_clr_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_clr[i]);
+ f << stringf(") %s <= 1'b0;\n", reg_bit_name.c_str());
+ f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
+ if (ff.sig_set[i].wire == NULL)
+ f << stringf("%s", sig_set_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_set[i]);
+ f << stringf(") %s <= 1'b1;\n", reg_bit_name.c_str());
+ f << stringf("%s" " else ", indent.c_str());
+ } else if (ff.has_arst) {
+ f << stringf("if (%s", ff.pol_arst ? "" : "!");
+ if (ff.sig_arst[i].wire == NULL)
+ f << stringf("%s", sig_arst_name.c_str());
+ else
+ dump_sigspec(f, ff.sig_arst);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, val_arst);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
}
- else
- {
- // for rd-transparent read-ports make something like:
- // reg [..] temp_id;
- // always @(posedge clk)
- // temp_id <= r_addr;
- // assign r_data = array_reg[temp_id];
- std::string temp_id = next_auto_id();
- lof_reg_declarations.push_back( stringf("reg [%d:0] %s;\n", sig_rd_addr.size() - 1, temp_id.c_str()) );
- {
- std::ostringstream os;
- dump_sigspec(os, sig_rd_addr);
- std::string line = stringf("%s <= %s;\n", temp_id.c_str(), os.str().c_str());
- clk_to_lof_body[clk_domain_str].push_back(line);
+
+ if (ff.has_srst && ff.has_en && ff.ce_over_srst) {
+ f << stringf("if (%s", ff.pol_en ? "" : "!");
+ dump_sigspec(f, ff.sig_en);
+ f << stringf(")\n");
+ f << stringf("%s" " if (%s", indent.c_str(), ff.pol_srst ? "" : "!");
+ dump_sigspec(f, ff.sig_srst);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, val_srst);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
+ } else {
+ if (ff.has_srst) {
+ f << stringf("if (%s", ff.pol_srst ? "" : "!");
+ dump_sigspec(f, ff.sig_srst);
+ f << stringf(") %s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, val_srst);
+ f << stringf(";\n");
+ f << stringf("%s" " else ", indent.c_str());
}
- {
- std::ostringstream os;
- dump_sigspec(os, sig_rd_data);
- 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);
+ if (ff.has_en) {
+ f << stringf("if (%s", ff.pol_en ? "" : "!");
+ dump_sigspec(f, ff.sig_en);
+ f << stringf(") ");
}
}
- } 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);
- }
- }
-
- int nwrite_ports = cell->parameters[ID::WR_PORTS].as_int();
- RTLIL::SigSpec sig_wr_clk, sig_wr_data, sig_wr_addr, sig_wr_en;
- bool wr_clk_posedge;
- // write ports
- for (int i=0; i < nwrite_ports; i++)
- {
- sig_wr_clk = cell->getPort(ID::WR_CLK).extract(i);
- sig_wr_data = cell->getPort(ID::WR_DATA).extract(i*width, width);
- sig_wr_addr = cell->getPort(ID::WR_ADDR).extract(i*abits, abits);
- sig_wr_en = cell->getPort(ID::WR_EN).extract(i*width, width);
- wr_clk_posedge = cell->parameters[ID::WR_CLK_POLARITY].extract(i).as_bool();
- {
- std::ostringstream os;
- dump_sigspec(os, sig_wr_clk);
- clk_domain_str = stringf("%sedge %s", wr_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>();
+ f << stringf("%s <= ", reg_bit_name.c_str());
+ dump_sigspec(f, sig_d);
+ f << stringf(";\n");
}
- // make something like:
- // always @(posedge clk)
- // if (wr_en_bit) memid[w_addr][??] <= w_data[??];
- // ...
- for (int i = 0; i < GetSize(sig_wr_en); i++)
+ else
{
- int start_i = i, width = 1;
- SigBit wen_bit = sig_wr_en[i];
-
- while (i+1 < GetSize(sig_wr_en) && active_sigmap(sig_wr_en[i+1]) == active_sigmap(wen_bit))
- i++, width++;
-
- if (wen_bit == State::S0)
- continue;
-
- std::ostringstream os;
- if (wen_bit != State::S1)
- {
- os << stringf("if (");
- dump_sigspec(os, wen_bit);
- os << stringf(") ");
+ // Latches.
+ f << stringf("%s" "always%s\n", indent.c_str(), systemverilog ? "_latch" : " @*");
+
+ f << stringf("%s" " ", indent.c_str());
+ if (ff.has_sr) {
+ f << stringf("if (%s", ff.pol_clr ? "" : "!");
+ dump_sigspec(f, ff.sig_clr[i]);
+ f << stringf(") %s = 1'b0;\n", reg_bit_name.c_str());
+ f << stringf("%s" " else if (%s", indent.c_str(), ff.pol_set ? "" : "!");
+ dump_sigspec(f, ff.sig_set[i]);
+ f << stringf(") %s = 1'b1;\n", reg_bit_name.c_str());
+ if (ff.has_d)
+ f << stringf("%s" " else ", indent.c_str());
+ } else if (ff.has_arst) {
+ f << stringf("if (%s", ff.pol_arst ? "" : "!");
+ dump_sigspec(f, ff.sig_arst);
+ f << stringf(") %s = ", reg_bit_name.c_str());
+ dump_sigspec(f, val_arst);
+ f << stringf(";\n");
+ if (ff.has_d)
+ f << stringf("%s" " else ", indent.c_str());
+ }
+ if (ff.has_d) {
+ f << stringf("if (%s", ff.pol_en ? "" : "!");
+ dump_sigspec(f, ff.sig_en);
+ f << stringf(") %s = ", reg_bit_name.c_str());
+ dump_sigspec(f, sig_d);
+ f << stringf(";\n");
}
- os << stringf("%s[", mem_id.c_str());
- dump_sigspec(os, sig_wr_addr);
- if (width == GetSize(sig_wr_en))
- os << stringf("] <= ");
- else
- os << stringf("][%d:%d] <= ", i, start_i);
- dump_sigspec(os, sig_wr_data.extract(start_i, width));
- os << stringf(";\n");
- clk_to_lof_body[clk_domain_str].push_back(os.str());
}
}
- // Output Verilog that looks something like this:
- // reg [..] _3_;
- // always @(posedge CLK2) begin
- // _3_ <= memory[D1ADDR];
- // if (A1EN)
- // memory[A1ADDR] <= A1DATA;
- // if (A2EN)
- // memory[A2ADDR] <= A2DATA;
- // ...
- // end
- // always @(negedge CLK1) begin
- // if (C1EN)
- // memory[C1ADDR] <= C1DATA;
- // end
- // ...
- // assign D1DATA = _3_;
- // assign D2DATA <= memory[D2ADDR];
-
- // the reg ... definitions
- for(auto &reg : lof_reg_declarations)
- {
- f << stringf("%s" "%s", indent.c_str(), reg.c_str());
- }
- // the block of expressions by clock domain
- for(auto &pair : clk_to_lof_body)
- {
- std::string clk_domain = pair.first;
- std::vector<std::string> lof_lines = pair.second;
- if( clk_domain != "")
- {
- f << stringf("%s" "always @(%s) begin\n", indent.c_str(), clk_domain.c_str());
- for(auto &line : lof_lines)
- f << stringf("%s%s" "%s", indent.c_str(), indent.c_str(), line.c_str());
- f << stringf("%s" "end\n", indent.c_str());
- }
- else
- {
- // the non-clocked assignments
- for(auto &line : lof_lines)
- f << stringf("%s" "%s", indent.c_str(), line.c_str());
- }
+
+ if (!out_is_reg_wire) {
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, ff.sig_q);
+ f << stringf(" = %s;\n", reg_name.c_str());
}
return true;
@@ -1410,7 +1338,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
if (cell->type.in(ID($assert), ID($assume), ID($cover)))
{
- f << stringf("%s" "always @* if (", indent.c_str());
+ f << stringf("%s" "always%s if (", indent.c_str(), systemverilog ? "_comb" : " @*");
dump_sigspec(f, cell->getPort(ID::EN));
f << stringf(") %s(", cell->type.c_str()+1);
dump_sigspec(f, cell->getPort(ID::A));
@@ -1528,14 +1456,17 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
return true;
}
- // FIXME: $_SR_[PN][PN]_, $_DLATCH_[PN]_, $_DLATCHSR_[PN][PN][PN]_
- // FIXME: $sr, $dlatch, $memrd, $memwr, $fsm
+ // FIXME: $fsm
return false;
}
void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
{
+ // Handled by dump_memory
+ if (cell->type.in(ID($mem), ID($memwr), ID($memrd), ID($meminit)))
+ return;
+
if (cell->type[0] == '$' && !noexpr) {
if (dump_cell_expr(f, indent, cell))
return;
@@ -1602,7 +1533,7 @@ void dump_cell(std::ostream &f, std::string indent, RTLIL::Cell *cell)
}
}
- if (siminit && reg_ct.count(cell->type) && cell->hasPort(ID::Q)) {
+ if (siminit && RTLIL::builtin_ff_cell_types().count(cell->type) && cell->hasPort(ID::Q) && !cell->type.in(ID($ff), ID($_FF_))) {
std::stringstream ss;
dump_reg_init(ss, cell->getPort(ID::Q));
if (!ss.str().empty()) {
@@ -1717,7 +1648,9 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
return;
}
- f << stringf("%s" "always @* begin\n", indent.c_str());
+ f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
+ if (!systemverilog)
+ f << indent + " " << "if (" << id("\\initial") << ") begin end\n";
dump_case_body(f, indent, &proc->root_case, true);
std::string backup_indent = indent;
@@ -1728,11 +1661,11 @@ void dump_process(std::ostream &f, std::string indent, RTLIL::Process *proc, boo
indent = backup_indent;
if (sync->type == RTLIL::STa) {
- f << stringf("%s" "always @* begin\n", indent.c_str());
+ f << stringf("%s" "always%s begin\n", indent.c_str(), systemverilog ? "_comb" : " @*");
} else if (sync->type == RTLIL::STi) {
f << stringf("%s" "initial begin\n", indent.c_str());
} else {
- f << stringf("%s" "always @(", indent.c_str());
+ f << stringf("%s" "always%s @(", indent.c_str(), systemverilog ? "_ff" : "");
if (sync->type == RTLIL::STp || sync->type == RTLIL::ST1)
f << stringf("posedge ");
if (sync->type == RTLIL::STn || sync->type == RTLIL::ST0)
@@ -1810,7 +1743,7 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
std::set<std::pair<RTLIL::Wire*,int>> reg_bits;
for (auto cell : module->cells())
{
- if (!reg_ct.count(cell->type) || !cell->hasPort(ID::Q))
+ if (!RTLIL::builtin_ff_cell_types().count(cell->type) || !cell->hasPort(ID::Q) || cell->type.in(ID($ff), ID($_FF_)))
continue;
RTLIL::SigSpec sig = cell->getPort(ID::Q);
@@ -1850,11 +1783,14 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
}
f << stringf(");\n");
+ if (!systemverilog && !module->processes.empty())
+ f << indent + " " << "reg " << id("\\initial") << " = 0;\n";
+
for (auto w : module->wires())
dump_wire(f, indent + " ", w);
- for (auto it = module->memories.begin(); it != module->memories.end(); ++it)
- dump_memory(f, indent + " ", it->second);
+ for (auto &mem : Mem::get_all_memories(module))
+ dump_memory(f, indent + " ", mem);
for (auto cell : module->cells())
dump_cell(f, indent + " ", cell);
@@ -1873,7 +1809,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") { }
- void help() YS_OVERRIDE
+ void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
@@ -1881,6 +1817,9 @@ struct VerilogBackend : public Backend {
log("\n");
log("Write the current design to a Verilog file.\n");
log("\n");
+ log(" -sv\n");
+ log(" with this option, SystemVerilog constructs like always_comb are used\n");
+ log("\n");
log(" -norename\n");
log(" without this option all internal object names (the ones with a dollar\n");
log(" instead of a backslash prefix) are changed to short names in the\n");
@@ -1953,7 +1892,7 @@ struct VerilogBackend : public Backend {
log("this command is called on a design with RTLIL processes.\n");
log("\n");
}
- void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing Verilog backend.\n");
@@ -1976,37 +1915,14 @@ struct VerilogBackend : public Backend {
auto_name_map.clear();
reg_wires.clear();
- reg_ct.clear();
-
- reg_ct.insert(ID($dff));
- reg_ct.insert(ID($adff));
- reg_ct.insert(ID($dffe));
- reg_ct.insert(ID($dlatch));
-
- reg_ct.insert(ID($_DFF_N_));
- reg_ct.insert(ID($_DFF_P_));
-
- reg_ct.insert(ID($_DFF_NN0_));
- reg_ct.insert(ID($_DFF_NN1_));
- reg_ct.insert(ID($_DFF_NP0_));
- reg_ct.insert(ID($_DFF_NP1_));
- reg_ct.insert(ID($_DFF_PN0_));
- reg_ct.insert(ID($_DFF_PN1_));
- reg_ct.insert(ID($_DFF_PP0_));
- reg_ct.insert(ID($_DFF_PP1_));
-
- reg_ct.insert(ID($_DFFSR_NNN_));
- reg_ct.insert(ID($_DFFSR_NNP_));
- reg_ct.insert(ID($_DFFSR_NPN_));
- reg_ct.insert(ID($_DFFSR_NPP_));
- reg_ct.insert(ID($_DFFSR_PNN_));
- reg_ct.insert(ID($_DFFSR_PNP_));
- reg_ct.insert(ID($_DFFSR_PPN_));
- reg_ct.insert(ID($_DFFSR_PPP_));
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
std::string arg = args[argidx];
+ if (arg == "-sv") {
+ systemverilog = true;
+ continue;
+ }
if (arg == "-norename") {
norename = true;
continue;
@@ -2095,7 +2011,6 @@ struct VerilogBackend : public Backend {
auto_name_map.clear();
reg_wires.clear();
- reg_ct.clear();
}
} VerilogBackend;