aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--CODEOWNERS37
-rw-r--r--Makefile2
-rw-r--r--README.md2
-rw-r--r--backends/aiger/xaiger.cc52
-rw-r--r--backends/btor/btor.cc95
-rw-r--r--backends/btor/test_cells.sh2
-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)151
-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/firrtl/firrtl.cc64
-rw-r--r--backends/ilang/ilang_backend.cc2
-rw-r--r--backends/json/json.cc4
-rw-r--r--backends/smt2/smt2.cc10
-rw-r--r--backends/smt2/smtbmc.py5
-rw-r--r--backends/smt2/smtio.py39
-rw-r--r--backends/smv/smv.cc5
-rw-r--r--backends/smv/test_cells.sh4
-rw-r--r--backends/verilog/verilog_backend.cc89
-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.cc523
-rw-r--r--frontends/ilang/ilang_lexer.l4
-rw-r--r--frontends/ilang/ilang_parser.y3
-rw-r--r--frontends/json/jsonparse.cc7
-rw-r--r--frontends/verific/verific.cc7
-rw-r--r--frontends/verilog/verilog_lexer.l24
-rw-r--r--frontends/verilog/verilog_parser.y308
-rw-r--r--kernel/calc.cc47
-rw-r--r--kernel/celledges.cc2
-rw-r--r--kernel/celltypes.h4
-rw-r--r--kernel/constids.inc1
-rw-r--r--kernel/hashlib.h10
-rw-r--r--kernel/rtlil.cc18
-rw-r--r--kernel/rtlil.h48
-rw-r--r--kernel/satgen.h42
-rw-r--r--manual/CHAPTER_CellLib.tex23
-rw-r--r--manual/CHAPTER_Overview.tex9
-rw-r--r--manual/PRESENTATION_Prog.tex2
-rw-r--r--manual/PRESENTATION_Prog/Makefile5
-rw-r--r--misc/py_wrap_generator.py20
-rw-r--r--passes/cmds/Makefile.inc1
-rw-r--r--passes/cmds/blackbox.cc26
-rw-r--r--passes/cmds/printattrs.cc90
-rw-r--r--passes/cmds/stat.cc2
-rw-r--r--passes/fsm/fsm_extract.cc2
-rw-r--r--passes/hierarchy/hierarchy.cc23
-rw-r--r--passes/memory/memory_share.cc2
-rw-r--r--passes/opt/opt_expr.cc58
-rw-r--r--passes/opt/opt_share.cc4
-rw-r--r--passes/opt/share.cc6
-rw-r--r--passes/opt/wreduce.cc4
-rw-r--r--passes/sat/qbfsat.cc133
-rw-r--r--passes/techmap/.gitignore1
-rw-r--r--passes/techmap/Makefile.inc13
-rw-r--r--passes/techmap/abc9_ops.cc85
-rw-r--r--passes/techmap/flatten.cc357
-rw-r--r--passes/techmap/simplemap.cc6
-rw-r--r--passes/techmap/simplemap.h2
-rw-r--r--passes/techmap/techmap.cc762
-rw-r--r--passes/tests/test_cell.cc4
-rw-r--r--techlibs/common/abc9_unmap.v2
-rw-r--r--techlibs/common/simlib.v91
-rw-r--r--techlibs/common/techmap.v107
-rw-r--r--tests/arch/ecp5/latches_abc9.ys13
-rw-r--r--tests/arch/ice40/mux.ys3
-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/simple/constmuldivmod.v42
-rw-r--r--tests/svtypes/logic_rom.sv6
-rw-r--r--tests/svtypes/logic_rom.ys3
-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/printattr.ys14
-rw-r--r--tests/various/shregmap.v4
-rw-r--r--tests/verilog/bug2042-sv.ys2
90 files changed, 3526 insertions, 978 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 3b36c3182..638ce558b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -66,6 +66,7 @@ Yosys 0.9 .. Yosys 0.9-dev
- Added "design -delete"
- Added "select -unset"
- Use YosysHQ/abc instead of upstream berkeley-abc/abc
+ - Added $divfloor and $modfloor cells
Yosys 0.8 .. Yosys 0.9
----------------------
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/Makefile b/Makefile
index cd6179879..e589b2b67 100644
--- a/Makefile
+++ b/Makefile
@@ -717,7 +717,7 @@ ifneq ($(ABCREV),default)
echo 'REEBE: NOP pbagnvaf ybpny zbqvsvpngvbaf! Frg NOPERI=qrsnhyg va Lbflf Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; false; \
fi
# set a variable so the test fails if git fails to run - when comparing outputs directly, empty string would match empty string
- $(Q) if ! (cd abc && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \
+ $(Q) if ! (cd abc 2> /dev/null && rev="`git rev-parse $(ABCREV)`" && test "`git rev-parse HEAD`" == "$$rev"); then \
test $(ABCPULL) -ne 0 || { echo 'REEBE: NOP abg hc gb qngr naq NOPCHYY frg gb 0 va Znxrsvyr!' | tr 'A-Za-z' 'N-ZA-Mn-za-m'; exit 1; }; \
echo "Pulling ABC from $(ABCURL):"; set -x; \
test -d abc || git clone $(ABCURL) abc; \
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 14c8484e8..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));
@@ -266,26 +268,32 @@ struct BtorWorker
goto okay;
}
- if (cell->type.in(ID($div), ID($mod)))
+ if (cell->type.in(ID($div), ID($mod), ID($modfloor)))
{
+ bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
+ bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
+
string btor_op;
if (cell->type == ID($div)) btor_op = "div";
+ // "rem" = truncating modulo
if (cell->type == ID($mod)) btor_op = "rem";
+ // "mod" = flooring modulo
+ if (cell->type == ID($modfloor)) {
+ // "umod" doesn't exist because it's the same as "urem"
+ btor_op = a_signed || b_signed ? "mod" : "rem";
+ }
log_assert(!btor_op.empty());
int width = GetSize(cell->getPort(ID::Y));
width = std::max(width, GetSize(cell->getPort(ID::A)));
width = std::max(width, GetSize(cell->getPort(ID::B)));
- bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false;
- bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false;
-
int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed);
int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed);
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));
@@ -311,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));
@@ -338,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));
@@ -369,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));
@@ -408,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));
@@ -441,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));
@@ -482,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));
@@ -515,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));
@@ -554,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);
@@ -579,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;
@@ -634,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));
@@ -1043,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));
@@ -1067,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);
}
@@ -1091,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)));
}
@@ -1133,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());
}
}
@@ -1158,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));
@@ -1179,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;
@@ -1250,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)));
@@ -1347,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");
@@ -1374,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);
@@ -1386,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/btor/test_cells.sh b/backends/btor/test_cells.sh
index e0f1a0514..3f077201a 100644
--- a/backends/btor/test_cells.sh
+++ b/backends/btor/test_cells.sh
@@ -6,7 +6,7 @@ rm -rf test_cells.tmp
mkdir -p test_cells.tmp
cd test_cells.tmp
-../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod'
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor'
for fn in test_*.il; do
../../../yosys -p "
diff --git a/backends/cxxrtl/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 549404184..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;
@@ -513,10 +522,11 @@ struct CxxrtlWorker {
bool elide_public = false;
bool localize_internal = false;
bool localize_public = false;
- bool run_opt_clean_purge = false;
bool run_proc_flatten = false;
bool max_opt_level = false;
+ bool debug_info = false;
+
std::ostringstream f;
std::string indent;
int temporary = 0;
@@ -1594,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()) {
@@ -1642,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";
@@ -1690,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);
@@ -1704,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";
@@ -1722,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()) {
@@ -1735,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()) {
@@ -1756,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";
@@ -1766,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("");
}
@@ -1775,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";
@@ -1785,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("");
}
@@ -2009,6 +2105,7 @@ struct CxxrtlWorker {
log("Module `%s' contains feedback arcs through wires:\n", log_id(module));
for (auto wire : feedback_wires)
log(" %s\n", log_id(wire));
+ log("\n");
}
for (auto wire : module->wires()) {
@@ -2040,20 +2137,20 @@ struct CxxrtlWorker {
log("Module `%s' contains buffered combinatorial wires:\n", log_id(module));
for (auto wire : buffered_wires)
log(" %s\n", log_id(wire));
+ log("\n");
}
eval_converges[module] = feedback_wires.empty() && buffered_wires.empty();
}
if (has_feedback_arcs || has_buffered_wires) {
// Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated
- // by optimizing the design, if after `opt_clean -purge` there are any feedback wires remaining, it is very
+ // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very
// likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message.
const char *why_pessimistic = nullptr;
if (has_feedback_arcs)
why_pessimistic = "feedback wires";
else if (has_buffered_wires)
why_pessimistic = "buffered combinatorial wires";
- log("\n");
log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic);
if (!max_opt_level)
log("Increasing the optimization level may eliminate %s from the design.\n", why_pessimistic);
@@ -2087,34 +2184,40 @@ struct CxxrtlWorker {
void prepare_design(RTLIL::Design *design)
{
+ bool did_anything = false;
bool has_sync_init, has_packed_mem;
log_push();
check_design(design, has_sync_init, has_packed_mem);
if (run_proc_flatten) {
Pass::call(design, "proc");
Pass::call(design, "flatten");
+ did_anything = true;
} else if (has_sync_init) {
// We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those
// in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.)
Pass::call(design, "proc_prune");
Pass::call(design, "proc_clean");
Pass::call(design, "proc_init");
+ did_anything = true;
}
- if (has_packed_mem)
+ if (has_packed_mem) {
Pass::call(design, "memory_unpack");
+ did_anything = true;
+ }
// Recheck the design if it was modified.
if (has_sync_init || has_packed_mem)
check_design(design, has_sync_init, has_packed_mem);
log_assert(!(has_sync_init || has_packed_mem));
- if (run_opt_clean_purge)
- Pass::call(design, "opt_clean -purge");
log_pop();
+ if (did_anything)
+ log_spacer();
analyze_design(design);
}
};
struct CxxrtlBackend : public Backend {
- static const int DEFAULT_OPT_LEVEL = 6;
+ static const int DEFAULT_OPT_LEVEL = 5;
+ static const int DEFAULT_DEBUG_LEVEL = 1;
CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { }
void help() YS_OVERRIDE
@@ -2308,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");
@@ -2327,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;
@@ -2340,6 +2463,7 @@ struct CxxrtlBackend : public Backend {
extra_args(f, filename, args, argidx);
switch (opt_level) {
+ // the highest level here must match DEFAULT_OPT_LEVEL
case 5:
worker.max_opt_level = true;
worker.run_proc_flatten = true;
@@ -2362,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/firrtl/firrtl.cc b/backends/firrtl/firrtl.cc
index f6dae1d8c..b1d8500bb 100644
--- a/backends/firrtl/firrtl.cc
+++ b/backends/firrtl/firrtl.cc
@@ -392,7 +392,34 @@ struct FirrtlWorker
return result;
}
- void run()
+ void emit_extmodule()
+ {
+ std::string moduleFileinfo = getFileinfo(module);
+ f << stringf(" extmodule %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
+ vector<std::string> port_decls;
+
+ for (auto wire : module->wires())
+ {
+ const auto wireName = make_id(wire->name);
+ std::string wireFileinfo = getFileinfo(wire);
+
+ if (wire->port_input && wire->port_output)
+ {
+ log_error("Module port %s.%s is inout!\n", log_id(module), log_id(wire));
+ }
+ port_decls.push_back(stringf(" %s %s: UInt<%d> %s\n", wire->port_input ? "input" : "output",
+ wireName, wire->width, wireFileinfo.c_str()));
+ }
+
+ for (auto &str : port_decls)
+ {
+ f << str;
+ }
+
+ f << stringf("\n");
+ }
+
+ void emit_module()
{
std::string moduleFileinfo = getFileinfo(module);
f << stringf(" module %s: %s\n", make_id(module->name), moduleFileinfo.c_str());
@@ -446,7 +473,7 @@ struct FirrtlWorker
string y_id = make_id(cell->name);
std::string cellFileinfo = getFileinfo(cell);
- if (cell->type.in(ID($not), ID($logic_not), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))
+ if (cell->type.in(ID($not), ID($logic_not), ID($_NOT_), ID($neg), ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_bool), ID($reduce_xnor)))
{
string a_expr = make_expr(cell->getPort(ID::A));
wire_decls.push_back(stringf(" wire %s: UInt<%d> %s\n", y_id.c_str(), y_width, cellFileinfo.c_str()));
@@ -462,7 +489,7 @@ struct FirrtlWorker
// Assume the FIRRTL width is a single bit.
firrtl_width = 1;
- if (cell->type == ID($not)) primop = "not";
+ if (cell->type.in(ID($not), ID($_NOT_))) primop = "not";
else if (cell->type == ID($neg)) {
primop = "neg";
firrtl_is_signed = true; // Result of "neg" is signed (an SInt).
@@ -494,7 +521,7 @@ struct FirrtlWorker
continue;
}
- if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or), ID($eq), ID($eqx),
+ if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_), ID($eq), ID($eqx),
ID($gt), ID($ge), ID($lt), ID($le), ID($ne), ID($nex), ID($shr), ID($sshr), ID($sshl), ID($shl),
ID($logic_and), ID($logic_or), ID($pow)))
{
@@ -524,7 +551,7 @@ struct FirrtlWorker
// For the arithmetic ops, expand operand widths to result widths befor performing the operation.
// This corresponds (according to iverilog) to what verilog compilers implement.
- if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($xnor), ID($and), ID($or)))
+ if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($xor), ID($_XOR_), ID($xnor), ID($and), ID($_AND_), ID($or), ID($_OR_)))
{
if (a_width < y_width) {
a_expr = stringf("pad(%s, %d)", a_expr.c_str(), y_width);
@@ -558,19 +585,20 @@ struct FirrtlWorker
firrtl_is_signed = a_signed | b_signed;
firrtl_width = a_width;
} else if (cell->type == ID($mod)) {
+ // "rem" = truncating modulo
primop = "rem";
firrtl_width = min(a_width, b_width);
- } else if (cell->type == ID($and)) {
+ } else if (cell->type.in(ID($and), ID($_AND_))) {
primop = "and";
always_uint = true;
firrtl_width = max(a_width, b_width);
}
- else if (cell->type == ID($or) ) {
+ else if (cell->type.in(ID($or), ID($_OR_))) {
primop = "or";
always_uint = true;
firrtl_width = max(a_width, b_width);
}
- else if (cell->type == ID($xor)) {
+ else if (cell->type.in(ID($xor), ID($_XOR_))) {
primop = "xor";
always_uint = true;
firrtl_width = max(a_width, b_width);
@@ -694,7 +722,8 @@ struct FirrtlWorker
}
}
- if (!cell->parameters.at(ID::B_SIGNED).as_bool()) {
+ auto it = cell->parameters.find(ID::B_SIGNED);
+ if (it == cell->parameters.end() || !it->second.as_bool()) {
b_expr = "asUInt(" + b_expr + ")";
}
@@ -723,9 +752,10 @@ struct FirrtlWorker
continue;
}
- if (cell->type.in(ID($mux)))
+ if (cell->type.in(ID($mux), ID($_MUX_)))
{
- int width = cell->parameters.at(ID::WIDTH).as_int();
+ auto it = cell->parameters.find(ID::WIDTH);
+ int width = it == cell->parameters.end()? 1 : it->second.as_int();
string a_expr = make_expr(cell->getPort(ID::A));
string b_expr = make_expr(cell->getPort(ID::B));
string s_expr = make_expr(cell->getPort(ID::S));
@@ -1076,6 +1106,18 @@ struct FirrtlWorker
for (auto str : wire_exprs)
f << str;
+
+ f << stringf("\n");
+ }
+
+ void run()
+ {
+ // Blackboxes should be emitted as `extmodule`s in firrtl. Only ports are
+ // emitted in such a case.
+ if (module->get_blackbox_attribute())
+ emit_extmodule();
+ else
+ emit_module();
}
};
diff --git a/backends/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/smt2.cc b/backends/smt2/smt2.cc
index 3e67e55f2..26f17bcb3 100644
--- a/backends/smt2/smt2.cc
+++ b/backends/smt2/smt2.cc
@@ -590,7 +590,17 @@ struct Smt2Worker
if (cell->type == ID($sub)) return export_bvop(cell, "(bvsub A B)");
if (cell->type == ID($mul)) return export_bvop(cell, "(bvmul A B)");
if (cell->type == ID($div)) return export_bvop(cell, "(bvUdiv A B)", 'd');
+ // "rem" = truncating modulo
if (cell->type == ID($mod)) return export_bvop(cell, "(bvUrem A B)", 'd');
+ // "mod" = flooring modulo
+ if (cell->type == ID($modfloor)) {
+ // bvumod doesn't exist because it's the same as bvurem
+ if (cell->getParam(ID::A_SIGNED).as_bool()) {
+ return export_bvop(cell, "(bvsmod A B)", 'd');
+ } else {
+ return export_bvop(cell, "(bvurem A B)", 'd');
+ }
+ }
if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool)) &&
2*GetSize(cell->getPort(ID::A).chunks()) < GetSize(cell->getPort(ID::A))) {
diff --git a/backends/smt2/smtbmc.py b/backends/smt2/smtbmc.py
index cc3ebb129..03f001bfd 100644
--- a/backends/smt2/smtbmc.py
+++ b/backends/smt2/smtbmc.py
@@ -1557,8 +1557,9 @@ else: # not tempind, covermode
smt_assert(get_constr_expr(constr_asserts, i))
print_msg("Solving for step %d.." % (last_check_step))
- if smt_check_sat() != "sat":
- print("%s No solution found!" % smt.timestamp())
+ status = smt_check_sat()
+ if status != "sat":
+ print("%s No solution found! (%s)" % (smt.timestamp(), status))
retstatus = "FAILED"
break
diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py
index 69f59df79..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:
@@ -121,6 +121,7 @@ class SmtIo:
self.logic_bv = True
self.logic_dt = False
self.forall = False
+ self.timeout = 0
self.produce_models = True
self.smt2cache = [list()]
self.p = None
@@ -135,6 +136,7 @@ class SmtIo:
self.debug_file = opts.debug_file
self.dummy_file = opts.dummy_file
self.timeinfo = opts.timeinfo
+ self.timeout = opts.timeout
self.unroll = opts.unroll
self.noincr = opts.noincr
self.info_stmts = opts.info_stmts
@@ -147,6 +149,7 @@ class SmtIo:
self.debug_file = None
self.dummy_file = None
self.timeinfo = os.name != "nt"
+ self.timeout = 0
self.unroll = False
self.noincr = False
self.info_stmts = list()
@@ -172,22 +175,32 @@ class SmtIo:
self.unroll = False
if self.solver == "yices":
- if self.noincr:
+ if self.noincr or self.forall:
self.popen_vargs = ['yices-smt2'] + self.solver_opts
else:
self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts
+ if self.timeout != 0:
+ self.popen_vargs.append('-t')
+ self.popen_vargs.append('%d' % self.timeout);
if self.solver == "z3":
self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts
+ if self.timeout != 0:
+ self.popen_vargs.append('-T:%d' % self.timeout);
if self.solver == "cvc4":
if self.noincr:
self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
else:
self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts
+ if self.timeout != 0:
+ self.popen_vargs.append('--tlimit=%d000' % self.timeout);
if self.solver == "mathsat":
self.popen_vargs = ['mathsat'] + self.solver_opts
+ if self.timeout != 0:
+ print('timeout option is not supported for mathsat.')
+ sys.exit(1)
if self.solver == "boolector":
if self.noincr:
@@ -195,6 +208,9 @@ class SmtIo:
else:
self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts
self.unroll = True
+ if self.timeout != 0:
+ print('timeout option is not supported for boolector.')
+ sys.exit(1)
if self.solver == "abc":
if len(self.solver_opts) > 0:
@@ -204,6 +220,9 @@ class SmtIo:
self.logic_ax = False
self.unroll = True
self.noincr = True
+ if self.timeout != 0:
+ print('timeout option is not supported for abc.')
+ sys.exit(1)
if self.solver == "dummy":
assert self.dummy_file is not None
@@ -232,12 +251,16 @@ class SmtIo:
if self.logic_uf: self.logic += "UF"
if self.logic_bv: self.logic += "BV"
if self.logic_dt: self.logic = "ALL"
+ if self.solver == "yices" and self.forall: self.logic = "BV"
self.setup_done = True
for stmt in self.info_stmts:
self.write(stmt)
+ if self.forall and self.solver == "yices":
+ self.write("(set-option :yices-ef-max-iters 1000000000)")
+
if self.produce_models:
self.write("(set-option :produce-models true)")
@@ -706,7 +729,7 @@ class SmtIo:
if self.forall:
result = self.read()
- while result not in ["sat", "unsat", "unknown"]:
+ while result not in ["sat", "unsat", "unknown", "timeout", "interrupted", ""]:
print("%s %s: %s" % (self.timestamp(), self.solver, result))
result = self.read()
else:
@@ -717,7 +740,7 @@ class SmtIo:
print("(check-sat)", file=self.debug_file)
self.debug_file.flush()
- if result not in ["sat", "unsat"]:
+ if result not in ["sat", "unsat", "unknown", "timeout", "interrupted"]:
if result == "":
print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True)
else:
@@ -927,7 +950,7 @@ class SmtIo:
class SmtOpts:
def __init__(self):
self.shortopts = "s:S:v"
- self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
+ self.longopts = ["unroll", "noincr", "noprogress", "timeout=", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"]
self.solver = "yices"
self.solver_opts = list()
self.debug_print = False
@@ -936,6 +959,7 @@ class SmtOpts:
self.unroll = False
self.noincr = False
self.timeinfo = os.name != "nt"
+ self.timeout = 0
self.logic = None
self.info_stmts = list()
self.nocomments = False
@@ -945,6 +969,8 @@ class SmtOpts:
self.solver = a
elif o == "-S":
self.solver_opts.append(a)
+ elif o == "--timeout":
+ self.timeout = int(a)
elif o == "-v":
self.debug_print = True
elif o == "--unroll":
@@ -976,6 +1002,9 @@ class SmtOpts:
-S <opt>
pass <opt> as command line argument to the solver
+ --timeout <value>
+ set the solver timeout to the specified value (in seconds).
+
--logic <smt2_logic>
use the specified SMT2 logic (e.g. QF_AUFBV)
diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc
index 7113ebc97..2fc7099f4 100644
--- a/backends/smv/smv.cc
+++ b/backends/smv/smv.cc
@@ -358,7 +358,8 @@ struct SmvWorker
continue;
}
- if (cell->type.in(ID($div), ID($mod)))
+ // SMV has a "mod" operator, but its semantics don't seem to be well-defined - to be safe, don't generate it at all
+ if (cell->type.in(ID($div)/*, ID($mod), ID($modfloor)*/))
{
int width_y = GetSize(cell->getPort(ID::Y));
int width = max(width_y, GetSize(cell->getPort(ID::A)));
@@ -366,7 +367,7 @@ struct SmvWorker
string expr_a, expr_b, op;
if (cell->type == ID($div)) op = "/";
- if (cell->type == ID($mod)) op = "mod";
+ //if (cell->type == ID($mod)) op = "mod";
if (cell->getParam(ID::A_SIGNED).as_bool())
{
diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh
index 63de465c0..145b9c33b 100644
--- a/backends/smv/test_cells.sh
+++ b/backends/smv/test_cells.sh
@@ -7,8 +7,8 @@ mkdir -p test_cells.tmp
cd test_cells.tmp
# don't test $mul to reduce runtime
-# don't test $div and $mod to reduce runtime and avoid "div by zero" message
-../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod'
+# don't test $div/$mod/$divfloor/$modfloor to reduce runtime and avoid "div by zero" message
+../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$divfloor /$modfloor'
cat > template.txt << "EOT"
%module main
diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc
index 11b2ae10f..4f44a053a 100644
--- a/backends/verilog/verilog_backend.cc
+++ b/backends/verilog/verilog_backend.cc
@@ -740,6 +740,95 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
#undef HANDLE_UNIOP
#undef HANDLE_BINOP
+ if (cell->type == ID($divfloor))
+ {
+ // wire [MAXLEN+1:0] _0_, _1_, _2_;
+ // assign _0_ = $signed(A);
+ // assign _1_ = $signed(B);
+ // assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1));
+ // assign Y = $signed(_2_) / $signed(_1_);
+
+ if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+ SigSpec sig_a = cell->getPort(ID::A);
+ SigSpec sig_b = cell->getPort(ID::B);
+
+ std::string buf_a = next_auto_id();
+ std::string buf_b = next_auto_id();
+ std::string buf_num = next_auto_id();
+ int size_a = GetSize(sig_a);
+ int size_b = GetSize(sig_b);
+ int size_y = GetSize(cell->getPort(ID::Y));
+ int size_max = std::max(size_a, std::max(size_b, size_y));
+
+ // intentionally one wider than maximum width
+ f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str());
+ f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str());
+ dump_cell_expr_port(f, cell, "A", true);
+ f << stringf(";\n");
+ f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str());
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(";\n");
+
+ f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str());
+ f << stringf("(");
+ dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+ f << stringf(" == ");
+ dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+ f << stringf(") || ");
+ dump_sigspec(f, sig_a);
+ f << stringf(" == 0 ? %s : ", buf_a.c_str());
+ f << stringf("$signed(%s - (", buf_a.c_str());
+ dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+ f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str());
+
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort(ID::Y));
+ f << stringf(" = $signed(%s) / ", buf_num.c_str());
+ dump_attributes(f, "", cell->attributes, ' ');
+ f << stringf("$signed(%s);\n", buf_b.c_str());
+ return true;
+ } else {
+ // same as truncating division
+ dump_cell_expr_binop(f, indent, cell, "/");
+ return true;
+ }
+ }
+
+ if (cell->type == ID($modfloor))
+ {
+ // wire truncated = $signed(A) % $signed(B);
+ // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated);
+
+ if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) {
+ SigSpec sig_a = cell->getPort(ID::A);
+ SigSpec sig_b = cell->getPort(ID::B);
+
+ std::string temp_id = next_auto_id();
+ f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str());
+ dump_cell_expr_port(f, cell, "A", true);
+ f << stringf(" %% ");
+ dump_attributes(f, "", cell->attributes, ' ');
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(";\n");
+
+ f << stringf("%s" "assign ", indent.c_str());
+ dump_sigspec(f, cell->getPort(ID::Y));
+ f << stringf(" = (");
+ dump_sigspec(f, sig_a.extract(sig_a.size()-1));
+ f << stringf(" == ");
+ dump_sigspec(f, sig_b.extract(sig_b.size()-1));
+ f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str());
+ dump_cell_expr_port(f, cell, "B", true);
+ f << stringf(" + $signed(%s);\n", temp_id.c_str());
+ return true;
+ } else {
+ // same as truncating modulo
+ dump_cell_expr_binop(f, indent, cell, "%");
+ return true;
+ }
+ }
+
if (cell->type == ID($shift))
{
f << stringf("%s" "assign ", indent.c_str());
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 3314819fb..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) {
@@ -3540,8 +3857,8 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg
}
}
- // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg'
- if (type == AST_MEMORY && (get_bool_attribute(ID::mem2reg) || (flags & AstNode::MEM2REG_FL_ALL) || !is_reg))
+ // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg' or 'logic'
+ if (type == AST_MEMORY && (get_bool_attribute(ID::mem2reg) || (flags & AstNode::MEM2REG_FL_ALL) || !(is_reg || is_logic)))
mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED;
if (type == AST_MODULE && get_bool_attribute(ID::mem2reg))
diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l
index 62f53d18e..3362ed641 100644
--- a/frontends/ilang/ilang_lexer.l
+++ b/frontends/ilang/ilang_lexer.l
@@ -91,8 +91,10 @@ USING_YOSYS_NAMESPACE
[0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; }
-?[0-9]+ {
char *end = nullptr;
+ errno = 0;
long value = strtol(yytext, &end, 10);
- if (end != yytext + strlen(yytext))
+ log_assert(end == yytext + strlen(yytext));
+ if (errno == ERANGE)
return TOK_INVALID; // literal out of range of long
if (value < INT_MIN || value > INT_MAX)
return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms)
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/verific/verific.cc b/frontends/verific/verific.cc
index fe4bda68e..cb0368fd5 100644
--- a/frontends/verific/verific.cc
+++ b/frontends/verific/verific.cc
@@ -974,6 +974,7 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
module->memories[memory->name] = memory;
int number_of_bits = net->Size();
+ number_of_bits = 1 << ceil_log2(number_of_bits);
int bits_in_word = number_of_bits;
FOREACH_PORTREF_OF_NET(net, si, pr) {
if (pr->GetInst()->Type() == OPER_READ_PORT) {
@@ -1265,9 +1266,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
int numchunks = int(inst->OutputSize()) / memory->width;
int chunksbits = ceil_log2(numchunks);
- if ((numchunks * memory->width) != int(inst->OutputSize()) || (numchunks & (numchunks - 1)) != 0)
- log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetInput()->Name());
-
for (int i = 0; i < numchunks; i++)
{
RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)};
@@ -1295,9 +1293,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::se
int numchunks = int(inst->Input2Size()) / memory->width;
int chunksbits = ceil_log2(numchunks);
- if ((numchunks * memory->width) != int(inst->Input2Size()) || (numchunks & (numchunks - 1)) != 0)
- log_error("Import of asymmetric memories of this type is not supported yet: %s %s\n", inst->Name(), inst->GetOutput()->Name());
-
for (int i = 0; i < numchunks; i++)
{
RTLIL::SigSpec addr = {operatorInput1(inst), RTLIL::Const(i, chunksbits)};
diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l
index f6a3ac4db..e6fa6361e 100644
--- a/frontends/verilog/verilog_lexer.l
+++ b/frontends/verilog/verilog_lexer.l
@@ -48,16 +48,18 @@ USING_YOSYS_NAMESPACE
using namespace AST;
using namespace VERILOG_FRONTEND;
+#define YYSTYPE FRONTEND_VERILOG_YYSTYPE
+#define YYLTYPE FRONTEND_VERILOG_YYLTYPE
+
YOSYS_NAMESPACE_BEGIN
namespace VERILOG_FRONTEND {
std::vector<std::string> fn_stack;
std::vector<int> ln_stack;
+ YYLTYPE real_location;
+ YYLTYPE old_location;
}
YOSYS_NAMESPACE_END
-#define YYSTYPE FRONTEND_VERILOG_YYSTYPE
-#define YYLTYPE FRONTEND_VERILOG_YYLTYPE
-
#define SV_KEYWORD(_tok) \
if (sv_mode) return _tok; \
log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\
@@ -73,9 +75,6 @@ YOSYS_NAMESPACE_END
#define YY_INPUT(buf,result,max_size) \
result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size)
-YYLTYPE real_location;
-YYLTYPE old_location;
-
#define YY_USER_ACTION \
old_location = real_location; \
real_location.first_line = real_location.last_line; \
@@ -128,7 +127,9 @@ static bool isUserType(std::string &s)
%x BASED_CONST
%%
- int comment_caller;
+ // Initialise comment_caller to something to avoid a "maybe undefined"
+ // warning from GCC.
+ int comment_caller = INITIAL;
<INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* {
fn_stack.push_back(current_filename);
@@ -262,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); }
@@ -276,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/calc.cc b/kernel/calc.cc
index 4a4840771..ae18809d3 100644
--- a/kernel/calc.cc
+++ b/kernel/calc.cc
@@ -489,6 +489,7 @@ RTLIL::Const RTLIL::const_mul(const RTLIL::Const &arg1, const RTLIL::Const &arg2
return big2const(y, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
}
+// truncating division
RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
{
int undef_bit_pos = -1;
@@ -502,6 +503,7 @@ RTLIL::Const RTLIL::const_div(const RTLIL::Const &arg1, const RTLIL::Const &arg2
return big2const(result_neg ? -(a / b) : (a / b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
}
+// truncating modulo
RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
{
int undef_bit_pos = -1;
@@ -515,6 +517,51 @@ RTLIL::Const RTLIL::const_mod(const RTLIL::Const &arg1, const RTLIL::Const &arg2
return big2const(result_neg ? -(a % b) : (a % b), result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
}
+RTLIL::Const RTLIL::const_divfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos);
+ if (b.isZero())
+ return RTLIL::Const(RTLIL::State::Sx, result_len);
+
+ bool result_pos = (a.getSign() == BigInteger::negative) == (b.getSign() == BigInteger::negative);
+ a = a.getSign() == BigInteger::negative ? -a : a;
+ b = b.getSign() == BigInteger::negative ? -b : b;
+ BigInteger result;
+
+ if (result_pos || a == 0) {
+ result = a / b;
+ } else {
+ // bigint division with negative numbers is wonky, make sure we only negate at the very end
+ result = -((a + b - 1) / b);
+ }
+ return big2const(result, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
+RTLIL::Const RTLIL::const_modfloor(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
+{
+ int undef_bit_pos = -1;
+ BigInteger a = const2big(arg1, signed1, undef_bit_pos);
+ BigInteger b = const2big(arg2, signed2, undef_bit_pos);
+ if (b.isZero())
+ return RTLIL::Const(RTLIL::State::Sx, result_len);
+
+ BigInteger::Sign a_sign = a.getSign();
+ BigInteger::Sign b_sign = b.getSign();
+ a = a_sign == BigInteger::negative ? -a : a;
+ b = b_sign == BigInteger::negative ? -b : b;
+ BigInteger truncated = a_sign == BigInteger::negative ? -(a % b) : (a % b);
+ BigInteger modulo;
+
+ if (truncated == 0 || (a_sign == b_sign)) {
+ modulo = truncated;
+ } else {
+ modulo = b_sign == BigInteger::negative ? truncated - b : truncated + b;
+ }
+ return big2const(modulo, result_len >= 0 ? result_len : max(arg1.bits.size(), arg2.bits.size()), min(undef_bit_pos, 0));
+}
+
RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len)
{
int undef_bit_pos = -1;
diff --git a/kernel/celledges.cc b/kernel/celledges.cc
index 54e0168e2..314e7c77e 100644
--- a/kernel/celledges.cc
+++ b/kernel/celledges.cc
@@ -187,7 +187,7 @@ bool YOSYS_NAMESPACE_PREFIX AbstractCellEdgesDatabase::add_edges_from_cell(RTLIL
return true;
}
- // FIXME: $mul $div $mod $slice $concat
+ // FIXME: $mul $div $mod $divfloor $modfloor $slice $concat
// FIXME: $lut $sop $alu $lcu $macc $fa
return false;
diff --git a/kernel/celltypes.h b/kernel/celltypes.h
index 450865ce9..db54436cb 100644
--- a/kernel/celltypes.h
+++ b/kernel/celltypes.h
@@ -114,7 +114,7 @@ struct CellTypes
ID($and), ID($or), ID($xor), ID($xnor),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
- ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($pow),
+ ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
ID($logic_and), ID($logic_or), ID($concat), ID($macc)
};
@@ -304,6 +304,8 @@ struct CellTypes
HANDLE_CELL_TYPE(mul)
HANDLE_CELL_TYPE(div)
HANDLE_CELL_TYPE(mod)
+ HANDLE_CELL_TYPE(divfloor)
+ HANDLE_CELL_TYPE(modfloor)
HANDLE_CELL_TYPE(pow)
HANDLE_CELL_TYPE(pos)
HANDLE_CELL_TYPE(neg)
diff --git a/kernel/constids.inc b/kernel/constids.inc
index 93c3a7609..383d7c615 100644
--- a/kernel/constids.inc
+++ b/kernel/constids.inc
@@ -171,6 +171,7 @@ X(techmap_autopurge)
X(_TECHMAP_BITS_CONNMAP_)
X(_TECHMAP_CELLTYPE_)
X(techmap_celltype)
+X(_TECHMAP_FAIL_)
X(techmap_maccmap)
X(_TECHMAP_REPLACE_)
X(techmap_simplemap)
diff --git a/kernel/hashlib.h b/kernel/hashlib.h
index 592d6e577..5c87b55f5 100644
--- a/kernel/hashlib.h
+++ b/kernel/hashlib.h
@@ -207,6 +207,7 @@ class dict
entry_t() { }
entry_t(const std::pair<K, T> &udata, int next) : udata(udata), next(next) { }
entry_t(std::pair<K, T> &&udata, int next) : udata(std::move(udata)), next(next) { }
+ bool operator<(const entry_t &other) const { return udata.first < other.udata.first; }
};
std::vector<int> hashtable;
@@ -615,6 +616,15 @@ public:
return !operator==(other);
}
+ unsigned int hash() const {
+ unsigned int h = mkhash_init;
+ for (auto &entry : entries) {
+ h ^= hash_ops<K>::hash(entry.udata.first);
+ h ^= hash_ops<T>::hash(entry.udata.second);
+ }
+ return h;
+ }
+
void reserve(size_t n) { entries.reserve(n); }
size_t size() const { return entries.size(); }
bool empty() const { return entries.empty(); }
diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc
index 196e301b6..109113370 100644
--- a/kernel/rtlil.cc
+++ b/kernel/rtlil.cc
@@ -948,7 +948,7 @@ namespace {
return;
}
- if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($pow))) {
+ if (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow))) {
param_bool(ID::A_SIGNED);
param_bool(ID::B_SIGNED);
port(ID::A, param(ID::A_WIDTH));
@@ -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;
}
@@ -1884,6 +1885,18 @@ RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *oth
return cell;
}
+RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memory *other)
+{
+ RTLIL::Memory *mem = new RTLIL::Memory;
+ mem->name = name;
+ mem->width = other->width;
+ mem->start_offset = other->start_offset;
+ mem->size = other->size;
+ mem->attributes = other->attributes;
+ memories[mem->name] = mem;
+ return mem;
+}
+
#define DEF_METHOD(_func, _y_size, _type) \
RTLIL::Cell* RTLIL::Module::add ## _func(RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed, const std::string &src) { \
RTLIL::Cell *cell = addCell(name, _type); \
@@ -1949,6 +1962,8 @@ DEF_METHOD(Sub, max(sig_a.size(), sig_b.size()), ID($sub))
DEF_METHOD(Mul, max(sig_a.size(), sig_b.size()), ID($mul))
DEF_METHOD(Div, max(sig_a.size(), sig_b.size()), ID($div))
DEF_METHOD(Mod, max(sig_a.size(), sig_b.size()), ID($mod))
+DEF_METHOD(DivFloor, max(sig_a.size(), sig_b.size()), ID($divfloor))
+DEF_METHOD(ModFloor, max(sig_a.size(), sig_b.size()), ID($modfloor))
DEF_METHOD(LogicAnd, 1, ID($logic_and))
DEF_METHOD(LogicOr, 1, ID($logic_or))
#undef DEF_METHOD
@@ -2445,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 11c45bbec..86b4e25b6 100644
--- a/kernel/rtlil.h
+++ b/kernel/rtlil.h
@@ -150,9 +150,6 @@ namespace RTLIL
if (!p[0])
return 0;
- log_assert(p[0] == '$' || p[0] == '\\');
- log_assert(p[1] != 0);
-
auto it = global_id_index_.find((char*)p);
if (it != global_id_index_.end()) {
#ifndef YOSYS_NO_IDS_REFCNT
@@ -165,6 +162,11 @@ namespace RTLIL
return it->second;
}
+ log_assert(p[0] == '$' || p[0] == '\\');
+ log_assert(p[1] != 0);
+ for (const char *c = p; *c; c++)
+ log_assert((unsigned)*c > (unsigned)' ');
+
#ifndef YOSYS_NO_IDS_REFCNT
if (global_free_idx_list_.empty()) {
if (global_id_storage_.empty()) {
@@ -296,8 +298,8 @@ namespace RTLIL
// The methods below are just convenience functions for better compatibility with std::string.
- bool operator==(const std::string &rhs) const { return str() == rhs; }
- bool operator!=(const std::string &rhs) const { return str() != rhs; }
+ bool operator==(const std::string &rhs) const { return c_str() == rhs; }
+ bool operator!=(const std::string &rhs) const { return c_str() != rhs; }
bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; }
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
@@ -466,6 +468,8 @@ namespace RTLIL
RTLIL::Const const_sub (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
RTLIL::Const const_mul (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
RTLIL::Const const_div (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_divfloor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
+ RTLIL::Const const_modfloor (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
RTLIL::Const const_mod (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
RTLIL::Const const_pow (const RTLIL::Const &arg1, const RTLIL::Const &arg2, bool signed1, bool signed2, int result_len);
@@ -721,7 +725,7 @@ struct RTLIL::SigBit
SigBit(const RTLIL::SigChunk &chunk);
SigBit(const RTLIL::SigChunk &chunk, int index);
SigBit(const RTLIL::SigSpec &sig);
- SigBit(const RTLIL::SigBit &sigbit);
+ SigBit(const RTLIL::SigBit &sigbit) = default;
RTLIL::SigBit &operator =(const RTLIL::SigBit &other) = default;
bool operator <(const RTLIL::SigBit &other) const;
@@ -1134,8 +1138,14 @@ public:
return design->selected_member(name, member->name);
}
- RTLIL::Wire* wire(RTLIL::IdString id) { return wires_.count(id) ? wires_.at(id) : nullptr; }
- RTLIL::Cell* cell(RTLIL::IdString id) { return cells_.count(id) ? cells_.at(id) : nullptr; }
+ RTLIL::Wire* wire(RTLIL::IdString id) {
+ auto it = wires_.find(id);
+ return it == wires_.end() ? nullptr : it->second;
+ }
+ RTLIL::Cell* cell(RTLIL::IdString id) {
+ auto it = cells_.find(id);
+ return it == cells_.end() ? nullptr : it->second;
+ }
RTLIL::ObjRange<RTLIL::Wire*> wires() { return RTLIL::ObjRange<RTLIL::Wire*>(&wires_, &refcount_wires_); }
RTLIL::ObjRange<RTLIL::Cell*> cells() { return RTLIL::ObjRange<RTLIL::Cell*>(&cells_, &refcount_cells_); }
@@ -1160,6 +1170,8 @@ public:
RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type);
RTLIL::Cell *addCell(RTLIL::IdString name, const RTLIL::Cell *other);
+ RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other);
+
// The add* methods create a cell and return the created cell. All signals must exist in advance.
RTLIL::Cell* addNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
@@ -1196,8 +1208,12 @@ public:
RTLIL::Cell* addAdd (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addSub (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addMul (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
+ // truncating division
RTLIL::Cell* addDiv (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
+ // truncating modulo
RTLIL::Cell* addMod (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
+ RTLIL::Cell* addDivFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
+ RTLIL::Cell* addModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
RTLIL::Cell* addPow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, const RTLIL::SigSpec &sig_y, bool a_signed = false, bool b_signed = false, const std::string &src = "");
RTLIL::Cell* addLogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_y, bool is_signed = false, const std::string &src = "");
@@ -1222,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 = "");
@@ -1295,8 +1308,12 @@ public:
RTLIL::SigSpec Add (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Sub (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Mul (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
+ // truncating division
RTLIL::SigSpec Div (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
+ // truncating modulo
RTLIL::SigSpec Mod (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
+ RTLIL::SigSpec DivFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
+ RTLIL::SigSpec ModFloor (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool is_signed = false, const std::string &src = "");
RTLIL::SigSpec Pow (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, const RTLIL::SigSpec &sig_b, bool a_signed = false, bool b_signed = false, const std::string &src = "");
RTLIL::SigSpec LogicNot (RTLIL::IdString name, const RTLIL::SigSpec &sig_a, bool is_signed = false, const std::string &src = "");
@@ -1353,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);
@@ -1494,7 +1511,6 @@ inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire) : wire(wire), offset(0) { log_as
inline RTLIL::SigBit::SigBit(RTLIL::Wire *wire, int offset) : wire(wire), offset(offset) { log_assert(wire != nullptr); }
inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk) : wire(chunk.wire) { log_assert(chunk.width == 1); if (wire) offset = chunk.offset; else data = chunk.data[0]; }
inline RTLIL::SigBit::SigBit(const RTLIL::SigChunk &chunk, int index) : wire(chunk.wire) { if (wire) offset = chunk.offset + index; else data = chunk.data[index]; }
-inline RTLIL::SigBit::SigBit(const RTLIL::SigBit &sigbit) : wire(sigbit.wire), data(sigbit.data){ if (wire) offset = sigbit.offset; }
inline bool RTLIL::SigBit::operator<(const RTLIL::SigBit &other) const {
if (wire == other.wire)
diff --git a/kernel/satgen.h b/kernel/satgen.h
index 88b84b7e6..3929a8708 100644
--- a/kernel/satgen.h
+++ b/kernel/satgen.h
@@ -279,7 +279,7 @@ struct SatGen
bool arith_undef_handled = false;
bool is_arith_compare = cell->type.in(ID($lt), ID($le), ID($ge), ID($gt));
- if (model_undef && (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod)) || is_arith_compare))
+ if (model_undef && (cell->type.in(ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor)) || is_arith_compare))
{
std::vector<int> undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep);
@@ -293,7 +293,7 @@ struct SatGen
int undef_any_b = ez->expression(ezSAT::OpOr, undef_b);
int undef_y_bit = ez->OR(undef_any_a, undef_any_b);
- if (cell->type.in(ID($div), ID($mod))) {
+ if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
std::vector<int> b = importSigSpec(cell->getPort(ID::B), timestep);
undef_y_bit = ez->OR(undef_y_bit, ez->NOT(ez->expression(ezSAT::OpOr, b)));
}
@@ -935,7 +935,7 @@ struct SatGen
return true;
}
- if (cell->type.in(ID($div), ID($mod)))
+ if (cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
{
std::vector<int> a = importDefSigSpec(cell->getPort(ID::A), timestep);
std::vector<int> b = importDefSigSpec(cell->getPort(ID::B), timestep);
@@ -970,23 +970,48 @@ struct SatGen
}
std::vector<int> y_tmp = ignore_div_by_zero ? yy : ez->vec_var(y.size());
+
+ // modulo calculation
+ std::vector<int> modulo_trunc;
+ int floored_eq_trunc;
+ if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
+ modulo_trunc = ez->vec_ite(a.back(), ez->vec_neg(chain_buf), chain_buf);
+ // floor == trunc when sgn(a) == sgn(b) or trunc == 0
+ floored_eq_trunc = ez->OR(ez->IFF(a.back(), b.back()), ez->NOT(ez->expression(ezSAT::OpOr, modulo_trunc)));
+ } else {
+ modulo_trunc = chain_buf;
+ floored_eq_trunc = ez->CONST_TRUE;
+ }
+
if (cell->type == ID($div)) {
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(ez->XOR(a.back(), b.back()), ez->vec_neg(y_u), y_u)));
else
ez->assume(ez->vec_eq(y_tmp, y_u));
- } else {
+ } else if (cell->type == ID($mod)) {
+ ez->assume(ez->vec_eq(y_tmp, modulo_trunc));
+ } else if (cell->type == ID($divfloor)) {
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
- ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(a.back(), ez->vec_neg(chain_buf), chain_buf)));
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(
+ ez->XOR(a.back(), b.back()),
+ ez->vec_neg(ez->vec_ite(
+ ez->vec_reduce_or(modulo_trunc),
+ ez->vec_add(y_u, ez->vec_const_unsigned(1, y_u.size())),
+ y_u
+ )),
+ y_u
+ )));
else
- ez->assume(ez->vec_eq(y_tmp, chain_buf));
+ ez->assume(ez->vec_eq(y_tmp, y_u));
+ } else if (cell->type == ID($modfloor)) {
+ ez->assume(ez->vec_eq(y_tmp, ez->vec_ite(floored_eq_trunc, modulo_trunc, ez->vec_add(modulo_trunc, b))));
}
if (ignore_div_by_zero) {
ez->assume(ez->expression(ezSAT::OpOr, b));
} else {
std::vector<int> div_zero_result;
- if (cell->type == ID($div)) {
+ if (cell->type.in(ID($div), ID($divfloor))) {
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool()) {
std::vector<int> all_ones(y.size(), ez->CONST_TRUE);
std::vector<int> only_first_one(y.size(), ez->CONST_FALSE);
@@ -996,7 +1021,8 @@ struct SatGen
div_zero_result.insert(div_zero_result.end(), cell->getPort(ID::A).size(), ez->CONST_TRUE);
div_zero_result.insert(div_zero_result.end(), y.size() - div_zero_result.size(), ez->CONST_FALSE);
}
- } else {
+ } else if (cell->type.in(ID($mod), ID($modfloor))) {
+ // a mod 0 = a
int copy_a_bits = min(cell->getPort(ID::A).size(), cell->getPort(ID::B).size());
div_zero_result.insert(div_zero_result.end(), a.begin(), a.begin() + copy_a_bits);
if (cell->parameters[ID::A_SIGNED].as_bool() && cell->parameters[ID::B_SIGNED].as_bool())
diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex
index 55abd9b96..32c530582 100644
--- a/manual/CHAPTER_CellLib.tex
+++ b/manual/CHAPTER_CellLib.tex
@@ -139,6 +139,8 @@ Verilog & Cell Type \\
\lstinline[language=Verilog]; Y = A * B; & {\tt \$mul} \\
\lstinline[language=Verilog]; Y = A / B; & {\tt \$div} \\
\lstinline[language=Verilog]; Y = A % B; & {\tt \$mod} \\
+\multicolumn{1}{c}{\tt [N/A]} & {\tt \$divfloor} \\
+\multicolumn{1}{c}{\tt [N/A]} & {\tt \$modfoor} \\
\lstinline[language=Verilog]; Y = A ** B; & {\tt \$pow} \\
\end{tabular}
\caption{Cell types for binary operators with their corresponding Verilog expressions.}
@@ -161,6 +163,27 @@ For the binary cells that output a logical value ({\tt \$logic\_and}, {\tt \$log
{\tt \$gt}), when the \B{Y\_WIDTH} parameter is greater than 1, the output is zero-extended,
and only the least significant bit varies.
+Division and modulo cells are available in two rounding modes. The original {\tt \$div} and {\tt \$mod}
+cells are based on truncating division, and correspond to the semantics of the verilog {\tt /} and
+{\tt \%} operators. The {\tt \$divfloor} and {\tt \$modfloor} cells represent flooring division and
+flooring modulo, the latter of which is also known as ``remainder'' in several languages. See
+table~\ref{tab:CellLib_divmod} for a side-by-side comparison between the different semantics.
+
+\begin{table}[h]
+\hfil
+\begin{tabular}{lr|rr|rr}
+\multirow{2}{*}{Division} & \multirow{2}{*}{Result} & \multicolumn{2}{c|}{Truncating} & \multicolumn{2}{c}{Flooring} \\
+ & & {\tt \$div} & {\tt \$mod} & {\tt \$divfloor} & {\tt \$modfloor} \\
+\hline
+{\tt -10 / 3} & {\tt -3.3} & {\tt -3} & {\tt -1} & {\tt -4} & {\tt 2} \\
+{\tt 10 / -3} & {\tt -3.3} & {\tt -3} & {\tt 1} & {\tt -4} & {\tt -2} \\
+{\tt -10 / -3} & {\tt 3.3} & {\tt 3} & {\tt -1} & {\tt 3} & {\tt -1} \\
+{\tt 10 / 3} & {\tt 3.3} & {\tt 3} & {\tt 1} & {\tt 3} & {\tt 1} \\
+\end{tabular}
+\caption{Comparison between different rounding modes for division and modulo cells.}
+\label{tab:CellLib_divmod}
+\end{table}
+
\subsection{Multiplexers}
Multiplexers are generated by the Verilog HDL frontend for {\tt
diff --git a/manual/CHAPTER_Overview.tex b/manual/CHAPTER_Overview.tex
index be37d8d39..ac0f48e47 100644
--- a/manual/CHAPTER_Overview.tex
+++ b/manual/CHAPTER_Overview.tex
@@ -184,9 +184,12 @@ may hold important information for Yosys developers can be used without
disturbing external tools. For example the Verilog backend assigns names in the form {\tt \_{\it integer}\_}.
\end{itemize}
-In order to avoid programming errors, the RTLIL data structures check if all
-identifiers start with either a backslash or a dollar sign and generate a
-runtime error if this rule is violated.
+Whitespace and control characters (any character with an ASCII code 32 or less) are not allowed
+in RTLIL identifiers; most frontends and backends cannot support these characters in identifiers.
+
+In order to avoid programming errors, the RTLIL data structures check if all identifiers start
+with either a backslash or a dollar sign, and contain no whitespace or control characters.
+Violating these rules results in a runtime error.
All RTLIL identifiers are case sensitive.
diff --git a/manual/PRESENTATION_Prog.tex b/manual/PRESENTATION_Prog.tex
index b85eda892..a9416f82a 100644
--- a/manual/PRESENTATION_Prog.tex
+++ b/manual/PRESENTATION_Prog.tex
@@ -307,7 +307,7 @@ cell name from the internal cell library:
\begin{lstlisting}[xleftmargin=1cm, basicstyle=\ttfamily\fontsize{6pt}{7pt}\selectfont]
$not $pos $neg $and $or $xor $xnor $reduce_and $reduce_or $reduce_xor $reduce_xnor
$reduce_bool $shl $shr $sshl $sshr $lt $le $eq $ne $eqx $nex $ge $gt $add $sub $mul $div $mod
-$pow $logic_not $logic_and $logic_or $mux $pmux $slice $concat $lut $assert $sr $dff
+$divfloor $modfloor $pow $logic_not $logic_and $logic_or $mux $pmux $slice $concat $lut $assert $sr $dff
$dffsr $adff $dlatch $dlatchsr $memrd $memwr $mem $fsm $_NOT_ $_AND_ $_OR_ $_XOR_ $_MUX_ $_SR_NN_
$_SR_NP_ $_SR_PN_ $_SR_PP_ $_DFF_N_ $_DFF_P_ $_DFF_NN0_ $_DFF_NN1_ $_DFF_NP0_ $_DFF_NP1_ $_DFF_PN0_
$_DFF_PN1_ $_DFF_PP0_ $_DFF_PP1_ $_DFFSR_NNN_ $_DFFSR_NNP_ $_DFFSR_NPN_ $_DFFSR_NPP_ $_DFFSR_PNN_
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/cmds/Makefile.inc b/passes/cmds/Makefile.inc
index a88980eaf..53bfd40c6 100644
--- a/passes/cmds/Makefile.inc
+++ b/passes/cmds/Makefile.inc
@@ -39,3 +39,4 @@ OBJS += passes/cmds/bugpoint.o
endif
OBJS += passes/cmds/scratchpad.o
OBJS += passes/cmds/logger.o
+OBJS += passes/cmds/printattrs.o
diff --git a/passes/cmds/blackbox.cc b/passes/cmds/blackbox.cc
index 5c0405f15..b8297cd77 100644
--- a/passes/cmds/blackbox.cc
+++ b/passes/cmds/blackbox.cc
@@ -48,31 +48,7 @@ struct BlackboxPass : public Pass {
for (auto module : design->selected_whole_modules_warn())
{
- pool<Cell*> remove_cells;
- pool<Wire*> remove_wires;
-
- for (auto cell : module->cells())
- remove_cells.insert(cell);
-
- for (auto wire : module->wires())
- if (wire->port_id == 0)
- remove_wires.insert(wire);
-
- for (auto it = module->memories.begin(); it != module->memories.end(); ++it)
- delete it->second;
- module->memories.clear();
-
- for (auto it = module->processes.begin(); it != module->processes.end(); ++it)
- delete it->second;
- module->processes.clear();
-
- module->new_connections(std::vector<RTLIL::SigSig>());
-
- for (auto cell : remove_cells)
- module->remove(cell);
-
- module->remove(remove_wires);
-
+ module->makeblackbox();
module->set_bool_attribute(ID::blackbox);
}
}
diff --git a/passes/cmds/printattrs.cc b/passes/cmds/printattrs.cc
new file mode 100644
index 000000000..80dbfa259
--- /dev/null
+++ b/passes/cmds/printattrs.cc
@@ -0,0 +1,90 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2020 Alberto Gonzalez <boqwxp@airmail.cc>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct PrintAttrsPass : public Pass {
+ PrintAttrsPass() : Pass("printattrs", "print attributes of selected objects") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" printattrs [selection]\n");
+ log("\n");
+ log("Print all attributes of the selected objects.\n");
+ log("\n");
+ log("\n");
+ }
+
+ static std::string get_indent_str(const unsigned int indent) {
+ return stringf("%*s", indent, "");
+ }
+
+ static void log_const(const RTLIL::IdString &s, const RTLIL::Const &x, const unsigned int indent) {
+ if (x.flags == RTLIL::CONST_FLAG_STRING)
+ log("%s(* %s=\"%s\" *)\n", get_indent_str(indent).c_str(), log_id(s), x.decode_string().c_str());
+ else if (x.flags == RTLIL::CONST_FLAG_NONE)
+ log("%s(* %s=%s *)\n", get_indent_str(indent).c_str(), log_id(s), x.as_string().c_str());
+ else
+ log_assert(x.flags == RTLIL::CONST_FLAG_STRING || x.flags == RTLIL::CONST_FLAG_NONE); //intended to fail
+ }
+
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ size_t argidx = 1;
+ extra_args(args, argidx, design);
+
+ unsigned int indent = 0;
+ for (auto mod : design->selected_modules())
+ {
+ if (design->selected_whole_module(mod)) {
+ log("%s%s\n", get_indent_str(indent).c_str(), log_id(mod->name));
+ indent += 2;
+ for (auto &it : mod->attributes)
+ log_const(it.first, it.second, indent);
+ }
+
+ for (auto cell : mod->selected_cells()) {
+ log("%s%s\n", get_indent_str(indent).c_str(), log_id(cell->name));
+ indent += 2;
+ for (auto &it : cell->attributes)
+ log_const(it.first, it.second, indent);
+ indent -= 2;
+ }
+
+ for (auto wire : mod->selected_wires()) {
+ log("%s%s\n", get_indent_str(indent).c_str(), log_id(wire->name));
+ indent += 2;
+ for (auto &it : wire->attributes)
+ log_const(it.first, it.second, indent);
+ indent -= 2;
+ }
+
+ if (design->selected_whole_module(mod))
+ indent -= 2;
+ }
+
+ log("\n");
+ }
+} PrintAttrsPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/cmds/stat.cc b/passes/cmds/stat.cc
index 6c4bc0e5b..30436d829 100644
--- a/passes/cmds/stat.cc
+++ b/passes/cmds/stat.cc
@@ -109,7 +109,7 @@ struct statdata_t
ID($lut), ID($and), ID($or), ID($xor), ID($xnor),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
- ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($pow), ID($alu))) {
+ ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow), ID($alu))) {
int width_a = cell->hasPort(ID::A) ? GetSize(cell->getPort(ID::A)) : 0;
int width_b = cell->hasPort(ID::B) ? GetSize(cell->getPort(ID::B)) : 0;
int width_y = cell->hasPort(ID::Y) ? GetSize(cell->getPort(ID::Y)) : 0;
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/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc
index 95d74d1eb..b4eb0f1dd 100644
--- a/passes/hierarchy/hierarchy.cc
+++ b/passes/hierarchy/hierarchy.cc
@@ -254,16 +254,6 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
// some lists, so that the ports for sub-modules can be replaced further down:
for (auto &conn : cell->connections()) {
if(mod->wire(conn.first) != nullptr && mod->wire(conn.first)->get_bool_attribute(ID::is_interface)) { // Check if the connection is present as an interface in the sub-module's port list
- //const pool<string> &interface_type_pool = mod->wire(conn.first)->get_strpool_attribute(ID::interface_type);
- //for (auto &d : interface_type_pool) { // TODO: Compare interface type to type in parent module (not crucially important, but good for robustness)
- //}
-
- // Find if the sub-module has set a modport for the current interface connection:
- const pool<string> &interface_modport_pool = mod->wire(conn.first)->get_strpool_attribute(ID::interface_modport);
- std::string interface_modport = "";
- for (auto &d : interface_modport_pool) {
- interface_modport = "\\" + d;
- }
if(conn.second.bits().size() == 1 && conn.second.bits()[0].wire->get_bool_attribute(ID::is_interface)) { // Check if the connected wire is a potential interface in the parent module
std::string interface_name_str = conn.second.bits()[0].wire->name.str();
interface_name_str.replace(0,23,""); // Strip the prefix '$dummywireforinterface' from the dummy wire to get the name
@@ -297,9 +287,12 @@ bool expand_module(RTLIL::Design *design, RTLIL::Module *module, bool flag_check
connections_to_remove.push_back(conn.first);
interfaces_to_add_to_submodule[conn.first] = interfaces_in_module.at(interface_name2);
- // Add modports to a dict which will be passed to AstModule::derive
- if (interface_modport != "") {
- modports_used_in_submodule[conn.first] = interface_modport;
+ // Find if the sub-module has set a modport for the current
+ // interface connection. Add any modports to a dict which will
+ // be passed to AstModule::derive
+ string modport_name = mod->wire(conn.first)->get_string_attribute(ID::interface_modport);
+ if (!modport_name.empty()) {
+ modports_used_in_submodule[conn.first] = "\\" + modport_name;
}
}
else not_found_interface = true;
@@ -574,9 +567,9 @@ struct HierarchyPass : public Pass {
log("\n");
log("In parametric designs, a module might exists in several variations with\n");
log("different parameter values. This pass looks at all modules in the current\n");
- log("design an re-runs the language frontends for the parametric modules as\n");
+ log("design and re-runs the language frontends for the parametric modules as\n");
log("needed. It also resolves assignments to wired logic data types (wand/wor),\n");
- log("resolves positional module parameters, unroll array instances, and more.\n");
+ log("resolves positional module parameters, unrolls array instances, and more.\n");
log("\n");
log(" -check\n");
log(" also check the design hierarchy. this generates an error when\n");
diff --git a/passes/memory/memory_share.cc b/passes/memory/memory_share.cc
index 477246687..e11958bd6 100644
--- a/passes/memory/memory_share.cc
+++ b/passes/memory/memory_share.cc
@@ -715,6 +715,8 @@ struct MemoryShareWorker
cone_ct.cell_types.erase(ID($mul));
cone_ct.cell_types.erase(ID($mod));
cone_ct.cell_types.erase(ID($div));
+ cone_ct.cell_types.erase(ID($modfloor));
+ cone_ct.cell_types.erase(ID($divfloor));
cone_ct.cell_types.erase(ID($pow));
cone_ct.cell_types.erase(ID($shl));
cone_ct.cell_types.erase(ID($shr));
diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc
index 777a24777..e5b8bda95 100644
--- a/passes/opt/opt_expr.cc
+++ b/passes/opt/opt_expr.cc
@@ -864,7 +864,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
skip_fine_alu:
if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($shift), ID($shiftx), ID($shl), ID($shr), ID($sshl), ID($sshr),
- ID($lt), ID($le), ID($ge), ID($gt), ID($neg), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($pow)))
+ ID($lt), ID($le), ID($ge), ID($gt), ID($neg), ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow)))
{
RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A));
RTLIL::SigSpec sig_b = cell->hasPort(ID::B) ? assign_map(cell->getPort(ID::B)) : RTLIL::SigSpec();
@@ -883,7 +883,7 @@ skip_fine_alu:
if (0) {
found_the_x_bit:
cover_list("opt.opt_expr.xbit", "$reduce_xor", "$reduce_xnor", "$shl", "$shr", "$sshl", "$sshr", "$shift", "$shiftx",
- "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$pow", cell->type.str());
+ "$lt", "$le", "$ge", "$gt", "$neg", "$add", "$sub", "$mul", "$div", "$mod", "$divfloor", "$modfloor", "$pow", cell->type.str());
if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
replace_cell(assign_map, module, cell, "x-bit in input", ID::Y, RTLIL::State::Sx);
else
@@ -1469,6 +1469,8 @@ skip_identity:
FOLD_2ARG_CELL(mul)
FOLD_2ARG_CELL(div)
FOLD_2ARG_CELL(mod)
+ FOLD_2ARG_CELL(divfloor)
+ FOLD_2ARG_CELL(modfloor)
FOLD_2ARG_CELL(pow)
FOLD_1ARG_CELL(pos)
@@ -1583,9 +1585,11 @@ skip_identity:
}
}
- if (!keepdc && cell->type.in(ID($div), ID($mod)))
+ if (!keepdc && cell->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor)))
{
+ bool a_signed = cell->parameters[ID::A_SIGNED].as_bool();
bool b_signed = cell->parameters[ID::B_SIGNED].as_bool();
+ SigSpec sig_a = assign_map(cell->getPort(ID::A));
SigSpec sig_b = assign_map(cell->getPort(ID::B));
SigSpec sig_y = assign_map(cell->getPort(ID::Y));
@@ -1610,11 +1614,13 @@ skip_identity:
for (int i = 1; i < (b_signed ? sig_b.size()-1 : sig_b.size()); i++)
if (b_val == (1 << i))
{
- if (cell->type == ID($div))
+ if (cell->type.in(ID($div), ID($divfloor)))
{
cover("opt.opt_expr.div_shift");
- log_debug("Replacing divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ bool is_truncating = cell->type == ID($div);
+ log_debug("Replacing %s-divide-by-%d cell `%s' in module `%s' with shift-by-%d.\n",
+ is_truncating ? "truncating" : "flooring",
b_val, cell->name.c_str(), module->name.c_str(), i);
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(i, 6);
@@ -1622,17 +1628,35 @@ skip_identity:
while (GetSize(new_b) > 1 && new_b.back() == RTLIL::State::S0)
new_b.pop_back();
- cell->type = ID($shr);
+ cell->type = ID($sshr);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->parameters[ID::B_SIGNED] = false;
cell->setPort(ID::B, new_b);
+
+ // Truncating division is the same as flooring division, except when
+ // the result is negative and there is a remainder - then trunc = floor + 1
+ if (is_truncating && a_signed) {
+ Wire *flooring = module->addWire(NEW_ID, sig_y.size());
+ cell->setPort(ID::Y, flooring);
+
+ Wire *result_neg = module->addWire(NEW_ID);
+ module->addXor(NEW_ID, sig_a[sig_a.size()-1], sig_b[sig_b.size()-1], result_neg);
+ Wire *rem_nonzero = module->addWire(NEW_ID);
+ module->addReduceOr(NEW_ID, sig_a.extract(0, i), rem_nonzero);
+ Wire *should_add = module->addWire(NEW_ID);
+ module->addAnd(NEW_ID, result_neg, rem_nonzero, should_add);
+ module->addAdd(NEW_ID, flooring, should_add, sig_y);
+ }
+
cell->check();
}
- else
+ else if (cell->type.in(ID($mod), ID($modfloor)))
{
cover("opt.opt_expr.mod_mask");
- log_debug("Replacing modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ bool is_truncating = cell->type == ID($mod);
+ log_debug("Replacing %s-modulo-by-%d cell `%s' in module `%s' with bitmask.\n",
+ is_truncating ? "truncating" : "flooring",
b_val, cell->name.c_str(), module->name.c_str());
std::vector<RTLIL::SigBit> new_b = RTLIL::SigSpec(State::S1, i);
@@ -1643,6 +1667,24 @@ skip_identity:
cell->type = ID($and);
cell->parameters[ID::B_WIDTH] = GetSize(new_b);
cell->setPort(ID::B, new_b);
+
+ // truncating modulo has the same masked bits as flooring modulo, but
+ // the sign bits are those of A (except when R=0)
+ if (is_truncating && a_signed) {
+ Wire *flooring = module->addWire(NEW_ID, sig_y.size());
+ cell->setPort(ID::Y, flooring);
+ SigSpec truncating = SigSpec(flooring).extract(0, i);
+
+ Wire *rem_nonzero = module->addWire(NEW_ID);
+ module->addReduceOr(NEW_ID, truncating, rem_nonzero);
+ SigSpec a_sign = sig_a[sig_a.size()-1];
+ Wire *extend_bit = module->addWire(NEW_ID);
+ module->addAnd(NEW_ID, a_sign, rem_nonzero, extend_bit);
+
+ truncating.append(extend_bit);
+ module->addPos(NEW_ID, truncating, sig_y, true);
+ }
+
cell->check();
}
diff --git a/passes/opt/opt_share.cc b/passes/opt/opt_share.cc
index 1f69c98f4..cbace7bac 100644
--- a/passes/opt/opt_share.cc
+++ b/passes/opt/opt_share.cc
@@ -103,7 +103,7 @@ bool cell_supported(RTLIL::Cell *cell)
if (sig_bi.is_fully_const() && sig_ci.is_fully_const() && sig_bi == sig_ci)
return true;
- } else if (cell->type.in(LOGICAL_OPS, SHIFT_OPS, BITWISE_OPS, RELATIONAL_OPS, ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($concat))) {
+ } else if (cell->type.in(LOGICAL_OPS, SHIFT_OPS, BITWISE_OPS, RELATIONAL_OPS, ID($add), ID($sub), ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($concat))) {
return true;
}
@@ -130,7 +130,7 @@ bool mergeable(RTLIL::Cell *a, RTLIL::Cell *b)
RTLIL::IdString decode_port_semantics(RTLIL::Cell *cell, RTLIL::IdString port_name)
{
- if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($concat), SHIFT_OPS) && port_name == ID::B)
+ if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt), ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($concat), SHIFT_OPS) && port_name == ID::B)
return port_name;
return "";
diff --git a/passes/opt/share.cc b/passes/opt/share.cc
index 2839507b0..988253edf 100644
--- a/passes/opt/share.cc
+++ b/passes/opt/share.cc
@@ -376,7 +376,7 @@ struct ShareWorker
continue;
}
- if (cell->type.in(ID($mul), ID($div), ID($mod))) {
+ if (cell->type.in(ID($mul), ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
if (config.opt_aggressive || cell->parameters.at(ID::Y_WIDTH).as_int() >= 4)
shareable_cells.insert(cell);
continue;
@@ -1133,6 +1133,8 @@ struct ShareWorker
cone_ct.cell_types.erase(ID($mul));
cone_ct.cell_types.erase(ID($mod));
cone_ct.cell_types.erase(ID($div));
+ cone_ct.cell_types.erase(ID($modfloor));
+ cone_ct.cell_types.erase(ID($divfloor));
cone_ct.cell_types.erase(ID($pow));
cone_ct.cell_types.erase(ID($shl));
cone_ct.cell_types.erase(ID($shr));
@@ -1512,6 +1514,8 @@ struct SharePass : public Pass {
config.generic_bin_ops.insert(ID($sub));
config.generic_bin_ops.insert(ID($div));
config.generic_bin_ops.insert(ID($mod));
+ config.generic_bin_ops.insert(ID($divfloor));
+ config.generic_bin_ops.insert(ID($modfloor));
// config.generic_bin_ops.insert(ID($pow));
config.generic_uni_ops.insert(ID($logic_not));
diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc
index 195400bf0..f60f2f8a8 100644
--- a/passes/opt/wreduce.cc
+++ b/passes/opt/wreduce.cc
@@ -37,7 +37,7 @@ struct WreduceConfig
ID($and), ID($or), ID($xor), ID($xnor),
ID($shl), ID($shr), ID($sshl), ID($sshr), ID($shift), ID($shiftx),
ID($lt), ID($le), ID($eq), ID($ne), ID($eqx), ID($nex), ID($ge), ID($gt),
- ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($pow),
+ ID($add), ID($sub), ID($mul), // ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow),
ID($mux), ID($pmux),
ID($dff), ID($adff)
});
@@ -545,7 +545,7 @@ struct WreducePass : public Pass {
}
}
- if (c->type.in(ID($div), ID($mod), ID($pow)))
+ if (c->type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor), ID($pow)))
{
SigSpec A = c->getPort(ID::A);
int original_a_width = GetSize(A);
diff --git a/passes/sat/qbfsat.cc b/passes/sat/qbfsat.cc
index c42760488..d6dbf8ef4 100644
--- a/passes/sat/qbfsat.cc
+++ b/passes/sat/qbfsat.cc
@@ -41,6 +41,8 @@ struct QbfSolveOptions {
bool specialize, specialize_from_file, write_solution, nocleanup, dump_final_smt2, assume_outputs, assume_neg;
bool nooptimize, nobisection;
bool sat, unsat, show_smtbmc;
+ enum Solver{Z3, Yices, CVC4} solver;
+ int timeout;
std::string specialize_soln_file;
std::string write_soln_soln_file;
std::string dump_final_smt2_file;
@@ -48,12 +50,29 @@ struct QbfSolveOptions {
QbfSolveOptions() : specialize(false), specialize_from_file(false), write_solution(false),
nocleanup(false), dump_final_smt2(false), assume_outputs(false), assume_neg(false),
nooptimize(false), nobisection(false), sat(false), unsat(false), show_smtbmc(false),
- argidx(0) {};
+ solver(Yices), timeout(0), argidx(0) {};
};
+std::string get_solver_name(const QbfSolveOptions &opt) {
+ if (opt.solver == opt.Solver::Z3)
+ return "z3";
+ else if (opt.solver == opt.Solver::Yices)
+ return "yices";
+ else if (opt.solver == opt.Solver::CVC4)
+ return "cvc4";
+ else
+ log_cmd_error("unknown solver specified.\n");
+ return "";
+}
+
void recover_solution(QbfSolutionType &sol) {
YS_REGEX_TYPE sat_regex = YS_REGEX_COMPILE("Status: PASSED");
YS_REGEX_TYPE unsat_regex = YS_REGEX_COMPILE("Solver Error.*model is not available");
+ YS_REGEX_TYPE unsat_regex2 = YS_REGEX_COMPILE("Status: FAILED");
+ YS_REGEX_TYPE timeout_regex = YS_REGEX_COMPILE("No solution found! \\(timeout\\)");
+ YS_REGEX_TYPE timeout_regex2 = YS_REGEX_COMPILE("No solution found! \\(interrupted\\)");
+ YS_REGEX_TYPE unknown_regex = YS_REGEX_COMPILE("No solution found! \\(unknown\\)");
+ YS_REGEX_TYPE unknown_regex2 = YS_REGEX_COMPILE("Unexpected EOF response from solver");
YS_REGEX_TYPE memout_regex = YS_REGEX_COMPILE("Solver Error:.*error \"out of memory\"");
YS_REGEX_TYPE hole_value_regex = YS_REGEX_COMPILE_WITH_SUBS("Value for anyconst in [a-zA-Z0-9_]* \\(([^:]*:[^\\)]*)\\): (.*)");
#ifndef NDEBUG
@@ -74,14 +93,40 @@ void recover_solution(QbfSolutionType &sol) {
#endif
sol.hole_to_value[loc] = val;
}
- else if (YS_REGEX_NS::regex_search(x, sat_regex))
+ else if (YS_REGEX_NS::regex_search(x, sat_regex)) {
sat_regex_found = true;
- else if (YS_REGEX_NS::regex_search(x, unsat_regex))
+ sol.sat = true;
+ sol.unknown = false;
+ }
+ else if (YS_REGEX_NS::regex_search(x, unsat_regex)) {
unsat_regex_found = true;
+ sol.sat = false;
+ sol.unknown = false;
+ }
else if (YS_REGEX_NS::regex_search(x, memout_regex)) {
sol.unknown = true;
log_warning("solver ran out of memory\n");
}
+ else if (YS_REGEX_NS::regex_search(x, timeout_regex)) {
+ sol.unknown = true;
+ log_warning("solver timed out\n");
+ }
+ else if (YS_REGEX_NS::regex_search(x, timeout_regex2)) {
+ sol.unknown = true;
+ log_warning("solver timed out\n");
+ }
+ else if (YS_REGEX_NS::regex_search(x, unknown_regex)) {
+ sol.unknown = true;
+ log_warning("solver returned \"unknown\"\n");
+ }
+ else if (YS_REGEX_NS::regex_search(x, unsat_regex2)) {
+ unsat_regex_found = true;
+ sol.sat = false;
+ sol.unknown = false;
+ }
+ else if (YS_REGEX_NS::regex_search(x, unknown_regex2)) {
+ sol.unknown = true;
+ }
}
#ifndef NDEBUG
log_assert(!sol.unknown && sol.sat? sat_regex_found : true);
@@ -315,31 +360,23 @@ QbfSolutionType call_qbf_solver(RTLIL::Module *mod, const QbfSolveOptions &opt,
QbfSolutionType ret;
const std::string yosys_smtbmc_exe = proc_self_dirname() + "yosys-smtbmc";
const std::string smt2_command = "write_smt2 -stbv -wires " + tempdir_name + "/problem" + (iter_num != 0? stringf("%d", iter_num) : "") + ".smt2";
- const std::string smtbmc_cmd = yosys_smtbmc_exe + " -s z3 -t 1 -g --binary " + (opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file + " " : "") + tempdir_name + "/problem" + (iter_num != 0? stringf("%d", iter_num) : "") + ".smt2 2>&1";
const std::string smtbmc_warning = "z3: WARNING:";
- const bool show_smtbmc = opt.show_smtbmc;
+ const std::string smtbmc_cmd = yosys_smtbmc_exe + " -s " + (get_solver_name(opt)) + (opt.timeout != 0? stringf(" --timeout %d", opt.timeout) : "") + " -t 1 -g --binary " + (opt.dump_final_smt2? "--dump-smt2 " + opt.dump_final_smt2_file + " " : "") + tempdir_name + "/problem" + (iter_num != 0? stringf("%d", iter_num) : "") + ".smt2 2>&1";
Pass::call(mod->design, smt2_command);
- auto process_line = [&ret, &smtbmc_warning, &show_smtbmc, &quiet](const std::string &line) {
+ auto process_line = [&ret, &smtbmc_warning, &opt, &quiet](const std::string &line) {
ret.stdout_lines.push_back(line.substr(0, line.size()-1)); //don't include trailing newline
auto warning_pos = line.find(smtbmc_warning);
if (warning_pos != std::string::npos)
log_warning("%s", line.substr(warning_pos + smtbmc_warning.size() + 1).c_str());
else
- if (show_smtbmc && !quiet)
+ if (opt.show_smtbmc && !quiet)
log("smtbmc output: %s", line.c_str());
};
log_header(mod->design, "Solving QBF-SAT problem.\n");
if (!quiet) log("Launching \"%s\".\n", smtbmc_cmd.c_str());
- int retval = run_command(smtbmc_cmd, process_line);
- if (retval == 0) {
- ret.sat = true;
- ret.unknown = false;
- } else if (retval == 1) {
- ret.sat = false;
- ret.unknown = false;
- }
+ run_command(smtbmc_cmd, process_line);
recover_solution(ret);
return ret;
@@ -486,6 +523,35 @@ QbfSolveOptions parse_args(const std::vector<std::string> &args) {
opt.nobisection = true;
continue;
}
+ else if (args[opt.argidx] == "-solver") {
+ if (args.size() <= opt.argidx + 1)
+ log_cmd_error("solver not specified.\n");
+ else {
+ if (args[opt.argidx+1] == "z3")
+ opt.solver = opt.Solver::Z3;
+ else if (args[opt.argidx+1] == "yices")
+ opt.solver = opt.Solver::Yices;
+ else if (args[opt.argidx+1] == "cvc4")
+ opt.solver = opt.Solver::CVC4;
+ else
+ log_cmd_error("Unknown solver \"%s\".\n", args[opt.argidx+1].c_str());
+ opt.argidx++;
+ }
+ continue;
+ }
+ else if (args[opt.argidx] == "-timeout") {
+ if (args.size() <= opt.argidx + 1)
+ log_cmd_error("timeout not specified.\n");
+ else {
+ int timeout = atoi(args[opt.argidx+1].c_str());
+ if (timeout > 0)
+ opt.timeout = timeout;
+ else
+ log_cmd_error("timeout must be greater than 0.\n");
+ opt.argidx++;
+ }
+ continue;
+ }
else if (args[opt.argidx] == "-sat") {
opt.sat = true;
continue;
@@ -563,21 +629,20 @@ struct QbfSatPass : public Pass {
log("\n");
log(" qbfsat [options] [selection]\n");
log("\n");
- log("This command solves a 2QBF-SAT problem defined over the currently selected module.\n");
- log("Existentially-quantified variables are declared by assigning a wire \"$anyconst\".\n");
- log("Universally-quantified variables may be explicitly declared by assigning a wire\n");
- log("\"$allconst\", but module inputs will be treated as universally-quantified variables\n");
- log("by default.\n");
+ log("This command solves an \"exists-forall\" 2QBF-SAT problem defined over the currently\n");
+ log("selected module. Existentially-quantified variables are declared by assigning a wire\n");
+ log("\"$anyconst\". Universally-quantified variables may be explicitly declared by assigning\n");
+ log("a wire \"$allconst\", but module inputs will be treated as universally-quantified\n");
+ log("variables by default.\n");
log("\n");
log(" -nocleanup\n");
- log(" Do not delete temporary files and directories. Useful for\n");
- log(" debugging.\n");
+ log(" Do not delete temporary files and directories. Useful for debugging.\n");
log("\n");
log(" -dump-final-smt2 <file>\n");
log(" Pass the --dump-smt2 option to yosys-smtbmc.\n");
log("\n");
log(" -assume-outputs\n");
- log(" Add an $assume cell for the conjunction of all one-bit module output wires.\n");
+ log(" Add an \"$assume\" cell for the conjunction of all one-bit module output wires.\n");
log("\n");
log(" -assume-negative-polarity\n");
log(" When adding $assume cells for one-bit module output wires, assume they are\n");
@@ -586,15 +651,21 @@ struct QbfSatPass : public Pass {
log("\n");
log(" -nooptimize\n");
log(" Ignore \"\\minimize\" and \"\\maximize\" attributes, do not emit \"(maximize)\" or\n");
- log(" \"(minimize)\" in the SMTLIBv2, and generally make no attempt to optimize anything.\n");
+ log(" \"(minimize)\" in the SMT-LIBv2, and generally make no attempt to optimize anything.\n");
log("\n");
log(" -nobisection\n");
log(" If a wire is marked with the \"\\minimize\" or \"\\maximize\" attribute, do not\n");
log(" attempt to optimize that value with the default iterated solving and threshold\n");
log(" bisection approach. Instead, have yosys-smtbmc emit a \"(minimize)\" or \"(maximize)\"\n");
- log(" command in the SMTLIBv2 output and hope that the solver supports optimizing\n");
+ log(" command in the SMT-LIBv2 output and hope that the solver supports optimizing\n");
log(" quantified bitvector problems.\n");
log("\n");
+ log(" -solver <solver>\n");
+ log(" Use a particular solver. Choose one of: \"z3\", \"yices\", and \"cvc4\".\n");
+ log("\n");
+ log(" -timeout <value>\n");
+ log(" Set the per-iteration timeout in seconds.\n");
+ log("\n");
log(" -sat\n");
log(" Generate an error if the solver does not return \"sat\".\n");
log("\n");
@@ -605,15 +676,16 @@ struct QbfSatPass : public Pass {
log(" Print the output from yosys-smtbmc.\n");
log("\n");
log(" -specialize\n");
- log(" Replace all \"$anyconst\" cells with constant values determined by the solver.\n");
+ log(" If the problem is satisfiable, replace each \"$anyconst\" cell with its\n");
+ log(" corresponding constant value from the model produced by the solver.\n");
log("\n");
log(" -specialize-from-file <solution file>\n");
- log(" Do not run the solver, but instead only attempt to replace all \"$anyconst\"\n");
- log(" cells in the current module with values provided by the specified file.\n");
+ log(" Do not run the solver, but instead only attempt to replace each \"$anyconst\"\n");
+ log(" cell in the current module with a constant value provided by the specified file.\n");
log("\n");
log(" -write-solution <solution file>\n");
- log(" Write the assignments discovered by the solver for all \"$anyconst\" cells\n");
- log(" to the specified file.");
+ log(" If the problem is satisfiable, write the corresponding constant value for each\n");
+ log(" \"$anyconst\" cell from the model produced by the solver to the specified file.");
log("\n");
log("\n");
}
@@ -641,7 +713,6 @@ struct QbfSatPass : public Pass {
QbfSolutionType ret = qbf_solve(module, opt);
module = design->module(module_name);
if (ret.unknown) {
- log_warning("solver did not give an answer\n");
if (opt.sat || opt.unsat)
log_cmd_error("expected problem to be %s\n", opt.sat? "SAT" : "UNSAT");
}
diff --git a/passes/techmap/.gitignore b/passes/techmap/.gitignore
deleted file mode 100644
index e6dcc6bc0..000000000
--- a/passes/techmap/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-techmap.inc
diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc
index 1802ba0de..a54b4913d 100644
--- a/passes/techmap/Makefile.inc
+++ b/passes/techmap/Makefile.inc
@@ -1,4 +1,5 @@
+OBJS += passes/techmap/flatten.o
OBJS += passes/techmap/techmap.o
OBJS += passes/techmap/simplemap.o
OBJS += passes/techmap/dfflibmap.o
@@ -45,18 +46,6 @@ OBJS += passes/techmap/flowmap.o
OBJS += passes/techmap/extractinv.o
endif
-GENFILES += passes/techmap/techmap.inc
-
-passes/techmap/techmap.inc: techlibs/common/techmap.v
- $(Q) mkdir -p $(dir $@)
- $(P) echo "// autogenerated from $<" > $@.new
- $(Q) echo "static char stdcells_code[] = {" >> $@.new
- $(Q) od -v -td1 -An $< | $(SED) -e 's/[0-9][0-9]*/&,/g' >> $@.new
- $(Q) echo "0};" >> $@.new
- $(Q) mv $@.new $@
-
-passes/techmap/techmap.o: passes/techmap/techmap.inc
-
ifeq ($(DISABLE_SPAWN),0)
TARGETS += $(PROGRAM_PREFIX)yosys-filterlib$(EXE)
EXTRA_OBJS += passes/techmap/filterlib.o
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/passes/techmap/flatten.cc b/passes/techmap/flatten.cc
new file mode 100644
index 000000000..027bb137d
--- /dev/null
+++ b/passes/techmap/flatten.cc
@@ -0,0 +1,357 @@
+/*
+ * yosys -- Yosys Open SYnthesis Suite
+ *
+ * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "kernel/yosys.h"
+#include "kernel/utils.h"
+#include "kernel/sigtools.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+void apply_prefix(IdString prefix, IdString &id)
+{
+ if (id[0] == '\\')
+ id = stringf("%s.%s", prefix.c_str(), id.c_str()+1);
+ else
+ id = stringf("$flatten%s.%s", prefix.c_str(), id.c_str());
+}
+
+void apply_prefix(IdString prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
+{
+ vector<SigChunk> chunks = sig;
+ for (auto &chunk : chunks)
+ if (chunk.wire != nullptr) {
+ IdString wire_name = chunk.wire->name;
+ apply_prefix(prefix, wire_name);
+ log_assert(module->wire(wire_name) != nullptr);
+ chunk.wire = module->wire(wire_name);
+ }
+ sig = chunks;
+}
+
+struct FlattenWorker
+{
+ bool ignore_wb = false;
+
+ void flatten_cell(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Cell *cell, RTLIL::Module *tpl, std::vector<RTLIL::Cell*> &new_cells)
+ {
+ if (tpl->processes.size() != 0) {
+ log("Flattening yielded processes:");
+ for (auto &it : tpl->processes)
+ log(" %s",log_id(it.first));
+ log("\n");
+ log_error("Flattening yielded processes -> this is not supported.\n");
+ }
+
+ pool<string> extra_src_attrs = cell->get_strpool_attribute(ID::src);
+
+ dict<IdString, IdString> memory_renames;
+
+ for (auto &it : tpl->memories) {
+ IdString m_name = it.first;
+ apply_prefix(cell->name, m_name);
+ RTLIL::Memory *m = module->addMemory(m_name, it.second);
+ if (m->attributes.count(ID::src))
+ m->add_strpool_attribute(ID::src, extra_src_attrs);
+ memory_renames[it.first] = m->name;
+ design->select(module, m);
+ }
+
+ dict<IdString, IdString> positional_ports;
+ dict<Wire*, IdString> temp_renamed_wires;
+
+ for (auto tpl_w : tpl->wires())
+ {
+ if (tpl_w->port_id > 0)
+ {
+ IdString posportname = stringf("$%d", tpl_w->port_id);
+ positional_ports.emplace(posportname, tpl_w->name);
+ }
+ IdString w_name = tpl_w->name;
+ apply_prefix(cell->name, w_name);
+ RTLIL::Wire *w = module->wire(w_name);
+ if (w != nullptr) {
+ if (!w->get_bool_attribute(ID::hierconn)) {
+ temp_renamed_wires[w] = w->name;
+ module->rename(w, NEW_ID);
+ w = nullptr;
+ } else {
+ w->attributes.erase(ID::hierconn);
+ if (GetSize(w) < GetSize(tpl_w)) {
+ log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w),
+ log_id(tpl), log_id(tpl_w), log_id(module), log_id(cell));
+ w->width = GetSize(tpl_w);
+ }
+ }
+ }
+ if (w == nullptr) {
+ w = module->addWire(w_name, tpl_w);
+ w->port_input = false;
+ w->port_output = false;
+ w->port_id = 0;
+ if (w->attributes.count(ID::src))
+ w->add_strpool_attribute(ID::src, extra_src_attrs);
+ }
+ design->select(module, w);
+ }
+
+ SigMap sigmap(module);
+
+ SigMap tpl_sigmap(tpl);
+ pool<SigBit> tpl_written_bits;
+
+ for (auto tpl_cell : tpl->cells())
+ for (auto &conn : tpl_cell->connections())
+ if (tpl_cell->output(conn.first))
+ for (auto bit : tpl_sigmap(conn.second))
+ tpl_written_bits.insert(bit);
+ for (auto &conn : tpl->connections())
+ for (auto bit : tpl_sigmap(conn.first))
+ tpl_written_bits.insert(bit);
+
+ SigMap port_signal_map;
+
+ for (auto &it : cell->connections())
+ {
+ IdString portname = it.first;
+ if (positional_ports.count(portname) > 0)
+ portname = positional_ports.at(portname);
+ if (tpl->wire(portname) == nullptr || tpl->wire(portname)->port_id == 0) {
+ if (portname.begins_with("$"))
+ log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
+ continue;
+ }
+
+ if (GetSize(it.second) == 0)
+ continue;
+
+ RTLIL::Wire *w = tpl->wire(portname);
+ RTLIL::SigSig c;
+
+ if (w->port_output && !w->port_input) {
+ c.first = it.second;
+ c.second = RTLIL::SigSpec(w);
+ apply_prefix(cell->name, c.second, module);
+ } else if (!w->port_output && w->port_input) {
+ c.first = RTLIL::SigSpec(w);
+ c.second = it.second;
+ apply_prefix(cell->name, c.first, module);
+ } else {
+ SigSpec sig_tpl = w, sig_tpl_pf = w, sig_mod = it.second;
+ apply_prefix(cell->name, sig_tpl_pf, module);
+ for (int i = 0; i < GetSize(sig_tpl) && i < GetSize(sig_mod); i++) {
+ if (tpl_written_bits.count(tpl_sigmap(sig_tpl[i]))) {
+ c.first.append(sig_mod[i]);
+ c.second.append(sig_tpl_pf[i]);
+ } else {
+ c.first.append(sig_tpl_pf[i]);
+ c.second.append(sig_mod[i]);
+ }
+ }
+ }
+
+ if (c.second.size() > c.first.size())
+ c.second.remove(c.first.size(), c.second.size() - c.first.size());
+
+ if (c.second.size() < c.first.size())
+ c.second.append(RTLIL::SigSpec(RTLIL::State::S0, c.first.size() - c.second.size()));
+
+ log_assert(c.first.size() == c.second.size());
+
+ // connect internal and external wires
+
+ if (sigmap(c.first).has_const())
+ log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n",
+ log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second));
+
+ module->connect(c);
+ }
+
+ for (auto tpl_cell : tpl->cells())
+ {
+ IdString c_name = tpl_cell->name;
+ apply_prefix(cell->name, c_name);
+
+ RTLIL::Cell *c = module->addCell(c_name, tpl_cell);
+ new_cells.push_back(c);
+ design->select(module, c);
+
+ for (auto &conn : c->connections())
+ {
+ RTLIL::SigSpec new_conn = conn.second;
+ apply_prefix(cell->name, new_conn, module);
+ port_signal_map.apply(new_conn);
+ c->setPort(conn.first, std::move(new_conn));
+ }
+
+ if (c->type.in(ID($memrd), ID($memwr), ID($meminit))) {
+ IdString memid = c->getParam(ID::MEMID).decode_string();
+ log_assert(memory_renames.count(memid) != 0);
+ c->setParam(ID::MEMID, Const(memory_renames[memid].str()));
+ }
+
+ if (c->type == ID($mem)) {
+ IdString memid = c->getParam(ID::MEMID).decode_string();
+ apply_prefix(cell->name, memid);
+ c->setParam(ID::MEMID, Const(memid.c_str()));
+ }
+
+ if (c->attributes.count(ID::src))
+ c->add_strpool_attribute(ID::src, extra_src_attrs);
+ }
+
+ for (auto &it : tpl->connections()) {
+ RTLIL::SigSig c = it;
+ apply_prefix(cell->name.str(), c.first, module);
+ apply_prefix(cell->name.str(), c.second, module);
+ port_signal_map.apply(c.first);
+ port_signal_map.apply(c.second);
+ module->connect(c);
+ }
+
+ module->remove(cell);
+
+ for (auto &it : temp_renamed_wires)
+ {
+ Wire *w = it.first;
+ IdString name = it.second;
+ IdString altname = module->uniquify(name);
+ Wire *other_w = module->wire(name);
+ module->rename(other_w, altname);
+ module->rename(w, name);
+ }
+ }
+
+ void flatten_module(RTLIL::Design *design, RTLIL::Module *module, pool<RTLIL::Module*> &used_modules)
+ {
+ if (!design->selected(module) || module->get_blackbox_attribute(ignore_wb))
+ return;
+
+ std::vector<RTLIL::Cell*> worklist = module->selected_cells();
+ while (!worklist.empty())
+ {
+ RTLIL::Cell *cell = worklist.back();
+ worklist.pop_back();
+
+ if (!design->has(cell->type))
+ continue;
+
+ RTLIL::Module *tpl = design->module(cell->type);
+ if (tpl->get_blackbox_attribute(ignore_wb))
+ continue;
+
+ if (cell->get_bool_attribute(ID::keep_hierarchy) || tpl->get_bool_attribute(ID::keep_hierarchy)) {
+ log("Keeping %s.%s (found keep_hierarchy attribute).\n", log_id(module), log_id(cell));
+ used_modules.insert(tpl);
+ continue;
+ }
+
+ log_debug("Flattening %s.%s (%s).\n", log_id(module), log_id(cell), log_id(cell->type));
+ // If a design is fully selected and has a top module defined, topological sorting ensures that all cells
+ // added during flattening are black boxes, and flattening is finished in one pass. However, when flattening
+ // individual modules, this isn't the case, and the newly added cells might have to be flattened further.
+ flatten_cell(design, module, cell, tpl, worklist);
+ }
+ }
+};
+
+struct FlattenPass : public Pass {
+ FlattenPass() : Pass("flatten", "flatten design") { }
+ void help() YS_OVERRIDE
+ {
+ // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+ log("\n");
+ log(" flatten [options] [selection]\n");
+ log("\n");
+ log("This pass flattens the design by replacing cells by their implementation. This\n");
+ log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
+ log("pass is using the current design as mapping library.\n");
+ log("\n");
+ log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
+ log("flattened by this command.\n");
+ log("\n");
+ log(" -wb\n");
+ log(" Ignore the 'whitebox' attribute on cell implementations.\n");
+ log("\n");
+ }
+ void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+ {
+ log_header(design, "Executing FLATTEN pass (flatten design).\n");
+ log_push();
+
+ FlattenWorker worker;
+
+ size_t argidx;
+ for (argidx = 1; argidx < args.size(); argidx++) {
+ if (args[argidx] == "-wb") {
+ worker.ignore_wb = true;
+ continue;
+ }
+ break;
+ }
+ extra_args(args, argidx, design);
+
+ RTLIL::Module *top = nullptr;
+ if (design->full_selection())
+ for (auto module : design->modules())
+ if (module->get_bool_attribute(ID::top))
+ top = module;
+
+ pool<RTLIL::Module*> used_modules;
+ if (top == nullptr)
+ used_modules = design->modules();
+ else
+ used_modules.insert(top);
+
+ TopoSort<RTLIL::Module*, IdString::compare_ptr_by_name<RTLIL::Module>> topo_modules;
+ pool<RTLIL::Module*> worklist = used_modules;
+ while (!worklist.empty()) {
+ RTLIL::Module *module = worklist.pop();
+ for (auto cell : module->selected_cells()) {
+ RTLIL::Module *tpl = design->module(cell->type);
+ if (tpl != nullptr) {
+ if (topo_modules.database.count(tpl) == 0)
+ worklist.insert(tpl);
+ topo_modules.edge(tpl, module);
+ }
+ }
+ }
+
+ if (!topo_modules.sort())
+ log_error("Cannot flatten a design containing recursive instantiations.\n");
+
+ for (auto module : topo_modules.sorted)
+ worker.flatten_module(design, module, used_modules);
+
+ if (top != nullptr)
+ for (auto module : design->modules().to_vector())
+ if (!used_modules[module] && !module->get_blackbox_attribute(worker.ignore_wb)) {
+ log("Deleting now unused module %s.\n", log_id(module));
+ design->remove(module);
+ }
+
+ log_pop();
+ }
+} FlattenPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/passes/techmap/simplemap.cc b/passes/techmap/simplemap.cc
index b65b3e972..214157a64 100644
--- a/passes/techmap/simplemap.cc
+++ b/passes/techmap/simplemap.cc
@@ -522,7 +522,7 @@ void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell)
}
}
-void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers)
+void simplemap_get_mappers(dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers)
{
mappers[ID($not)] = simplemap_not;
mappers[ID($pos)] = simplemap_pos;
@@ -559,7 +559,7 @@ void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTL
void simplemap(RTLIL::Module *module, RTLIL::Cell *cell)
{
- static std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
+ static dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
static bool initialized_mappers = false;
if (!initialized_mappers) {
@@ -595,7 +595,7 @@ struct SimplemapPass : public Pass {
log_header(design, "Executing SIMPLEMAP pass (map simple cells to gate primitives).\n");
extra_args(args, 1, design);
- std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
+ dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> mappers;
simplemap_get_mappers(mappers);
for (auto mod : design->modules()) {
diff --git a/passes/techmap/simplemap.h b/passes/techmap/simplemap.h
index c2d73ea79..5091050a1 100644
--- a/passes/techmap/simplemap.h
+++ b/passes/techmap/simplemap.h
@@ -42,7 +42,7 @@ extern void simplemap_adff(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap_dlatch(RTLIL::Module *module, RTLIL::Cell *cell);
extern void simplemap(RTLIL::Module *module, RTLIL::Cell *cell);
-extern void simplemap_get_mappers(std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers);
+extern void simplemap_get_mappers(dict<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> &mappers);
YOSYS_NAMESPACE_END
diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc
index a554be257..535db9465 100644
--- a/passes/techmap/techmap.cc
+++ b/passes/techmap/techmap.cc
@@ -27,7 +27,6 @@
#include <string.h>
#include "simplemap.h"
-#include "passes/techmap/techmap.inc"
YOSYS_NAMESPACE_BEGIN
@@ -51,27 +50,23 @@ void apply_prefix(IdString prefix, RTLIL::SigSpec &sig, RTLIL::Module *module)
{
vector<SigChunk> chunks = sig;
for (auto &chunk : chunks)
- if (chunk.wire != NULL) {
+ if (chunk.wire != nullptr) {
IdString wire_name = chunk.wire->name;
apply_prefix(prefix, wire_name);
- log_assert(module->wires_.count(wire_name) > 0);
- chunk.wire = module->wires_[wire_name];
+ log_assert(module->wire(wire_name) != nullptr);
+ chunk.wire = module->wire(wire_name);
}
sig = chunks;
}
struct TechmapWorker
{
- std::map<RTLIL::IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers;
- std::map<std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache;
- std::map<RTLIL::Module*, bool> techmap_do_cache;
- std::set<RTLIL::Module*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Module>> module_queue;
+ dict<IdString, void(*)(RTLIL::Module*, RTLIL::Cell*)> simplemap_mappers;
+ dict<std::pair<IdString, dict<IdString, RTLIL::Const>>, RTLIL::Module*> techmap_cache;
+ dict<RTLIL::Module*, bool> techmap_do_cache;
+ pool<RTLIL::Module*> module_queue;
dict<Module*, SigMap> sigmaps;
- pool<IdString> flatten_do_list;
- pool<IdString> flatten_done_list;
- pool<Cell*> flatten_keep_list;
-
pool<string> log_msg_cache;
struct TechmapWireData {
@@ -79,31 +74,20 @@ struct TechmapWorker
RTLIL::SigSpec value;
};
- typedef std::map<std::string, std::vector<TechmapWireData>> TechmapWires;
+ typedef dict<IdString, std::vector<TechmapWireData>> TechmapWires;
- bool extern_mode;
- bool assert_mode;
- bool flatten_mode;
- bool recursive_mode;
- bool autoproc_mode;
- bool ignore_wb;
-
- TechmapWorker()
- {
- extern_mode = false;
- assert_mode = false;
- flatten_mode = false;
- recursive_mode = false;
- autoproc_mode = false;
- ignore_wb = false;
- }
+ bool extern_mode = false;
+ bool assert_mode = false;
+ bool recursive_mode = false;
+ bool autoproc_mode = false;
+ bool ignore_wb = false;
std::string constmap_tpl_name(SigMap &sigmap, RTLIL::Module *tpl, RTLIL::Cell *cell, bool verbose)
{
std::string constmap_info;
- std::map<RTLIL::SigBit, std::pair<RTLIL::IdString, int>> connbits_map;
+ dict<RTLIL::SigBit, std::pair<IdString, int>> connbits_map;
- for (auto conn : cell->connections())
+ for (auto &conn : cell->connections())
for (int i = 0; i < GetSize(conn.second); i++) {
RTLIL::SigBit bit = sigmap(conn.second[i]);
if (bit.wire == nullptr) {
@@ -117,7 +101,7 @@ struct TechmapWorker
constmap_info += stringf("|%s %d %s %d", log_id(conn.first), i,
log_id(connbits_map.at(bit).first), connbits_map.at(bit).second);
} else {
- connbits_map[bit] = std::pair<RTLIL::IdString, int>(conn.first, i);
+ connbits_map.emplace(bit, std::make_pair(conn.first, i));
constmap_info += stringf("|%s %d", log_id(conn.first), i);
}
}
@@ -129,24 +113,25 @@ struct TechmapWorker
{
TechmapWires result;
- if (module == NULL)
+ if (module == nullptr)
return result;
- for (auto &it : module->wires_) {
- const char *p = it.first.c_str();
+ for (auto w : module->wires()) {
+ const char *p = w->name.c_str();
if (*p == '$')
continue;
const char *q = strrchr(p+1, '.');
- p = q ? q+1 : p+1;
+ if (q)
+ p = q;
- if (!strncmp(p, "_TECHMAP_", 9)) {
+ if (!strncmp(p, "\\_TECHMAP_", 10)) {
TechmapWireData record;
- record.wire = it.second;
- record.value = it.second;
+ record.wire = w;
+ record.value = w;
result[p].push_back(record);
- it.second->attributes[ID::keep] = RTLIL::Const(1);
- it.second->attributes[ID::_techmap_special_] = RTLIL::Const(1);
+ w->set_bool_attribute(ID::keep);
+ w->set_bool_attribute(ID::_techmap_special_);
}
}
@@ -165,7 +150,7 @@ struct TechmapWorker
if (tpl->processes.size() != 0) {
log("Technology map yielded processes:");
for (auto &it : tpl->processes)
- log(" %s",RTLIL::id2cstr(it.first));
+ log(" %s",log_id(it.first));
log("\n");
if (autoproc_mode) {
Pass::call_on_module(tpl->design, tpl, "proc");
@@ -178,89 +163,71 @@ struct TechmapWorker
pool<string> extra_src_attrs = cell->get_strpool_attribute(ID::src);
orig_cell_name = cell->name.str();
- if (!flatten_mode) {
- for (auto &it : tpl->cells_)
- if (it.first == ID::_TECHMAP_REPLACE_) {
- module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str());
- break;
- }
- }
+ for (auto tpl_cell : tpl->cells())
+ if (tpl_cell->name == ID::_TECHMAP_REPLACE_) {
+ module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str());
+ break;
+ }
dict<IdString, IdString> memory_renames;
for (auto &it : tpl->memories) {
IdString m_name = it.first;
apply_prefix(cell->name, m_name);
- RTLIL::Memory *m = new RTLIL::Memory;
- m->name = m_name;
- m->width = it.second->width;
- m->start_offset = it.second->start_offset;
- m->size = it.second->size;
- m->attributes = it.second->attributes;
+ RTLIL::Memory *m = module->addMemory(m_name, it.second);
if (m->attributes.count(ID::src))
m->add_strpool_attribute(ID::src, extra_src_attrs);
- module->memories[m->name] = m;
memory_renames[it.first] = m->name;
design->select(module, m);
}
- std::map<RTLIL::IdString, RTLIL::IdString> positional_ports;
+ dict<IdString, IdString> positional_ports;
dict<Wire*, IdString> temp_renamed_wires;
pool<SigBit> autopurge_tpl_bits;
- for (auto &it : tpl->wires_)
+ for (auto tpl_w : tpl->wires())
{
- if (it.second->port_id > 0)
+ if (tpl_w->port_id > 0)
{
- IdString posportname = stringf("$%d", it.second->port_id);
- positional_ports[posportname] = it.first;
+ IdString posportname = stringf("$%d", tpl_w->port_id);
+ positional_ports.emplace(posportname, tpl_w->name);
- if (!flatten_mode && it.second->get_bool_attribute(ID::techmap_autopurge) &&
- (!cell->hasPort(it.second->name) || !GetSize(cell->getPort(it.second->name))) &&
+ if (tpl_w->get_bool_attribute(ID::techmap_autopurge) &&
+ (!cell->hasPort(tpl_w->name) || !GetSize(cell->getPort(tpl_w->name))) &&
(!cell->hasPort(posportname) || !GetSize(cell->getPort(posportname))))
{
if (sigmaps.count(tpl) == 0)
sigmaps[tpl].set(tpl);
- for (auto bit : sigmaps.at(tpl)(it.second))
+ for (auto bit : sigmaps.at(tpl)(tpl_w))
if (bit.wire != nullptr)
autopurge_tpl_bits.insert(bit);
}
}
- IdString w_name = it.second->name;
+ IdString w_name = tpl_w->name;
apply_prefix(cell->name, w_name);
RTLIL::Wire *w = module->wire(w_name);
if (w != nullptr) {
- if (!flatten_mode || !w->get_bool_attribute(ID::hierconn)) {
- temp_renamed_wires[w] = w->name;
- module->rename(w, NEW_ID);
- w = nullptr;
- } else {
- w->attributes.erase(ID::hierconn);
- if (GetSize(w) < GetSize(it.second)) {
- log_warning("Widening signal %s.%s to match size of %s.%s (via %s.%s).\n", log_id(module), log_id(w),
- log_id(tpl), log_id(it.second), log_id(module), log_id(cell));
- w->width = GetSize(it.second);
- }
- }
+ temp_renamed_wires[w] = w->name;
+ module->rename(w, NEW_ID);
+ w = nullptr;
}
if (w == nullptr) {
- w = module->addWire(w_name, it.second);
+ w = module->addWire(w_name, tpl_w);
w->port_input = false;
w->port_output = false;
w->port_id = 0;
- if (!flatten_mode)
- w->attributes.erase(ID::techmap_autopurge);
- if (it.second->get_bool_attribute(ID::_techmap_special_))
+ w->attributes.erase(ID::techmap_autopurge);
+ if (tpl_w->get_bool_attribute(ID::_techmap_special_))
w->attributes.clear();
if (w->attributes.count(ID::src))
w->add_strpool_attribute(ID::src, extra_src_attrs);
}
design->select(module, w);
- if (it.second->name.begins_with("\\_TECHMAP_REPLACE_.")) {
- IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), it.second->name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
- Wire *replace_w = module->addWire(replace_name, it.second);
+ if (tpl_w->name.begins_with("\\_TECHMAP_REPLACE_.")) {
+ IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), tpl_w->name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
+ Wire *replace_w = module->addWire(replace_name, tpl_w);
module->connect(replace_w, w);
}
}
@@ -268,24 +235,23 @@ struct TechmapWorker
SigMap tpl_sigmap(tpl);
pool<SigBit> tpl_written_bits;
- for (auto &it1 : tpl->cells_)
- for (auto &it2 : it1.second->connections_)
- if (it1.second->output(it2.first))
- for (auto bit : tpl_sigmap(it2.second))
+ for (auto tpl_cell : tpl->cells())
+ for (auto &conn : tpl_cell->connections())
+ if (tpl_cell->output(conn.first))
+ for (auto bit : tpl_sigmap(conn.second))
tpl_written_bits.insert(bit);
- for (auto &it1 : tpl->connections_)
- for (auto bit : tpl_sigmap(it1.first))
+ for (auto &conn : tpl->connections())
+ for (auto bit : tpl_sigmap(conn.first))
tpl_written_bits.insert(bit);
SigMap port_signal_map;
- SigSig port_signal_assign;
for (auto &it : cell->connections())
{
- RTLIL::IdString portname = it.first;
+ IdString portname = it.first;
if (positional_ports.count(portname) > 0)
portname = positional_ports.at(portname);
- if (tpl->wires_.count(portname) == 0 || tpl->wires_.at(portname)->port_id == 0) {
+ if (tpl->wire(portname) == nullptr || tpl->wire(portname)->port_id == 0) {
if (portname.begins_with("$"))
log_error("Can't map port `%s' of cell `%s' to template `%s'!\n", portname.c_str(), cell->name.c_str(), tpl->name.c_str());
continue;
@@ -294,7 +260,7 @@ struct TechmapWorker
if (GetSize(it.second) == 0)
continue;
- RTLIL::Wire *w = tpl->wires_.at(portname);
+ RTLIL::Wire *w = tpl->wire(portname);
RTLIL::SigSig c, extra_connect;
if (w->port_output && !w->port_input) {
@@ -333,76 +299,57 @@ struct TechmapWorker
log_assert(c.first.size() == c.second.size());
- if (flatten_mode)
- {
- // more conservative approach:
- // connect internal and external wires
-
- if (sigmaps.count(module) == 0)
- sigmaps[module].set(module);
-
- if (sigmaps.at(module)(c.first).has_const())
- log_error("Mismatch in directionality for cell port %s.%s.%s: %s <= %s\n",
- log_id(module), log_id(cell), log_id(it.first), log_signal(c.first), log_signal(c.second));
+ // replace internal wires that are connected to external wires
+ if (w->port_output && !w->port_input) {
+ port_signal_map.add(c.second, c.first);
+ } else
+ if (!w->port_output && w->port_input) {
+ port_signal_map.add(c.first, c.second);
+ } else {
module->connect(c);
+ extra_connect = SigSig();
}
- else
- {
- // approach that yields nicer outputs:
- // replace internal wires that are connected to external wires
-
- if (w->port_output && !w->port_input) {
- port_signal_map.add(c.second, c.first);
- } else
- if (!w->port_output && w->port_input) {
- port_signal_map.add(c.first, c.second);
- } else {
- module->connect(c);
- extra_connect = SigSig();
- }
- for (auto &attr : w->attributes) {
- if (attr.first == ID::src)
- continue;
- auto lhs = GetSize(extra_connect.first);
- auto rhs = GetSize(extra_connect.second);
- if (lhs > rhs)
- extra_connect.first.remove(rhs, lhs-rhs);
- else if (rhs > lhs)
- extra_connect.second.remove(lhs, rhs-lhs);
- module->connect(extra_connect);
- break;
- }
+ for (auto &attr : w->attributes) {
+ if (attr.first == ID::src)
+ continue;
+ auto lhs = GetSize(extra_connect.first);
+ auto rhs = GetSize(extra_connect.second);
+ if (lhs > rhs)
+ extra_connect.first.remove(rhs, lhs-rhs);
+ else if (rhs > lhs)
+ extra_connect.second.remove(lhs, rhs-lhs);
+ module->connect(extra_connect);
+ break;
}
}
- for (auto &it : tpl->cells_)
+ for (auto tpl_cell : tpl->cells())
{
- IdString c_name = it.second->name.str();
- bool techmap_replace_cell = (!flatten_mode) && (c_name == ID::_TECHMAP_REPLACE_);
+ IdString c_name = tpl_cell->name;
- if (techmap_replace_cell)
+ if (c_name == ID::_TECHMAP_REPLACE_)
c_name = orig_cell_name;
- else if (it.second->name.begins_with("\\_TECHMAP_REPLACE_."))
+ else if (tpl_cell->name.begins_with("\\_TECHMAP_REPLACE_."))
c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
else
apply_prefix(cell->name, c_name);
- RTLIL::Cell *c = module->addCell(c_name, it.second);
+ RTLIL::Cell *c = module->addCell(c_name, tpl_cell);
design->select(module, c);
- if (!flatten_mode && c->type.begins_with("\\$"))
+ if (c->type.begins_with("\\$"))
c->type = c->type.substr(1);
vector<IdString> autopurge_ports;
- for (auto &it2 : c->connections_)
+ for (auto &conn : c->connections())
{
bool autopurge = false;
if (!autopurge_tpl_bits.empty()) {
- autopurge = GetSize(it2.second) != 0;
- for (auto &bit : sigmaps.at(tpl)(it2.second))
+ autopurge = GetSize(conn.second) != 0;
+ for (auto &bit : sigmaps.at(tpl)(conn.second))
if (!autopurge_tpl_bits.count(bit)) {
autopurge = false;
break;
@@ -410,10 +357,12 @@ struct TechmapWorker
}
if (autopurge) {
- autopurge_ports.push_back(it2.first);
+ autopurge_ports.push_back(conn.first);
} else {
- apply_prefix(cell->name, it2.second, module);
- port_signal_map.apply(it2.second);
+ RTLIL::SigSpec new_conn = conn.second;
+ apply_prefix(cell->name, new_conn, module);
+ port_signal_map.apply(new_conn);
+ c->setPort(conn.first, std::move(new_conn));
}
}
@@ -435,7 +384,7 @@ struct TechmapWorker
if (c->attributes.count(ID::src))
c->add_strpool_attribute(ID::src, extra_src_attrs);
- if (techmap_replace_cell)
+ if (c_name == ID::_TECHMAP_REPLACE_)
for (auto attr : cell->attributes)
if (!c->attributes.count(attr.first))
c->attributes[attr.first] = attr.second;
@@ -463,8 +412,8 @@ struct TechmapWorker
}
}
- bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, std::set<RTLIL::Cell*> &handled_cells,
- const std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> &celltypeMap, bool in_recursion)
+ bool techmap_module(RTLIL::Design *design, RTLIL::Module *module, RTLIL::Design *map, pool<RTLIL::Cell*> &handled_cells,
+ const dict<IdString, pool<IdString>> &celltypeMap, bool in_recursion)
{
std::string mapmsg_prefix = in_recursion ? "Recursively mapping" : "Mapping";
@@ -489,13 +438,13 @@ struct TechmapWorker
}
}
- TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
- std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
- std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
+ TopoSort<RTLIL::Cell*, IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
+ dict<RTLIL::Cell*, pool<RTLIL::SigBit>> cell_to_inbit;
+ dict<RTLIL::SigBit, pool<RTLIL::Cell*>> outbit_to_cell;
- for (auto cell : module->cells())
+ for (auto cell : module->selected_cells())
{
- if (!design->selected(module, cell) || handled_cells.count(cell) > 0)
+ if (handled_cells.count(cell) > 0)
continue;
std::string cell_type = cell->type.str();
@@ -508,22 +457,6 @@ struct TechmapWorker
continue;
}
- if (flatten_mode) {
- bool keepit = cell->get_bool_attribute(ID::keep_hierarchy);
- for (auto &tpl_name : celltypeMap.at(cell_type))
- if (map->modules_[tpl_name]->get_bool_attribute(ID::keep_hierarchy))
- keepit = true;
- if (keepit) {
- if (!flatten_keep_list[cell]) {
- log("Keeping %s.%s (found keep_hierarchy property).\n", log_id(module), log_id(cell));
- flatten_keep_list.insert(cell);
- }
- if (!flatten_done_list[cell->type])
- flatten_do_list.insert(cell->type);
- continue;
- }
- }
-
for (auto &conn : cell->connections())
{
RTLIL::SigSpec sig = sigmap(conn.second);
@@ -533,7 +466,7 @@ struct TechmapWorker
continue;
for (auto &tpl_name : celltypeMap.at(cell_type)) {
- RTLIL::Module *tpl = map->modules_[tpl_name];
+ RTLIL::Module *tpl = map->module(tpl_name);
RTLIL::Wire *port = tpl->wire(conn.first);
if (port && port->port_input)
cell_to_inbit[cell].insert(sig.begin(), sig.end());
@@ -566,189 +499,188 @@ struct TechmapWorker
for (auto &tpl_name : celltypeMap.at(cell_type))
{
- RTLIL::IdString derived_name = tpl_name;
- RTLIL::Module *tpl = map->modules_[tpl_name];
- std::map<RTLIL::IdString, RTLIL::Const> parameters(cell->parameters.begin(), cell->parameters.end());
+ IdString derived_name = tpl_name;
+ RTLIL::Module *tpl = map->module(tpl_name);
+ dict<IdString, RTLIL::Const> parameters(cell->parameters);
if (tpl->get_blackbox_attribute(ignore_wb))
continue;
- if (!flatten_mode)
- {
- std::string extmapper_name;
-
- if (tpl->get_bool_attribute(ID::techmap_simplemap))
- extmapper_name = "simplemap";
-
- if (tpl->get_bool_attribute(ID::techmap_maccmap))
- extmapper_name = "maccmap";
+ std::string extmapper_name;
- if (tpl->attributes.count(ID::techmap_wrap))
- extmapper_name = "wrap";
+ if (tpl->get_bool_attribute(ID::techmap_simplemap))
+ extmapper_name = "simplemap";
- if (!extmapper_name.empty())
- {
- cell->type = cell_type;
-
- if ((extern_mode && !in_recursion) || extmapper_name == "wrap")
- {
- std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type));
+ if (tpl->get_bool_attribute(ID::techmap_maccmap))
+ extmapper_name = "maccmap";
- for (auto &c : cell->parameters)
- m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second));
+ if (tpl->attributes.count(ID::techmap_wrap))
+ extmapper_name = "wrap";
- if (extmapper_name == "wrap")
- m_name += ":" + sha1(tpl->attributes.at(ID::techmap_wrap).decode_string());
-
- RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design;
- RTLIL::Module *extmapper_module = extmapper_design->module(m_name);
+ if (!extmapper_name.empty())
+ {
+ cell->type = cell_type;
- if (extmapper_module == nullptr)
- {
- extmapper_module = extmapper_design->addModule(m_name);
- RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell);
-
- extmapper_cell->set_src_attribute(cell->get_src_attribute());
-
- int port_counter = 1;
- for (auto &c : extmapper_cell->connections_) {
- RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second));
- if (w->name.in(ID::Y, ID::Q))
- w->port_output = true;
- else
- w->port_input = true;
- w->port_id = port_counter++;
- c.second = w;
- }
+ if ((extern_mode && !in_recursion) || extmapper_name == "wrap")
+ {
+ std::string m_name = stringf("$extern:%s:%s", extmapper_name.c_str(), log_id(cell->type));
- extmapper_module->fixup_ports();
- extmapper_module->check();
+ for (auto &c : cell->parameters)
+ m_name += stringf(":%s=%s", log_id(c.first), log_signal(c.second));
- if (extmapper_name == "simplemap") {
- log("Creating %s with simplemap.\n", log_id(extmapper_module));
- if (simplemap_mappers.count(extmapper_cell->type) == 0)
- log_error("No simplemap mapper for cell type %s found!\n", log_id(extmapper_cell->type));
- simplemap_mappers.at(extmapper_cell->type)(extmapper_module, extmapper_cell);
- extmapper_module->remove(extmapper_cell);
- }
+ if (extmapper_name == "wrap")
+ m_name += ":" + sha1(tpl->attributes.at(ID::techmap_wrap).decode_string());
- if (extmapper_name == "maccmap") {
- log("Creating %s with maccmap.\n", log_id(extmapper_module));
- if (extmapper_cell->type != ID($macc))
- log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type));
- maccmap(extmapper_module, extmapper_cell);
- extmapper_module->remove(extmapper_cell);
- }
+ RTLIL::Design *extmapper_design = extern_mode && !in_recursion ? design : tpl->design;
+ RTLIL::Module *extmapper_module = extmapper_design->module(m_name);
- if (extmapper_name == "wrap") {
- std::string cmd_string = tpl->attributes.at(ID::techmap_wrap).decode_string();
- log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
- mkdebug.on();
- Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
- log_continue = true;
- }
+ if (extmapper_module == nullptr)
+ {
+ extmapper_module = extmapper_design->addModule(m_name);
+ RTLIL::Cell *extmapper_cell = extmapper_module->addCell(cell->type, cell);
+
+ extmapper_cell->set_src_attribute(cell->get_src_attribute());
+
+ int port_counter = 1;
+ for (auto &c : extmapper_cell->connections_) {
+ RTLIL::Wire *w = extmapper_module->addWire(c.first, GetSize(c.second));
+ if (w->name.in(ID::Y, ID::Q))
+ w->port_output = true;
+ else
+ w->port_input = true;
+ w->port_id = port_counter++;
+ c.second = w;
}
- cell->type = extmapper_module->name;
- cell->parameters.clear();
+ extmapper_module->fixup_ports();
+ extmapper_module->check();
- if (!extern_mode || in_recursion) {
- tpl = extmapper_module;
- goto use_wrapper_tpl;
+ if (extmapper_name == "simplemap") {
+ log("Creating %s with simplemap.\n", log_id(extmapper_module));
+ if (simplemap_mappers.count(extmapper_cell->type) == 0)
+ log_error("No simplemap mapper for cell type %s found!\n", log_id(extmapper_cell->type));
+ simplemap_mappers.at(extmapper_cell->type)(extmapper_module, extmapper_cell);
+ extmapper_module->remove(extmapper_cell);
}
- auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type));
- if (!log_msg_cache.count(msg)) {
- log_msg_cache.insert(msg);
- log("%s\n", msg.c_str());
- }
- log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
- }
- else
- {
- auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type));
- if (!log_msg_cache.count(msg)) {
- log_msg_cache.insert(msg);
- log("%s\n", msg.c_str());
+ if (extmapper_name == "maccmap") {
+ log("Creating %s with maccmap.\n", log_id(extmapper_module));
+ if (extmapper_cell->type != ID($macc))
+ log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(extmapper_cell->type));
+ maccmap(extmapper_module, extmapper_cell);
+ extmapper_module->remove(extmapper_cell);
}
- log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
- if (extmapper_name == "simplemap") {
- if (simplemap_mappers.count(cell->type) == 0)
- log_error("No simplemap mapper for cell type %s found!\n", RTLIL::id2cstr(cell->type));
- simplemap_mappers.at(cell->type)(module, cell);
+ if (extmapper_name == "wrap") {
+ std::string cmd_string = tpl->attributes.at(ID::techmap_wrap).decode_string();
+ log("Running \"%s\" on wrapper %s.\n", cmd_string.c_str(), log_id(extmapper_module));
+ mkdebug.on();
+ Pass::call_on_module(extmapper_design, extmapper_module, cmd_string);
+ log_continue = true;
}
+ }
- if (extmapper_name == "maccmap") {
- if (cell->type != ID($macc))
- log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type));
- maccmap(module, cell);
- }
+ cell->type = extmapper_module->name;
+ cell->parameters.clear();
- module->remove(cell);
- cell = NULL;
+ if (!extern_mode || in_recursion) {
+ tpl = extmapper_module;
+ goto use_wrapper_tpl;
}
- did_something = true;
- mapped_cell = true;
- break;
+ auto msg = stringf("Using extmapper %s for cells of type %s.", log_id(extmapper_module), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) to %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(extmapper_module));
}
+ else
+ {
+ auto msg = stringf("Using extmapper %s for cells of type %s.", extmapper_name.c_str(), log_id(cell->type));
+ if (!log_msg_cache.count(msg)) {
+ log_msg_cache.insert(msg);
+ log("%s\n", msg.c_str());
+ }
+ log_debug("%s %s.%s (%s) with %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), extmapper_name.c_str());
+
+ if (extmapper_name == "simplemap") {
+ if (simplemap_mappers.count(cell->type) == 0)
+ log_error("No simplemap mapper for cell type %s found!\n", log_id(cell->type));
+ simplemap_mappers.at(cell->type)(module, cell);
+ }
+
+ if (extmapper_name == "maccmap") {
+ if (cell->type != ID($macc))
+ log_error("The maccmap mapper can only map $macc (not %s) cells!\n", log_id(cell->type));
+ maccmap(module, cell);
+ }
- for (auto conn : cell->connections()) {
- if (conn.first.begins_with("$"))
- continue;
- if (tpl->wires_.count(conn.first) > 0 && tpl->wires_.at(conn.first)->port_id > 0)
- continue;
- if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0)
- goto next_tpl;
- parameters[conn.first] = conn.second.as_const();
+ module->remove(cell);
+ cell = nullptr;
}
- if (0) {
- next_tpl:
+ did_something = true;
+ mapped_cell = true;
+ break;
+ }
+
+ for (auto &conn : cell->connections()) {
+ if (conn.first.begins_with("$"))
continue;
- }
+ if (tpl->wire(conn.first) != nullptr && tpl->wire(conn.first)->port_id > 0)
+ continue;
+ if (!conn.second.is_fully_const() || parameters.count(conn.first) > 0 || tpl->avail_parameters.count(conn.first) == 0)
+ goto next_tpl;
+ parameters[conn.first] = conn.second.as_const();
+ }
- if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0)
- parameters[ID::_TECHMAP_CELLTYPE_] = RTLIL::unescape_id(cell->type);
+ if (0) {
+ next_tpl:
+ continue;
+ }
- for (auto conn : cell->connections()) {
- if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))) != 0) {
- std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector();
- for (auto &bit : v)
- bit = RTLIL::SigBit(bit.wire == NULL ? RTLIL::State::S1 : RTLIL::State::S0);
- parameters[stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
- }
- if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))) != 0) {
- std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector();
- for (auto &bit : v)
- if (bit.wire != NULL)
- bit = RTLIL::SigBit(RTLIL::State::Sx);
- parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
- }
- if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) {
- auto sig = sigmap(conn.second);
- RTLIL::Const value(State::Sx, sig.size());
- for (int i = 0; i < sig.size(); i++) {
- auto it = init_bits.find(sig[i]);
- if (it != init_bits.end()) {
- value[i] = it->second;
- }
+ if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0)
+ parameters.emplace(ID::_TECHMAP_CELLTYPE_, RTLIL::unescape_id(cell->type));
+
+ for (auto &conn : cell->connections()) {
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first))) != 0) {
+ std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector();
+ for (auto &bit : v)
+ bit = RTLIL::SigBit(bit.wire == nullptr ? RTLIL::State::S1 : RTLIL::State::S0);
+ parameters.emplace(stringf("\\_TECHMAP_CONSTMSK_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const());
+ }
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first))) != 0) {
+ std::vector<RTLIL::SigBit> v = sigmap(conn.second).to_sigbit_vector();
+ for (auto &bit : v)
+ if (bit.wire != nullptr)
+ bit = RTLIL::SigBit(RTLIL::State::Sx);
+ parameters.emplace(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const());
+ }
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first))) != 0) {
+ auto sig = sigmap(conn.second);
+ RTLIL::Const value(State::Sx, sig.size());
+ for (int i = 0; i < sig.size(); i++) {
+ auto it = init_bits.find(sig[i]);
+ if (it != init_bits.end()) {
+ value[i] = it->second;
}
- parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value;
}
+ parameters.emplace(stringf("\\_TECHMAP_WIREINIT_%s_", log_id(conn.first)), value);
}
+ }
+ {
int unique_bit_id_counter = 0;
- std::map<RTLIL::SigBit, int> unique_bit_id;
+ dict<RTLIL::SigBit, int> unique_bit_id;
unique_bit_id[RTLIL::State::S0] = unique_bit_id_counter++;
unique_bit_id[RTLIL::State::S1] = unique_bit_id_counter++;
unique_bit_id[RTLIL::State::Sx] = unique_bit_id_counter++;
unique_bit_id[RTLIL::State::Sz] = unique_bit_id_counter++;
- for (auto conn : cell->connections())
- if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) {
- for (auto &bit : sigmap(conn.second).to_sigbit_vector())
+ for (auto &conn : cell->connections())
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first))) != 0) {
+ for (auto &bit : sigmap(conn.second))
if (unique_bit_id.count(bit) == 0)
unique_bit_id[bit] = unique_bit_id_counter++;
}
@@ -763,14 +695,17 @@ struct TechmapWorker
if (tpl->avail_parameters.count(ID::_TECHMAP_BITS_CONNMAP_))
parameters[ID::_TECHMAP_BITS_CONNMAP_] = bits;
- for (auto conn : cell->connections())
- if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+ for (auto &conn : cell->connections())
+ if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first))) != 0) {
RTLIL::Const value;
- for (auto &bit : sigmap(conn.second).to_sigbit_vector()) {
- RTLIL::Const chunk(unique_bit_id.at(bit), bits);
- value.bits.insert(value.bits.end(), chunk.bits.begin(), chunk.bits.end());
+ for (auto &bit : sigmap(conn.second)) {
+ int val = unique_bit_id.at(bit);
+ for (int i = 0; i < bits; i++) {
+ value.bits.push_back((val & 1) != 0 ? State::S1 : State::S0);
+ val = val >> 1;
+ }
}
- parameters[stringf("\\_TECHMAP_CONNMAP_%s_", RTLIL::id2cstr(conn.first))] = value;
+ parameters.emplace(stringf("\\_TECHMAP_CONNMAP_%s_", log_id(conn.first)), value);
}
}
@@ -778,34 +713,31 @@ struct TechmapWorker
use_wrapper_tpl:;
// do not register techmap_wrap modules with techmap_cache
} else {
- std::pair<RTLIL::IdString, std::map<RTLIL::IdString, RTLIL::Const>> key(tpl_name, parameters);
- if (techmap_cache.count(key) > 0) {
- tpl = techmap_cache[key];
+ std::pair<IdString, dict<IdString, RTLIL::Const>> key(tpl_name, parameters);
+ auto it = techmap_cache.find(key);
+ if (it != techmap_cache.end()) {
+ tpl = it->second;
} else {
if (parameters.size() != 0) {
mkdebug.on();
- derived_name = tpl->derive(map, dict<RTLIL::IdString, RTLIL::Const>(parameters.begin(), parameters.end()));
+ derived_name = tpl->derive(map, parameters);
tpl = map->module(derived_name);
log_continue = true;
}
- techmap_cache[key] = tpl;
+ techmap_cache.emplace(std::move(key), tpl);
}
}
- if (flatten_mode) {
- techmap_do_cache[tpl] = true;
- } else {
- RTLIL::Module *constmapped_tpl = map->module(constmap_tpl_name(sigmap, tpl, cell, false));
- if (constmapped_tpl != nullptr)
- tpl = constmapped_tpl;
- }
+ RTLIL::Module *constmapped_tpl = map->module(constmap_tpl_name(sigmap, tpl, cell, false));
+ if (constmapped_tpl != nullptr)
+ tpl = constmapped_tpl;
if (techmap_do_cache.count(tpl) == 0)
{
bool keep_running = true;
techmap_do_cache[tpl] = true;
- std::set<std::string> techmap_wire_names;
+ pool<IdString> techmap_wire_names;
while (keep_running)
{
@@ -815,11 +747,11 @@ struct TechmapWorker
for (auto &it : twd)
techmap_wire_names.insert(it.first);
- for (auto &it : twd["_TECHMAP_FAIL_"]) {
+ for (auto &it : twd[ID::_TECHMAP_FAIL_]) {
RTLIL::SigSpec value = it.value;
if (value.is_fully_const() && value.as_bool()) {
log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n",
- derived_name.c_str(), RTLIL::id2cstr(it.wire->name), log_signal(value));
+ derived_name.c_str(), log_id(it.wire->name), log_signal(value));
techmap_do_cache[tpl] = false;
}
}
@@ -829,13 +761,13 @@ struct TechmapWorker
for (auto &it : twd)
{
- if (it.first.compare(0, 12, "_TECHMAP_DO_") != 0 || it.second.empty())
+ if (!it.first.begins_with("\\_TECHMAP_DO_") || it.second.empty())
continue;
auto &data = it.second.front();
if (!data.value.is_fully_const())
- log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(data.wire->name), log_signal(data.value));
+ log_error("Techmap yielded config wire %s with non-const value %s.\n", log_id(data.wire->name), log_signal(data.value));
techmap_wire_names.erase(it.first);
@@ -851,7 +783,7 @@ struct TechmapWorker
cmd_string = cmd_string.substr(strlen("CONSTMAP; "));
log("Analyzing pattern of constant bits for this cell:\n");
- RTLIL::IdString new_tpl_name = constmap_tpl_name(sigmap, tpl, cell, true);
+ IdString new_tpl_name = constmap_tpl_name(sigmap, tpl, cell, true);
log("Creating constmapped module `%s'.\n", log_id(new_tpl_name));
log_assert(map->module(new_tpl_name) == nullptr);
@@ -862,16 +794,16 @@ struct TechmapWorker
techmap_do_cache[new_tpl] = true;
tpl = new_tpl;
- std::map<RTLIL::SigBit, RTLIL::SigBit> port_new2old_map;
- std::map<RTLIL::SigBit, RTLIL::SigBit> port_connmap;
- std::map<RTLIL::SigBit, RTLIL::SigBit> cellbits_to_tplbits;
+ dict<RTLIL::SigBit, RTLIL::SigBit> port_new2old_map;
+ dict<RTLIL::SigBit, RTLIL::SigBit> port_connmap;
+ dict<RTLIL::SigBit, RTLIL::SigBit> cellbits_to_tplbits;
for (auto wire : tpl->wires().to_vector())
{
if (!wire->port_input || wire->port_output)
continue;
- RTLIL::IdString port_name = wire->name;
+ IdString port_name = wire->name;
tpl->rename(wire, NEW_ID);
RTLIL::Wire *new_wire = tpl->addWire(port_name, wire);
@@ -879,12 +811,12 @@ struct TechmapWorker
wire->port_id = 0;
for (int i = 0; i < wire->width; i++) {
- port_new2old_map[RTLIL::SigBit(new_wire, i)] = RTLIL::SigBit(wire, i);
- port_connmap[RTLIL::SigBit(wire, i)] = RTLIL::SigBit(new_wire, i);
+ port_new2old_map.emplace(RTLIL::SigBit(new_wire, i), RTLIL::SigBit(wire, i));
+ port_connmap.emplace(RTLIL::SigBit(wire, i), RTLIL::SigBit(new_wire, i));
}
}
- for (auto conn : cell->connections())
+ for (auto &conn : cell->connections())
for (int i = 0; i < GetSize(conn.second); i++)
{
RTLIL::SigBit bit = sigmap(conn.second[i]);
@@ -926,7 +858,7 @@ struct TechmapWorker
log_assert(!strncmp(q, "_TECHMAP_DO_", 12));
std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12);
- while (tpl->wires_.count(new_name))
+ while (tpl->wire(new_name) != nullptr)
new_name += "_";
tpl->rename(data.wire->name, new_name);
@@ -937,17 +869,17 @@ struct TechmapWorker
TechmapWires twd = techmap_find_special_wires(tpl);
for (auto &it : twd) {
- if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
- log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
+ if (it.first != ID::_TECHMAP_FAIL_ && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.begins_with("\\_TECHMAP_DO_") && !it.first.begins_with("\\_TECHMAP_DONE_"))
+ log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first));
if (techmap_do_cache[tpl])
for (auto &it2 : it.second)
if (!it2.value.is_fully_const())
- log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value));
+ log_error("Techmap yielded config wire %s with non-const value %s.\n", log_id(it2.wire->name), log_signal(it2.value));
techmap_wire_names.erase(it.first);
}
for (auto &it : techmap_wire_names)
- log_error("Techmap special wire %s disappeared. This is considered a fatal error.\n", RTLIL::id2cstr(it));
+ log_error("Techmap special wire %s disappeared. This is considered a fatal error.\n", log_id(it));
if (recursive_mode) {
if (log_continue) {
@@ -970,10 +902,10 @@ struct TechmapWorker
TechmapWires twd = techmap_find_special_wires(tpl);
for (auto &it : twd) {
- if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_") {
+ if (it.first.begins_with("\\_TECHMAP_REMOVEINIT_")) {
for (auto &it2 : it.second) {
auto val = it2.value.as_const();
- auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1));
+ auto wirename = RTLIL::escape_id(it.first.substr(21, it.first.size() - 21 - 1));
auto it = cell->connections().find(wirename);
if (it != cell->connections().end()) {
auto sig = sigmap(it->second);
@@ -1015,7 +947,7 @@ struct TechmapWorker
}
log_debug("%s %s.%s (%s) using %s.\n", mapmsg_prefix.c_str(), log_id(module), log_id(cell), log_id(cell->type), log_id(tpl));
techmap_module_worker(design, module, cell, tpl);
- cell = NULL;
+ cell = nullptr;
}
did_something = true;
mapped_cell = true;
@@ -1275,8 +1207,7 @@ struct TechmapPass : public Pass {
RTLIL::Design *map = new RTLIL::Design;
if (map_files.empty()) {
- std::istringstream f(stdcells_code);
- Frontend::frontend_call(map, &f, "<techmap.v>", verilog_frontend);
+ Frontend::frontend_call(map, nullptr, "+/techmap.v", verilog_frontend);
} else {
for (auto &fn : map_files)
if (fn.compare(0, 1, "%") == 0) {
@@ -1285,35 +1216,30 @@ struct TechmapPass : public Pass {
log_cmd_error("Can't open saved design `%s'.\n", fn.c_str()+1);
}
for (auto mod : saved_designs.at(fn.substr(1))->modules())
- if (!map->has(mod->name))
+ if (!map->module(mod->name))
map->add(mod->clone());
} else {
- std::ifstream f;
- rewrite_filename(fn);
- f.open(fn.c_str());
- yosys_input_files.insert(fn);
- if (f.fail())
- log_cmd_error("Can't open map file `%s'\n", fn.c_str());
- Frontend::frontend_call(map, &f, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "ilang" : verilog_frontend));
+ Frontend::frontend_call(map, nullptr, fn, (fn.size() > 3 && fn.compare(fn.size()-3, std::string::npos, ".il") == 0 ? "ilang" : verilog_frontend));
}
}
log_header(design, "Continuing TECHMAP pass.\n");
- std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
- for (auto &it : map->modules_) {
- if (it.second->attributes.count(ID::techmap_celltype) && !it.second->attributes.at(ID::techmap_celltype).bits.empty()) {
- char *p = strdup(it.second->attributes.at(ID::techmap_celltype).decode_string().c_str());
- for (char *q = strtok(p, " \t\r\n"); q; q = strtok(NULL, " \t\r\n"))
- celltypeMap[RTLIL::escape_id(q)].insert(it.first);
+ dict<IdString, pool<IdString>> celltypeMap;
+ for (auto module : map->modules()) {
+ if (module->attributes.count(ID::techmap_celltype) && !module->attributes.at(ID::techmap_celltype).bits.empty()) {
+ char *p = strdup(module->attributes.at(ID::techmap_celltype).decode_string().c_str());
+ for (char *q = strtok(p, " \t\r\n"); q; q = strtok(nullptr, " \t\r\n"))
+ celltypeMap[RTLIL::escape_id(q)].insert(module->name);
free(p);
} else {
- string module_name = it.first.str();
- if (it.first.begins_with("\\$"))
- module_name = module_name.substr(1);
- celltypeMap[module_name].insert(it.first);
+ IdString module_name = module->name.begins_with("\\$") ?
+ module->name.substr(1) : module->name.str();
+ celltypeMap[module_name].insert(module->name);
}
}
+ for (auto &i : celltypeMap)
+ i.second.sort(RTLIL::sort_by_id_str());
for (auto module : design->modules())
worker.module_queue.insert(module);
@@ -1325,7 +1251,7 @@ struct TechmapPass : public Pass {
int module_max_iter = max_iter;
bool did_something = true;
- std::set<RTLIL::Cell*> handled_cells;
+ pool<RTLIL::Cell*> handled_cells;
while (did_something) {
did_something = false;
if (worker.techmap_module(design, module, map, handled_cells, celltypeMap, false))
@@ -1344,100 +1270,4 @@ struct TechmapPass : public Pass {
}
} TechmapPass;
-struct FlattenPass : public Pass {
- FlattenPass() : Pass("flatten", "flatten design") { }
- void help() YS_OVERRIDE
- {
- // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
- log("\n");
- log(" flatten [options] [selection]\n");
- log("\n");
- log("This pass flattens the design by replacing cells by their implementation. This\n");
- log("pass is very similar to the 'techmap' pass. The only difference is that this\n");
- log("pass is using the current design as mapping library.\n");
- log("\n");
- log("Cells and/or modules with the 'keep_hierarchy' attribute set will not be\n");
- log("flattened by this command.\n");
- log("\n");
- log(" -wb\n");
- log(" Ignore the 'whitebox' attribute on cell implementations.\n");
- log("\n");
- }
- void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
- {
- log_header(design, "Executing FLATTEN pass (flatten design).\n");
- log_push();
-
- TechmapWorker worker;
- worker.flatten_mode = true;
-
- size_t argidx;
- for (argidx = 1; argidx < args.size(); argidx++) {
- if (args[argidx] == "-wb") {
- worker.ignore_wb = true;
- continue;
- }
- break;
- }
- extra_args(args, argidx, design);
-
-
- std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap;
- for (auto module : design->modules())
- celltypeMap[module->name].insert(module->name);
-
- RTLIL::Module *top_mod = NULL;
- if (design->full_selection())
- for (auto mod : design->modules())
- if (mod->get_bool_attribute(ID::top))
- top_mod = mod;
-
- std::set<RTLIL::Cell*> handled_cells;
- if (top_mod != NULL) {
- worker.flatten_do_list.insert(top_mod->name);
- while (!worker.flatten_do_list.empty()) {
- auto mod = design->module(*worker.flatten_do_list.begin());
- while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { }
- worker.flatten_done_list.insert(mod->name);
- worker.flatten_do_list.erase(mod->name);
- }
- } else {
- for (auto mod : vector<Module*>(design->modules())) {
- while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { }
- }
- }
-
- log_suppressed();
- log("No more expansions possible.\n");
-
- if (top_mod != NULL)
- {
- pool<RTLIL::IdString> used_modules, new_used_modules;
- new_used_modules.insert(top_mod->name);
- while (!new_used_modules.empty()) {
- pool<RTLIL::IdString> queue;
- queue.swap(new_used_modules);
- for (auto modname : queue)
- used_modules.insert(modname);
- for (auto modname : queue)
- for (auto cell : design->module(modname)->cells())
- if (design->module(cell->type) && !used_modules[cell->type])
- new_used_modules.insert(cell->type);
- }
-
- dict<RTLIL::IdString, RTLIL::Module*> new_modules;
- for (auto mod : vector<Module*>(design->modules()))
- if (used_modules[mod->name] || mod->get_blackbox_attribute(worker.ignore_wb)) {
- new_modules[mod->name] = mod;
- } else {
- log("Deleting now unused module %s.\n", log_id(mod));
- delete mod;
- }
- design->modules_.swap(new_modules);
- }
-
- log_pop();
- }
-} FlattenPass;
-
PRIVATE_NAMESPACE_END
diff --git a/passes/tests/test_cell.cc b/passes/tests/test_cell.cc
index cdbe922b2..c6801007d 100644
--- a/passes/tests/test_cell.cc
+++ b/passes/tests/test_cell.cc
@@ -264,7 +264,7 @@ static void create_gold_module(RTLIL::Design *design, RTLIL::IdString cell_type,
cell->setPort(ID::Y, wire);
}
- if (muxdiv && cell_type.in(ID($div), ID($mod))) {
+ if (muxdiv && cell_type.in(ID($div), ID($mod), ID($divfloor), ID($modfloor))) {
auto b_not_zero = module->ReduceBool(NEW_ID, cell->getPort(ID::B));
auto div_out = module->addWire(NEW_ID, GetSize(cell->getPort(ID::Y)));
module->addMux(NEW_ID, RTLIL::SigSpec(0, GetSize(div_out)), div_out, b_not_zero, cell->getPort(ID::Y));
@@ -839,6 +839,8 @@ struct TestCellPass : public Pass {
cell_types[ID($mul)] = "ABSY";
cell_types[ID($div)] = "ABSY";
cell_types[ID($mod)] = "ABSY";
+ cell_types[ID($divfloor)] = "ABSY";
+ cell_types[ID($modfloor)] = "ABSY";
// cell_types[ID($pow)] = "ABsY";
cell_types[ID($logic_not)] = "ASY";
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/techlibs/common/simlib.v b/techlibs/common/simlib.v
index 2cdddeabb..125b8e013 100644
--- a/techlibs/common/simlib.v
+++ b/techlibs/common/simlib.v
@@ -997,6 +997,12 @@ endmodule
// --------------------------------------------------------
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $div (A, B, Y)
+//-
+//- Division with truncated result (rounded towards 0).
+//-
module \$div (A, B, Y);
parameter A_SIGNED = 0;
@@ -1021,6 +1027,14 @@ endmodule
// --------------------------------------------------------
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $mod (A, B, Y)
+//-
+//- Modulo/remainder of division with truncated result (rounded towards 0).
+//-
+//- Invariant: $div(A, B) * B + $mod(A, B) == A
+//-
module \$mod (A, B, Y);
parameter A_SIGNED = 0;
@@ -1044,6 +1058,83 @@ endgenerate
endmodule
// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $divfloor (A, B, Y)
+//-
+//- Division with floored result (rounded towards negative infinity).
+//-
+module \$divfloor (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ localparam WIDTH =
+ A_WIDTH >= B_WIDTH && A_WIDTH >= Y_WIDTH ? A_WIDTH :
+ B_WIDTH >= A_WIDTH && B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
+ wire [WIDTH:0] A_buf, B_buf, N_buf;
+ assign A_buf = $signed(A);
+ assign B_buf = $signed(B);
+ assign N_buf = (A[A_WIDTH-1] == B[B_WIDTH-1]) || A == 0 ? A_buf : $signed(A_buf - (B[B_WIDTH-1] ? B_buf+1 : B_buf-1));
+ assign Y = $signed(N_buf) / $signed(B_buf);
+ end else begin:BLOCK2
+ assign Y = A / B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
+
+// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+//-
+//- $modfloor (A, B, Y)
+//-
+//- Modulo/remainder of division with floored result (rounded towards negative infinity).
+//-
+//- Invariant: $divfloor(A, B) * B + $modfloor(A, B) == A
+//-
+module \$modfloor (A, B, Y);
+
+parameter A_SIGNED = 0;
+parameter B_SIGNED = 0;
+parameter A_WIDTH = 0;
+parameter B_WIDTH = 0;
+parameter Y_WIDTH = 0;
+
+input [A_WIDTH-1:0] A;
+input [B_WIDTH-1:0] B;
+output [Y_WIDTH-1:0] Y;
+
+generate
+ if (A_SIGNED && B_SIGNED) begin:BLOCK1
+ localparam WIDTH = B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
+ wire [WIDTH-1:0] B_buf, Y_trunc;
+ assign B_buf = $signed(B);
+ assign Y_trunc = $signed(A) % $signed(B);
+ // flooring mod is the same as truncating mod for positive division results (A and B have
+ // the same sign), as well as when there's no remainder.
+ // For all other cases, they behave as `floor - trunc = B`
+ assign Y = (A[A_WIDTH-1] == B[B_WIDTH-1]) || Y_trunc == 0 ? Y_trunc : $signed(B_buf) + $signed(Y_trunc);
+ end else begin:BLOCK2
+ // no difference between truncating and flooring for unsigned
+ assign Y = A % B;
+ end
+endgenerate
+
+endmodule
+
+// --------------------------------------------------------
`ifndef SIMLIB_NOPOW
module \$pow (A, B, Y);
diff --git a/techlibs/common/techmap.v b/techlibs/common/techmap.v
index c1efc378b..eafe8d4da 100644
--- a/techlibs/common/techmap.v
+++ b/techlibs/common/techmap.v
@@ -364,7 +364,8 @@ module \$__div_mod_u (A, B, Y, R);
end endgenerate
endmodule
-module \$__div_mod (A, B, Y, R);
+// truncating signed division/modulo
+module \$__div_mod_trunc (A, B, Y, R);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 1;
@@ -420,7 +421,7 @@ module _90_div (A, B, Y);
(* force_downto *)
output [Y_WIDTH-1:0] Y;
- \$__div_mod #(
+ \$__div_mod_trunc #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
.A_WIDTH(A_WIDTH),
@@ -448,7 +449,107 @@ module _90_mod (A, B, Y);
(* force_downto *)
output [Y_WIDTH-1:0] Y;
- \$__div_mod #(
+ \$__div_mod_trunc #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) div_mod (
+ .A(A),
+ .B(B),
+ .R(Y)
+ );
+endmodule
+
+// flooring signed division/modulo
+module \$__div_mod_floor (A, B, Y, R);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ localparam WIDTH =
+ A_WIDTH >= B_WIDTH && A_WIDTH >= Y_WIDTH ? A_WIDTH :
+ B_WIDTH >= A_WIDTH && B_WIDTH >= Y_WIDTH ? B_WIDTH : Y_WIDTH;
+
+ input [A_WIDTH-1:0] A;
+ input [B_WIDTH-1:0] B;
+ output [Y_WIDTH-1:0] Y, R;
+
+ wire [WIDTH-1:0] A_buf, B_buf;
+ \$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(WIDTH)) A_conv (.A(A), .Y(A_buf));
+ \$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(WIDTH)) B_conv (.A(B), .Y(B_buf));
+
+ wire [WIDTH-1:0] A_buf_u, B_buf_u, Y_u, R_u, R_s;
+ assign A_buf_u = A_SIGNED && A_buf[WIDTH-1] ? -A_buf : A_buf;
+ assign B_buf_u = B_SIGNED && B_buf[WIDTH-1] ? -B_buf : B_buf;
+
+ \$__div_mod_u #(
+ .WIDTH(WIDTH)
+ ) div_mod_u (
+ .A(A_buf_u),
+ .B(B_buf_u),
+ .Y(Y_u),
+ .R(R_u)
+ );
+
+ // For negative results, if there was a remainder, subtract one to turn
+ // the round towards 0 into a round towards -inf
+ assign Y = A_SIGNED && B_SIGNED && (A_buf[WIDTH-1] != B_buf[WIDTH-1]) ? (R_u == 0 ? -Y_u : -Y_u-1) : Y_u;
+
+ // truncating modulo
+ assign R_s = A_SIGNED && B_SIGNED && A_buf[WIDTH-1] ? -R_u : R_u;
+ // Flooring modulo differs from truncating modulo only if it is nonzero and
+ // A and B have different signs - then `floor - trunc = B`
+ assign R = (R_s != 0) && A_SIGNED && B_SIGNED && (A_buf[WIDTH-1] != B_buf[WIDTH-1]) ? $signed(B_buf) + $signed(R_s) : R_s;
+endmodule
+
+(* techmap_celltype = "$divfloor" *)
+module _90_divfloor (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ (* force_downto *)
+ input [A_WIDTH-1:0] A;
+ (* force_downto *)
+ input [B_WIDTH-1:0] B;
+ (* force_downto *)
+ output [Y_WIDTH-1:0] Y;
+
+ \$__div_mod_floor #(
+ .A_SIGNED(A_SIGNED),
+ .B_SIGNED(B_SIGNED),
+ .A_WIDTH(A_WIDTH),
+ .B_WIDTH(B_WIDTH),
+ .Y_WIDTH(Y_WIDTH)
+ ) div_mod (
+ .A(A),
+ .B(B),
+ .Y(Y)
+ );
+endmodule
+
+(* techmap_celltype = "$modfloor" *)
+module _90_modfloor (A, B, Y);
+ parameter A_SIGNED = 0;
+ parameter B_SIGNED = 0;
+ parameter A_WIDTH = 1;
+ parameter B_WIDTH = 1;
+ parameter Y_WIDTH = 1;
+
+ (* force_downto *)
+ input [A_WIDTH-1:0] A;
+ (* force_downto *)
+ input [B_WIDTH-1:0] B;
+ (* force_downto *)
+ output [Y_WIDTH-1:0] Y;
+
+ \$__div_mod_floor #(
.A_SIGNED(A_SIGNED),
.B_SIGNED(B_SIGNED),
.A_WIDTH(A_WIDTH),
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/ice40/mux.ys b/tests/arch/ice40/mux.ys
index 99822391d..2b661fd6b 100644
--- a/tests/arch/ice40/mux.ys
+++ b/tests/arch/ice40/mux.ys
@@ -35,6 +35,7 @@ proc
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd mux16 # Constrain all select calls below inside the top module
-select -assert-count 11 t:SB_LUT4
+select -assert-min 11 t:SB_LUT4
+select -assert-max 12 t:SB_LUT4
select -assert-none t:SB_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/simple/constmuldivmod.v b/tests/simple/constmuldivmod.v
index d1d8be862..5dd8f9295 100644
--- a/tests/simple/constmuldivmod.v
+++ b/tests/simple/constmuldivmod.v
@@ -1,4 +1,4 @@
-module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y);
+module constmuldivmod(input [7:0] A, input [5:0] mode, output reg [7:0] Y);
always @* begin
case (mode)
0: Y = A / 8'd0;
@@ -21,6 +21,46 @@ module constmuldivmod(input [7:0] A, input [2:0] mode, output reg [7:0] Y);
13: Y = A % 8'd8;
14: Y = A * 8'd8;
+ 15: Y = $signed(A) / $signed(8'd0);
+ 16: Y = $signed(A) % $signed(8'd0);
+ 17: Y = $signed(A) * $signed(8'd0);
+
+ 18: Y = $signed(A) / $signed(8'd1);
+ 19: Y = $signed(A) % $signed(8'd1);
+ 20: Y = $signed(A) * $signed(8'd1);
+
+ 21: Y = $signed(A) / $signed(8'd2);
+ 22: Y = $signed(A) % $signed(8'd2);
+ 23: Y = $signed(A) * $signed(8'd2);
+
+ 24: Y = $signed(A) / $signed(8'd4);
+ 25: Y = $signed(A) % $signed(8'd4);
+ 26: Y = $signed(A) * $signed(8'd4);
+
+ 27: Y = $signed(A) / $signed(8'd8);
+ 28: Y = $signed(A) % $signed(8'd8);
+ 29: Y = $signed(A) * $signed(8'd8);
+
+ 30: Y = $signed(A) / $signed(-8'd0);
+ 31: Y = $signed(A) % $signed(-8'd0);
+ 32: Y = $signed(A) * $signed(-8'd0);
+
+ 33: Y = $signed(A) / $signed(-8'd1);
+ 34: Y = $signed(A) % $signed(-8'd1);
+ 35: Y = $signed(A) * $signed(-8'd1);
+
+ 36: Y = $signed(A) / $signed(-8'd2);
+ 37: Y = $signed(A) % $signed(-8'd2);
+ 38: Y = $signed(A) * $signed(-8'd2);
+
+ 39: Y = $signed(A) / $signed(-8'd4);
+ 40: Y = $signed(A) % $signed(-8'd4);
+ 41: Y = $signed(A) * $signed(-8'd4);
+
+ 42: Y = $signed(A) / $signed(-8'd8);
+ 43: Y = $signed(A) % $signed(-8'd8);
+ 44: Y = $signed(A) * $signed(-8'd8);
+
default: Y = 8'd16 * A;
endcase
end
diff --git a/tests/svtypes/logic_rom.sv b/tests/svtypes/logic_rom.sv
new file mode 100644
index 000000000..45fe0a4ca
--- /dev/null
+++ b/tests/svtypes/logic_rom.sv
@@ -0,0 +1,6 @@
+module top(input [3:0] addr, output [7:0] data);
+ logic [7:0] mem[0:15];
+ assign data = mem[addr];
+ integer i;
+ initial for(i = 0; i < 16; i = i + 1) mem[i] = i;
+endmodule
diff --git a/tests/svtypes/logic_rom.ys b/tests/svtypes/logic_rom.ys
new file mode 100644
index 000000000..7b079c136
--- /dev/null
+++ b/tests/svtypes/logic_rom.ys
@@ -0,0 +1,3 @@
+read_verilog -sv logic_rom.sv
+prep -top top
+select -assert-count 1 t:$mem r:SIZE=16 %i r:WIDTH=8 %i
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/printattr.ys b/tests/various/printattr.ys
new file mode 100644
index 000000000..afc6d8eb6
--- /dev/null
+++ b/tests/various/printattr.ys
@@ -0,0 +1,14 @@
+logger -expect log ".*cells_not_processed=[01]* .*" 1
+logger -expect log ".*src=.<<EOT:1\.1-9\.10. .*" 1
+read_verilog <<EOT
+module mux2(a, b, s, y);
+ input a, b, s;
+ output y;
+
+ wire s_n = ~s;
+ wire t0 = s & a;
+ wire t1 = s_n & b;
+ assign y = t0 | t1;
+endmodule
+EOT
+printattrs
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);