aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS37
-rw-r--r--README.md2
-rw-r--r--backends/aiger/xaiger.cc52
-rw-r--r--backends/btor/btor.cc81
-rw-r--r--backends/cxxrtl/Makefile.inc2
-rw-r--r--backends/cxxrtl/cxxrtl.h73
-rw-r--r--backends/cxxrtl/cxxrtl_backend.cc (renamed from backends/cxxrtl/cxxrtl.cc)131
-rw-r--r--backends/cxxrtl/cxxrtl_capi.cc60
-rw-r--r--backends/cxxrtl/cxxrtl_capi.h151
-rw-r--r--backends/cxxrtl/cxxrtl_vcd.h214
-rw-r--r--backends/cxxrtl/cxxrtl_vcd_capi.cc83
-rw-r--r--backends/cxxrtl/cxxrtl_vcd_capi.h107
-rw-r--r--backends/ilang/ilang_backend.cc2
-rw-r--r--backends/json/json.cc4
-rw-r--r--backends/smt2/smtio.py2
-rw-r--r--frontends/aiger/aigerparse.cc7
-rw-r--r--frontends/ast/ast.cc3
-rw-r--r--frontends/ast/ast.h8
-rw-r--r--frontends/ast/genrtlil.cc3
-rw-r--r--frontends/ast/simplify.cc519
-rw-r--r--frontends/ilang/ilang_parser.y3
-rw-r--r--frontends/json/jsonparse.cc7
-rw-r--r--frontends/verilog/verilog_lexer.l9
-rw-r--r--frontends/verilog/verilog_parser.y308
-rw-r--r--kernel/rtlil.cc2
-rw-r--r--kernel/rtlil.h11
-rw-r--r--manual/PRESENTATION_Prog/Makefile5
-rw-r--r--misc/py_wrap_generator.py20
-rw-r--r--passes/fsm/fsm_extract.cc2
-rw-r--r--passes/techmap/abc9_ops.cc85
-rw-r--r--techlibs/common/abc9_unmap.v2
-rw-r--r--tests/arch/ecp5/latches_abc9.ys13
-rw-r--r--tests/arch/intel_alm/.gitignore2
-rw-r--r--tests/arch/xilinx/abc9_dff.ys57
-rw-r--r--tests/arch/xilinx/pmgen_xilinx_srl.ys2
-rw-r--r--tests/arch/xilinx/xilinx_srl.v2
-rw-r--r--tests/svtypes/struct_array.sv22
-rw-r--r--tests/svtypes/struct_simple.sv48
-rw-r--r--tests/svtypes/typedef_struct.sv42
-rw-r--r--tests/svtypes/union_simple.sv72
-rw-r--r--tests/various/abc9.ys3
-rw-r--r--tests/various/attrib07_func_call.v2
-rw-r--r--tests/various/constmsk_testmap.v2
-rw-r--r--tests/various/shregmap.v4
-rw-r--r--tests/verilog/bug2042-sv.ys2
45 files changed, 1938 insertions, 330 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 000000000..a73779920
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,37 @@
+## CODE NOTIFICATIONS
+# Register yourself here to be notified about modifications
+# for any files you have an interest in/know your way around.
+
+# Each line is a file pattern followed by one or more users.
+# Both github usernames and email addresses are supported.
+# Order is important; the last matching pattern takes the most
+# precedence. Previous matches will not be applied.
+
+
+# PATH (can use glob) USERNAME(S)
+
+passes/cmds/scratchpad.cc @nakengelhardt
+frontends/rpc/ @whitequark
+backends/cxxrtl/ @whitequark
+passes/cmds/bugpoint.cc @whitequark
+passes/techmap/flowmap.cc @whitequark
+passes/opt/opt_lut.cc @whitequark
+
+
+## External Contributors
+# Only users with write permission to the repository get review
+# requests automatically, but we add information for other
+# contributors here too, so we know who to ask to take a look.
+# These still override previous lines, so be careful not to
+# accidentally disable any of the above rules.
+
+techlibs/intel_alm/ @ZirconiumX
+
+# pyosys
+misc/*.py @btut
+
+backends/firrtl @ucbjrl @azidar
+
+passes/sat/qbfsat.cc @boqwxp
+passes/cmds/exec.cc @boqwxp
+passes/cmds/printattrs.cc @boqwxp
diff --git a/README.md b/README.md
index c17c0c3b1..770c62459 100644
--- a/README.md
+++ b/README.md
@@ -556,6 +556,8 @@ from SystemVerilog:
- enums are supported (including inside packages)
- but are currently not strongly typed
+- packed structs and unions are supported.
+
- SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether
ports are inputs or outputs are supported.
diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc
index 6b910eecd..8bbadbc91 100644
--- a/backends/aiger/xaiger.cc
+++ b/backends/aiger/xaiger.cc
@@ -85,7 +85,7 @@ struct XAigerWriter
dict<SigBit, SigBit> not_map, alias_map;
dict<SigBit, pair<SigBit, SigBit>> and_map;
vector<SigBit> ci_bits, co_bits;
- dict<SigBit, Cell*> ff_bits;
+ vector<Cell*> ff_list;
dict<SigBit, float> arrival_times;
vector<pair<int, int>> aig_gates;
@@ -156,7 +156,7 @@ struct XAigerWriter
// promote keep wires
for (auto wire : module->wires())
- if (wire->get_bool_attribute(ID::keep))
+ if (wire->get_bool_attribute(ID::keep) || wire->get_bool_attribute(ID::abc9_keep))
sigmap.add(wire);
for (auto wire : module->wires()) {
@@ -232,8 +232,7 @@ struct XAigerWriter
unused_bits.erase(D);
undriven_bits.erase(Q);
alias_map[Q] = D;
- auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell));
- log_assert(r.second);
+ ff_list.emplace_back(cell);
continue;
}
@@ -420,8 +419,7 @@ struct XAigerWriter
aig_map[bit] = 2*aig_m;
}
- for (const auto &i : ff_bits) {
- const Cell *cell = i.second;
+ for (auto cell : ff_list) {
const SigBit &q = sigmap(cell->getPort(ID::Q));
aig_m++, aig_i++;
log_assert(!aig_map.count(q));
@@ -468,8 +466,8 @@ struct XAigerWriter
aig_outputs.push_back(aig);
}
- for (auto &i : ff_bits) {
- const SigBit &d = i.first;
+ for (auto cell : ff_list) {
+ const SigBit &d = sigmap(cell->getPort(ID::D));
aig_o++;
aig_outputs.push_back(aig_map.at(d));
}
@@ -541,16 +539,16 @@ struct XAigerWriter
std::stringstream h_buffer;
auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1);
write_h_buffer(1);
- log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits));
- write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size());
- log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits));
- write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits));
- log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits));
- write_h_buffer(input_bits.size() + ff_bits.size());
- log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits));
- write_h_buffer(output_bits.size() + ff_bits.size());
+ log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits));
+ write_h_buffer(GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits));
+ log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits));
+ write_h_buffer(GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits));
+ log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_list));
+ write_h_buffer(GetSize(input_bits) + GetSize(ff_list));
+ log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_list));
+ write_h_buffer(GetSize(output_bits) + GetSize(ff_list));
log_debug("boxNum = %d\n", GetSize(box_list));
- write_h_buffer(box_list.size());
+ write_h_buffer(GetSize(box_list));
auto write_buffer_float = [](std::stringstream &buffer, float f32) {
buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32));
@@ -564,7 +562,7 @@ struct XAigerWriter
//for (auto bit : output_bits)
// write_o_buffer(0);
- if (!box_list.empty() || !ff_bits.empty()) {
+ if (!box_list.empty() || !ff_list.empty()) {
dict<IdString, std::tuple<int,int,int>> cell_cache;
int box_count = 0;
@@ -601,17 +599,17 @@ struct XAigerWriter
std::stringstream r_buffer;
auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1);
- log_debug("flopNum = %d\n", GetSize(ff_bits));
- write_r_buffer(ff_bits.size());
+ log_debug("flopNum = %d\n", GetSize(ff_list));
+ write_r_buffer(ff_list.size());
std::stringstream s_buffer;
auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1);
- write_s_buffer(ff_bits.size());
+ write_s_buffer(ff_list.size());
dict<SigSpec, int> clk_to_mergeability;
- for (const auto &i : ff_bits) {
- const SigBit &d = i.first;
- const Cell *cell = i.second;
+ for (const auto cell : ff_list) {
+ const SigBit &d = sigmap(cell->getPort(ID::D));
+ const SigBit &q = sigmap(cell->getPort(ID::Q));
SigSpec clk_and_pol{sigmap(cell->getPort(ID::C)), cell->type[6] == 'P' ? State::S1 : State::S0};
auto r = clk_to_mergeability.insert(std::make_pair(clk_and_pol, clk_to_mergeability.size()+1));
@@ -619,8 +617,7 @@ struct XAigerWriter
log_assert(mergeability > 0);
write_r_buffer(mergeability);
- SigBit Q = sigmap(cell->getPort(ID::Q));
- State init = init_map.at(Q, State::Sx);
+ State init = init_map.at(q, State::Sx);
log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init));
if (init == State::S1)
write_s_buffer(1);
@@ -700,8 +697,6 @@ struct XAigerWriter
for (auto wire : module->wires())
{
- SigSpec sig = sigmap(wire);
-
for (int i = 0; i < GetSize(wire); i++)
{
RTLIL::SigBit b(wire, i);
@@ -714,7 +709,6 @@ struct XAigerWriter
if (output_bits.count(b)) {
int o = ordered_outputs.at(b);
output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire));
- continue;
}
}
}
diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc
index 2816d3246..9ac312480 100644
--- a/backends/btor/btor.cc
+++ b/backends/btor/btor.cc
@@ -40,6 +40,7 @@ struct BtorWorker
bool verbose;
bool single_bad;
bool cover_mode;
+ bool print_internal_names;
int next_nid = 1;
int initstate_nid = -1;
@@ -78,7 +79,7 @@ struct BtorWorker
vector<string> info_lines;
dict<int, int> info_clocks;
- void btorf(const char *fmt, ...)
+ void btorf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3))
{
va_list ap;
va_start(ap, fmt);
@@ -86,7 +87,7 @@ struct BtorWorker
va_end(ap);
}
- void infof(const char *fmt, ...)
+ void infof(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3))
{
va_list ap;
va_start(ap, fmt);
@@ -98,6 +99,7 @@ struct BtorWorker
string getinfo(T *obj, bool srcsym = false)
{
string infostr = log_id(obj);
+ if (!srcsym && !print_internal_names && infostr[0] == '$') return "";
if (obj->attributes.count(ID::src)) {
string src = obj->attributes.at(ID::src).decode_string().c_str();
if (srcsym && infostr[0] == '$') {
@@ -117,7 +119,7 @@ struct BtorWorker
infostr += " ; " + src;
}
}
- return infostr;
+ return " " + infostr;
}
void btorf_push(const string &id)
@@ -242,7 +244,7 @@ struct BtorWorker
btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero);
nid = next_nid++;
- btorf("%d ite %d %d %d %d %s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str());
+ btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str());
}
else
{
@@ -250,7 +252,7 @@ struct BtorWorker
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());
+ btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -291,7 +293,7 @@ struct BtorWorker
int sid = get_bv_sid(width);
int nid = next_nid++;
- btorf("%d %c%s %d %d %d %s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
+ btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -317,12 +319,12 @@ struct BtorWorker
if (cell->type == ID($_ANDNOT_)) {
btorf("%d not %d %d\n", nid1, sid, nid_b);
- btorf("%d and %d %d %d %s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str());
+ btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str());
}
if (cell->type == ID($_ORNOT_)) {
btorf("%d not %d %d\n", nid1, sid, nid_b);
- btorf("%d or %d %d %d %s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str());
+ btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str());
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -344,13 +346,13 @@ struct BtorWorker
if (cell->type == ID($_OAI3_)) {
btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c);
- btorf("%d not %d %d %s\n", nid3, sid, nid2, getinfo(cell).c_str());
+ btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str());
}
if (cell->type == ID($_AOI3_)) {
btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c);
- btorf("%d not %d %d %s\n", nid3, sid, nid2, getinfo(cell).c_str());
+ btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str());
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -375,14 +377,14 @@ struct BtorWorker
btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d);
btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2);
- btorf("%d not %d %d %s\n", nid4, sid, nid3, getinfo(cell).c_str());
+ btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str());
}
if (cell->type == ID($_AOI4_)) {
btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b);
btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d);
btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2);
- btorf("%d not %d %d %s\n", nid4, sid, nid3, getinfo(cell).c_str());
+ btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str());
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -414,9 +416,9 @@ struct BtorWorker
int nid = next_nid++;
if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt))) {
- btorf("%d %c%s %d %d %d %s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
+ btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
} else {
- btorf("%d %s %d %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
+ btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -447,7 +449,7 @@ struct BtorWorker
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
int nid = next_nid++;
- btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
+ btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -488,9 +490,9 @@ struct BtorWorker
int nid = next_nid++;
if (btor_op != "not")
- btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
+ btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str());
else
- btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
+ btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -521,11 +523,11 @@ struct BtorWorker
if (cell->type == ID($reduce_xnor)) {
int nid2 = next_nid++;
- btorf("%d %s %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
- btorf("%d not %d %d %d\n", nid2, sid, nid);
+ btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
+ btorf("%d not %d %d\n", nid2, sid, nid);
nid = nid2;
} else {
- btorf("%d %s %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
+ btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str());
}
SigSpec sig = sigmap(cell->getPort(ID::Y));
@@ -560,9 +562,9 @@ struct BtorWorker
int tmp = nid;
nid = next_nid++;
btorf("%d ite %d %d %d %d\n", tmp, sid, nid_s, nid_b, nid_a);
- btorf("%d not %d %d %s\n", nid, sid, tmp, getinfo(cell).c_str());
+ btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell).c_str());
} else {
- btorf("%d ite %d %d %d %d %s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str());
+ btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str());
}
add_nid_sig(nid, sig_y);
@@ -585,7 +587,7 @@ struct BtorWorker
int nid_s = get_sig_nid(sig_s.extract(i));
int nid2 = next_nid++;
if (i == GetSize(sig_s)-1)
- btorf("%d ite %d %d %d %d %s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str());
+ btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str());
else
btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid);
nid = nid2;
@@ -640,7 +642,7 @@ struct BtorWorker
int sid = get_bv_sid(GetSize(sig_q));
int nid = next_nid++;
- if (symbol.empty())
+ if (symbol.empty() || (!print_internal_names && symbol[0] == '$'))
btorf("%d state %d\n", nid, sid);
else
btorf("%d state %d %s\n", nid, sid, log_id(symbol));
@@ -1049,8 +1051,8 @@ struct BtorWorker
return nid;
}
- BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, string info_filename) :
- f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), info_filename(info_filename)
+ BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename) :
+ f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename)
{
if (!info_filename.empty())
infof("name %s\n", log_id(module));
@@ -1073,7 +1075,7 @@ struct BtorWorker
int sid = get_bv_sid(GetSize(sig));
int nid = next_nid++;
- btorf("%d input %d %s\n", nid, sid, getinfo(wire).c_str());
+ btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str());
add_nid_sig(nid, sig);
}
@@ -1097,7 +1099,7 @@ struct BtorWorker
btorf_push(stringf("output %s", log_id(wire)));
int nid = get_sig_nid(wire);
- btorf("%d output %d %s\n", next_nid++, nid, getinfo(wire).c_str());
+ btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire).c_str());
btorf_pop(stringf("output %s", log_id(wire)));
}
@@ -1139,10 +1141,10 @@ struct BtorWorker
bad_properties.push_back(nid_en_and_not_a);
} else {
if (cover_mode) {
- infof("bad %d %s\n", nid_en_and_not_a, getinfo(cell, true).c_str());
+ infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true).c_str());
} else {
int nid = next_nid++;
- btorf("%d bad %d %s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str());
+ btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str());
}
}
@@ -1164,7 +1166,7 @@ struct BtorWorker
bad_properties.push_back(nid_en_and_a);
} else {
int nid = next_nid++;
- btorf("%d bad %d %s\n", nid, nid_en_and_a, getinfo(cell, true).c_str());
+ btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true).c_str());
}
btorf_pop(log_id(cell));
@@ -1185,7 +1187,7 @@ struct BtorWorker
continue;
int this_nid = next_nid++;
- btorf("%d uext %d %d %d %s\n", this_nid, sid, nid, 0, getinfo(wire).c_str());
+ btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire).c_str());
btorf_pop(stringf("wire %s", log_id(wire)));
continue;
@@ -1256,14 +1258,14 @@ struct BtorWorker
}
int nid2 = next_nid++;
- btorf("%d next %d %d %d %s\n", nid2, sid, nid, nid_head, getinfo(cell).c_str());
+ 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());
+ btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str());
}
btorf_pop(stringf("next %s", log_id(cell)));
@@ -1353,10 +1355,13 @@ struct BtorBackend : public Backend {
log(" -i <filename>\n");
log(" Create additional info file with auxiliary information\n");
log("\n");
+ log(" -x\n");
+ 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
{
- bool verbose = false, single_bad = false, cover_mode = false;
+ bool verbose = false, single_bad = false, cover_mode = false, print_internal_names = false;
string info_filename;
log_header(design, "Executing BTOR backend.\n");
@@ -1380,6 +1385,10 @@ struct BtorBackend : public Backend {
info_filename = args[++argidx];
continue;
}
+ if (args[argidx] == "-x") {
+ print_internal_names = true;
+ continue;
+ }
break;
}
extra_args(f, filename, args, argidx);
@@ -1392,7 +1401,7 @@ struct BtorBackend : public Backend {
*f << stringf("; BTOR description generated by %s for module %s.\n",
yosys_version_str, log_id(topmod));
- BtorWorker(*f, topmod, verbose, single_bad, cover_mode, info_filename);
+ BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename);
*f << stringf("; end of yosys output\n");
}
diff --git a/backends/cxxrtl/Makefile.inc b/backends/cxxrtl/Makefile.inc
index f93e65f85..aaa304502 100644
--- a/backends/cxxrtl/Makefile.inc
+++ b/backends/cxxrtl/Makefile.inc
@@ -1,2 +1,2 @@
-OBJS += backends/cxxrtl/cxxrtl.o
+OBJS += backends/cxxrtl/cxxrtl_backend.o
diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h
index 701510b7f..5f74899fd 100644
--- a/backends/cxxrtl/cxxrtl.h
+++ b/backends/cxxrtl/cxxrtl.h
@@ -33,13 +33,15 @@
#include <memory>
#include <sstream>
-// The cxxrtl support library implements compile time specialized arbitrary width arithmetics, as well as provides
+#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.
namespace cxxrtl {
-// All arbitrary-width values in cxxrtl are backed by arrays of unsigned integers called chunks. The chunk size
+// All arbitrary-width values in CXXRTL are backed by arrays of unsigned integers called chunks. The chunk size
// is the same regardless of the value width to simplify manipulating values via FFI interfaces, e.g. driving
// and introspecting the simulation in Python.
//
@@ -49,6 +51,8 @@ namespace cxxrtl {
// invisible to the compiler, (b) we often operate on non-power-of-2 values and have to clear the high bits anyway.
// 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;
+
template<typename T>
struct chunk_traits {
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
@@ -65,7 +69,7 @@ template<size_t Bits>
struct value : public expr_base<value<Bits>> {
static constexpr size_t bits = Bits;
- using chunk = chunk_traits<uint32_t>;
+ using chunk = chunk_traits<chunk_t>;
static constexpr chunk::type msb_mask = (Bits % chunk::bits == 0) ? chunk::mask
: chunk::mask >> (chunk::bits - (Bits % chunk::bits));
@@ -712,6 +716,58 @@ struct metadata {
typedef std::map<std::string, metadata> metadata_map;
+// 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 {
+ enum : uint32_t {
+ VALUE = CXXRTL_VALUE,
+ WIRE = CXXRTL_WIRE,
+ MEMORY = CXXRTL_MEMORY,
+ };
+
+ debug_item(const ::cxxrtl_object &object) : cxxrtl_object(object) {}
+
+ template<size_t Bits>
+ debug_item(value<Bits> &item) {
+ 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;
+ }
+
+ template<size_t Bits>
+ debug_item(wire<Bits> &item) {
+ 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;
+ }
+
+ template<size_t Width>
+ debug_item(memory<Width> &item) {
+ 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;
+ }
+};
+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 module {
module() {}
virtual ~module() {}
@@ -731,11 +787,18 @@ struct module {
} while (commit() && !converged);
return deltas;
}
+
+ virtual void debug_info(debug_items &items, std::string path = "") {}
};
} // namespace cxxrtl
-// Definitions of internal Yosys cells. Other than the functions in this namespace, cxxrtl is fully generic
+// Internal structure used to communicate with the implementation of the C interface.
+typedef struct _cxxrtl_toplevel {
+ std::unique_ptr<cxxrtl::module> module;
+} *cxxrtl_toplevel;
+
+// Definitions of internal Yosys cells. Other than the functions in this namespace, CXXRTL is fully generic
// and indepenent of Yosys implementation details.
//
// The `write_cxxrtl` pass translates internal cells (cells with names that start with `$`) to calls of these
@@ -921,7 +984,7 @@ value<BitsY> sshr_uu(const value<BitsA> &a, const value<BitsB> &b) {
template<size_t BitsY, size_t BitsA, size_t BitsB>
value<BitsY> sshr_su(const value<BitsA> &a, const value<BitsB> &b) {
- return a.template shr(b).template scast<BitsY>();
+ return a.template sshr(b).template scast<BitsY>();
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl_backend.cc
index 0cceecbba..64af5dab8 100644
--- a/backends/cxxrtl/cxxrtl.cc
+++ b/backends/cxxrtl/cxxrtl_backend.cc
@@ -502,6 +502,15 @@ std::string escape_cxx_string(const std::string &input)
return output;
}
+template<class T>
+std::string get_hdl_name(T *object)
+{
+ if (object->has_attribute(ID::hdlname))
+ return object->get_string_attribute(ID::hdlname);
+ else
+ return object->name.str();
+}
+
struct CxxrtlWorker {
bool split_intf = false;
std::string intf_filename;
@@ -516,6 +525,8 @@ struct CxxrtlWorker {
bool run_proc_flatten = false;
bool max_opt_level = false;
+ bool debug_info = false;
+
std::ostringstream f;
std::string indent;
int temporary = 0;
@@ -1593,6 +1604,34 @@ struct CxxrtlWorker {
dec_indent();
}
+ void dump_debug_info_method(RTLIL::Module *module)
+ {
+ inc_indent();
+ f << indent << "assert(path.empty() || path[path.size() - 1] == ' ');\n";
+ for (auto wire : module->wires()) {
+ if (wire->name[0] != '\\')
+ continue;
+ if (localized_wires.count(wire))
+ continue;
+ f << indent << "items.emplace(path + " << escape_cxx_string(get_hdl_name(wire));
+ f << ", debug_item(" << mangle(wire) << "));\n";
+ }
+ 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";
+ }
+ dec_indent();
+ }
+
void dump_metadata_map(const dict<RTLIL::IdString, RTLIL::Const> &metadata_map)
{
if (metadata_map.empty()) {
@@ -1641,6 +1680,12 @@ struct CxxrtlWorker {
dump_commit_method(module);
f << indent << "}\n";
f << "\n";
+ if (debug_info) {
+ f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n";
+ dump_debug_info_method(module);
+ f << indent << "}\n";
+ f << "\n";
+ }
f << indent << "static std::unique_ptr<" << mangle(module);
f << template_params(module, /*is_decl=*/false) << "> ";
f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
@@ -1689,7 +1734,7 @@ struct CxxrtlWorker {
if (cell_module->get_bool_attribute(ID(cxxrtl_blackbox))) {
f << indent << "std::unique_ptr<" << mangle(cell_module) << template_args(cell) << "> ";
f << mangle(cell) << " = " << mangle(cell_module) << template_args(cell);
- f << "::create(" << escape_cxx_string(cell->name.str()) << ", ";
+ f << "::create(" << escape_cxx_string(get_hdl_name(cell)) << ", ";
dump_metadata_map(cell->parameters);
f << ", ";
dump_metadata_map(cell->attributes);
@@ -1703,6 +1748,8 @@ struct CxxrtlWorker {
f << "\n";
f << indent << "bool eval() override;\n";
f << indent << "bool commit() override;\n";
+ if (debug_info)
+ f << indent << "void debug_info(debug_items &items, std::string path = \"\") override;\n";
dec_indent();
f << indent << "}; // struct " << mangle(module) << "\n";
f << "\n";
@@ -1721,10 +1768,17 @@ struct CxxrtlWorker {
dump_commit_method(module);
f << indent << "}\n";
f << "\n";
+ if (debug_info) {
+ f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
+ dump_debug_info_method(module);
+ f << indent << "}\n";
+ f << "\n";
+ }
}
void dump_design(RTLIL::Design *design)
{
+ RTLIL::Module *top_module = nullptr;
std::vector<RTLIL::Module*> modules;
TopoSort<RTLIL::Module*> topo_design;
for (auto module : design->modules()) {
@@ -1734,6 +1788,8 @@ struct CxxrtlWorker {
modules.push_back(module); // cxxrtl blackboxes first
if (module->get_blackbox_attribute() || module->get_bool_attribute(ID(cxxrtl_blackbox)))
continue;
+ if (module->get_bool_attribute(ID::top))
+ top_module = module;
topo_design.node(module);
for (auto cell : module->cells()) {
@@ -1755,6 +1811,25 @@ struct CxxrtlWorker {
f << "#ifndef " << include_guard << "\n";
f << "#define " << include_guard << "\n";
f << "\n";
+ if (top_module != nullptr && debug_info) {
+ f << "#include <backends/cxxrtl/cxxrtl_capi.h>\n";
+ f << "\n";
+ f << "#ifdef __cplusplus\n";
+ f << "extern \"C\" {\n";
+ f << "#endif\n";
+ f << "\n";
+ f << "cxxrtl_toplevel " << design_ns << "_create();\n";
+ f << "\n";
+ f << "#ifdef __cplusplus\n";
+ f << "}\n";
+ f << "#endif\n";
+ f << "\n";
+ } else {
+ f << "// The CXXRTL C API is not available because the design is built without debug information.\n";
+ f << "\n";
+ }
+ f << "#ifdef __cplusplus\n";
+ f << "\n";
f << "#include <backends/cxxrtl/cxxrtl.h>\n";
f << "\n";
f << "using namespace cxxrtl;\n";
@@ -1765,6 +1840,8 @@ struct CxxrtlWorker {
dump_module_intf(module);
f << "} // namespace " << design_ns << "\n";
f << "\n";
+ f << "#endif // __cplusplus\n";
+ f << "\n";
f << "#endif\n";
*intf_f << f.str(); f.str("");
}
@@ -1774,6 +1851,15 @@ struct CxxrtlWorker {
else
f << "#include <backends/cxxrtl/cxxrtl.h>\n";
f << "\n";
+ f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
+ f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
+ f << "#include <backends/cxxrtl/cxxrtl_capi.cc>\n";
+ f << "#endif\n";
+ f << "\n";
+ f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
+ f << "#include <backends/cxxrtl/cxxrtl_vcd_capi.cc>\n";
+ f << "#endif\n";
+ f << "\n";
f << "using namespace cxxrtl_yosys;\n";
f << "\n";
f << "namespace " << design_ns << " {\n";
@@ -1784,6 +1870,17 @@ struct CxxrtlWorker {
dump_module_impl(module);
}
f << "} // namespace " << design_ns << "\n";
+ f << "\n";
+ if (top_module != nullptr && debug_info) {
+ f << "cxxrtl_toplevel " << design_ns << "_create() {\n";
+ inc_indent();
+ f << indent << "return new _cxxrtl_toplevel { ";
+ f << "std::make_unique<" << design_ns << "::" << mangle(top_module) << ">()";
+ f << " };\n";
+ dec_indent();
+ f << "}\n";
+ }
+
*impl_f << f.str(); f.str("");
}
@@ -2120,6 +2217,7 @@ struct CxxrtlWorker {
struct CxxrtlBackend : public Backend {
static const int DEFAULT_OPT_LEVEL = 5;
+ static const int DEFAULT_DEBUG_LEVEL = 1;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
void help() YS_OVERRIDE
@@ -2313,10 +2411,22 @@ struct CxxrtlBackend : public Backend {
log(" -O5\n");
log(" like -O4, and run `proc; flatten` first.\n");
log("\n");
+ log(" -g <level>\n");
+ log(" set the debug level. the default is -g%d. higher debug levels provide\n", DEFAULT_DEBUG_LEVEL);
+ log(" more visibility and generate more code, but do not pessimize evaluation.\n");
+ log("\n");
+ log(" -g0\n");
+ log(" no debug information.\n");
+ log("\n");
+ log(" -g1\n");
+ log(" debug information for non-localized public wires.\n");
+ log("\n");
}
+
void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
int opt_level = DEFAULT_OPT_LEVEL;
+ int debug_level = DEFAULT_DEBUG_LEVEL;
CxxrtlWorker worker;
log_header(design, "Executing CXXRTL backend.\n");
@@ -2332,6 +2442,14 @@ struct CxxrtlBackend : public Backend {
opt_level = std::stoi(args[argidx].substr(2));
continue;
}
+ if (args[argidx] == "-g" && argidx+1 < args.size()) {
+ debug_level = std::stoi(args[++argidx]);
+ continue;
+ }
+ if (args[argidx].substr(0, 2) == "-g" && args[argidx].size() == 3 && isdigit(args[argidx][2])) {
+ debug_level = std::stoi(args[argidx].substr(2));
+ continue;
+ }
if (args[argidx] == "-header") {
worker.split_intf = true;
continue;
@@ -2368,6 +2486,17 @@ struct CxxrtlBackend : public Backend {
log_cmd_error("Invalid optimization level %d.\n", opt_level);
}
+ switch (debug_level) {
+ // the highest level here must match DEFAULT_DEBUG_LEVEL
+ case 1:
+ worker.debug_info = true;
+ YS_FALLTHROUGH
+ case 0:
+ break;
+ default:
+ log_cmd_error("Invalid optimization level %d.\n", opt_level);
+ }
+
std::ofstream intf_f;
if (worker.split_intf) {
if (filename == "<stdout>")
diff --git a/backends/cxxrtl/cxxrtl_capi.cc b/backends/cxxrtl/cxxrtl_capi.cc
new file mode 100644
index 000000000..489d72da5
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_capi.cc
@@ -0,0 +1,60 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.h`.
+
+#include <backends/cxxrtl/cxxrtl.h>
+#include <backends/cxxrtl/cxxrtl_capi.h>
+
+struct _cxxrtl_handle {
+ std::unique_ptr<cxxrtl::module> module;
+ cxxrtl::debug_items objects;
+};
+
+// Private function for use by other units of the C API.
+const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle) {
+ return handle->objects;
+}
+
+cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design) {
+ cxxrtl_handle handle = new _cxxrtl_handle;
+ handle->module = std::move(design->module);
+ handle->module->debug_info(handle->objects);
+ delete design;
+ return handle;
+}
+
+void cxxrtl_destroy(cxxrtl_handle handle) {
+ delete handle;
+}
+
+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;
+}
+
+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));
+}
diff --git a/backends/cxxrtl/cxxrtl_capi.h b/backends/cxxrtl/cxxrtl_capi.h
new file mode 100644
index 000000000..bee5a94c7
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_capi.h
@@ -0,0 +1,151 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CXXRTL_CAPI_H
+#define CXXRTL_CAPI_H
+
+// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.cc`.
+//
+// The CXXRTL C API makes it possible to drive CXXRTL designs using C or any other language that
+// supports the C ABI, for example, Python. It does not provide a way to implement black boxes.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opaque reference to a design toplevel.
+//
+// A design toplevel can only be used to create a design handle.
+typedef struct _cxxrtl_toplevel *cxxrtl_toplevel;
+
+// The constructor for a design toplevel is provided as a part of generated code for that design.
+// Its prototype matches:
+//
+// cxxrtl_toplevel <design-name>_create();
+
+// Opaque reference to a design handle.
+//
+// A design handle is required by all operations in the C API.
+typedef struct _cxxrtl_handle *cxxrtl_handle;
+
+// Create a design handle from a design toplevel.
+//
+// The `design` is consumed by this operation and cannot be used afterwards.
+cxxrtl_handle cxxrtl_create(cxxrtl_toplevel design);
+
+// Release all resources used by a design and its handle.
+void cxxrtl_destroy(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.
+enum cxxrtl_type {
+ // Values correspond to singly buffered netlist nodes, i.e. nodes driven exclusively by
+ // combinatorial cells, or toplevel input nodes.
+ //
+ // Values can be inspected via the `curr` pointer and modified via the `next` pointer (which are
+ // equal for values); however, note that changes to the bits driven by combinatorial cells will
+ // be ignored.
+ //
+ // Values always have depth 1.
+ 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.
+ //
+ // Wires can be inspected via the `curr` pointer and modified via the `next` pointer (which are
+ // distinct for wires); however, note that changes to the bits driven by combinatorial cells will
+ // be ignored.
+ //
+ // Wires always have depth 1.
+ CXXRTL_WIRE = 1,
+
+ // Memories correspond to memory cells.
+ //
+ // Memories can be inspected and modified via the `curr` pointer. Due to a limitation of this
+ // API, memories cannot yet be modified in a guaranteed race-free way, and the `next` pointer is
+ // always NULL.
+ CXXRTL_MEMORY = 2,
+
+ // More object types will be added in the future, but the existing ones will never change.
+};
+
+// Description of a simulated object.
+//
+// The `data` array can be accessed directly to inspect and, if applicable, modify the bits
+// stored in the object.
+struct cxxrtl_object {
+ // Type of the object.
+ //
+ // All objects have the same memory layout determined by `width` and `depth`, but the type
+ // determines all other properties of the object.
+ uint32_t type; // actually `enum cxxrtl_type`
+
+ // Width of the object in bits.
+ size_t width;
+
+ // Depth of the object. Only meaningful for memories; for other objects, always 1.
+ size_t depth;
+
+ // 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
+ // the simulation code, and must be always written as 0 when modified by user code.
+ // In memories, every element is stored contiguously. Therefore, the total number of chunks
+ // in any object is `((width + 31) / 32) * depth`.
+ //
+ // To allow the simulation to be partitioned into multiple independent units communicating
+ // through wires, the bits are double buffered. To avoid race conditions, user code should
+ // always read from `curr` and write to `next`. The `curr` pointer is always valid; for objects
+ // that cannot be modified, or cannot be modified in a race-free way, `next` is NULL.
+ uint32_t *curr;
+ uint32_t *next;
+
+ // More description fields will be added in the future, but the existing ones will never change.
+};
+
+// Retrieve description of a simulated object.
+//
+// The `name` is the full hierarchical name of the object in the Yosys notation, where public names
+// have a `\` prefix and hierarchy levels are separated by single spaces. For example, if
+// 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);
+
+// 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.
+// 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));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/backends/cxxrtl/cxxrtl_vcd.h b/backends/cxxrtl/cxxrtl_vcd.h
new file mode 100644
index 000000000..5706917ca
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_vcd.h
@@ -0,0 +1,214 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CXXRTL_VCD_H
+#define CXXRTL_VCD_H
+
+#include <backends/cxxrtl/cxxrtl.h>
+
+namespace cxxrtl {
+
+class vcd_writer {
+ struct variable {
+ size_t ident;
+ size_t width;
+ chunk_t *curr;
+ size_t prev_off;
+ };
+
+ std::vector<std::string> current_scope;
+ std::vector<variable> variables;
+ std::vector<chunk_t> cache;
+ bool streaming = false;
+
+ void emit_timescale(unsigned number, const std::string &unit) {
+ assert(!streaming);
+ assert(number == 1 || number == 10 || number == 100);
+ assert(unit == "s" || unit == "ms" || unit == "us" ||
+ unit == "ns" || unit == "ps" || unit == "fs");
+ buffer += "$timescale " + std::to_string(number) + " " + unit + " $end\n";
+ }
+
+ void emit_scope(const std::vector<std::string> &scope) {
+ assert(!streaming);
+ while (current_scope.size() > scope.size() ||
+ (current_scope.size() > 0 &&
+ current_scope[current_scope.size() - 1] != scope[current_scope.size() - 1])) {
+ buffer += "$upscope $end\n";
+ current_scope.pop_back();
+ }
+ while (current_scope.size() < scope.size()) {
+ buffer += "$scope module " + scope[current_scope.size()] + " $end\n";
+ current_scope.push_back(scope[current_scope.size()]);
+ }
+ }
+
+ void emit_ident(size_t ident) {
+ do {
+ buffer += '!' + ident % 94; // "base94"
+ ident /= 94;
+ } while (ident != 0);
+ }
+
+ void emit_var(const variable &var, const std::string &type, const std::string &name) {
+ assert(!streaming);
+ buffer += "$var " + type + " " + std::to_string(var.width) + " ";
+ emit_ident(var.ident);
+ buffer += " " + name + " $end\n";
+ }
+
+ void emit_enddefinitions() {
+ assert(!streaming);
+ buffer += "$enddefinitions $end\n";
+ streaming = true;
+ }
+
+ void emit_time(uint64_t timestamp) {
+ assert(streaming);
+ buffer += "#" + std::to_string(timestamp) + "\n";
+ }
+
+ void emit_scalar(const variable &var) {
+ assert(streaming);
+ assert(var.width == 1);
+ buffer += (*var.curr ? '1' : '0');
+ emit_ident(var.ident);
+ buffer += '\n';
+ }
+
+ void emit_vector(const variable &var) {
+ assert(streaming);
+ buffer += 'b';
+ for (size_t bit = var.width - 1; bit != (size_t)-1; bit--) {
+ bool bit_curr = var.curr[bit / (8 * sizeof(chunk_t))] & (1 << (bit % (8 * sizeof(chunk_t))));
+ buffer += (bit_curr ? '1' : '0');
+ }
+ buffer += ' ';
+ emit_ident(var.ident);
+ buffer += '\n';
+ }
+
+ void append_variable(size_t width, chunk_t *curr) {
+ const size_t chunks = (width + (sizeof(chunk_t) * 8 - 1)) / (sizeof(chunk_t) * 8);
+ variables.emplace_back(variable { variables.size(), width, curr, cache.size() });
+ cache.insert(cache.end(), &curr[0], &curr[chunks]);
+ }
+
+ bool test_variable(const variable &var) {
+ 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;
+ } else {
+ std::copy(&var.curr[0], &var.curr[chunks], &cache[var.prev_off]);
+ return true;
+ }
+ }
+
+ static std::vector<std::string> split_hierarchy(const std::string &hier_name) {
+ std::vector<std::string> hierarchy;
+ size_t prev = 0;
+ while (true) {
+ size_t curr = hier_name.find_first_of(' ', prev + 1);
+ if (curr > hier_name.size())
+ curr = hier_name.size();
+ if (curr > prev + 1)
+ hierarchy.push_back(hier_name.substr(prev, curr - prev));
+ if (curr == hier_name.size())
+ break;
+ prev = curr + 1;
+ }
+ return hierarchy;
+ }
+
+public:
+ std::string buffer;
+
+ void timescale(unsigned number, const std::string &unit) {
+ emit_timescale(number, unit);
+ }
+
+ void add(const std::string &hier_name, const debug_item &item) {
+ std::vector<std::string> scope = split_hierarchy(hier_name);
+ std::string name = scope.back();
+ scope.pop_back();
+
+ emit_scope(scope);
+ switch (item.type) {
+ // Not the best naming but oh well...
+ case debug_item::VALUE:
+ append_variable(item.width, item.curr);
+ emit_var(variables.back(), "wire", name);
+ break;
+ case debug_item::WIRE:
+ append_variable(item.width, item.curr);
+ emit_var(variables.back(), "reg", name);
+ 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) + ']';
+ append_variable(item.width, nth_curr);
+ emit_var(variables.back(), "reg", nth_name);
+ }
+ break;
+ }
+ }
+ }
+
+ template<class Filter>
+ 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);
+ }
+
+ void add(const debug_items &items) {
+ this->template add(items, [](const std::string &, const debug_item &) {
+ return true;
+ });
+ }
+
+ 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;
+ });
+ }
+
+ void sample(uint64_t timestamp) {
+ bool first_sample = !streaming;
+ if (first_sample) {
+ emit_scope({});
+ emit_enddefinitions();
+ }
+ emit_time(timestamp);
+ for (auto var : variables)
+ if (test_variable(var) || first_sample) {
+ if (var.width == 1)
+ emit_scalar(var);
+ else
+ emit_vector(var);
+ }
+ }
+};
+
+}
+
+#endif
diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.cc b/backends/cxxrtl/cxxrtl_vcd_capi.cc
new file mode 100644
index 000000000..46e4f1c45
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_vcd_capi.cc
@@ -0,0 +1,83 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.h`.
+
+#include <backends/cxxrtl/cxxrtl_vcd.h>
+#include <backends/cxxrtl/cxxrtl_vcd_capi.h>
+
+extern const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle);
+
+struct _cxxrtl_vcd {
+ cxxrtl::vcd_writer writer;
+ bool flush = false;
+};
+
+cxxrtl_vcd cxxrtl_vcd_create() {
+ return new _cxxrtl_vcd;
+}
+
+void cxxrtl_vcd_destroy(cxxrtl_vcd vcd) {
+ delete vcd;
+}
+
+void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit) {
+ vcd->writer.timescale(number, unit);
+}
+
+void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, cxxrtl_object *object) {
+ // Note the copy. We don't know whether `object` came from a design (in which case it is
+ // an instance of `debug_item`), or from user code (in which case it is an instance of
+ // `cxxrtl_object`), so casting the pointer wouldn't be safe.
+ vcd->writer.add(name, debug_item(*object));
+}
+
+void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle) {
+ vcd->writer.add(cxxrtl_debug_items_from_handle(handle));
+}
+
+void cxxrtl_vcd_add_from_if(cxxrtl_vcd vcd, cxxrtl_handle handle, void *data,
+ int (*filter)(void *data, const char *name,
+ const cxxrtl_object *object)) {
+ vcd->writer.add(cxxrtl_debug_items_from_handle(handle),
+ [=](const std::string &name, const debug_item &item) {
+ return filter(data, name.c_str(), static_cast<const cxxrtl_object*>(&item));
+ });
+}
+
+void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle) {
+ vcd->writer.add_without_memories(cxxrtl_debug_items_from_handle(handle));
+}
+
+void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time) {
+ if (vcd->flush) {
+ vcd->writer.buffer.clear();
+ vcd->flush = false;
+ }
+ vcd->writer.sample(time);
+}
+
+void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size) {
+ if (vcd->flush) {
+ vcd->writer.buffer.clear();
+ vcd->flush = false;
+ }
+ *data = vcd->writer.buffer.c_str();
+ *size = vcd->writer.buffer.size();
+ vcd->flush = true;
+}
diff --git a/backends/cxxrtl/cxxrtl_vcd_capi.h b/backends/cxxrtl/cxxrtl_vcd_capi.h
new file mode 100644
index 000000000..6a7fb9f47
--- /dev/null
+++ b/backends/cxxrtl/cxxrtl_vcd_capi.h
@@ -0,0 +1,107 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 whitequark <whitequark@whitequark.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CXXRTL_VCD_CAPI_H
+#define CXXRTL_VCD_CAPI_H
+
+// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.cc`.
+//
+// The CXXRTL C API for VCD writing makes it possible to insert virtual probes into designs and
+// dump waveforms to Value Change Dump files.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <backends/cxxrtl/cxxrtl_capi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Opaque reference to a VCD writer.
+typedef struct _cxxrtl_vcd *cxxrtl_vcd;
+
+// Create a VCD writer.
+cxxrtl_vcd cxxrtl_vcd_create();
+
+// Release all resources used by a VCD writer.
+void cxxrtl_vcd_destroy(cxxrtl_vcd vcd);
+
+// Set VCD timescale.
+//
+// The `number` must be 1, 10, or 100, and the `unit` must be one of `"s"`, `"ms"`, `"us"`, `"ns"`,
+// `"ps"`, or `"fs"`.
+//
+// Timescale can only be set before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_timescale(cxxrtl_vcd vcd, int number, const char *unit);
+
+// Schedule a specific CXXRTL object to be sampled.
+//
+// The `name` is a full hierarchical name as described for `cxxrtl_get`; it does not need to match
+// the original name of `object`, if any. The `object` must outlive the VCD writer, but there are
+// no other requirements; if desired, it can be provided by user code, rather than come from
+// a design.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add(cxxrtl_vcd vcd, const char *name, struct cxxrtl_object *object);
+
+// Schedule all CXXRTL objects in a simulation.
+//
+// The design `handle` must outlive the VCD writer.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add_from(cxxrtl_vcd vcd, cxxrtl_handle handle);
+
+// Schedule CXXRTL objects in a simulation that match a given predicate.
+//
+// For every object in the simulation, `filter` is called with the provided `data`, the full
+// hierarchical name of the object (see `cxxrtl_get` for details), and the object description.
+// The object will be sampled if the predicate returns a non-zero value.
+//
+// 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));
+
+// Schedule all CXXRTL objects in a simulation except for memories.
+//
+// The design `handle` must outlive the VCD writer.
+//
+// Objects can only be scheduled before the first call to `cxxrtl_vcd_sample`.
+void cxxrtl_vcd_add_from_without_memories(cxxrtl_vcd vcd, cxxrtl_handle handle);
+
+// Sample all scheduled objects.
+//
+// First, `time` is written to the internal buffer. Second, the values of every signal changed since
+// the previous call to `cxxrtl_vcd_sample` (all values if this is the first call) are written to
+// the internal buffer. The contents of the buffer can be retrieved with `cxxrtl_vcd_read`.
+void cxxrtl_vcd_sample(cxxrtl_vcd vcd, uint64_t time);
+
+// Retrieve buffered VCD data.
+//
+// The pointer to the start of the next chunk of VCD data is assigned to `*data`, and the length
+// of that chunk is assigned to `*size`. The pointer to the data is valid until the next call to
+// `cxxrtl_vcd_sample` or `cxxrtl_vcd_read`. Once all of the buffered data has been retrieved,
+// this function will always return zero sized chunks.
+void cxxrtl_vcd_read(cxxrtl_vcd vcd, const char **data, size_t *size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/backends/ilang/ilang_backend.cc b/backends/ilang/ilang_backend.cc
index 6e3882d2d..3a418de3c 100644
--- a/backends/ilang/ilang_backend.cc
+++ b/backends/ilang/ilang_backend.cc
@@ -131,6 +131,8 @@ void ILANG_BACKEND::dump_wire(std::ostream &f, std::string indent, const RTLIL::
f << stringf("output %d ", wire->port_id);
if (wire->port_input && wire->port_output)
f << stringf("inout %d ", wire->port_id);
+ if (wire->is_signed)
+ f << stringf("signed ");
f << stringf("%s\n", wire->name.c_str());
}
diff --git a/backends/json/json.cc b/backends/json/json.cc
index 1a8b757ef..5edc50f60 100644
--- a/backends/json/json.cc
+++ b/backends/json/json.cc
@@ -160,6 +160,8 @@ struct JsonWriter
f << stringf(" \"offset\": %d,\n", w->start_offset);
if (w->upto)
f << stringf(" \"upto\": 1,\n");
+ if (w->is_signed)
+ f << stringf(" \"signed\": %d,\n", w->is_signed);
f << stringf(" \"bits\": %s\n", get_bits(w).c_str());
f << stringf(" }");
first = false;
@@ -227,6 +229,8 @@ struct JsonWriter
f << stringf(" \"offset\": %d,\n", w->start_offset);
if (w->upto)
f << stringf(" \"upto\": 1,\n");
+ if (w->is_signed)
+ f << stringf(" \"signed\": %d,\n", w->is_signed);
f << stringf(" \"attributes\": {");
write_parameters(w->attributes);
f << stringf("\n }\n");
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 9f7c8c6d9..72ab39d39 100644
--- a/backends/smt2/smtio.py
+++ b/backends/smt2/smtio.py
@@ -39,7 +39,7 @@ if os.name == "posix":
smtio_stacksize = 128 * 1024 * 1024
if os.uname().sysname == "Darwin":
# MacOS has rather conservative stack limits
- smtio_stacksize = 16 * 1024 * 1024
+ smtio_stacksize = 8 * 1024 * 1024
if current_rlimit_stack[1] != resource.RLIM_INFINITY:
smtio_stacksize = min(smtio_stacksize, current_rlimit_stack[1])
if current_rlimit_stack[0] < smtio_stacksize:
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc
index d25587e48..fef788267 100644
--- a/frontends/aiger/aigerparse.cc
+++ b/frontends/aiger/aigerparse.cc
@@ -775,7 +775,6 @@ void AigerReader::post_process()
}
}
- dict<int, Wire*> mergeability_to_clock;
for (uint32_t i = 0; i < flopNum; i++) {
RTLIL::Wire *d = outputs[outputs.size() - flopNum + i];
log_assert(d);
@@ -895,7 +894,9 @@ void AigerReader::post_process()
}
else if (type == "box") {
RTLIL::Cell* cell = module->cell(stringf("$box%d", variable));
- if (cell) // ABC could have optimised this box away
+ if (!cell)
+ log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s));
+ else
module->rename(cell, escaped_s);
}
else
@@ -907,6 +908,8 @@ void AigerReader::post_process()
auto name = wp.first;
int min = wp.second.first;
int max = wp.second.second;
+ if (min == 0 && max == 0)
+ continue;
RTLIL::Wire *wire = module->wire(name);
if (wire)
diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc
index 689fa9fb4..03fd272da 100644
--- a/frontends/ast/ast.cc
+++ b/frontends/ast/ast.cc
@@ -171,6 +171,9 @@ std::string AST::type2str(AstNodeType type)
X(AST_PACKAGE)
X(AST_WIRETYPE)
X(AST_TYPEDEF)
+ X(AST_STRUCT)
+ X(AST_UNION)
+ X(AST_STRUCT_ITEM)
#undef X
default:
log_abort();
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h
index 8932108e3..6d556fae2 100644
--- a/frontends/ast/ast.h
+++ b/frontends/ast/ast.h
@@ -143,7 +143,7 @@ namespace AST
AST_GENCASE,
AST_GENBLOCK,
AST_TECALL,
-
+
AST_POSEDGE,
AST_NEGEDGE,
AST_EDGE,
@@ -156,7 +156,10 @@ namespace AST
AST_PACKAGE,
AST_WIRETYPE,
- AST_TYPEDEF
+ AST_TYPEDEF,
+ AST_STRUCT,
+ AST_UNION,
+ AST_STRUCT_ITEM
};
struct AstSrcLocType {
@@ -306,6 +309,7 @@ namespace AST
// helpers for enum
void allocateDefaultEnumValues();
+ void annotateTypedEnums(AstNode *template_node);
};
// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc
index cdc3adc9c..9546558aa 100644
--- a/frontends/ast/genrtlil.cc
+++ b/frontends/ast/genrtlil.cc
@@ -991,6 +991,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
case AST_MODPORT:
case AST_MODPORTMEMBER:
case AST_TYPEDEF:
+ case AST_STRUCT:
+ case AST_UNION:
break;
case AST_INTERFACEPORT: {
// If a port in a module with unknown type is found, mark it with the attribute 'is_interface'
@@ -1065,6 +1067,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
wire->port_input = is_input;
wire->port_output = is_output;
wire->upto = range_swapped;
+ wire->is_signed = is_signed;
for (auto &attr : attributes) {
if (attr.second->type != AST_CONSTANT)
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc
index 3d690c1f5..6970135d0 100644
--- a/frontends/ast/simplify.cc
+++ b/frontends/ast/simplify.cc
@@ -168,6 +168,321 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg
}
+void AstNode::annotateTypedEnums(AstNode *template_node)
+{
+ //check if enum
+ if (template_node->attributes.count(ID::enum_type)) {
+ //get reference to enum node:
+ std::string enum_type = template_node->attributes[ID::enum_type]->str.c_str();
+ // log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type));
+ // log("current scope:\n");
+ // for (auto &it : current_scope)
+ // log(" %s\n", it.first.c_str());
+ log_assert(current_scope.count(enum_type) == 1);
+ AstNode *enum_node = current_scope.at(enum_type);
+ log_assert(enum_node->type == AST_ENUM);
+ //get width from 1st enum item:
+ log_assert(enum_node->children.size() >= 1);
+ AstNode *enum_item0 = enum_node->children[0];
+ log_assert(enum_item0->type == AST_ENUM_ITEM);
+ int width;
+ if (!enum_item0->range_valid)
+ width = 1;
+ else if (enum_item0->range_swapped)
+ width = enum_item0->range_right - enum_item0->range_left + 1;
+ else
+ width = enum_item0->range_left - enum_item0->range_right + 1;
+ log_assert(width > 0);
+ //add declared enum items:
+ for (auto enum_item : enum_node->children){
+ log_assert(enum_item->type == AST_ENUM_ITEM);
+ //get is_signed
+ bool is_signed;
+ if (enum_item->children.size() == 1){
+ is_signed = false;
+ } else if (enum_item->children.size() == 2){
+ log_assert(enum_item->children[1]->type == AST_RANGE);
+ is_signed = enum_item->children[1]->is_signed;
+ } else {
+ log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n",
+ enum_item->children.size(),
+ enum_item->str.c_str(), enum_node->str.c_str()
+ );
+ }
+ //start building attribute string
+ std::string enum_item_str = "\\enum_value_";
+ //get enum item value
+ if(enum_item->children[0]->type != AST_CONSTANT){
+ log_error("expected const, got %s for %s (%s)\n",
+ type2str(enum_item->children[0]->type).c_str(),
+ enum_item->str.c_str(), enum_node->str.c_str()
+ );
+ }
+ RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed);
+ enum_item_str.append(val.as_string());
+ //set attribute for available val to enum item name mappings
+ attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str);
+ }
+ }
+}
+
+static bool name_has_dot(const std::string &name, std::string &struct_name)
+{
+ // check if plausible struct member name \sss.mmm
+ std::string::size_type pos;
+ if (name.substr(0, 1) == "\\" && (pos = name.find('.', 0)) != std::string::npos) {
+ struct_name = name.substr(0, pos);
+ return true;
+ }
+ return false;
+}
+
+static AstNode *make_range(int left, int right, bool is_signed = false)
+{
+ // generate a pre-validated range node for a fixed signal range.
+ auto range = new AstNode(AST_RANGE);
+ range->range_left = left;
+ range->range_right = right;
+ range->range_valid = true;
+ range->children.push_back(AstNode::mkconst_int(left, true));
+ range->children.push_back(AstNode::mkconst_int(right, true));
+ range->is_signed = is_signed;
+ return range;
+}
+
+static int range_width(AstNode *node, AstNode *rnode)
+{
+ log_assert(rnode->type==AST_RANGE);
+ if (!rnode->range_valid) {
+ log_file_error(node->filename, node->location.first_line, "Size must be constant in packed struct/union member %s\n", node->str.c_str());
+
+ }
+ // note: range swapping has already been checked for
+ return rnode->range_left - rnode->range_right + 1;
+}
+
+[[noreturn]] static void struct_array_packing_error(AstNode *node)
+{
+ log_file_error(node->filename, node->location.first_line, "Unpacked array in packed struct/union member %s\n", node->str.c_str());
+}
+
+static void save_struct_array_width(AstNode *node, int width)
+{
+ // stash the stride for the array
+ node->multirange_dimensions.push_back(width);
+
+}
+
+static int get_struct_array_width(AstNode *node)
+{
+ // the stride for the array, 1 if not an array
+ return (node->multirange_dimensions.empty() ? 1 : node->multirange_dimensions.back());
+
+}
+
+static int size_packed_struct(AstNode *snode, int base_offset)
+{
+ // Struct members will be laid out in the structure contiguously from left to right.
+ // Union members all have zero offset from the start of the union.
+ // Determine total packed size and assign offsets. Store these in the member node.
+ bool is_union = (snode->type == AST_UNION);
+ int offset = 0;
+ int packed_width = -1;
+ // examine members from last to first
+ for (auto it = snode->children.rbegin(); it != snode->children.rend(); ++it) {
+ auto node = *it;
+ int width;
+ if (node->type == AST_STRUCT || node->type == AST_UNION) {
+ // embedded struct or union
+ width = size_packed_struct(node, base_offset + offset);
+ }
+ else {
+ log_assert(node->type == AST_STRUCT_ITEM);
+ if (node->children.size() > 0 && node->children[0]->type == AST_RANGE) {
+ // member width e.g. bit [7:0] a
+ width = range_width(node, node->children[0]);
+ if (node->children.size() == 2) {
+ if (node->children[1]->type == AST_RANGE) {
+ // unpacked array e.g. bit [63:0] a [0:3]
+ auto rnode = node->children[1];
+ int array_count = range_width(node, rnode);
+ if (array_count == 1) {
+ // C-type array size e.g. bit [63:0] a [4]
+ array_count = rnode->range_left;
+ }
+ save_struct_array_width(node, width);
+ width *= array_count;
+ }
+ else {
+ // array element must be single bit for a packed array
+ struct_array_packing_error(node);
+ }
+ }
+ // range nodes are now redundant
+ node->children.clear();
+ }
+ else if (node->children.size() == 1 && node->children[0]->type == AST_MULTIRANGE) {
+ // packed 2D array, e.g. bit [3:0][63:0] a
+ auto rnode = node->children[0];
+ if (rnode->children.size() != 2) {
+ // packed arrays can only be 2D
+ struct_array_packing_error(node);
+ }
+ int array_count = range_width(node, rnode->children[0]);
+ width = range_width(node, rnode->children[1]);
+ save_struct_array_width(node, width);
+ width *= array_count;
+ // range nodes are now redundant
+ node->children.clear();
+ }
+ else if (node->range_left < 0) {
+ // 1 bit signal: bit, logic or reg
+ width = 1;
+ }
+ else {
+ // already resolved and compacted
+ width = node->range_left - node->range_right + 1;
+ }
+ if (is_union) {
+ node->range_right = base_offset;
+ node->range_left = base_offset + width - 1;
+ }
+ else {
+ node->range_right = base_offset + offset;
+ node->range_left = base_offset + offset + width - 1;
+ }
+ node->range_valid = true;
+ }
+ if (is_union) {
+ // check that all members have the same size
+ if (packed_width == -1) {
+ // first member
+ packed_width = width;
+ }
+ else {
+ if (packed_width != width) {
+
+ log_file_error(node->filename, node->location.first_line, "member %s of a packed union has %d bits, expecting %d\n", node->str.c_str(), width, packed_width);
+ }
+ }
+ }
+ else {
+ offset += width;
+ }
+ }
+ return (is_union ? packed_width : offset);
+}
+
+[[noreturn]] static void struct_op_error(AstNode *node)
+{
+ log_file_error(node->filename, node->location.first_line, "Unsupported operation for struct/union member %s\n", node->str.c_str()+1);
+}
+
+static AstNode *node_int(int ival)
+{
+ // maybe mkconst_int should have default values for the common integer case
+ return AstNode::mkconst_int(ival, true, 32);
+}
+
+static AstNode *offset_indexed_range(int offset_right, int stride, AstNode *left_expr, AstNode *right_expr)
+{
+ // adjust the range expressions to add an offset into the struct
+ // and maybe index using an array stride
+ auto left = left_expr->clone();
+ auto right = right_expr->clone();
+ if (stride == 1) {
+ // just add the offset
+ left = new AstNode(AST_ADD, node_int(offset_right), left);
+ right = new AstNode(AST_ADD, node_int(offset_right), right);
+ }
+ else {
+ // newleft = offset_right - 1 + (left + 1) * stride
+ left = new AstNode(AST_ADD, new AstNode(AST_SUB, node_int(offset_right), node_int(1)),
+ new AstNode(AST_MUL, node_int(stride), new AstNode(AST_ADD, left, node_int(1))));
+ // newright = offset_right + right * stride
+ right = new AstNode(AST_ADD, node_int(offset_right), new AstNode(AST_MUL, right, node_int(stride)));
+ }
+ return new AstNode(AST_RANGE, left, right);
+}
+
+static AstNode *make_struct_member_range(AstNode *node, AstNode *member_node)
+{
+ // Work out the range in the packed array that corresponds to a struct member
+ // taking into account any range operations applicable to the current node
+ // such as array indexing or slicing
+ int range_left = member_node->range_left;
+ int range_right = member_node->range_right;
+ if (node->children.empty()) {
+ // no range operations apply, return the whole width
+ }
+ else if (node->children.size() == 1 && node->children[0]->type == AST_RANGE) {
+ auto rnode = node->children[0];
+ int stride = get_struct_array_width(member_node);
+ if (rnode->children.size() == 1) {
+ // index e.g. s.a[i]
+ return offset_indexed_range(range_right, stride, rnode->children[0], rnode->children[0]);
+ }
+ else if (rnode->children.size() == 2) {
+ // slice e.g. s.a[i:j]
+ return offset_indexed_range(range_right, stride, rnode->children[0], rnode->children[1]);
+ }
+ else {
+ struct_op_error(node);
+ }
+ }
+ else {
+ // TODO multirange, i.e. bit slice after array index s.a[i][p:q]
+ struct_op_error(node);
+ }
+ return make_range(range_left, range_right);
+}
+
+static void add_members_to_scope(AstNode *snode, std::string name)
+{
+ // add all the members in a struct or union to local scope
+ // in case later referenced in assignments
+ log_assert(snode->type==AST_STRUCT || snode->type==AST_UNION);
+ for (auto *node : snode->children) {
+ if (node->type != AST_STRUCT_ITEM) {
+ // embedded struct or union
+ add_members_to_scope(node, name + "." + node->str);
+ }
+ else {
+ auto member_name = name + "." + node->str;
+ current_scope[member_name] = node;
+ }
+ }
+}
+
+static int get_max_offset(AstNode *node)
+{
+ // get the width from the MS member in the struct
+ // as members are laid out from left to right in the packed wire
+ log_assert(node->type==AST_STRUCT || node->type==AST_UNION);
+ while (node->type != AST_STRUCT_ITEM) {
+ node = node->children[0];
+ }
+ return node->range_left;
+}
+
+static AstNode *make_packed_struct(AstNode *template_node, std::string &name)
+{
+ // create a wire for the packed struct
+ auto wnode = new AstNode(AST_WIRE);
+ wnode->str = name;
+ wnode->is_logic = true;
+ wnode->range_valid = true;
+ wnode->is_signed = template_node->is_signed;
+ int offset = get_max_offset(template_node);
+ auto range = make_range(offset, 0);
+ wnode->children.push_back(range);
+ // make sure this node is the one in scope for this name
+ current_scope[name] = wnode;
+ // add all the struct members to scope under the wire's name
+ add_members_to_scope(template_node, name);
+ return wnode;
+}
+
// convert the AST into a simpler AST that has all parameters substituted by their
// values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL().
@@ -567,6 +882,32 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
break;
+ case AST_STRUCT:
+ case AST_UNION:
+ if (!basic_prep) {
+ for (auto *node : children) {
+ // resolve any ranges
+ while (!node->basic_prep && node->simplify(true, false, false, stage, -1, false, false)) {
+ did_something = true;
+ }
+ }
+ // determine member offsets and widths
+ size_packed_struct(this, 0);
+
+ // instance rather than just a type in a typedef or outer struct?
+ if (!str.empty() && str[0] == '\\') {
+ // instance so add a wire for the packed structure
+ auto wnode = make_packed_struct(this, str);
+ log_assert(current_ast_mod);
+ current_ast_mod->children.push_back(wnode);
+ }
+ basic_prep = true;
+ }
+ break;
+
+ case AST_STRUCT_ITEM:
+ break;
+
case AST_ENUM:
//log("\nENUM %s: %d child %d\n", str.c_str(), basic_prep, children[0]->basic_prep);
if (!basic_prep) {
@@ -884,10 +1225,12 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
// resolve typedefs
if (type == AST_TYPEDEF) {
log_assert(children.size() == 1);
- log_assert(children[0]->type == AST_WIRE || children[0]->type == AST_MEMORY);
- while(children[0]->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param))
+ auto type_node = children[0];
+ log_assert(type_node->type == AST_WIRE || type_node->type == AST_MEMORY || type_node->type == AST_STRUCT || type_node->type == AST_UNION);
+ while (type_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {
did_something = true;
- log_assert(!children[0]->is_custom_type);
+ }
+ log_assert(!type_node->is_custom_type);
}
// resolve types of wires
@@ -895,100 +1238,57 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
if (is_custom_type) {
log_assert(children.size() >= 1);
log_assert(children[0]->type == AST_WIRETYPE);
- if (!current_scope.count(children[0]->str))
- log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[0]->str.c_str());
- AstNode *resolved_type = current_scope.at(children[0]->str);
- if (resolved_type->type != AST_TYPEDEF)
- log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[0]->str.c_str());
- log_assert(resolved_type->children.size() == 1);
- AstNode *templ = resolved_type->children[0];
+ auto type_name = children[0]->str;
+ if (!current_scope.count(type_name)) {
+ log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", type_name.c_str());
+ }
+ AstNode *resolved_type_node = current_scope.at(type_name);
+ if (resolved_type_node->type != AST_TYPEDEF)
+ log_file_error(filename, location.first_line, "`%s' does not name a type\n", type_name.c_str());
+ log_assert(resolved_type_node->children.size() == 1);
+ AstNode *template_node = resolved_type_node->children[0];
+
+ // Ensure typedef itself is fully simplified
+ while (template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+
+ if (template_node->type == AST_STRUCT || template_node->type == AST_UNION) {
+ // replace with wire representing the packed structure
+ newNode = make_packed_struct(template_node, str);
+ current_scope[str] = this;
+ goto apply_newNode;
+ }
+
// Remove type reference
delete children[0];
children.erase(children.begin());
- // Ensure typedef itself is fully simplified
- while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
-
if (type == AST_WIRE)
- type = templ->type;
- is_reg = templ->is_reg;
- is_logic = templ->is_logic;
- is_signed = templ->is_signed;
- is_string = templ->is_string;
- is_custom_type = templ->is_custom_type;
-
- range_valid = templ->range_valid;
- range_swapped = templ->range_swapped;
- range_left = templ->range_left;
- range_right = templ->range_right;
- attributes[ID::wiretype] = mkconst_str(resolved_type->str);
- //check if enum
- if (templ->attributes.count(ID::enum_type)){
- //get reference to enum node:
- const std::string &enum_type = templ->attributes[ID::enum_type]->str;
- // log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type));
- // log("current scope:\n");
- // for (auto &it : current_scope)
- // log(" %s\n", it.first.c_str());
- log_assert(current_scope.count(enum_type) == 1);
- AstNode *enum_node = current_scope.at(enum_type);
- log_assert(enum_node->type == AST_ENUM);
- //get width from 1st enum item:
- log_assert(enum_node->children.size() >= 1);
- AstNode *enum_item0 = enum_node->children[0];
- log_assert(enum_item0->type == AST_ENUM_ITEM);
- int width;
- if (!enum_item0->range_valid)
- width = 1;
- else if (enum_item0->range_swapped)
- width = enum_item0->range_right - enum_item0->range_left + 1;
- else
- width = enum_item0->range_left - enum_item0->range_right + 1;
- log_assert(width > 0);
- //add declared enum items:
- for (auto enum_item : enum_node->children){
- log_assert(enum_item->type == AST_ENUM_ITEM);
- //get is_signed
- bool is_signed;
- if (enum_item->children.size() == 1){
- is_signed = false;
- } else if (enum_item->children.size() == 2){
- log_assert(enum_item->children[1]->type == AST_RANGE);
- is_signed = enum_item->children[1]->is_signed;
- } else {
- log_error("enum_item children size==%lu, expected 1 or 2 for %s (%s)\n",
- enum_item->children.size(),
- enum_item->str.c_str(), enum_node->str.c_str()
- );
- }
- //start building attribute string
- std::string enum_item_str = "\\enum_value_";
- //get enum item value
- if(enum_item->children[0]->type != AST_CONSTANT){
- log_error("expected const, got %s for %s (%s)\n",
- type2str(enum_item->children[0]->type).c_str(),
- enum_item->str.c_str(), enum_node->str.c_str()
- );
- }
- RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed);
- enum_item_str.append(val.as_string());
- //set attribute for available val to enum item name mappings
- attributes[enum_item_str] = mkconst_str(enum_item->str);
- }
- }
+ type = template_node->type;
+ is_reg = template_node->is_reg;
+ is_logic = template_node->is_logic;
+ is_signed = template_node->is_signed;
+ is_string = template_node->is_string;
+ is_custom_type = template_node->is_custom_type;
+
+ range_valid = template_node->range_valid;
+ range_swapped = template_node->range_swapped;
+ range_left = template_node->range_left;
+ range_right = template_node->range_right;
+
+ attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
+
+ // if an enum then add attributes to support simulator tracing
+ annotateTypedEnums(template_node);
// Insert clones children from template at beginning
- for (int i = 0; i < GetSize(templ->children); i++)
- children.insert(children.begin() + i, templ->children[i]->clone());
+ for (int i = 0; i < GetSize(template_node->children); i++)
+ children.insert(children.begin() + i, template_node->children[i]->clone());
if (type == AST_MEMORY && GetSize(children) == 1) {
// Single-bit memories must have [0:0] range
- AstNode *rng = new AstNode(AST_RANGE);
- rng->children.push_back(AstNode::mkconst_int(0, true));
- rng->children.push_back(AstNode::mkconst_int(0, true));
+ AstNode *rng = make_range(0, 0);
children.insert(children.begin(), rng);
}
-
did_something = true;
}
log_assert(!is_custom_type);
@@ -1001,29 +1301,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
log_assert(children[1]->type == AST_WIRETYPE);
if (!current_scope.count(children[1]->str))
log_file_error(filename, location.first_line, "Unknown identifier `%s' used as type name\n", children[1]->str.c_str());
- AstNode *resolved_type = current_scope.at(children[1]->str);
- if (resolved_type->type != AST_TYPEDEF)
+ AstNode *resolved_type_node = current_scope.at(children[1]->str);
+ if (resolved_type_node->type != AST_TYPEDEF)
log_file_error(filename, location.first_line, "`%s' does not name a type\n", children[1]->str.c_str());
- log_assert(resolved_type->children.size() == 1);
- AstNode *templ = resolved_type->children[0];
+ log_assert(resolved_type_node->children.size() == 1);
+ AstNode *template_node = resolved_type_node->children[0];
delete children[1];
children.pop_back();
// Ensure typedef itself is fully simplified
- while(templ->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
+ while(template_node->simplify(const_fold, at_zero, in_lvalue, stage, width_hint, sign_hint, in_param)) {};
- if (templ->type == AST_MEMORY)
+ if (template_node->type == AST_MEMORY)
log_file_error(filename, location.first_line, "unpacked array type `%s' cannot be used for a parameter\n", children[1]->str.c_str());
- is_signed = templ->is_signed;
- is_string = templ->is_string;
- is_custom_type = templ->is_custom_type;
-
- range_valid = templ->range_valid;
- range_swapped = templ->range_swapped;
- range_left = templ->range_left;
- range_right = templ->range_right;
- attributes[ID::wiretype] = mkconst_str(resolved_type->str);
- for (auto template_child : templ->children)
+ is_signed = template_node->is_signed;
+ is_string = template_node->is_string;
+ is_custom_type = template_node->is_custom_type;
+
+ range_valid = template_node->range_valid;
+ range_swapped = template_node->range_swapped;
+ range_left = template_node->range_left;
+ range_right = template_node->range_right;
+ attributes[ID::wiretype] = mkconst_str(resolved_type_node->str);
+ for (auto template_child : template_node->children)
children.push_back(template_child->clone());
did_something = true;
}
@@ -1216,6 +1516,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
}
}
+ if (type == AST_IDENTIFIER && !basic_prep) {
+ // check if a plausible struct member sss.mmmm
+ std::string sname;
+ if (name_has_dot(str, sname)) {
+ if (current_scope.count(str) > 0) {
+ auto item_node = current_scope[str];
+ if (item_node->type == AST_STRUCT_ITEM) {
+ // structure member, rewrite this node to reference the packed struct wire
+ auto range = make_struct_member_range(this, item_node);
+ newNode = new AstNode(AST_IDENTIFIER, range);
+ newNode->str = sname;
+ newNode->basic_prep = true;
+ goto apply_newNode;
+ }
+ }
+ }
+ }
// annotate identifiers using scope resolution and create auto-wires as needed
if (type == AST_IDENTIFIER) {
if (current_scope.count(str) == 0) {
diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y
index 118f13de9..879ef4af9 100644
--- a/frontends/ilang/ilang_parser.y
+++ b/frontends/ilang/ilang_parser.y
@@ -192,6 +192,9 @@ wire_options:
wire_options TOK_UPTO {
current_wire->upto = true;
} |
+ wire_options TOK_SIGNED {
+ current_wire->is_signed = true;
+ } |
wire_options TOK_OFFSET TOK_INT {
current_wire->start_offset = $3;
} |
diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc
index 7aceffbfc..8ae7c6578 100644
--- a/frontends/json/jsonparse.cc
+++ b/frontends/json/jsonparse.cc
@@ -309,6 +309,12 @@ void json_import(Design *design, string &modname, JsonNode *node)
port_wire->upto = val->data_number != 0;
}
+ if (port_node->data_dict.count("signed") != 0) {
+ JsonNode *val = port_node->data_dict.at("signed");
+ if (val->type == 'N')
+ port_wire->is_signed = val->data_number != 0;
+ }
+
if (port_node->data_dict.count("offset") != 0) {
JsonNode *val = port_node->data_dict.at("offset");
if (val->type == 'N')
@@ -573,4 +579,3 @@ struct JsonFrontend : public Frontend {
} JsonFrontend;
YOSYS_NAMESPACE_END
-
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index 02fa0031b..e6fa6361e 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -263,7 +263,10 @@ static bool isUserType(std::string &s)
"final" { SV_KEYWORD(TOK_FINAL); }
"logic" { SV_KEYWORD(TOK_LOGIC); }
"var" { SV_KEYWORD(TOK_VAR); }
-"bit" { SV_KEYWORD(TOK_REG); }
+"bit" { SV_KEYWORD(TOK_LOGIC); }
+"int" { SV_KEYWORD(TOK_INT); }
+"byte" { SV_KEYWORD(TOK_BYTE); }
+"shortint" { SV_KEYWORD(TOK_SHORTINT); }
"eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
"s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); }
@@ -277,11 +280,15 @@ static bool isUserType(std::string &s)
"reg" { return TOK_REG; }
"integer" { return TOK_INTEGER; }
"signed" { return TOK_SIGNED; }
+"unsigned" { SV_KEYWORD(TOK_UNSIGNED); }
"genvar" { return TOK_GENVAR; }
"real" { return TOK_REAL; }
"enum" { SV_KEYWORD(TOK_ENUM); }
"typedef" { SV_KEYWORD(TOK_TYPEDEF); }
+"struct" { SV_KEYWORD(TOK_STRUCT); }
+"union" { SV_KEYWORD(TOK_UNION); }
+"packed" { SV_KEYWORD(TOK_PACKED); }
[0-9][0-9_]* {
yylval->string = new std::string(yytext);
diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y
index c8223f41d..b34a62248 100644
--- a/frontends/verilog/verilog_parser.y
+++ b/frontends/verilog/verilog_parser.y
@@ -161,6 +161,23 @@ static bool isInLocalScope(const std::string *name)
return (user_types->count(*name) > 0);
}
+static AstNode *getTypeDefinitionNode(std::string type_name)
+{
+ // return the definition nodes from the typedef statement
+ auto user_types = user_type_stack.back();
+ log_assert(user_types->count(type_name) > 0);
+ auto typedef_node = (*user_types)[type_name];
+ log_assert(typedef_node->type == AST_TYPEDEF);
+ return typedef_node->children[0];
+}
+
+static AstNode *copyTypeDefinition(std::string type_name)
+{
+ // return a copy of the template from a typedef definition
+ auto typedef_node = getTypeDefinitionNode(type_name);
+ return typedef_node->clone();
+}
+
static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true)
{
auto range = new AstNode(AST_RANGE);
@@ -175,6 +192,35 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned =
auto range = makeRange(msb, lsb, isSigned);
parent->children.push_back(range);
}
+
+static AstNode *checkRange(AstNode *type_node, AstNode *range_node)
+{
+ if (type_node->range_left >= 0 && type_node->range_right >= 0) {
+ // type already restricts the range
+ if (range_node) {
+ frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
+ }
+ else {
+ range_node = makeRange(type_node->range_left, type_node->range_right, false);
+ }
+ }
+ if (range_node && range_node->children.size() != 2) {
+ frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
+ }
+ return range_node;
+}
+
+static void rewriteAsMemoryNode(AstNode *node, AstNode *rangeNode)
+{
+ node->type = AST_MEMORY;
+ if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
+ // SV array size [n], rewrite as [n-1:0]
+ rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
+ rangeNode->children.push_back(AstNode::mkconst_int(0, false));
+ }
+ node->children.push_back(rangeNode);
+}
+
%}
%define api.prefix {frontend_verilog_yy}
@@ -223,14 +269,16 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned =
%token TOK_POS_INDEXED TOK_NEG_INDEXED TOK_PROPERTY TOK_ENUM TOK_TYPEDEF
%token TOK_RAND TOK_CONST TOK_CHECKER TOK_ENDCHECKER TOK_EVENTUALLY
%token TOK_INCREMENT TOK_DECREMENT TOK_UNIQUE TOK_PRIORITY
+%token TOK_STRUCT TOK_PACKED TOK_UNSIGNED TOK_INT TOK_BYTE TOK_SHORTINT TOK_UNION
%type <ast> range range_or_multirange non_opt_range non_opt_multirange range_or_signed_int
%type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list
%type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id integral_number
%type <string> type_name
-%type <ast> opt_enum_init
+%type <ast> opt_enum_init enum_type struct_type non_wire_data_type
%type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff
%type <al> attr case_attr
+%type <ast> struct_union
%type <specify_target_ptr> specify_target
%type <specify_triple_ptr> specify_triple specify_opt_triple
@@ -520,9 +568,10 @@ package_body:
;
package_body_stmt:
- typedef_decl |
- localparam_decl |
- param_decl;
+ typedef_decl
+ | localparam_decl
+ | param_decl
+ ;
interface:
TOK_INTERFACE {
@@ -582,6 +631,7 @@ wire_type_token_list:
astbuf3->is_custom_type = true;
astbuf3->children.push_back(new AstNode(AST_WIRETYPE));
astbuf3->children.back()->str = *$1;
+ delete $1;
};
wire_type_token_io:
@@ -682,15 +732,9 @@ range_or_multirange:
non_opt_multirange { $$ = $1; };
range_or_signed_int:
- range {
- $$ = $1;
- } |
- TOK_INTEGER {
- $$ = new AstNode(AST_RANGE);
- $$->children.push_back(AstNode::mkconst_int(31, true));
- $$->children.push_back(AstNode::mkconst_int(0, true));
- $$->is_signed = true;
- };
+ range { $$ = $1; }
+ | TOK_INTEGER { $$ = makeRange(); }
+ ;
module_body:
module_body module_body_stmt |
@@ -700,7 +744,7 @@ module_body:
module_body_stmt:
task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt |
- enum_decl |
+ enum_decl | struct_decl |
always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block;
checker_decl:
@@ -841,18 +885,7 @@ task_func_port:
}
albuf = $1;
astbuf1 = $2;
- astbuf2 = $3;
- if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
- if (astbuf2) {
- frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions (task/function arguments)");
- } else {
- astbuf2 = new AstNode(AST_RANGE);
- astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
- astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
- }
- }
- if (astbuf2 && astbuf2->children.size() != 2)
- frontend_verilog_yyerror("task/function argument range must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
+ astbuf2 = checkRange(astbuf1, $3);
} wire_name |
{
if (!astbuf1) {
@@ -1387,6 +1420,10 @@ single_defparam_decl:
ast_stack.back()->children.push_back(node);
};
+/////////
+// enum
+/////////
+
enum_type: TOK_ENUM {
static int enum_count;
// create parent node for the enum
@@ -1397,31 +1434,40 @@ enum_type: TOK_ENUM {
// create the template for the names
astbuf1 = new AstNode(AST_ENUM_ITEM);
astbuf1->children.push_back(AstNode::mkconst_int(0, true));
- } param_signed enum_base_type '{' enum_name_list '}' { // create template for the enum vars
- auto tnode = astbuf1->clone();
- delete astbuf1;
- astbuf1 = tnode;
- tnode->type = AST_WIRE;
- tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str);
- // drop constant but keep any range
- delete tnode->children[0];
- tnode->children.erase(tnode->children.begin()); }
+ } enum_base_type '{' enum_name_list '}' { // create template for the enum vars
+ auto tnode = astbuf1->clone();
+ delete astbuf1;
+ astbuf1 = tnode;
+ tnode->type = AST_WIRE;
+ tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str);
+ // drop constant but keep any range
+ delete tnode->children[0];
+ tnode->children.erase(tnode->children.begin());
+ $$ = astbuf1; }
;
-enum_base_type: int_vec param_range
- | int_atom
- | /* nothing */ {astbuf1->is_reg = true; addRange(astbuf1); }
+enum_base_type: type_atom type_signing
+ | type_vec type_signing range { if ($3) astbuf1->children.push_back($3); }
+ | /* nothing */ { astbuf1->is_reg = true; addRange(astbuf1); }
;
-int_atom: TOK_INTEGER {astbuf1->is_reg=true; addRange(astbuf1); } // probably should do byte, range [7:0] here
+type_atom: TOK_INTEGER { astbuf1->is_reg = true; addRange(astbuf1); } // 4-state signed
+ | TOK_INT { astbuf1->is_reg = true; addRange(astbuf1); } // 2-state signed
+ | TOK_SHORTINT { astbuf1->is_reg = true; addRange(astbuf1, 15, 0); } // 2-state signed
+ | TOK_BYTE { astbuf1->is_reg = true; addRange(astbuf1, 7, 0); } // 2-state signed
;
-int_vec: TOK_REG {astbuf1->is_reg = true;}
- | TOK_LOGIC {astbuf1->is_logic = true;}
+type_vec: TOK_REG { astbuf1->is_reg = true; } // unsigned
+ | TOK_LOGIC { astbuf1->is_logic = true; } // unsigned
;
-enum_name_list:
- enum_name_decl
+type_signing:
+ TOK_SIGNED { astbuf1->is_signed = true; }
+ | TOK_UNSIGNED { astbuf1->is_signed = false; }
+ | // optional
+ ;
+
+enum_name_list: enum_name_decl
| enum_name_list ',' enum_name_decl
;
@@ -1433,6 +1479,7 @@ enum_name_decl:
auto node = astbuf1->clone();
node->str = *$1;
delete $1;
+ SET_AST_NODE_LOC(node, @1, @1);
delete node->children[0];
node->children[0] = $2 ?: new AstNode(AST_NONE);
astbuf2->children.push_back(node);
@@ -1456,32 +1503,122 @@ enum_var: TOK_ID {
ast_stack.back()->children.push_back(node);
node->str = *$1;
delete $1;
+ SET_AST_NODE_LOC(node, @1, @1);
node->is_enum = true;
}
;
-enum_decl: enum_type enum_var_list ';' {
- //enum_type creates astbuf1 for use by typedef only
- delete astbuf1;
- }
+enum_decl: enum_type enum_var_list ';' { delete $1; }
+ ;
+
+//////////////////
+// struct or union
+//////////////////
+
+struct_decl: struct_type struct_var_list ';' { delete astbuf2; }
+ ;
+
+struct_type: struct_union { astbuf2 = $1; } struct_body { $$ = astbuf2; }
+ ;
+
+struct_union:
+ TOK_STRUCT { $$ = new AstNode(AST_STRUCT); }
+ | TOK_UNION { $$ = new AstNode(AST_UNION); }
+ ;
+
+struct_body: opt_packed '{' struct_member_list '}'
+ ;
+
+opt_packed: TOK_PACKED opt_signed_struct
+ | { frontend_verilog_yyerror("Only PACKED supported at this time"); }
+ ;
+
+opt_signed_struct:
+ TOK_SIGNED { astbuf2->is_signed = true; }
+ | TOK_UNSIGNED { astbuf2->is_signed = false; }
+ | // default is unsigned
+ ;
+
+struct_member_list: struct_member
+ | struct_member_list struct_member
+ ;
+
+struct_member: struct_member_type member_name_list ';' { delete astbuf1; }
+ ;
+
+member_name_list:
+ member_name
+ | member_name_list ',' member_name
+ ;
+
+member_name: TOK_ID {
+ astbuf1->str = $1->substr(1);
+ delete $1;
+ astbuf3 = astbuf1->clone();
+ SET_AST_NODE_LOC(astbuf3, @1, @1);
+ astbuf2->children.push_back(astbuf3);
+ } range { if ($3) astbuf3->children.push_back($3); }
+ ;
+
+struct_member_type: { astbuf1 = new AstNode(AST_STRUCT_ITEM); } member_type_token
+ ;
+
+member_type_token:
+ member_type
+ | hierarchical_type_id {
+ // use a clone of the typedef definition nodes
+ auto template_node = copyTypeDefinition(*$1);
+ delete $1;
+ switch (template_node->type) {
+ case AST_WIRE:
+ template_node->type = AST_STRUCT_ITEM;
+ break;
+ case AST_STRUCT:
+ case AST_UNION:
+ break;
+ default:
+ frontend_verilog_yyerror("Invalid type for struct member: %s", type2str(template_node->type).c_str());
+ }
+ delete astbuf1;
+ astbuf1 = template_node;
+ }
+ | struct_union {
+ // stash state on ast_stack
+ ast_stack.push_back(astbuf2);
+ astbuf2 = $1;
+ } struct_body {
+ astbuf1 = astbuf2;
+ // recover state
+ astbuf2 = ast_stack.back();
+ ast_stack.pop_back();
+ }
+ ;
+
+member_type: type_atom type_signing
+ | type_vec type_signing range_or_multirange { if ($3) astbuf1->children.push_back($3); }
+ ;
+
+struct_var_list: struct_var
+ | struct_var_list ',' struct_var
;
+struct_var: TOK_ID { auto *var_node = astbuf2->clone();
+ var_node->str = *$1;
+ delete $1;
+ SET_AST_NODE_LOC(var_node, @1, @1);
+ ast_stack.back()->children.push_back(var_node);
+ }
+ ;
+
+/////////
+// wire
+/////////
+
wire_decl:
attr wire_type range {
albuf = $1;
astbuf1 = $2;
- astbuf2 = $3;
- if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
- if (astbuf2) {
- frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
- } else {
- astbuf2 = new AstNode(AST_RANGE);
- astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
- astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
- }
- }
- if (astbuf2 && astbuf2->children.size() != 2)
- frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
+ astbuf2 = checkRange(astbuf1, $3);
} delay wire_name_list {
delete astbuf1;
if (astbuf2 != NULL)
@@ -1603,19 +1740,9 @@ wire_name:
if (node->is_input || node->is_output)
frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions.");
if (!astbuf2 && !node->is_custom_type) {
- AstNode *rng = new AstNode(AST_RANGE);
- rng->children.push_back(AstNode::mkconst_int(0, true));
- rng->children.push_back(AstNode::mkconst_int(0, true));
- node->children.push_back(rng);
- }
- node->type = AST_MEMORY;
- auto *rangeNode = $2;
- if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
- // SV array size [n], rewrite as [n-1:0]
- rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
- rangeNode->children.push_back(AstNode::mkconst_int(0, false));
+ addRange(node, 0, 0, false);
}
- node->children.push_back(rangeNode);
+ rewriteAsMemoryNode(node, $2);
}
if (current_function_or_task == NULL) {
if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) {
@@ -1663,42 +1790,23 @@ type_name: TOK_ID // first time seen
typedef_decl:
TOK_TYPEDEF wire_type range type_name range_or_multirange ';' {
astbuf1 = $2;
- astbuf2 = $3;
- if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) {
- if (astbuf2) {
- frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions.");
- } else {
- astbuf2 = new AstNode(AST_RANGE);
- astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true));
- astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true));
- }
- }
- if (astbuf2 && astbuf2->children.size() != 2)
- frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]");
+ astbuf2 = checkRange(astbuf1, $3);
if (astbuf2)
astbuf1->children.push_back(astbuf2);
if ($5 != NULL) {
if (!astbuf2) {
- AstNode *rng = new AstNode(AST_RANGE);
- rng->children.push_back(AstNode::mkconst_int(0, true));
- rng->children.push_back(AstNode::mkconst_int(0, true));
- astbuf1->children.push_back(rng);
+ addRange(astbuf1, 0, 0, false);
}
- astbuf1->type = AST_MEMORY;
- auto *rangeNode = $5;
- if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) {
- // SV array size [n], rewrite as [n-1:0]
- rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true));
- rangeNode->children.push_back(AstNode::mkconst_int(0, false));
- }
- astbuf1->children.push_back(rangeNode);
+ rewriteAsMemoryNode(astbuf1, $5);
}
- addTypedefNode($4, astbuf1);
- } |
- TOK_TYPEDEF enum_type type_name ';' {
- addTypedefNode($3, astbuf1);
- }
+ addTypedefNode($4, astbuf1); }
+ | TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); }
+ ;
+
+non_wire_data_type:
+ enum_type
+ | struct_type
;
cell_stmt:
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index dc1add0ff..109113370 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -1862,6 +1862,7 @@ RTLIL::Wire *RTLIL::Module::addWire(RTLIL::IdString name, const RTLIL::Wire *oth
wire->port_input = other->port_input;
wire->port_output = other->port_output;
wire->upto = other->upto;
+ wire->is_signed = other->is_signed;
wire->attributes = other->attributes;
return wire;
}
@@ -2459,6 +2460,7 @@ RTLIL::Wire::Wire()
port_input = false;
port_output = false;
upto = false;
+ is_signed = false;
#ifdef WITH_PYTHON
RTLIL::Wire::get_all_wires()->insert(std::pair<unsigned int, RTLIL::Wire*>(hashidx_, this));
diff --git a/kernel/rtlil.h b/kernel/rtlil.h
index 8d2e42b42..86b4e25b6 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -1238,13 +1238,10 @@ public:
RTLIL::Cell* addFf (RTLIL::IdString name, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, const std::string &src = "");
RTLIL::Cell* addDff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, const std::string &src = "");
RTLIL::Cell* addDffe (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, bool en_polarity = true, const std::string &src = "");
- RTLIL::Cell* addDffsr (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr,
- RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = "");
- RTLIL::Cell* addAdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q,
- RTLIL::Const arst_value, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addDffsr (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool clk_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addAdff (RTLIL::IdString name, const RTLIL::SigSpec &sig_clk, const RTLIL::SigSpec &sig_arst, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, RTLIL::Const arst_value, bool clk_polarity = true, bool arst_polarity = true, const std::string &src = "");
RTLIL::Cell* addDlatch (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_d, const RTLIL::SigSpec &sig_q, bool en_polarity = true, const std::string &src = "");
- RTLIL::Cell* addDlatchsr (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr,
- RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = "");
+ RTLIL::Cell* addDlatchsr (RTLIL::IdString name, const RTLIL::SigSpec &sig_en, const RTLIL::SigSpec &sig_set, const RTLIL::SigSpec &sig_clr, RTLIL::SigSpec sig_d, const RTLIL::SigSpec &sig_q, bool en_polarity = true, bool set_polarity = true, bool clr_polarity = true, const std::string &src = "");
RTLIL::Cell* addBufGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const RTLIL::SigBit &sig_y, const std::string &src = "");
RTLIL::Cell* addNotGate (RTLIL::IdString name, const RTLIL::SigBit &sig_a, const RTLIL::SigBit &sig_y, const std::string &src = "");
@@ -1373,7 +1370,7 @@ public:
RTLIL::Module *module;
RTLIL::IdString name;
int width, start_offset, port_id;
- bool port_input, port_output, upto;
+ bool port_input, port_output, upto, is_signed;
#ifdef WITH_PYTHON
static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void);
diff --git a/manual/PRESENTATION_Prog/Makefile b/manual/PRESENTATION_Prog/Makefile
index 794f5c12c..7e3cf814b 100644
--- a/manual/PRESENTATION_Prog/Makefile
+++ b/manual/PRESENTATION_Prog/Makefile
@@ -1,8 +1,11 @@
all: test0.log test1.log test2.log
+CXXFLAGS=$(shell ../../yosys-config --cxxflags)
+DATDIR=$(shell ../../yosys-config --datdir)
+
my_cmd.so: my_cmd.cc
- ../../yosys-config --exec --cxx --cxxflags --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs
+ ../../yosys-config --exec --cxx $(subst $(DATDIR),../../share,$(CXXFLAGS)) --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs
test0.log: my_cmd.so
../../yosys -Ql test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' absval_ref.v
diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py
index fac5b48a4..fa23e3b2c 100644
--- a/misc/py_wrap_generator.py
+++ b/misc/py_wrap_generator.py
@@ -312,16 +312,16 @@ class PythonListTranslator(Translator):
text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);"
text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t")
tmp_name = tmp_name + "___tmp"
- text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");"
+ text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");"
elif types[0].name in classnames:
text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);"
if types[0].attr_type == attr_types.star:
- text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + "->get_cpp_obj());"
+ text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + "->get_cpp_obj());"
else:
- text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());"
+ text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());"
else:
text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);"
- text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");"
+ text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");"
text += prefix + "}"
return text
@@ -349,19 +349,24 @@ class PythonListTranslator(Translator):
text += prefix + "}"
return text
+class IDictTranslator(PythonListTranslator):
+ typename = "boost::python::list"
+ orig_name = "idict"
+ insert_name = ""
+
#Sub-type for std::set
class SetTranslator(PythonListTranslator):
- insert_name = "insert"
+ insert_name = ".insert"
orig_name = "std::set"
#Sub-type for std::vector
class VectorTranslator(PythonListTranslator):
- insert_name = "push_back"
+ insert_name = ".push_back"
orig_name = "std::vector"
#Sub-type for pool
class PoolTranslator(PythonListTranslator):
- insert_name = "insert"
+ insert_name = ".insert"
orig_name = "pool"
#Translates dict-types (dict, std::map), that only differ in their name and
@@ -528,6 +533,7 @@ known_containers = {
"std::set" : SetTranslator,
"std::vector" : VectorTranslator,
"pool" : PoolTranslator,
+ "idict" : IDictTranslator,
"dict" : DictTranslator,
"std::pair" : TupleTranslator,
"std::map" : MapTranslator
diff --git a/passes/fsm/fsm_extract.cc b/passes/fsm/fsm_extract.cc
index 3840aabc8..6f99886f0 100644
--- a/passes/fsm/fsm_extract.cc
+++ b/passes/fsm/fsm_extract.cc
@@ -394,7 +394,7 @@ static void extract_fsm(RTLIL::Wire *wire)
RTLIL::Cell *cell = module->cells_.at(cellport.first);
RTLIL::SigSpec port_sig = assign_map(cell->getPort(cellport.second));
RTLIL::SigSpec unconn_sig = port_sig.extract(ctrl_out);
- RTLIL::Wire *unconn_wire = module->addWire(stringf("$fsm_unconnect$%s$%d", log_signal(unconn_sig), autoidx++), unconn_sig.size());
+ RTLIL::Wire *unconn_wire = module->addWire(stringf("$fsm_unconnect$%d", autoidx++), unconn_sig.size());
port_sig.replace(unconn_sig, RTLIL::SigSpec(unconn_wire), &cell->connections_[cellport.second]);
}
}
diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc
index 8d55b18a0..873c37b9a 100644
--- a/passes/techmap/abc9_ops.cc
+++ b/passes/techmap/abc9_ops.cc
@@ -102,8 +102,6 @@ void check(RTLIL::Design *design, bool dff_mode)
auto inst_module = design->module(cell->type);
if (!inst_module)
continue;
- if (!inst_module->get_blackbox_attribute())
- continue;
IdString derived_type;
Module *derived_module;
if (cell->parameters.empty()) {
@@ -111,6 +109,10 @@ void check(RTLIL::Design *design, bool dff_mode)
derived_module = inst_module;
}
else {
+ // Check potential (since its value may depend on a parameter,
+ // but not its existence)
+ if (!inst_module->has_attribute(ID::abc9_flop))
+ continue;
derived_type = inst_module->derive(design, cell->parameters);
derived_module = design->module(derived_type);
log_assert(derived_module);
@@ -127,20 +129,20 @@ void check(RTLIL::Design *design, bool dff_mode)
for (auto derived_cell : derived_module->cells()) {
if (derived_cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_))) {
if (found)
- log_error("Module '%s' with (* abc9_flop *) contains more than one $_DFF_[NP]_ cell.\n", log_id(derived_module));
+ log_error("Whitebox '%s' with (* abc9_flop *) contains more than one $_DFF_[NP]_ cell.\n", log_id(derived_module));
found = true;
SigBit Q = derived_cell->getPort(ID::Q);
log_assert(GetSize(Q.wire) == 1);
if (!Q.wire->port_output)
- log_error("Module '%s' contains a %s cell where its 'Q' port does not drive a module output!\n", log_id(derived_module), log_id(derived_cell->type));
+ log_error("Whitebox '%s' with (* abc9_flop *) contains a %s cell where its 'Q' port does not drive a module output.\n", log_id(derived_module), log_id(derived_cell->type));
Const init = Q.wire->attributes.at(ID::init, State::Sx);
log_assert(GetSize(init) == 1);
}
else if (unsupported.count(derived_cell->type))
- log_error("Module '%s' with (* abc9_flop *) contains a %s cell, which is not supported for sequential synthesis.\n", log_id(derived_module), log_id(derived_cell->type));
+ log_error("Whitebox '%s' with (* abc9_flop *) contains a %s cell, which is not supported for sequential synthesis.\n", log_id(derived_module), log_id(derived_cell->type));
}
}
}
@@ -173,8 +175,6 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
auto inst_module = design->module(cell->type);
if (!inst_module)
continue;
- if (!inst_module->get_blackbox_attribute())
- continue;
IdString derived_type;
Module *derived_module;
if (cell->parameters.empty()) {
@@ -182,6 +182,10 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
derived_module = inst_module;
}
else {
+ // Check potential for any one of those three
+ // (since its value may depend on a parameter, but not its existence)
+ if (!inst_module->has_attribute(ID::abc9_flop) && !inst_module->has_attribute(ID::abc9_box) && !inst_module->get_bool_attribute(ID::abc9_bypass))
+ continue;
derived_type = inst_module->derive(design, cell->parameters);
derived_module = design->module(derived_type);
}
@@ -211,7 +215,7 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
// Block sequential synthesis on cells with (* init *) != 1'b0
// because ABC9 doesn't support them
if (init != State::S0) {
- log_warning("Module '%s' contains a %s cell with non-zero initial state -- this is not unsupported for ABC9 sequential synthesis. Treating as a blackbox.\n", log_id(derived_module), log_id(derived_cell->type));
+ log_warning("Whitebox '%s' with (* abc9_flop *) contains a %s cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox.\n", log_id(derived_module), log_id(derived_cell->type));
derived_module->set_bool_attribute(ID::abc9_flop, false);
}
break;
@@ -232,10 +236,8 @@ void prep_hier(RTLIL::Design *design, bool dff_mode)
auto w = unmap_module->addWire(port, derived_module->wire(port));
// Do not propagate (* init *) values into the box,
// in fact, remove it from outside too
- if (w->port_output && w->attributes.erase(ID::init)) {
- auto r = unmap_module->addWire(stringf("\\_TECHMAP_REMOVEINIT_%s_", log_id(port)));
- unmap_module->connect(r, State::S1);
- }
+ if (w->port_output)
+ w->attributes.erase(ID::init);
}
unmap_module->ports = derived_module->ports;
unmap_module->check();
@@ -719,8 +721,10 @@ void prep_xaiger(RTLIL::Module *module, bool dff)
bit_users[bit].insert(cell->name);
if (cell->output(conn.first) && !abc9_flop)
- for (auto bit : sigmap(conn.second))
- bit_drivers[bit].insert(cell->name);
+ for (const auto &chunk : conn.second.chunks())
+ if (!chunk.wire->get_bool_attribute(ID::abc9_keep))
+ for (auto b : sigmap(SigSpec(chunk)))
+ bit_drivers[b].insert(cell->name);
}
toposort.node(cell->name);
}
@@ -1110,7 +1114,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
for (auto w : mapped_mod->wires()) {
auto nw = module->addWire(remap_name(w->name), GetSize(w));
nw->start_offset = w->start_offset;
- // Remove all (* init *) since they only existon $_DFF_[NP]_
+ // Remove all (* init *) since they only exist on $_DFF_[NP]_
w->attributes.erase(ID::init);
}
@@ -1147,16 +1151,36 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
}
}
+ SigMap initmap;
+ if (dff_mode) {
+ // Build a sigmap prioritising bits with (* init *)
+ initmap.set(module);
+ for (auto w : module->wires()) {
+ auto it = w->attributes.find(ID::init);
+ if (it == w->attributes.end())
+ continue;
+ for (auto i = 0; i < GetSize(w); i++)
+ if (it->second[i] == State::S0 || it->second[i] == State::S1)
+ initmap.add(w);
+ }
+ }
+
std::vector<Cell*> boxes;
for (auto cell : module->cells().to_vector()) {
if (cell->has_keep_attr())
continue;
- // Short out $_DFF_[NP]_ cells since the flop box already has
- // all the information we need to reconstruct cell
+ // Short out (so that existing name can be preserved) and remove
+ // $_DFF_[NP]_ cells since flop box already has all the information
+ // we need to reconstruct them
if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep)) {
- module->connect(cell->getPort(ID::Q), cell->getPort(ID::D));
+ SigBit Q = cell->getPort(ID::Q);
+ module->connect(Q, cell->getPort(ID::D));
module->remove(cell);
+ auto Qi = initmap(Q);
+ auto it = Qi.wire->attributes.find(ID::init);
+ if (it != Qi.wire->attributes.end())
+ it->second[Qi.offset] = State::Sx;
}
else if (cell->type.in(ID($_AND_), ID($_NOT_)))
module->remove(cell);
@@ -1299,7 +1323,25 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
mapped_cell->connections_.erase(jt);
auto abc9_flop = box_module->get_bool_attribute(ID::abc9_flop);
- if (!abc9_flop) {
+ if (abc9_flop) {
+ // Link this sole flop box output to the output of the existing
+ // flop box, so that any (public) signal it drives will be
+ // preserved
+ SigBit old_q;
+ for (const auto &port_name : box_ports.at(existing_cell->type)) {
+ RTLIL::Wire *w = box_module->wire(port_name);
+ log_assert(w);
+ if (!w->port_output)
+ continue;
+ log_assert(old_q == SigBit());
+ log_assert(GetSize(w) == 1);
+ old_q = existing_cell->getPort(port_name);
+ }
+ auto new_q = outputs[0];
+ new_q.wire = module->wires_.at(remap_name(new_q.wire->name));
+ module->connect(old_q, new_q);
+ }
+ else {
for (const auto &i : inputs)
bit_users[i].insert(mapped_cell->name);
for (const auto &i : outputs)
@@ -1332,11 +1374,12 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
c.wire = module->wires_.at(remap_name(c.wire->name));
newsig.append(c);
}
- cell->setPort(port_name, newsig);
if (w->port_input && !abc9_flop)
for (const auto &i : newsig)
bit2sinks[i].push_back(cell);
+
+ cell->setPort(port_name, std::move(newsig));
}
}
@@ -1398,7 +1441,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
// treated as being "free"), in particular driving primary
// outputs (real primary outputs, or cells treated as blackboxes)
// or driving box inputs.
- // Instead of just mapping those $_NOT_ gates into 2-input $lut-s
+ // Instead of just mapping those $_NOT_ gates into 1-input $lut-s
// at an area and delay cost, see if it is possible to push
// this $_NOT_ into the driving LUT, or into all sink LUTs.
// When this is not possible, (i.e. this signal drives two primary
diff --git a/techlibs/common/abc9_unmap.v b/techlibs/common/abc9_unmap.v
index bcbe91477..c39648c62 100644
--- a/techlibs/common/abc9_unmap.v
+++ b/techlibs/common/abc9_unmap.v
@@ -1,5 +1,5 @@
(* techmap_celltype = "$__DFF_N__$abc9_flop $__DFF_P__$abc9_flop" *)
-module $__DFF_x__$abc9_flop (input C, D, Q, output n1);
+module $__DFF_x__$abc9_flop (input C, D, (* init = 1'b0 *) input Q, output n1);
parameter _TECHMAP_CELLTYPE_ = "";
generate if (_TECHMAP_CELLTYPE_ == "$__DFF_N__$abc9_flop")
$_DFF_N_ _TECHMAP_REPLACE_ (.C(C), .D(D), .Q(Q));
diff --git a/tests/arch/ecp5/latches_abc9.ys b/tests/arch/ecp5/latches_abc9.ys
new file mode 100644
index 000000000..4daf04050
--- /dev/null
+++ b/tests/arch/ecp5/latches_abc9.ys
@@ -0,0 +1,13 @@
+read_verilog <<EOT
+module top(input e, d, output q);
+reg l;
+always @*
+ if (e)
+ l = ~d;
+assign q = ~l;
+endmodule
+EOT
+# Can't run any sort of equivalence check because latches are blown to LUTs
+synth_ecp5 -abc9
+select -assert-count 2 t:LUT4
+select -assert-none t:LUT4 %% t:* %D
diff --git a/tests/arch/intel_alm/.gitignore b/tests/arch/intel_alm/.gitignore
new file mode 100644
index 000000000..ba42e1ee6
--- /dev/null
+++ b/tests/arch/intel_alm/.gitignore
@@ -0,0 +1,2 @@
+/*.log
+/run-test.mk
diff --git a/tests/arch/xilinx/abc9_dff.ys b/tests/arch/xilinx/abc9_dff.ys
index fd343969b..210e87477 100644
--- a/tests/arch/xilinx/abc9_dff.ys
+++ b/tests/arch/xilinx/abc9_dff.ys
@@ -50,10 +50,10 @@ FDCE_1 /*#(.INIT(1))*/ fd7(.C(C), .CE(1'b0), .D(D), .CLR(1'b0), .Q(Q[6]));
FDPE_1 #(.INIT(1)) fd8(.C(C), .CE(1'b0), .D(D), .PRE(1'b0), .Q(Q[7]));
endmodule
EOT
-logger -expect warning "Module '\$paramod\\FDRE\\INIT=1' contains a \$dff cell .*" 1
-logger -expect warning "Module '\$paramod\\FDRE_1\\INIT=1' contains a \$dff cell .*" 1
-logger -expect warning "Module 'FDSE' contains a \$dff cell .*" 1
-logger -expect warning "Module '\$paramod\\FDSE_1\\INIT=1' contains a \$dff cell .*" 1
+logger -expect warning "Whitebox '\$paramod\\FDRE\\INIT=1' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1
+logger -expect warning "Whitebox '\$paramod\\FDRE_1\\INIT=1' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1
+logger -expect warning "Whitebox 'FDSE' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1
+logger -expect warning "Whitebox '\$paramod\\FDSE_1\\INIT=1' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1
equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf
design -load postopt
select -assert-count 8 t:FD*
@@ -82,4 +82,53 @@ select -assert-count 1 t:FDPE
select -assert-count 2 t:INV
select -assert-count 0 t:FD* t:INV %% t:* %D
+
+design -reset
+read_verilog <<EOT
+module top(input clk, input d, output q);
+reg r;
+always @(posedge clk) begin
+r <= d;
+end
+assign q = ~r;
+endmodule
+EOT
+proc
+equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf
+design -load postopt
+select -assert-count 1 t:FDRE %co w:r %i
+
+
+design -reset
+read_verilog <<EOT
+module top(input clk, input a, b, output reg q1, output q2);
+reg r;
+always @(posedge clk) begin
+ q1 <= a | b;
+ r <= ~(~a & ~b);
+end
+assign q2 = r;
+endmodule
+EOT
+proc
+equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf
+design -load postopt
+select -assert-count 1 t:FDRE %co %a w:r %i
+
+
+design -reset
+read_verilog <<EOT
+module top(input clk, input a, b, output o);
+reg r1, r2;
+always @(posedge clk) begin
+ r1 <= a | b;
+ r2 <= ~(~a & ~b);
+end
+assign o = r1 | r2;
+endmodule
+EOT
+proc
+equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf
+
+
logger -expect-no-warnings
diff --git a/tests/arch/xilinx/pmgen_xilinx_srl.ys b/tests/arch/xilinx/pmgen_xilinx_srl.ys
index ea2f20487..e76fb20ab 100644
--- a/tests/arch/xilinx/pmgen_xilinx_srl.ys
+++ b/tests/arch/xilinx/pmgen_xilinx_srl.ys
@@ -1,6 +1,6 @@
read_verilog -icells <<EOT
module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO);
- parameter DEPTH = 1;
+ parameter DEPTH = 2;
parameter [DEPTH-1:0] INIT = 0;
parameter CLKPOL = 1;
parameter ENPOL = 2;
diff --git a/tests/arch/xilinx/xilinx_srl.v b/tests/arch/xilinx/xilinx_srl.v
index bc2a15ab2..29920da41 100644
--- a/tests/arch/xilinx/xilinx_srl.v
+++ b/tests/arch/xilinx/xilinx_srl.v
@@ -29,7 +29,7 @@ endmodule
module $__XILINX_SHREG_(input C, D, E, input [1:0] L, output Q);
parameter CLKPOL = 1;
parameter ENPOL = 1;
-parameter DEPTH = 1;
+parameter DEPTH = 2;
parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}};
reg [DEPTH-1:0] r = INIT;
wire clk = C ^ CLKPOL;
diff --git a/tests/svtypes/struct_array.sv b/tests/svtypes/struct_array.sv
new file mode 100644
index 000000000..022ad56c6
--- /dev/null
+++ b/tests/svtypes/struct_array.sv
@@ -0,0 +1,22 @@
+// test for array indexing in structures
+
+module top;
+
+ struct packed {
+ bit [5:0] [7:0] a; // 6 element packed array of bytes
+ bit [15:0] b; // filler for non-zero offset
+ } s;
+
+ initial begin
+ s = '0;
+
+ s.a[2:1] = 16'h1234;
+ s.a[5] = 8'h42;
+
+ s.b = '1;
+ s.b[1:0] = '0;
+ end
+
+ always_comb assert(s==64'h4200_0012_3400_FFFC);
+
+endmodule
diff --git a/tests/svtypes/struct_simple.sv b/tests/svtypes/struct_simple.sv
new file mode 100644
index 000000000..c74289cc3
--- /dev/null
+++ b/tests/svtypes/struct_simple.sv
@@ -0,0 +1,48 @@
+module top;
+ localparam BITS=8;
+
+ struct packed {
+ logic a;
+ logic[BITS-1:0] b;
+ byte c;
+ logic x, y;
+ } s;
+
+ struct packed signed {
+ integer a;
+ logic[15:0] b;
+ logic[7:0] c;
+ bit [7:0] d;
+ } pack1;
+
+ struct packed {
+ byte a;
+ struct packed {
+ byte x, y;
+ } b;
+ } s2;
+
+ assign s.a = '1;
+ assign s.b = '1;
+ assign s.c = 8'hAA;
+ assign s.x = '1;
+ logic[7:0] t;
+ assign t = s.b;
+ assign pack1.a = 42;
+ assign pack1.b = 16'hAAAA;
+ assign pack1.c = '1;
+ assign pack1.d = 8'h55;
+ assign s2.b.x = 'h42;
+
+ always_comb assert(s.a == 1'b1);
+ always_comb assert(s.c == 8'hAA);
+ always_comb assert(s.x == 1'b1);
+ always_comb assert(t == 8'hFF);
+ always_comb assert(pack1.a == 42);
+ always_comb assert(pack1.b == 16'hAAAA);
+ always_comb assert(pack1.c == 8'hFF);
+ always_comb assert(pack1[15:8] == 8'hFF);
+ always_comb assert(pack1.d == 8'h55);
+ always_comb assert(s2.b.x == 'h42);
+
+endmodule
diff --git a/tests/svtypes/typedef_struct.sv b/tests/svtypes/typedef_struct.sv
new file mode 100644
index 000000000..7ae007952
--- /dev/null
+++ b/tests/svtypes/typedef_struct.sv
@@ -0,0 +1,42 @@
+package p;
+
+typedef struct packed {
+ byte a;
+ byte b;
+} p_t;
+
+endpackage
+
+
+module top;
+
+ typedef logic[7:0] t_t;
+
+ typedef struct packed {
+ bit a;
+ logic[7:0] b;
+ t_t t;
+ } s_t;
+
+ s_t s;
+ s_t s1;
+
+ p::p_t ps;
+
+ assign s.a = '1;
+ assign s.b = '1;
+ assign s.t = 8'h55;
+ assign s1 = s;
+ assign ps.a = 8'hAA;
+ assign ps.b = 8'h55;
+
+ always_comb begin
+ assert(s.a == 1'b1);
+ assert(s.b == 8'hFF);
+ assert(s.t == 8'h55);
+ assert(s1.t == 8'h55);
+ assert(ps.a == 8'hAA);
+ assert(ps.b == 8'h55);
+ end
+
+endmodule
diff --git a/tests/svtypes/union_simple.sv b/tests/svtypes/union_simple.sv
new file mode 100644
index 000000000..12e4b376f
--- /dev/null
+++ b/tests/svtypes/union_simple.sv
@@ -0,0 +1,72 @@
+module top;
+
+ typedef struct packed {
+ byte a,b,c,d;
+ } byte4_t;
+
+ typedef union packed {
+ int x;
+ byte4_t y;
+ } w_t;
+
+ w_t w;
+
+ assign w.x = 'h42;
+ always_comb begin
+ assert(w.y.d == 8'h42);
+ end
+
+ typedef logic[4:0] reg_addr_t;
+ typedef logic[6:0] opcode_t;
+
+ typedef struct packed {
+ bit [6:0] func7;
+ reg_addr_t rs2;
+ reg_addr_t rs1;
+ bit [2:0] func3;
+ reg_addr_t rd;
+ opcode_t opcode;
+ } R_t;
+
+ typedef struct packed {
+ bit[11:0] imm;
+ reg_addr_t rs1;
+ bit[2:0] func3;
+ reg_addr_t rd;
+ opcode_t opcode;
+ } I_t;
+
+ typedef struct packed {
+ bit[19:0] imm;
+ reg_addr_t rd;
+ opcode_t opcode;
+ } U_t;
+
+ typedef union packed {
+ R_t r;
+ I_t i;
+ U_t u;
+ } instruction_t;
+
+ instruction_t ir1;
+ assign ir1 = 32'h0AA01EB7; // lui t4,0xAA01
+ always_comb begin
+ assert(ir1.u.opcode == 'h37);
+ assert(ir1.r.opcode == 'h37);
+ assert(ir1.u.rd == 'd29);
+ assert(ir1.r.rd == 'd29);
+ assert(ir1.u.imm == 'hAA01);
+ end
+
+ union packed {
+ int word;
+ struct packed {
+ byte a, b, c, d;
+ } byte4;
+ } u;
+ assign u.word = 'h42;
+ always_comb begin
+ assert(u.byte4.d == 'h42);
+ end
+
+endmodule
diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys
index ac714665f..a9880c722 100644
--- a/tests/various/abc9.ys
+++ b/tests/various/abc9.ys
@@ -97,4 +97,5 @@ select -assert-count 3 t:$_DFF_N_
select -assert-none c:ff1 c:ff2 c:ff4 %% c:* %D
clean
select -assert-count 2 a:init
-select -assert-none w:w w:z %% a:init %D
+select -assert-count 1 w:w a:init %i
+select -assert-count 1 c:ff4 %co c:ff4 %d %a a:init %i
diff --git a/tests/various/attrib07_func_call.v b/tests/various/attrib07_func_call.v
index f55ef2316..8c9fb2926 100644
--- a/tests/various/attrib07_func_call.v
+++ b/tests/various/attrib07_func_call.v
@@ -11,7 +11,7 @@ module foo(clk, rst, inp_a, inp_b, out);
input wire rst;
input wire [7:0] inp_a;
input wire [7:0] inp_b;
- output wire [7:0] out;
+ output reg [7:0] out;
always @(posedge clk)
if (rst) out <= 0;
diff --git a/tests/various/constmsk_testmap.v b/tests/various/constmsk_testmap.v
index fab1b1bbc..b6809c7c0 100644
--- a/tests/various/constmsk_testmap.v
+++ b/tests/various/constmsk_testmap.v
@@ -1,7 +1,7 @@
(* techmap_celltype = "$reduce_or" *)
module my_opt_reduce_or(...);
parameter A_SIGNED = 0;
- parameter A_WIDTH = 1;
+ parameter A_WIDTH = 2;
parameter Y_WIDTH = 1;
input [A_WIDTH-1:0] A;
diff --git a/tests/various/shregmap.v b/tests/various/shregmap.v
index 604c2c976..dc828eda7 100644
--- a/tests/various/shregmap.v
+++ b/tests/various/shregmap.v
@@ -13,7 +13,7 @@ assign q = {shift2[3], shift1[3]};
endmodule
module $__SHREG_DFF_P_(input C, D, output Q);
-parameter DEPTH = 1;
+parameter DEPTH = 2;
parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}};
reg [DEPTH-1:0] r = INIT;
always @(posedge C)
@@ -38,7 +38,7 @@ endmodule
module $__XILINX_SHREG_(input C, D, input [1:0] L, output Q);
parameter CLKPOL = 1;
parameter ENPOL = 1;
-parameter DEPTH = 1;
+parameter DEPTH = 2;
parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}};
reg [DEPTH-1:0] r = INIT;
wire clk = C ^ CLKPOL;
diff --git a/tests/verilog/bug2042-sv.ys b/tests/verilog/bug2042-sv.ys
index e815d7fc5..91989f412 100644
--- a/tests/verilog/bug2042-sv.ys
+++ b/tests/verilog/bug2042-sv.ys
@@ -2,7 +2,7 @@ read_verilog -sv <<EOT
module Task_Test_Top
(
input a,
-output b
+output reg b
);
task SomeTaskName(a);