diff options
34 files changed, 981 insertions, 321 deletions
diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 000000000..a73779920 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,37 @@ +## CODE NOTIFICATIONS +# Register yourself here to be notified about modifications +# for any files you have an interest in/know your way around. + +# Each line is a file pattern followed by one or more users. +# Both github usernames and email addresses are supported. +# Order is important; the last matching pattern takes the most +# precedence. Previous matches will not be applied. + + +# PATH (can use glob) USERNAME(S) + +passes/cmds/scratchpad.cc @nakengelhardt +frontends/rpc/ @whitequark +backends/cxxrtl/ @whitequark +passes/cmds/bugpoint.cc @whitequark +passes/techmap/flowmap.cc @whitequark +passes/opt/opt_lut.cc @whitequark + + +## External Contributors +# Only users with write permission to the repository get review +# requests automatically, but we add information for other +# contributors here too, so we know who to ask to take a look. +# These still override previous lines, so be careful not to +# accidentally disable any of the above rules. + +techlibs/intel_alm/ @ZirconiumX + +# pyosys +misc/*.py @btut + +backends/firrtl @ucbjrl @azidar + +passes/sat/qbfsat.cc @boqwxp +passes/cmds/exec.cc @boqwxp +passes/cmds/printattrs.cc @boqwxp @@ -556,6 +556,8 @@ from SystemVerilog: - enums are supported (including inside packages) - but are currently not strongly typed +- packed structs and unions are supported. + - SystemVerilog interfaces (SVIs) are supported. Modports for specifying whether ports are inputs or outputs are supported. diff --git a/backends/aiger/xaiger.cc b/backends/aiger/xaiger.cc index 6b910eecd..8bbadbc91 100644 --- a/backends/aiger/xaiger.cc +++ b/backends/aiger/xaiger.cc @@ -85,7 +85,7 @@ struct XAigerWriter dict<SigBit, SigBit> not_map, alias_map; dict<SigBit, pair<SigBit, SigBit>> and_map; vector<SigBit> ci_bits, co_bits; - dict<SigBit, Cell*> ff_bits; + vector<Cell*> ff_list; dict<SigBit, float> arrival_times; vector<pair<int, int>> aig_gates; @@ -156,7 +156,7 @@ struct XAigerWriter // promote keep wires for (auto wire : module->wires()) - if (wire->get_bool_attribute(ID::keep)) + if (wire->get_bool_attribute(ID::keep) || wire->get_bool_attribute(ID::abc9_keep)) sigmap.add(wire); for (auto wire : module->wires()) { @@ -232,8 +232,7 @@ struct XAigerWriter unused_bits.erase(D); undriven_bits.erase(Q); alias_map[Q] = D; - auto r YS_ATTRIBUTE(unused) = ff_bits.insert(std::make_pair(D, cell)); - log_assert(r.second); + ff_list.emplace_back(cell); continue; } @@ -420,8 +419,7 @@ struct XAigerWriter aig_map[bit] = 2*aig_m; } - for (const auto &i : ff_bits) { - const Cell *cell = i.second; + for (auto cell : ff_list) { const SigBit &q = sigmap(cell->getPort(ID::Q)); aig_m++, aig_i++; log_assert(!aig_map.count(q)); @@ -468,8 +466,8 @@ struct XAigerWriter aig_outputs.push_back(aig); } - for (auto &i : ff_bits) { - const SigBit &d = i.first; + for (auto cell : ff_list) { + const SigBit &d = sigmap(cell->getPort(ID::D)); aig_o++; aig_outputs.push_back(aig_map.at(d)); } @@ -541,16 +539,16 @@ struct XAigerWriter std::stringstream h_buffer; auto write_h_buffer = std::bind(write_buffer, std::ref(h_buffer), std::placeholders::_1); write_h_buffer(1); - log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_bits) + GetSize(ci_bits)); - write_h_buffer(input_bits.size() + ff_bits.size() + ci_bits.size()); - log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_bits) + GetSize(co_bits)); - write_h_buffer(output_bits.size() + GetSize(ff_bits) + GetSize(co_bits)); - log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_bits)); - write_h_buffer(input_bits.size() + ff_bits.size()); - log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_bits)); - write_h_buffer(output_bits.size() + ff_bits.size()); + log_debug("ciNum = %d\n", GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits)); + write_h_buffer(GetSize(input_bits) + GetSize(ff_list) + GetSize(ci_bits)); + log_debug("coNum = %d\n", GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits)); + write_h_buffer(GetSize(output_bits) + GetSize(ff_list) + GetSize(co_bits)); + log_debug("piNum = %d\n", GetSize(input_bits) + GetSize(ff_list)); + write_h_buffer(GetSize(input_bits) + GetSize(ff_list)); + log_debug("poNum = %d\n", GetSize(output_bits) + GetSize(ff_list)); + write_h_buffer(GetSize(output_bits) + GetSize(ff_list)); log_debug("boxNum = %d\n", GetSize(box_list)); - write_h_buffer(box_list.size()); + write_h_buffer(GetSize(box_list)); auto write_buffer_float = [](std::stringstream &buffer, float f32) { buffer.write(reinterpret_cast<const char*>(&f32), sizeof(f32)); @@ -564,7 +562,7 @@ struct XAigerWriter //for (auto bit : output_bits) // write_o_buffer(0); - if (!box_list.empty() || !ff_bits.empty()) { + if (!box_list.empty() || !ff_list.empty()) { dict<IdString, std::tuple<int,int,int>> cell_cache; int box_count = 0; @@ -601,17 +599,17 @@ struct XAigerWriter std::stringstream r_buffer; auto write_r_buffer = std::bind(write_buffer, std::ref(r_buffer), std::placeholders::_1); - log_debug("flopNum = %d\n", GetSize(ff_bits)); - write_r_buffer(ff_bits.size()); + log_debug("flopNum = %d\n", GetSize(ff_list)); + write_r_buffer(ff_list.size()); std::stringstream s_buffer; auto write_s_buffer = std::bind(write_buffer, std::ref(s_buffer), std::placeholders::_1); - write_s_buffer(ff_bits.size()); + write_s_buffer(ff_list.size()); dict<SigSpec, int> clk_to_mergeability; - for (const auto &i : ff_bits) { - const SigBit &d = i.first; - const Cell *cell = i.second; + for (const auto cell : ff_list) { + const SigBit &d = sigmap(cell->getPort(ID::D)); + const SigBit &q = sigmap(cell->getPort(ID::Q)); SigSpec clk_and_pol{sigmap(cell->getPort(ID::C)), cell->type[6] == 'P' ? State::S1 : State::S0}; auto r = clk_to_mergeability.insert(std::make_pair(clk_and_pol, clk_to_mergeability.size()+1)); @@ -619,8 +617,7 @@ struct XAigerWriter log_assert(mergeability > 0); write_r_buffer(mergeability); - SigBit Q = sigmap(cell->getPort(ID::Q)); - State init = init_map.at(Q, State::Sx); + State init = init_map.at(q, State::Sx); log_debug("Cell '%s' (type %s) has (* init *) value '%s'.\n", log_id(cell), log_id(cell->type), log_signal(init)); if (init == State::S1) write_s_buffer(1); @@ -700,8 +697,6 @@ struct XAigerWriter for (auto wire : module->wires()) { - SigSpec sig = sigmap(wire); - for (int i = 0; i < GetSize(wire); i++) { RTLIL::SigBit b(wire, i); @@ -714,7 +709,6 @@ struct XAigerWriter if (output_bits.count(b)) { int o = ordered_outputs.at(b); output_lines[o] += stringf("output %d %d %s\n", o - GetSize(co_bits), wire->start_offset+i, log_id(wire)); - continue; } } } diff --git a/backends/btor/btor.cc b/backends/btor/btor.cc index 2816d3246..9ac312480 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -40,6 +40,7 @@ struct BtorWorker bool verbose; bool single_bad; bool cover_mode; + bool print_internal_names; int next_nid = 1; int initstate_nid = -1; @@ -78,7 +79,7 @@ struct BtorWorker vector<string> info_lines; dict<int, int> info_clocks; - void btorf(const char *fmt, ...) + void btorf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) { va_list ap; va_start(ap, fmt); @@ -86,7 +87,7 @@ struct BtorWorker va_end(ap); } - void infof(const char *fmt, ...) + void infof(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 2, 3)) { va_list ap; va_start(ap, fmt); @@ -98,6 +99,7 @@ struct BtorWorker string getinfo(T *obj, bool srcsym = false) { string infostr = log_id(obj); + if (!srcsym && !print_internal_names && infostr[0] == '$') return ""; if (obj->attributes.count(ID::src)) { string src = obj->attributes.at(ID::src).decode_string().c_str(); if (srcsym && infostr[0] == '$') { @@ -117,7 +119,7 @@ struct BtorWorker infostr += " ; " + src; } } - return infostr; + return " " + infostr; } void btorf_push(const string &id) @@ -242,7 +244,7 @@ struct BtorWorker btorf("%d slt %d %d %d\n", nid_b_ltz, sid_bit, nid_b, nid_zero); nid = next_nid++; - btorf("%d ite %d %d %d %d %s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str()); + btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_b_ltz, nid_l, nid_r, getinfo(cell).c_str()); } else { @@ -250,7 +252,7 @@ struct BtorWorker int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); nid = next_nid++; - btorf("%d %s %d %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); + btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -291,7 +293,7 @@ struct BtorWorker int sid = get_bv_sid(width); int nid = next_nid++; - btorf("%d %c%s %d %d %d %s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); + btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -317,12 +319,12 @@ struct BtorWorker if (cell->type == ID($_ANDNOT_)) { btorf("%d not %d %d\n", nid1, sid, nid_b); - btorf("%d and %d %d %d %s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); + btorf("%d and %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); } if (cell->type == ID($_ORNOT_)) { btorf("%d not %d %d\n", nid1, sid, nid_b); - btorf("%d or %d %d %d %s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); + btorf("%d or %d %d %d%s\n", nid2, sid, nid_a, nid1, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -344,13 +346,13 @@ struct BtorWorker if (cell->type == ID($_OAI3_)) { btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d and %d %d %d\n", nid2, sid, nid1, nid_c); - btorf("%d not %d %d %s\n", nid3, sid, nid2, getinfo(cell).c_str()); + btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str()); } if (cell->type == ID($_AOI3_)) { btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d or %d %d %d\n", nid2, sid, nid1, nid_c); - btorf("%d not %d %d %s\n", nid3, sid, nid2, getinfo(cell).c_str()); + btorf("%d not %d %d%s\n", nid3, sid, nid2, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -375,14 +377,14 @@ struct BtorWorker btorf("%d or %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d or %d %d %d\n", nid2, sid, nid_c, nid_d); btorf("%d and %d %d %d\n", nid3, sid, nid1, nid2); - btorf("%d not %d %d %s\n", nid4, sid, nid3, getinfo(cell).c_str()); + btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str()); } if (cell->type == ID($_AOI4_)) { btorf("%d and %d %d %d\n", nid1, sid, nid_a, nid_b); btorf("%d and %d %d %d\n", nid2, sid, nid_c, nid_d); btorf("%d or %d %d %d\n", nid3, sid, nid1, nid2); - btorf("%d not %d %d %s\n", nid4, sid, nid3, getinfo(cell).c_str()); + btorf("%d not %d %d%s\n", nid4, sid, nid3, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -414,9 +416,9 @@ struct BtorWorker int nid = next_nid++; if (cell->type.in(ID($lt), ID($le), ID($ge), ID($gt))) { - btorf("%d %c%s %d %d %d %s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); + btorf("%d %c%s %d %d %d%s\n", nid, a_signed || b_signed ? 's' : 'u', btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } else { - btorf("%d %s %d %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); + btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -447,7 +449,7 @@ struct BtorWorker int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); int nid = next_nid++; - btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -488,9 +490,9 @@ struct BtorWorker int nid = next_nid++; if (btor_op != "not") - btorf("%d %s %d %d %d\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); + btorf("%d %s %d %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, nid_b, getinfo(cell).c_str()); else - btorf("%d %s %d %d\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -521,11 +523,11 @@ struct BtorWorker if (cell->type == ID($reduce_xnor)) { int nid2 = next_nid++; - btorf("%d %s %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); - btorf("%d not %d %d %d\n", nid2, sid, nid); + btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + btorf("%d not %d %d\n", nid2, sid, nid); nid = nid2; } else { - btorf("%d %s %d %d %s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); + btorf("%d %s %d %d%s\n", nid, btor_op.c_str(), sid, nid_a, getinfo(cell).c_str()); } SigSpec sig = sigmap(cell->getPort(ID::Y)); @@ -560,9 +562,9 @@ struct BtorWorker int tmp = nid; nid = next_nid++; btorf("%d ite %d %d %d %d\n", tmp, sid, nid_s, nid_b, nid_a); - btorf("%d not %d %d %s\n", nid, sid, tmp, getinfo(cell).c_str()); + btorf("%d not %d %d%s\n", nid, sid, tmp, getinfo(cell).c_str()); } else { - btorf("%d ite %d %d %d %d %s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str()); + btorf("%d ite %d %d %d %d%s\n", nid, sid, nid_s, nid_b, nid_a, getinfo(cell).c_str()); } add_nid_sig(nid, sig_y); @@ -585,7 +587,7 @@ struct BtorWorker int nid_s = get_sig_nid(sig_s.extract(i)); int nid2 = next_nid++; if (i == GetSize(sig_s)-1) - btorf("%d ite %d %d %d %d %s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str()); + btorf("%d ite %d %d %d %d%s\n", nid2, sid, nid_s, nid_b, nid, getinfo(cell).c_str()); else btorf("%d ite %d %d %d %d\n", nid2, sid, nid_s, nid_b, nid); nid = nid2; @@ -640,7 +642,7 @@ struct BtorWorker int sid = get_bv_sid(GetSize(sig_q)); int nid = next_nid++; - if (symbol.empty()) + if (symbol.empty() || (!print_internal_names && symbol[0] == '$')) btorf("%d state %d\n", nid, sid); else btorf("%d state %d %s\n", nid, sid, log_id(symbol)); @@ -1049,8 +1051,8 @@ struct BtorWorker return nid; } - BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, string info_filename) : - f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), info_filename(info_filename) + BtorWorker(std::ostream &f, RTLIL::Module *module, bool verbose, bool single_bad, bool cover_mode, bool print_internal_names, string info_filename) : + f(f), sigmap(module), module(module), verbose(verbose), single_bad(single_bad), cover_mode(cover_mode), print_internal_names(print_internal_names), info_filename(info_filename) { if (!info_filename.empty()) infof("name %s\n", log_id(module)); @@ -1073,7 +1075,7 @@ struct BtorWorker int sid = get_bv_sid(GetSize(sig)); int nid = next_nid++; - btorf("%d input %d %s\n", nid, sid, getinfo(wire).c_str()); + btorf("%d input %d%s\n", nid, sid, getinfo(wire).c_str()); add_nid_sig(nid, sig); } @@ -1097,7 +1099,7 @@ struct BtorWorker btorf_push(stringf("output %s", log_id(wire))); int nid = get_sig_nid(wire); - btorf("%d output %d %s\n", next_nid++, nid, getinfo(wire).c_str()); + btorf("%d output %d%s\n", next_nid++, nid, getinfo(wire).c_str()); btorf_pop(stringf("output %s", log_id(wire))); } @@ -1139,10 +1141,10 @@ struct BtorWorker bad_properties.push_back(nid_en_and_not_a); } else { if (cover_mode) { - infof("bad %d %s\n", nid_en_and_not_a, getinfo(cell, true).c_str()); + infof("bad %d%s\n", nid_en_and_not_a, getinfo(cell, true).c_str()); } else { int nid = next_nid++; - btorf("%d bad %d %s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str()); + btorf("%d bad %d%s\n", nid, nid_en_and_not_a, getinfo(cell, true).c_str()); } } @@ -1164,7 +1166,7 @@ struct BtorWorker bad_properties.push_back(nid_en_and_a); } else { int nid = next_nid++; - btorf("%d bad %d %s\n", nid, nid_en_and_a, getinfo(cell, true).c_str()); + btorf("%d bad %d%s\n", nid, nid_en_and_a, getinfo(cell, true).c_str()); } btorf_pop(log_id(cell)); @@ -1185,7 +1187,7 @@ struct BtorWorker continue; int this_nid = next_nid++; - btorf("%d uext %d %d %d %s\n", this_nid, sid, nid, 0, getinfo(wire).c_str()); + btorf("%d uext %d %d %d%s\n", this_nid, sid, nid, 0, getinfo(wire).c_str()); btorf_pop(stringf("wire %s", log_id(wire))); continue; @@ -1256,14 +1258,14 @@ struct BtorWorker } int nid2 = next_nid++; - btorf("%d next %d %d %d %s\n", nid2, sid, nid, nid_head, getinfo(cell).c_str()); + btorf("%d next %d %d %d%s\n", nid2, sid, nid, nid_head, getinfo(cell).c_str()); } else { SigSpec sig = sigmap(cell->getPort(ID::D)); int nid_q = get_sig_nid(sig); int sid = get_bv_sid(GetSize(sig)); - btorf("%d next %d %d %d %s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str()); + btorf("%d next %d %d %d%s\n", next_nid++, sid, nid, nid_q, getinfo(cell).c_str()); } btorf_pop(stringf("next %s", log_id(cell))); @@ -1353,10 +1355,13 @@ struct BtorBackend : public Backend { log(" -i <filename>\n"); log(" Create additional info file with auxiliary information\n"); log("\n"); + log(" -x\n"); + log(" Output symbols for internal netnames (starting with '$')\n"); + log("\n"); } void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { - bool verbose = false, single_bad = false, cover_mode = false; + bool verbose = false, single_bad = false, cover_mode = false, print_internal_names = false; string info_filename; log_header(design, "Executing BTOR backend.\n"); @@ -1380,6 +1385,10 @@ struct BtorBackend : public Backend { info_filename = args[++argidx]; continue; } + if (args[argidx] == "-x") { + print_internal_names = true; + continue; + } break; } extra_args(f, filename, args, argidx); @@ -1392,7 +1401,7 @@ struct BtorBackend : public Backend { *f << stringf("; BTOR description generated by %s for module %s.\n", yosys_version_str, log_id(topmod)); - BtorWorker(*f, topmod, verbose, single_bad, cover_mode, info_filename); + BtorWorker(*f, topmod, verbose, single_bad, cover_mode, print_internal_names, info_filename); *f << stringf("; end of yosys output\n"); } diff --git a/backends/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/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index d25587e48..fef788267 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -775,7 +775,6 @@ void AigerReader::post_process() } } - dict<int, Wire*> mergeability_to_clock; for (uint32_t i = 0; i < flopNum; i++) { RTLIL::Wire *d = outputs[outputs.size() - flopNum + i]; log_assert(d); @@ -895,7 +894,9 @@ void AigerReader::post_process() } else if (type == "box") { RTLIL::Cell* cell = module->cell(stringf("$box%d", variable)); - if (cell) // ABC could have optimised this box away + if (!cell) + log_debug("Box %d (%s) no longer exists.\n", variable, log_id(escaped_s)); + else module->rename(cell, escaped_s); } else @@ -907,6 +908,8 @@ void AigerReader::post_process() auto name = wp.first; int min = wp.second.first; int max = wp.second.second; + if (min == 0 && max == 0) + continue; RTLIL::Wire *wire = module->wire(name); if (wire) diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 689fa9fb4..03fd272da 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -171,6 +171,9 @@ std::string AST::type2str(AstNodeType type) X(AST_PACKAGE) X(AST_WIRETYPE) X(AST_TYPEDEF) + X(AST_STRUCT) + X(AST_UNION) + X(AST_STRUCT_ITEM) #undef X default: log_abort(); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 8932108e3..6d556fae2 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -143,7 +143,7 @@ namespace AST AST_GENCASE, AST_GENBLOCK, AST_TECALL, - + AST_POSEDGE, AST_NEGEDGE, AST_EDGE, @@ -156,7 +156,10 @@ namespace AST AST_PACKAGE, AST_WIRETYPE, - AST_TYPEDEF + AST_TYPEDEF, + AST_STRUCT, + AST_UNION, + AST_STRUCT_ITEM }; struct AstSrcLocType { @@ -306,6 +309,7 @@ namespace AST // helpers for enum void allocateDefaultEnumValues(); + void annotateTypedEnums(AstNode *template_node); }; // process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index cdc3adc9c..9546558aa 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -991,6 +991,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) case AST_MODPORT: case AST_MODPORTMEMBER: case AST_TYPEDEF: + case AST_STRUCT: + case AST_UNION: break; case AST_INTERFACEPORT: { // If a port in a module with unknown type is found, mark it with the attribute 'is_interface' @@ -1065,6 +1067,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) wire->port_input = is_input; wire->port_output = is_output; wire->upto = range_swapped; + wire->is_signed = is_signed; for (auto &attr : attributes) { if (attr.second->type != AST_CONSTANT) diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 3d690c1f5..e88331621 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -168,6 +168,197 @@ 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; +} + +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() == 1 && node->children[0]->type == AST_RANGE) { + auto rnode = node->children[0]; + width = (rnode->range_swapped ? rnode->range_right - rnode->range_left : + rnode->range_left - rnode->range_right) + 1; + // 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); +} + +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 +758,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 +1101,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 +1114,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 +1177,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; } @@ -1217,7 +1393,29 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } // annotate identifiers using scope resolution and create auto-wires as needed + if (type == AST_IDENTIFIER && !basic_prep) { + // check if a plausible struct member sss.mmmm + std::string sname; + if (name_has_dot(str, sname) && children.size() == 0) { + //dumpScope(); + if (current_scope.count(str) > 0) { + auto item_node = current_scope[str]; + if (item_node->type == AST_STRUCT_ITEM) { + //log("found struct item %s\n", item_node->str.c_str()); + // structure member, rewrite this node to reference the packed struct wire + auto range = make_range(item_node->range_left, item_node->range_right); + newNode = new AstNode(AST_IDENTIFIER, range); + newNode->str = sname; + //newNode->dumpAst(NULL, "* "); + newNode->basic_prep = true; + goto apply_newNode; + } + } + } + } if (type == AST_IDENTIFIER) { + //log("annotate ID %s, stage=%d cf=%d, ip=%d\n", str.c_str(), stage, const_fold, in_param); + //dumpScope(); if (current_scope.count(str) == 0) { AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; for (auto node : current_scope_ast->children) { diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 118f13de9..879ef4af9 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -192,6 +192,9 @@ wire_options: wire_options TOK_UPTO { current_wire->upto = true; } | + wire_options TOK_SIGNED { + current_wire->is_signed = true; + } | wire_options TOK_OFFSET TOK_INT { current_wire->start_offset = $3; } | diff --git a/frontends/json/jsonparse.cc b/frontends/json/jsonparse.cc index 7aceffbfc..8ae7c6578 100644 --- a/frontends/json/jsonparse.cc +++ b/frontends/json/jsonparse.cc @@ -309,6 +309,12 @@ void json_import(Design *design, string &modname, JsonNode *node) port_wire->upto = val->data_number != 0; } + if (port_node->data_dict.count("signed") != 0) { + JsonNode *val = port_node->data_dict.at("signed"); + if (val->type == 'N') + port_wire->is_signed = val->data_number != 0; + } + if (port_node->data_dict.count("offset") != 0) { JsonNode *val = port_node->data_dict.at("offset"); if (val->type == 'N') @@ -573,4 +579,3 @@ struct JsonFrontend : public Frontend { } JsonFrontend; YOSYS_NAMESPACE_END - diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 02fa0031b..e6fa6361e 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -263,7 +263,10 @@ static bool isUserType(std::string &s) "final" { SV_KEYWORD(TOK_FINAL); } "logic" { SV_KEYWORD(TOK_LOGIC); } "var" { SV_KEYWORD(TOK_VAR); } -"bit" { SV_KEYWORD(TOK_REG); } +"bit" { SV_KEYWORD(TOK_LOGIC); } +"int" { SV_KEYWORD(TOK_INT); } +"byte" { SV_KEYWORD(TOK_BYTE); } +"shortint" { SV_KEYWORD(TOK_SHORTINT); } "eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } "s_eventually" { if (formal_mode) return TOK_EVENTUALLY; SV_KEYWORD(TOK_EVENTUALLY); } @@ -277,11 +280,15 @@ static bool isUserType(std::string &s) "reg" { return TOK_REG; } "integer" { return TOK_INTEGER; } "signed" { return TOK_SIGNED; } +"unsigned" { SV_KEYWORD(TOK_UNSIGNED); } "genvar" { return TOK_GENVAR; } "real" { return TOK_REAL; } "enum" { SV_KEYWORD(TOK_ENUM); } "typedef" { SV_KEYWORD(TOK_TYPEDEF); } +"struct" { SV_KEYWORD(TOK_STRUCT); } +"union" { SV_KEYWORD(TOK_UNION); } +"packed" { SV_KEYWORD(TOK_PACKED); } [0-9][0-9_]* { yylval->string = new std::string(yytext); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index c8223f41d..c4867356c 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 + ; + +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 | 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; + auto member_node = astbuf1->clone(); + SET_AST_NODE_LOC(member_node, @1, @1); + astbuf2->children.push_back(member_node); + } + ; + +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 { if ($3) astbuf1->children.push_back($3); } + ; + +struct_var_list: struct_var + | struct_var_list ',' struct_var + ; + +struct_var: TOK_ID { auto *var_node = astbuf2->clone(); + var_node->str = *$1; + delete $1; + SET_AST_NODE_LOC(var_node, @1, @1); + ast_stack.back()->children.push_back(var_node); + } + ; + +///////// +// wire +///////// + wire_decl: attr wire_type range { albuf = $1; astbuf1 = $2; - astbuf2 = $3; - if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { - if (astbuf2) { - frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); - } else { - astbuf2 = new AstNode(AST_RANGE); - astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); - astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true)); - } - } - if (astbuf2 && astbuf2->children.size() != 2) - frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); + astbuf2 = checkRange(astbuf1, $3); } delay wire_name_list { delete astbuf1; if (astbuf2 != NULL) @@ -1603,19 +1740,9 @@ wire_name: if (node->is_input || node->is_output) frontend_verilog_yyerror("input/output/inout ports cannot have unpacked dimensions."); if (!astbuf2 && !node->is_custom_type) { - AstNode *rng = new AstNode(AST_RANGE); - rng->children.push_back(AstNode::mkconst_int(0, true)); - rng->children.push_back(AstNode::mkconst_int(0, true)); - node->children.push_back(rng); - } - node->type = AST_MEMORY; - auto *rangeNode = $2; - if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { - // SV array size [n], rewrite as [n-1:0] - rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); - rangeNode->children.push_back(AstNode::mkconst_int(0, false)); + addRange(node, 0, 0, false); } - node->children.push_back(rangeNode); + rewriteAsMemoryNode(node, $2); } if (current_function_or_task == NULL) { if (do_not_require_port_stubs && (node->is_input || node->is_output) && port_stubs.count(*$1) == 0) { @@ -1663,42 +1790,23 @@ type_name: TOK_ID // first time seen typedef_decl: TOK_TYPEDEF wire_type range type_name range_or_multirange ';' { astbuf1 = $2; - astbuf2 = $3; - if (astbuf1->range_left >= 0 && astbuf1->range_right >= 0) { - if (astbuf2) { - frontend_verilog_yyerror("integer/genvar types cannot have packed dimensions."); - } else { - astbuf2 = new AstNode(AST_RANGE); - astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_left, true)); - astbuf2->children.push_back(AstNode::mkconst_int(astbuf1->range_right, true)); - } - } - if (astbuf2 && astbuf2->children.size() != 2) - frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); + astbuf2 = checkRange(astbuf1, $3); if (astbuf2) astbuf1->children.push_back(astbuf2); if ($5 != NULL) { if (!astbuf2) { - AstNode *rng = new AstNode(AST_RANGE); - rng->children.push_back(AstNode::mkconst_int(0, true)); - rng->children.push_back(AstNode::mkconst_int(0, true)); - astbuf1->children.push_back(rng); + addRange(astbuf1, 0, 0, false); } - astbuf1->type = AST_MEMORY; - auto *rangeNode = $5; - if (rangeNode->type == AST_RANGE && rangeNode->children.size() == 1) { - // SV array size [n], rewrite as [n-1:0] - rangeNode->children[0] = new AstNode(AST_SUB, rangeNode->children[0], AstNode::mkconst_int(1, true)); - rangeNode->children.push_back(AstNode::mkconst_int(0, false)); - } - astbuf1->children.push_back(rangeNode); + rewriteAsMemoryNode(astbuf1, $5); } - addTypedefNode($4, astbuf1); - } | - TOK_TYPEDEF enum_type type_name ';' { - addTypedefNode($3, astbuf1); - } + addTypedefNode($4, astbuf1); } + | TOK_TYPEDEF non_wire_data_type type_name ';' { addTypedefNode($3, $2); } + ; + +non_wire_data_type: + enum_type + | struct_type ; cell_stmt: diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index ca4201b53..397edc4e7 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1862,6 +1862,7 @@ RTLIL::Wire *RTLIL::Module::addWire(RTLIL::IdString name, const RTLIL::Wire *oth wire->port_input = other->port_input; wire->port_output = other->port_output; wire->upto = other->upto; + wire->is_signed = other->is_signed; wire->attributes = other->attributes; return wire; } @@ -2447,6 +2448,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 8228523d5..51e573e76 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -1236,13 +1236,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 = ""); @@ -1371,7 +1368,7 @@ public: RTLIL::Module *module; RTLIL::IdString name; int width, start_offset, port_id; - bool port_input, port_output, upto; + bool port_input, port_output, upto, is_signed; #ifdef WITH_PYTHON static std::map<unsigned int, RTLIL::Wire*> *get_all_wires(void); diff --git a/manual/PRESENTATION_Prog/Makefile b/manual/PRESENTATION_Prog/Makefile index 794f5c12c..7e3cf814b 100644 --- a/manual/PRESENTATION_Prog/Makefile +++ b/manual/PRESENTATION_Prog/Makefile @@ -1,8 +1,11 @@ all: test0.log test1.log test2.log +CXXFLAGS=$(shell ../../yosys-config --cxxflags) +DATDIR=$(shell ../../yosys-config --datdir) + my_cmd.so: my_cmd.cc - ../../yosys-config --exec --cxx --cxxflags --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs + ../../yosys-config --exec --cxx $(subst $(DATDIR),../../share,$(CXXFLAGS)) --ldflags -o my_cmd.so -shared my_cmd.cc --ldlibs test0.log: my_cmd.so ../../yosys -Ql test0.log_new -m ./my_cmd.so -p 'my_cmd foo bar' absval_ref.v diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index fac5b48a4..fa23e3b2c 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -312,16 +312,16 @@ class PythonListTranslator(Translator): text += prefix + "\t" + known_containers[types[0].name].typename + " " + tmp_name + " = boost::python::extract<" + known_containers[types[0].name].typename + ">(" + varname + "[" + cntr_name + "]);" text += known_containers[types[0].name].translate(tmp_name, types[0].cont.args, prefix+"\t") tmp_name = tmp_name + "___tmp" - text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");" + text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");" elif types[0].name in classnames: text += prefix + "\t" + types[0].name + "* " + tmp_name + " = boost::python::extract<" + types[0].name + "*>(" + varname + "[" + cntr_name + "]);" if types[0].attr_type == attr_types.star: - text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + "->get_cpp_obj());" + text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + "->get_cpp_obj());" else: - text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());" + text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(*" + tmp_name + "->get_cpp_obj());" else: text += prefix + "\t" + types[0].name + " " + tmp_name + " = boost::python::extract<" + types[0].name + ">(" + varname + "[" + cntr_name + "]);" - text += prefix + "\t" + varname + "___tmp." + c.insert_name + "(" + tmp_name + ");" + text += prefix + "\t" + varname + "___tmp" + c.insert_name + "(" + tmp_name + ");" text += prefix + "}" return text @@ -349,19 +349,24 @@ class PythonListTranslator(Translator): text += prefix + "}" return text +class IDictTranslator(PythonListTranslator): + typename = "boost::python::list" + orig_name = "idict" + insert_name = "" + #Sub-type for std::set class SetTranslator(PythonListTranslator): - insert_name = "insert" + insert_name = ".insert" orig_name = "std::set" #Sub-type for std::vector class VectorTranslator(PythonListTranslator): - insert_name = "push_back" + insert_name = ".push_back" orig_name = "std::vector" #Sub-type for pool class PoolTranslator(PythonListTranslator): - insert_name = "insert" + insert_name = ".insert" orig_name = "pool" #Translates dict-types (dict, std::map), that only differ in their name and @@ -528,6 +533,7 @@ known_containers = { "std::set" : SetTranslator, "std::vector" : VectorTranslator, "pool" : PoolTranslator, + "idict" : IDictTranslator, "dict" : DictTranslator, "std::pair" : TupleTranslator, "std::map" : MapTranslator diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index 8d55b18a0..873c37b9a 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -102,8 +102,6 @@ void check(RTLIL::Design *design, bool dff_mode) auto inst_module = design->module(cell->type); if (!inst_module) continue; - if (!inst_module->get_blackbox_attribute()) - continue; IdString derived_type; Module *derived_module; if (cell->parameters.empty()) { @@ -111,6 +109,10 @@ void check(RTLIL::Design *design, bool dff_mode) derived_module = inst_module; } else { + // Check potential (since its value may depend on a parameter, + // but not its existence) + if (!inst_module->has_attribute(ID::abc9_flop)) + continue; derived_type = inst_module->derive(design, cell->parameters); derived_module = design->module(derived_type); log_assert(derived_module); @@ -127,20 +129,20 @@ void check(RTLIL::Design *design, bool dff_mode) for (auto derived_cell : derived_module->cells()) { if (derived_cell->type.in(ID($dff), ID($_DFF_N_), ID($_DFF_P_))) { if (found) - log_error("Module '%s' with (* abc9_flop *) contains more than one $_DFF_[NP]_ cell.\n", log_id(derived_module)); + log_error("Whitebox '%s' with (* abc9_flop *) contains more than one $_DFF_[NP]_ cell.\n", log_id(derived_module)); found = true; SigBit Q = derived_cell->getPort(ID::Q); log_assert(GetSize(Q.wire) == 1); if (!Q.wire->port_output) - log_error("Module '%s' contains a %s cell where its 'Q' port does not drive a module output!\n", log_id(derived_module), log_id(derived_cell->type)); + log_error("Whitebox '%s' with (* abc9_flop *) contains a %s cell where its 'Q' port does not drive a module output.\n", log_id(derived_module), log_id(derived_cell->type)); Const init = Q.wire->attributes.at(ID::init, State::Sx); log_assert(GetSize(init) == 1); } else if (unsupported.count(derived_cell->type)) - log_error("Module '%s' with (* abc9_flop *) contains a %s cell, which is not supported for sequential synthesis.\n", log_id(derived_module), log_id(derived_cell->type)); + log_error("Whitebox '%s' with (* abc9_flop *) contains a %s cell, which is not supported for sequential synthesis.\n", log_id(derived_module), log_id(derived_cell->type)); } } } @@ -173,8 +175,6 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) auto inst_module = design->module(cell->type); if (!inst_module) continue; - if (!inst_module->get_blackbox_attribute()) - continue; IdString derived_type; Module *derived_module; if (cell->parameters.empty()) { @@ -182,6 +182,10 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) derived_module = inst_module; } else { + // Check potential for any one of those three + // (since its value may depend on a parameter, but not its existence) + if (!inst_module->has_attribute(ID::abc9_flop) && !inst_module->has_attribute(ID::abc9_box) && !inst_module->get_bool_attribute(ID::abc9_bypass)) + continue; derived_type = inst_module->derive(design, cell->parameters); derived_module = design->module(derived_type); } @@ -211,7 +215,7 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) // Block sequential synthesis on cells with (* init *) != 1'b0 // because ABC9 doesn't support them if (init != State::S0) { - log_warning("Module '%s' contains a %s cell with non-zero initial state -- this is not unsupported for ABC9 sequential synthesis. Treating as a blackbox.\n", log_id(derived_module), log_id(derived_cell->type)); + log_warning("Whitebox '%s' with (* abc9_flop *) contains a %s cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox.\n", log_id(derived_module), log_id(derived_cell->type)); derived_module->set_bool_attribute(ID::abc9_flop, false); } break; @@ -232,10 +236,8 @@ void prep_hier(RTLIL::Design *design, bool dff_mode) auto w = unmap_module->addWire(port, derived_module->wire(port)); // Do not propagate (* init *) values into the box, // in fact, remove it from outside too - if (w->port_output && w->attributes.erase(ID::init)) { - auto r = unmap_module->addWire(stringf("\\_TECHMAP_REMOVEINIT_%s_", log_id(port))); - unmap_module->connect(r, State::S1); - } + if (w->port_output) + w->attributes.erase(ID::init); } unmap_module->ports = derived_module->ports; unmap_module->check(); @@ -719,8 +721,10 @@ void prep_xaiger(RTLIL::Module *module, bool dff) bit_users[bit].insert(cell->name); if (cell->output(conn.first) && !abc9_flop) - for (auto bit : sigmap(conn.second)) - bit_drivers[bit].insert(cell->name); + for (const auto &chunk : conn.second.chunks()) + if (!chunk.wire->get_bool_attribute(ID::abc9_keep)) + for (auto b : sigmap(SigSpec(chunk))) + bit_drivers[b].insert(cell->name); } toposort.node(cell->name); } @@ -1110,7 +1114,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) for (auto w : mapped_mod->wires()) { auto nw = module->addWire(remap_name(w->name), GetSize(w)); nw->start_offset = w->start_offset; - // Remove all (* init *) since they only existon $_DFF_[NP]_ + // Remove all (* init *) since they only exist on $_DFF_[NP]_ w->attributes.erase(ID::init); } @@ -1147,16 +1151,36 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) } } + SigMap initmap; + if (dff_mode) { + // Build a sigmap prioritising bits with (* init *) + initmap.set(module); + for (auto w : module->wires()) { + auto it = w->attributes.find(ID::init); + if (it == w->attributes.end()) + continue; + for (auto i = 0; i < GetSize(w); i++) + if (it->second[i] == State::S0 || it->second[i] == State::S1) + initmap.add(w); + } + } + std::vector<Cell*> boxes; for (auto cell : module->cells().to_vector()) { if (cell->has_keep_attr()) continue; - // Short out $_DFF_[NP]_ cells since the flop box already has - // all the information we need to reconstruct cell + // Short out (so that existing name can be preserved) and remove + // $_DFF_[NP]_ cells since flop box already has all the information + // we need to reconstruct them if (dff_mode && cell->type.in(ID($_DFF_N_), ID($_DFF_P_)) && !cell->get_bool_attribute(ID::abc9_keep)) { - module->connect(cell->getPort(ID::Q), cell->getPort(ID::D)); + SigBit Q = cell->getPort(ID::Q); + module->connect(Q, cell->getPort(ID::D)); module->remove(cell); + auto Qi = initmap(Q); + auto it = Qi.wire->attributes.find(ID::init); + if (it != Qi.wire->attributes.end()) + it->second[Qi.offset] = State::Sx; } else if (cell->type.in(ID($_AND_), ID($_NOT_))) module->remove(cell); @@ -1299,7 +1323,25 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) mapped_cell->connections_.erase(jt); auto abc9_flop = box_module->get_bool_attribute(ID::abc9_flop); - if (!abc9_flop) { + if (abc9_flop) { + // Link this sole flop box output to the output of the existing + // flop box, so that any (public) signal it drives will be + // preserved + SigBit old_q; + for (const auto &port_name : box_ports.at(existing_cell->type)) { + RTLIL::Wire *w = box_module->wire(port_name); + log_assert(w); + if (!w->port_output) + continue; + log_assert(old_q == SigBit()); + log_assert(GetSize(w) == 1); + old_q = existing_cell->getPort(port_name); + } + auto new_q = outputs[0]; + new_q.wire = module->wires_.at(remap_name(new_q.wire->name)); + module->connect(old_q, new_q); + } + else { for (const auto &i : inputs) bit_users[i].insert(mapped_cell->name); for (const auto &i : outputs) @@ -1332,11 +1374,12 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) c.wire = module->wires_.at(remap_name(c.wire->name)); newsig.append(c); } - cell->setPort(port_name, newsig); if (w->port_input && !abc9_flop) for (const auto &i : newsig) bit2sinks[i].push_back(cell); + + cell->setPort(port_name, std::move(newsig)); } } @@ -1398,7 +1441,7 @@ void reintegrate(RTLIL::Module *module, bool dff_mode) // treated as being "free"), in particular driving primary // outputs (real primary outputs, or cells treated as blackboxes) // or driving box inputs. - // Instead of just mapping those $_NOT_ gates into 2-input $lut-s + // Instead of just mapping those $_NOT_ gates into 1-input $lut-s // at an area and delay cost, see if it is possible to push // this $_NOT_ into the driving LUT, or into all sink LUTs. // When this is not possible, (i.e. this signal drives two primary diff --git a/techlibs/common/abc9_unmap.v b/techlibs/common/abc9_unmap.v index bcbe91477..c39648c62 100644 --- a/techlibs/common/abc9_unmap.v +++ b/techlibs/common/abc9_unmap.v @@ -1,5 +1,5 @@ (* techmap_celltype = "$__DFF_N__$abc9_flop $__DFF_P__$abc9_flop" *) -module $__DFF_x__$abc9_flop (input C, D, Q, output n1); +module $__DFF_x__$abc9_flop (input C, D, (* init = 1'b0 *) input Q, output n1); parameter _TECHMAP_CELLTYPE_ = ""; generate if (_TECHMAP_CELLTYPE_ == "$__DFF_N__$abc9_flop") $_DFF_N_ _TECHMAP_REPLACE_ (.C(C), .D(D), .Q(Q)); diff --git a/tests/arch/ecp5/latches_abc9.ys b/tests/arch/ecp5/latches_abc9.ys new file mode 100644 index 000000000..4daf04050 --- /dev/null +++ b/tests/arch/ecp5/latches_abc9.ys @@ -0,0 +1,13 @@ +read_verilog <<EOT +module top(input e, d, output q); +reg l; +always @* + if (e) + l = ~d; +assign q = ~l; +endmodule +EOT +# Can't run any sort of equivalence check because latches are blown to LUTs +synth_ecp5 -abc9 +select -assert-count 2 t:LUT4 +select -assert-none t:LUT4 %% t:* %D diff --git a/tests/arch/intel_alm/.gitignore b/tests/arch/intel_alm/.gitignore new file mode 100644 index 000000000..ba42e1ee6 --- /dev/null +++ b/tests/arch/intel_alm/.gitignore @@ -0,0 +1,2 @@ +/*.log +/run-test.mk diff --git a/tests/arch/xilinx/abc9_dff.ys b/tests/arch/xilinx/abc9_dff.ys index fd343969b..210e87477 100644 --- a/tests/arch/xilinx/abc9_dff.ys +++ b/tests/arch/xilinx/abc9_dff.ys @@ -50,10 +50,10 @@ FDCE_1 /*#(.INIT(1))*/ fd7(.C(C), .CE(1'b0), .D(D), .CLR(1'b0), .Q(Q[6])); FDPE_1 #(.INIT(1)) fd8(.C(C), .CE(1'b0), .D(D), .PRE(1'b0), .Q(Q[7])); endmodule EOT -logger -expect warning "Module '\$paramod\\FDRE\\INIT=1' contains a \$dff cell .*" 1 -logger -expect warning "Module '\$paramod\\FDRE_1\\INIT=1' contains a \$dff cell .*" 1 -logger -expect warning "Module 'FDSE' contains a \$dff cell .*" 1 -logger -expect warning "Module '\$paramod\\FDSE_1\\INIT=1' contains a \$dff cell .*" 1 +logger -expect warning "Whitebox '\$paramod\\FDRE\\INIT=1' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1 +logger -expect warning "Whitebox '\$paramod\\FDRE_1\\INIT=1' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1 +logger -expect warning "Whitebox 'FDSE' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1 +logger -expect warning "Whitebox '\$paramod\\FDSE_1\\INIT=1' with \(\* abc9_flop \*\) contains a \$dff cell with non-zero initial state -- this is not supported for ABC9 sequential synthesis. Treating as a blackbox\." 1 equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf design -load postopt select -assert-count 8 t:FD* @@ -82,4 +82,53 @@ select -assert-count 1 t:FDPE select -assert-count 2 t:INV select -assert-count 0 t:FD* t:INV %% t:* %D + +design -reset +read_verilog <<EOT +module top(input clk, input d, output q); +reg r; +always @(posedge clk) begin +r <= d; +end +assign q = ~r; +endmodule +EOT +proc +equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf +design -load postopt +select -assert-count 1 t:FDRE %co w:r %i + + +design -reset +read_verilog <<EOT +module top(input clk, input a, b, output reg q1, output q2); +reg r; +always @(posedge clk) begin + q1 <= a | b; + r <= ~(~a & ~b); +end +assign q2 = r; +endmodule +EOT +proc +equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf +design -load postopt +select -assert-count 1 t:FDRE %co %a w:r %i + + +design -reset +read_verilog <<EOT +module top(input clk, input a, b, output o); +reg r1, r2; +always @(posedge clk) begin + r1 <= a | b; + r2 <= ~(~a & ~b); +end +assign o = r1 | r2; +endmodule +EOT +proc +equiv_opt -assert -multiclock -map +/xilinx/cells_sim.v synth_xilinx -abc9 -dff -noiopad -noclkbuf + + logger -expect-no-warnings diff --git a/tests/arch/xilinx/pmgen_xilinx_srl.ys b/tests/arch/xilinx/pmgen_xilinx_srl.ys index ea2f20487..e76fb20ab 100644 --- a/tests/arch/xilinx/pmgen_xilinx_srl.ys +++ b/tests/arch/xilinx/pmgen_xilinx_srl.ys @@ -1,6 +1,6 @@ read_verilog -icells <<EOT module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO); - parameter DEPTH = 1; + parameter DEPTH = 2; parameter [DEPTH-1:0] INIT = 0; parameter CLKPOL = 1; parameter ENPOL = 2; diff --git a/tests/arch/xilinx/xilinx_srl.v b/tests/arch/xilinx/xilinx_srl.v index bc2a15ab2..29920da41 100644 --- a/tests/arch/xilinx/xilinx_srl.v +++ b/tests/arch/xilinx/xilinx_srl.v @@ -29,7 +29,7 @@ endmodule module $__XILINX_SHREG_(input C, D, E, input [1:0] L, output Q); parameter CLKPOL = 1; parameter ENPOL = 1; -parameter DEPTH = 1; +parameter DEPTH = 2; parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}}; reg [DEPTH-1:0] r = INIT; wire clk = C ^ CLKPOL; diff --git a/tests/svtypes/struct_simple.sv b/tests/svtypes/struct_simple.sv new file mode 100644 index 000000000..c74289cc3 --- /dev/null +++ b/tests/svtypes/struct_simple.sv @@ -0,0 +1,48 @@ +module top; + localparam BITS=8; + + struct packed { + logic a; + logic[BITS-1:0] b; + byte c; + logic x, y; + } s; + + struct packed signed { + integer a; + logic[15:0] b; + logic[7:0] c; + bit [7:0] d; + } pack1; + + struct packed { + byte a; + struct packed { + byte x, y; + } b; + } s2; + + assign s.a = '1; + assign s.b = '1; + assign s.c = 8'hAA; + assign s.x = '1; + logic[7:0] t; + assign t = s.b; + assign pack1.a = 42; + assign pack1.b = 16'hAAAA; + assign pack1.c = '1; + assign pack1.d = 8'h55; + assign s2.b.x = 'h42; + + always_comb assert(s.a == 1'b1); + always_comb assert(s.c == 8'hAA); + always_comb assert(s.x == 1'b1); + always_comb assert(t == 8'hFF); + always_comb assert(pack1.a == 42); + always_comb assert(pack1.b == 16'hAAAA); + always_comb assert(pack1.c == 8'hFF); + always_comb assert(pack1[15:8] == 8'hFF); + always_comb assert(pack1.d == 8'h55); + always_comb assert(s2.b.x == 'h42); + +endmodule diff --git a/tests/svtypes/typedef_struct.sv b/tests/svtypes/typedef_struct.sv new file mode 100644 index 000000000..7ae007952 --- /dev/null +++ b/tests/svtypes/typedef_struct.sv @@ -0,0 +1,42 @@ +package p; + +typedef struct packed { + byte a; + byte b; +} p_t; + +endpackage + + +module top; + + typedef logic[7:0] t_t; + + typedef struct packed { + bit a; + logic[7:0] b; + t_t t; + } s_t; + + s_t s; + s_t s1; + + p::p_t ps; + + assign s.a = '1; + assign s.b = '1; + assign s.t = 8'h55; + assign s1 = s; + assign ps.a = 8'hAA; + assign ps.b = 8'h55; + + always_comb begin + assert(s.a == 1'b1); + assert(s.b == 8'hFF); + assert(s.t == 8'h55); + assert(s1.t == 8'h55); + assert(ps.a == 8'hAA); + assert(ps.b == 8'h55); + end + +endmodule diff --git a/tests/svtypes/union_simple.sv b/tests/svtypes/union_simple.sv new file mode 100644 index 000000000..12e4b376f --- /dev/null +++ b/tests/svtypes/union_simple.sv @@ -0,0 +1,72 @@ +module top; + + typedef struct packed { + byte a,b,c,d; + } byte4_t; + + typedef union packed { + int x; + byte4_t y; + } w_t; + + w_t w; + + assign w.x = 'h42; + always_comb begin + assert(w.y.d == 8'h42); + end + + typedef logic[4:0] reg_addr_t; + typedef logic[6:0] opcode_t; + + typedef struct packed { + bit [6:0] func7; + reg_addr_t rs2; + reg_addr_t rs1; + bit [2:0] func3; + reg_addr_t rd; + opcode_t opcode; + } R_t; + + typedef struct packed { + bit[11:0] imm; + reg_addr_t rs1; + bit[2:0] func3; + reg_addr_t rd; + opcode_t opcode; + } I_t; + + typedef struct packed { + bit[19:0] imm; + reg_addr_t rd; + opcode_t opcode; + } U_t; + + typedef union packed { + R_t r; + I_t i; + U_t u; + } instruction_t; + + instruction_t ir1; + assign ir1 = 32'h0AA01EB7; // lui t4,0xAA01 + always_comb begin + assert(ir1.u.opcode == 'h37); + assert(ir1.r.opcode == 'h37); + assert(ir1.u.rd == 'd29); + assert(ir1.r.rd == 'd29); + assert(ir1.u.imm == 'hAA01); + end + + union packed { + int word; + struct packed { + byte a, b, c, d; + } byte4; + } u; + assign u.word = 'h42; + always_comb begin + assert(u.byte4.d == 'h42); + end + +endmodule diff --git a/tests/various/abc9.ys b/tests/various/abc9.ys index ac714665f..a9880c722 100644 --- a/tests/various/abc9.ys +++ b/tests/various/abc9.ys @@ -97,4 +97,5 @@ select -assert-count 3 t:$_DFF_N_ select -assert-none c:ff1 c:ff2 c:ff4 %% c:* %D clean select -assert-count 2 a:init -select -assert-none w:w w:z %% a:init %D +select -assert-count 1 w:w a:init %i +select -assert-count 1 c:ff4 %co c:ff4 %d %a a:init %i diff --git a/tests/various/attrib07_func_call.v b/tests/various/attrib07_func_call.v index f55ef2316..8c9fb2926 100644 --- a/tests/various/attrib07_func_call.v +++ b/tests/various/attrib07_func_call.v @@ -11,7 +11,7 @@ module foo(clk, rst, inp_a, inp_b, out); input wire rst; input wire [7:0] inp_a; input wire [7:0] inp_b; - output wire [7:0] out; + output reg [7:0] out; always @(posedge clk) if (rst) out <= 0; diff --git a/tests/various/constmsk_testmap.v b/tests/various/constmsk_testmap.v index fab1b1bbc..b6809c7c0 100644 --- a/tests/various/constmsk_testmap.v +++ b/tests/various/constmsk_testmap.v @@ -1,7 +1,7 @@ (* techmap_celltype = "$reduce_or" *) module my_opt_reduce_or(...); parameter A_SIGNED = 0; - parameter A_WIDTH = 1; + parameter A_WIDTH = 2; parameter Y_WIDTH = 1; input [A_WIDTH-1:0] A; diff --git a/tests/various/shregmap.v b/tests/various/shregmap.v index 604c2c976..dc828eda7 100644 --- a/tests/various/shregmap.v +++ b/tests/various/shregmap.v @@ -13,7 +13,7 @@ assign q = {shift2[3], shift1[3]}; endmodule module $__SHREG_DFF_P_(input C, D, output Q); -parameter DEPTH = 1; +parameter DEPTH = 2; parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}}; reg [DEPTH-1:0] r = INIT; always @(posedge C) @@ -38,7 +38,7 @@ endmodule module $__XILINX_SHREG_(input C, D, input [1:0] L, output Q); parameter CLKPOL = 1; parameter ENPOL = 1; -parameter DEPTH = 1; +parameter DEPTH = 2; parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}}; reg [DEPTH-1:0] r = INIT; wire clk = C ^ CLKPOL; diff --git a/tests/verilog/bug2042-sv.ys b/tests/verilog/bug2042-sv.ys index e815d7fc5..91989f412 100644 --- a/tests/verilog/bug2042-sv.ys +++ b/tests/verilog/bug2042-sv.ys @@ -2,7 +2,7 @@ read_verilog -sv <<EOT module Task_Test_Top ( input a, -output b +output reg b ); task SomeTaskName(a); |