diff options
57 files changed, 1083 insertions, 352 deletions
@@ -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 ---------------------- @@ -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/backends/btor/btor.cc b/backends/btor/btor.cc index 14c8484e8..2816d3246 100644 --- a/backends/btor/btor.cc +++ b/backends/btor/btor.cc @@ -266,20 +266,26 @@ struct BtorWorker goto okay; } - if (cell->type.in(ID($div), ID($mod))) + if (cell->type.in(ID($div), ID($mod), ID($modfloor))) { + bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; + bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; + string btor_op; if (cell->type == ID($div)) btor_op = "div"; + // "rem" = truncating modulo if (cell->type == ID($mod)) btor_op = "rem"; + // "mod" = flooring modulo + if (cell->type == ID($modfloor)) { + // "umod" doesn't exist because it's the same as "urem" + btor_op = a_signed || b_signed ? "mod" : "rem"; + } log_assert(!btor_op.empty()); int width = GetSize(cell->getPort(ID::Y)); width = std::max(width, GetSize(cell->getPort(ID::A))); width = std::max(width, GetSize(cell->getPort(ID::B))); - bool a_signed = cell->hasParam(ID::A_SIGNED) ? cell->getParam(ID::A_SIGNED).as_bool() : false; - bool b_signed = cell->hasParam(ID::B_SIGNED) ? cell->getParam(ID::B_SIGNED).as_bool() : false; - int nid_a = get_sig_nid(cell->getPort(ID::A), width, a_signed); int nid_b = get_sig_nid(cell->getPort(ID::B), width, b_signed); diff --git a/backends/btor/test_cells.sh b/backends/btor/test_cells.sh index e0f1a0514..3f077201a 100644 --- a/backends/btor/test_cells.sh +++ b/backends/btor/test_cells.sh @@ -6,7 +6,7 @@ rm -rf test_cells.tmp mkdir -p test_cells.tmp cd test_cells.tmp -../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod' +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$sop /$macc /$mul /$div /$mod /$divfloor /$modfloor' for fn in test_*.il; do ../../../yosys -p " diff --git a/backends/cxxrtl/cxxrtl.cc b/backends/cxxrtl/cxxrtl.cc index 549404184..0cceecbba 100644 --- a/backends/cxxrtl/cxxrtl.cc +++ b/backends/cxxrtl/cxxrtl.cc @@ -513,7 +513,6 @@ struct CxxrtlWorker { bool elide_public = false; bool localize_internal = false; bool localize_public = false; - bool run_opt_clean_purge = false; bool run_proc_flatten = false; bool max_opt_level = false; @@ -2009,6 +2008,7 @@ struct CxxrtlWorker { log("Module `%s' contains feedback arcs through wires:\n", log_id(module)); for (auto wire : feedback_wires) log(" %s\n", log_id(wire)); + log("\n"); } for (auto wire : module->wires()) { @@ -2040,20 +2040,20 @@ struct CxxrtlWorker { log("Module `%s' contains buffered combinatorial wires:\n", log_id(module)); for (auto wire : buffered_wires) log(" %s\n", log_id(wire)); + log("\n"); } eval_converges[module] = feedback_wires.empty() && buffered_wires.empty(); } if (has_feedback_arcs || has_buffered_wires) { // Although both non-feedback buffered combinatorial wires and apparent feedback wires may be eliminated - // by optimizing the design, if after `opt_clean -purge` there are any feedback wires remaining, it is very + // by optimizing the design, if after `proc; flatten` there are any feedback wires remaining, it is very // likely that these feedback wires are indicative of a true logic loop, so they get emphasized in the message. const char *why_pessimistic = nullptr; if (has_feedback_arcs) why_pessimistic = "feedback wires"; else if (has_buffered_wires) why_pessimistic = "buffered combinatorial wires"; - log("\n"); log_warning("Design contains %s, which require delta cycles during evaluation.\n", why_pessimistic); if (!max_opt_level) log("Increasing the optimization level may eliminate %s from the design.\n", why_pessimistic); @@ -2087,34 +2087,39 @@ struct CxxrtlWorker { void prepare_design(RTLIL::Design *design) { + bool did_anything = false; bool has_sync_init, has_packed_mem; log_push(); check_design(design, has_sync_init, has_packed_mem); if (run_proc_flatten) { Pass::call(design, "proc"); Pass::call(design, "flatten"); + did_anything = true; } else if (has_sync_init) { // We're only interested in proc_init, but it depends on proc_prune and proc_clean, so call those // in case they weren't already. (This allows `yosys foo.v -o foo.cc` to work.) Pass::call(design, "proc_prune"); Pass::call(design, "proc_clean"); Pass::call(design, "proc_init"); + did_anything = true; } - if (has_packed_mem) + if (has_packed_mem) { Pass::call(design, "memory_unpack"); + did_anything = true; + } // Recheck the design if it was modified. if (has_sync_init || has_packed_mem) check_design(design, has_sync_init, has_packed_mem); log_assert(!(has_sync_init || has_packed_mem)); - if (run_opt_clean_purge) - Pass::call(design, "opt_clean -purge"); log_pop(); + if (did_anything) + log_spacer(); analyze_design(design); } }; struct CxxrtlBackend : public Backend { - static const int DEFAULT_OPT_LEVEL = 6; + static const int DEFAULT_OPT_LEVEL = 5; CxxrtlBackend() : Backend("cxxrtl", "convert design to C++ RTL simulation") { } void help() YS_OVERRIDE @@ -2340,6 +2345,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; 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/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..9f7c8c6d9 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -121,6 +121,7 @@ class SmtIo: self.logic_bv = True self.logic_dt = False self.forall = False + self.timeout = 0 self.produce_models = True self.smt2cache = [list()] self.p = None @@ -135,6 +136,7 @@ class SmtIo: self.debug_file = opts.debug_file self.dummy_file = opts.dummy_file self.timeinfo = opts.timeinfo + self.timeout = opts.timeout self.unroll = opts.unroll self.noincr = opts.noincr self.info_stmts = opts.info_stmts @@ -147,6 +149,7 @@ class SmtIo: self.debug_file = None self.dummy_file = None self.timeinfo = os.name != "nt" + self.timeout = 0 self.unroll = False self.noincr = False self.info_stmts = list() @@ -172,22 +175,32 @@ class SmtIo: self.unroll = False if self.solver == "yices": - if self.noincr: + if self.noincr or self.forall: self.popen_vargs = ['yices-smt2'] + self.solver_opts else: self.popen_vargs = ['yices-smt2', '--incremental'] + self.solver_opts + if self.timeout != 0: + self.popen_vargs.append('-t') + self.popen_vargs.append('%d' % self.timeout); if self.solver == "z3": self.popen_vargs = ['z3', '-smt2', '-in'] + self.solver_opts + if self.timeout != 0: + self.popen_vargs.append('-T:%d' % self.timeout); if self.solver == "cvc4": if self.noincr: self.popen_vargs = ['cvc4', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts else: self.popen_vargs = ['cvc4', '--incremental', '--lang', 'smt2.6' if self.logic_dt else 'smt2'] + self.solver_opts + if self.timeout != 0: + self.popen_vargs.append('--tlimit=%d000' % self.timeout); if self.solver == "mathsat": self.popen_vargs = ['mathsat'] + self.solver_opts + if self.timeout != 0: + print('timeout option is not supported for mathsat.') + sys.exit(1) if self.solver == "boolector": if self.noincr: @@ -195,6 +208,9 @@ class SmtIo: else: self.popen_vargs = ['boolector', '--smt2', '-i'] + self.solver_opts self.unroll = True + if self.timeout != 0: + print('timeout option is not supported for boolector.') + sys.exit(1) if self.solver == "abc": if len(self.solver_opts) > 0: @@ -204,6 +220,9 @@ class SmtIo: self.logic_ax = False self.unroll = True self.noincr = True + if self.timeout != 0: + print('timeout option is not supported for abc.') + sys.exit(1) if self.solver == "dummy": assert self.dummy_file is not None @@ -232,12 +251,16 @@ class SmtIo: if self.logic_uf: self.logic += "UF" if self.logic_bv: self.logic += "BV" if self.logic_dt: self.logic = "ALL" + if self.solver == "yices" and self.forall: self.logic = "BV" self.setup_done = True for stmt in self.info_stmts: self.write(stmt) + if self.forall and self.solver == "yices": + self.write("(set-option :yices-ef-max-iters 1000000000)") + if self.produce_models: self.write("(set-option :produce-models true)") @@ -706,7 +729,7 @@ class SmtIo: if self.forall: result = self.read() - while result not in ["sat", "unsat", "unknown"]: + while result not in ["sat", "unsat", "unknown", "timeout", "interrupted", ""]: print("%s %s: %s" % (self.timestamp(), self.solver, result)) result = self.read() else: @@ -717,7 +740,7 @@ class SmtIo: print("(check-sat)", file=self.debug_file) self.debug_file.flush() - if result not in ["sat", "unsat"]: + if result not in ["sat", "unsat", "unknown", "timeout", "interrupted"]: if result == "": print("%s Unexpected EOF response from solver." % (self.timestamp()), flush=True) else: @@ -927,7 +950,7 @@ class SmtIo: class SmtOpts: def __init__(self): self.shortopts = "s:S:v" - self.longopts = ["unroll", "noincr", "noprogress", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"] + self.longopts = ["unroll", "noincr", "noprogress", "timeout=", "dump-smt2=", "logic=", "dummy=", "info=", "nocomments"] self.solver = "yices" self.solver_opts = list() self.debug_print = False @@ -936,6 +959,7 @@ class SmtOpts: self.unroll = False self.noincr = False self.timeinfo = os.name != "nt" + self.timeout = 0 self.logic = None self.info_stmts = list() self.nocomments = False @@ -945,6 +969,8 @@ class SmtOpts: self.solver = a elif o == "-S": self.solver_opts.append(a) + elif o == "--timeout": + self.timeout = int(a) elif o == "-v": self.debug_print = True elif o == "--unroll": @@ -976,6 +1002,9 @@ class SmtOpts: -S <opt> pass <opt> as command line argument to the solver + --timeout <value> + set the solver timeout to the specified value (in seconds). + --logic <smt2_logic> use the specified SMT2 logic (e.g. QF_AUFBV) diff --git a/backends/smv/smv.cc b/backends/smv/smv.cc index 7113ebc97..2fc7099f4 100644 --- a/backends/smv/smv.cc +++ b/backends/smv/smv.cc @@ -358,7 +358,8 @@ struct SmvWorker continue; } - if (cell->type.in(ID($div), ID($mod))) + // SMV has a "mod" operator, but its semantics don't seem to be well-defined - to be safe, don't generate it at all + if (cell->type.in(ID($div)/*, ID($mod), ID($modfloor)*/)) { int width_y = GetSize(cell->getPort(ID::Y)); int width = max(width_y, GetSize(cell->getPort(ID::A))); @@ -366,7 +367,7 @@ struct SmvWorker string expr_a, expr_b, op; if (cell->type == ID($div)) op = "/"; - if (cell->type == ID($mod)) op = "mod"; + //if (cell->type == ID($mod)) op = "mod"; if (cell->getParam(ID::A_SIGNED).as_bool()) { diff --git a/backends/smv/test_cells.sh b/backends/smv/test_cells.sh index 63de465c0..145b9c33b 100644 --- a/backends/smv/test_cells.sh +++ b/backends/smv/test_cells.sh @@ -7,8 +7,8 @@ mkdir -p test_cells.tmp cd test_cells.tmp # don't test $mul to reduce runtime -# don't test $div and $mod to reduce runtime and avoid "div by zero" message -../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod' +# don't test $div/$mod/$divfloor/$modfloor to reduce runtime and avoid "div by zero" message +../../../yosys -p 'test_cell -n 5 -w test all /$alu /$fa /$lcu /$lut /$macc /$mul /$div /$mod /$divfloor /$modfloor' cat > template.txt << "EOT" %module main diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index 11b2ae10f..4f44a053a 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -740,6 +740,95 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) #undef HANDLE_UNIOP #undef HANDLE_BINOP + if (cell->type == ID($divfloor)) + { + // wire [MAXLEN+1:0] _0_, _1_, _2_; + // assign _0_ = $signed(A); + // assign _1_ = $signed(B); + // assign _2_ = (A[-1] == B[-1]) || A == 0 ? _0_ : $signed(_0_ - (B[-1] ? _1_ + 1 : _1_ - 1)); + // assign Y = $signed(_2_) / $signed(_1_); + + if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) { + SigSpec sig_a = cell->getPort(ID::A); + SigSpec sig_b = cell->getPort(ID::B); + + std::string buf_a = next_auto_id(); + std::string buf_b = next_auto_id(); + std::string buf_num = next_auto_id(); + int size_a = GetSize(sig_a); + int size_b = GetSize(sig_b); + int size_y = GetSize(cell->getPort(ID::Y)); + int size_max = std::max(size_a, std::max(size_b, size_y)); + + // intentionally one wider than maximum width + f << stringf("%s" "wire [%d:0] %s, %s, %s;\n", indent.c_str(), size_max, buf_a.c_str(), buf_b.c_str(), buf_num.c_str()); + f << stringf("%s" "assign %s = ", indent.c_str(), buf_a.c_str()); + dump_cell_expr_port(f, cell, "A", true); + f << stringf(";\n"); + f << stringf("%s" "assign %s = ", indent.c_str(), buf_b.c_str()); + dump_cell_expr_port(f, cell, "B", true); + f << stringf(";\n"); + + f << stringf("%s" "assign %s = ", indent.c_str(), buf_num.c_str()); + f << stringf("("); + dump_sigspec(f, sig_a.extract(sig_a.size()-1)); + f << stringf(" == "); + dump_sigspec(f, sig_b.extract(sig_b.size()-1)); + f << stringf(") || "); + dump_sigspec(f, sig_a); + f << stringf(" == 0 ? %s : ", buf_a.c_str()); + f << stringf("$signed(%s - (", buf_a.c_str()); + dump_sigspec(f, sig_b.extract(sig_b.size()-1)); + f << stringf(" ? %s + 1 : %s - 1));\n", buf_b.c_str(), buf_b.c_str()); + + + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = $signed(%s) / ", buf_num.c_str()); + dump_attributes(f, "", cell->attributes, ' '); + f << stringf("$signed(%s);\n", buf_b.c_str()); + return true; + } else { + // same as truncating division + dump_cell_expr_binop(f, indent, cell, "/"); + return true; + } + } + + if (cell->type == ID($modfloor)) + { + // wire truncated = $signed(A) % $signed(B); + // assign Y = (A[-1] == B[-1]) || truncated == 0 ? truncated : $signed(B) + $signed(truncated); + + if (cell->getParam(ID::A_SIGNED).as_bool() && cell->getParam(ID::B_SIGNED).as_bool()) { + SigSpec sig_a = cell->getPort(ID::A); + SigSpec sig_b = cell->getPort(ID::B); + + std::string temp_id = next_auto_id(); + f << stringf("%s" "wire [%d:0] %s = ", indent.c_str(), GetSize(cell->getPort(ID::A))-1, temp_id.c_str()); + dump_cell_expr_port(f, cell, "A", true); + f << stringf(" %% "); + dump_attributes(f, "", cell->attributes, ' '); + dump_cell_expr_port(f, cell, "B", true); + f << stringf(";\n"); + + f << stringf("%s" "assign ", indent.c_str()); + dump_sigspec(f, cell->getPort(ID::Y)); + f << stringf(" = ("); + dump_sigspec(f, sig_a.extract(sig_a.size()-1)); + f << stringf(" == "); + dump_sigspec(f, sig_b.extract(sig_b.size()-1)); + f << stringf(") || %s == 0 ? %s : ", temp_id.c_str(), temp_id.c_str()); + dump_cell_expr_port(f, cell, "B", true); + f << stringf(" + $signed(%s);\n", temp_id.c_str()); + return true; + } else { + // same as truncating modulo + dump_cell_expr_binop(f, indent, cell, "%"); + return true; + } + } + if (cell->type == ID($shift)) { f << stringf("%s" "assign ", indent.c_str()); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 3314819fb..3d690c1f5 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -3540,8 +3540,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/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..02fa0031b 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); 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..ca4201b53 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)); @@ -1949,6 +1949,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 diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 11c45bbec..8228523d5 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_); } @@ -1196,8 +1206,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 = ""); @@ -1295,8 +1309,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 = ""); @@ -1494,7 +1512,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/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/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 95d74d1eb..f99d1509d 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -574,9 +574,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..873286608 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -45,18 +45,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/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..c88f7bd0a 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,21 +50,21 @@ 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; @@ -79,31 +78,21 @@ 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 flatten_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 +106,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 +118,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 +155,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"); @@ -179,8 +169,8 @@ struct TechmapWorker orig_cell_name = cell->name.str(); if (!flatten_mode) { - for (auto &it : tpl->cells_) - if (it.first == ID::_TECHMAP_REPLACE_) { + for (auto tpl_cell : tpl->cells()) + if (tpl_cell->name == ID::_TECHMAP_REPLACE_) { module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str()); break; } @@ -204,30 +194,30 @@ struct TechmapWorker 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 (!flatten_mode && 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) { @@ -237,30 +227,30 @@ struct TechmapWorker w = nullptr; } else { w->attributes.erase(ID::hierconn); - if (GetSize(w) < GetSize(it.second)) { + 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(it.second), log_id(module), log_id(cell)); - w->width = GetSize(it.second); + 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, 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_)) + 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 +258,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 +283,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) { @@ -377,19 +366,19 @@ struct TechmapWorker } } - for (auto &it : tpl->cells_) + for (auto tpl_cell : tpl->cells()) { - IdString c_name = it.second->name.str(); + IdString c_name = tpl_cell->name; bool techmap_replace_cell = (!flatten_mode) && (c_name == ID::_TECHMAP_REPLACE_); if (techmap_replace_cell) 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("\\$")) @@ -397,12 +386,12 @@ struct TechmapWorker 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 +399,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)); } } @@ -463,8 +454,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 +480,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(); @@ -511,7 +502,7 @@ struct TechmapWorker 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)) + if (map->module(tpl_name)->get_bool_attribute(ID::keep_hierarchy)) keepit = true; if (keepit) { if (!flatten_keep_list[cell]) { @@ -533,7 +524,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,9 +557,9 @@ 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; @@ -675,7 +666,7 @@ struct TechmapWorker 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)); + log_error("No simplemap mapper for cell type %s found!\n", log_id(cell->type)); simplemap_mappers.at(cell->type)(module, cell); } @@ -686,7 +677,7 @@ struct TechmapWorker } module->remove(cell); - cell = NULL; + cell = nullptr; } did_something = true; @@ -694,10 +685,10 @@ struct TechmapWorker break; } - for (auto conn : cell->connections()) { + 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) + 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; @@ -710,23 +701,23 @@ struct TechmapWorker } if (tpl->avail_parameters.count(ID::_TECHMAP_CELLTYPE_) != 0) - parameters[ID::_TECHMAP_CELLTYPE_] = RTLIL::unescape_id(cell->type); + parameters.emplace(ID::_TECHMAP_CELLTYPE_, RTLIL::unescape_id(cell->type)); - for (auto conn : cell->connections()) { - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))) != 0) { + 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 == NULL ? RTLIL::State::S1 : RTLIL::State::S0); - parameters[stringf("\\_TECHMAP_CONSTMSK_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); + 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_", RTLIL::id2cstr(conn.first))) != 0) { + 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 != NULL) + if (bit.wire != nullptr) bit = RTLIL::SigBit(RTLIL::State::Sx); - parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const(); + parameters.emplace(stringf("\\_TECHMAP_CONSTVAL_%s_", log_id(conn.first)), RTLIL::SigSpec(v).as_const()); } - if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) { + 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++) { @@ -735,20 +726,20 @@ struct TechmapWorker 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 +754,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,17 +772,18 @@ 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); } } @@ -805,7 +800,7 @@ struct TechmapWorker 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 +810,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 +824,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 +846,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 +857,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 +874,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 +921,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 +932,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 +965,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 +1010,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 +1270,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 +1279,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 +1314,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)) @@ -1382,18 +1371,20 @@ struct FlattenPass : public Pass { extra_args(args, argidx, design); - std::map<RTLIL::IdString, std::set<RTLIL::IdString, RTLIL::sort_by_id_str>> celltypeMap; + dict<IdString, pool<IdString>> celltypeMap; for (auto module : design->modules()) celltypeMap[module->name].insert(module->name); + for (auto &i : celltypeMap) + i.second.sort(RTLIL::sort_by_id_str()); - RTLIL::Module *top_mod = NULL; + RTLIL::Module *top_mod = nullptr; 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) { + pool<RTLIL::Cell*> handled_cells; + if (top_mod != nullptr) { worker.flatten_do_list.insert(top_mod->name); while (!worker.flatten_do_list.empty()) { auto mod = design->module(*worker.flatten_do_list.begin()); @@ -1402,20 +1393,19 @@ struct FlattenPass : public Pass { worker.flatten_do_list.erase(mod->name); } } else { - for (auto mod : vector<Module*>(design->modules())) { + for (auto mod : design->modules().to_vector()) while (worker.techmap_module(design, mod, design, handled_cells, celltypeMap, false)) { } - } } log_suppressed(); log("No more expansions possible.\n"); - if (top_mod != NULL) + if (top_mod != nullptr) { - pool<RTLIL::IdString> used_modules, new_used_modules; + pool<IdString> used_modules, new_used_modules; new_used_modules.insert(top_mod->name); while (!new_used_modules.empty()) { - pool<RTLIL::IdString> queue; + pool<IdString> queue; queue.swap(new_used_modules); for (auto modname : queue) used_modules.insert(modname); @@ -1425,15 +1415,11 @@ struct FlattenPass : public Pass { 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 { + for (auto mod : design->modules().to_vector()) + if (!used_modules[mod->name] && !mod->get_blackbox_attribute(worker.ignore_wb)) { log("Deleting now unused module %s.\n", log_id(mod)); - delete mod; + design->remove(mod); } - design->modules_.swap(new_modules); } log_pop(); 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/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/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/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/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); |