diff options
60 files changed, 1766 insertions, 383 deletions
@@ -55,10 +55,12 @@ Yosys 0.9 .. Yosys 0.9-dev - Added "check -mapped" - Added checking of SystemVerilog always block types (always_comb, always_latch and always_ff) + - Added support for SystemVerilog wildcard port connections (.*) - Added "xilinx_dffopt" pass - Added "scratchpad" pass - Added "abc9 -dff" - Added "synth_xilinx -dff" + - Added "opt_lut_ins" pass Yosys 0.8 .. Yosys 0.9 ---------------------- @@ -392,6 +392,10 @@ Verilog Attributes and non-standard features according to the type of the always. These are checked for correctness in ``proc_dlatch``. +- The cell attribute ``wildcard_port_conns`` represents wildcard port + connections (SystemVerilog ``.*``). These are resolved to concrete + connections to matching wires in ``hierarchy``. + - In addition to the ``(* ... *)`` attribute syntax, Yosys supports the non-standard ``{* ... *}`` attribute syntax to set default attributes for everything that comes after the ``{* ... *}`` statement. (Reset diff --git a/backends/edif/edif.cc b/backends/edif/edif.cc index 60a098917..616b754ce 100644 --- a/backends/edif/edif.cc +++ b/backends/edif/edif.cc @@ -326,7 +326,7 @@ struct EdifBackend : public Backend { continue; SigMap sigmap(module); - std::map<RTLIL::SigSpec, std::set<std::string>> net_join_db; + std::map<RTLIL::SigSpec, std::set<std::pair<std::string, bool>>> net_join_db; *f << stringf(" (cell %s\n", EDIF_DEF(module->name)); *f << stringf(" (cellType GENERIC)\n"); @@ -349,7 +349,7 @@ struct EdifBackend : public Backend { add_prop(p.first, p.second); *f << ")\n"; RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire)); - net_join_db[sig].insert(stringf("(portRef %s)", EDIF_REF(wire->name))); + net_join_db[sig].insert(make_pair(stringf("(portRef %s)", EDIF_REF(wire->name)), wire->port_input)); } else { int b[2]; b[wire->upto ? 0 : 1] = wire->start_offset; @@ -362,7 +362,7 @@ struct EdifBackend : public Backend { *f << ")\n"; for (int i = 0; i < wire->width; i++) { RTLIL::SigSpec sig = sigmap(RTLIL::SigSpec(wire, i)); - net_join_db[sig].insert(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1)); + net_join_db[sig].insert(make_pair(stringf("(portRef (member %s %d))", EDIF_REF(wire->name), GetSize(wire)-i-1), wire->port_input)); } } } @@ -391,7 +391,7 @@ struct EdifBackend : public Backend { log_warning("Bit %d of cell port %s.%s.%s driven by %s will be left unconnected in EDIF output.\n", i, log_id(module), log_id(cell), log_id(p.first), log_signal(sig[i])); else if (sig.size() == 1) - net_join_db[sig[i]].insert(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name))); + net_join_db[sig[i]].insert(make_pair(stringf("(portRef %s (instanceRef %s))", EDIF_REF(p.first), EDIF_REF(cell->name)), cell->output(p.first))); else { int member_idx = GetSize(sig)-i-1; auto m = design->module(cell->type); @@ -400,8 +400,8 @@ struct EdifBackend : public Backend { if (w) member_idx = GetSize(w)-i-1; } - net_join_db[sig[i]].insert(stringf("(portRef (member %s %d) (instanceRef %s))", - EDIF_REF(p.first), member_idx, EDIF_REF(cell->name))); + net_join_db[sig[i]].insert(make_pair(stringf("(portRef (member %s %d) (instanceRef %s))", + EDIF_REF(p.first), member_idx, EDIF_REF(cell->name)), cell->output(p.first))); } } } @@ -410,13 +410,13 @@ struct EdifBackend : public Backend { if (sig.wire == NULL && sig != RTLIL::State::S0 && sig != RTLIL::State::S1) { if (sig == RTLIL::State::Sx) { for (auto &ref : it.second) - log_warning("Exporting x-bit on %s as zero bit.\n", ref.c_str()); + log_warning("Exporting x-bit on %s as zero bit.\n", ref.first.c_str()); sig = RTLIL::State::S0; } else if (sig == RTLIL::State::Sz) { continue; } else { for (auto &ref : it.second) - log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.c_str()); + log_error("Don't know how to handle %s on %s.\n", log_signal(sig), ref.first.c_str()); log_abort(); } } @@ -433,7 +433,7 @@ struct EdifBackend : public Backend { } *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); for (auto &ref : it.second) - *f << stringf(" %s\n", ref.c_str()); + *f << stringf(" %s\n", ref.first.c_str()); if (sig.wire == NULL) { if (nogndvcc) log_error("Design contains constant nodes (map with \"hilomap\" first).\n"); @@ -448,6 +448,31 @@ struct EdifBackend : public Backend { add_prop(p.first, p.second); *f << stringf("\n )\n"); } + for (auto &wire_it : module->wires_) { + RTLIL::Wire *wire = wire_it.second; + if (!wire->get_bool_attribute(ID::keep)) + continue; + for(int i = 0; i < wire->width; i++) { + SigBit raw_sig = RTLIL::SigSpec(wire, i); + SigBit mapped_sig = sigmap(raw_sig); + if (raw_sig == mapped_sig || net_join_db.count(mapped_sig) == 0) + continue; + std::string netname = log_signal(raw_sig); + for (size_t i = 0; i < netname.size(); i++) + if (netname[i] == ' ' || netname[i] == '\\') + netname.erase(netname.begin() + i--); + *f << stringf(" (net %s (joined\n", EDIF_DEF(netname)); + auto &refs = net_join_db.at(mapped_sig); + for (auto &ref : refs) + if (ref.second) + *f << stringf(" %s\n", ref.first.c_str()); + *f << stringf(" )"); + if (attr_properties && raw_sig.wire != NULL) + for (auto &p : raw_sig.wire->attributes) + add_prop(p.first, p.second); + *f << stringf("\n )\n"); + } + } *f << stringf(" )\n"); *f << stringf(" )\n"); *f << stringf(" )\n"); diff --git a/backends/json/json.cc b/backends/json/json.cc index 107009ee4..5c67cb857 100644 --- a/backends/json/json.cc +++ b/backends/json/json.cc @@ -33,6 +33,7 @@ struct JsonWriter std::ostream &f; bool use_selection; bool aig_mode; + bool compat_int_mode; Design *design; Module *module; @@ -42,8 +43,9 @@ struct JsonWriter dict<SigBit, string> sigids; pool<Aig> aig_models; - JsonWriter(std::ostream &f, bool use_selection, bool aig_mode) : - f(f), use_selection(use_selection), aig_mode(aig_mode) { } + JsonWriter(std::ostream &f, bool use_selection, bool aig_mode, bool compat_int_mode) : + f(f), use_selection(use_selection), aig_mode(aig_mode), + compat_int_mode(compat_int_mode) { } string get_string(string str) { @@ -102,8 +104,7 @@ struct JsonWriter if (state < 2) str += " "; f << get_string(str); - } else - if (GetSize(value) == 32 && value.is_fully_def()) { + } else if (compat_int_mode && GetSize(value) == 32 && value.is_fully_def()) { if ((value.flags & RTLIL::ConstFlags::CONST_FLAG_SIGNED) != 0) f << stringf("%d", value.as_int()); else @@ -294,6 +295,10 @@ struct JsonBackend : public Backend { log(" -aig\n"); log(" include AIG models for the different gate types\n"); log("\n"); + log(" -compat-int\n"); + log(" emit 32-bit fully-defined parameter values directly\n"); + log(" as JSON numbers (for compatibility with old parsers)\n"); + log("\n"); log("\n"); log("The general syntax of the JSON output created by this command is as follows:\n"); log("\n"); @@ -368,10 +373,9 @@ struct JsonBackend : public Backend { log("connected to a constant driver are denoted as string \"0\", \"1\", \"x\", or\n"); log("\"z\" instead of a number.\n"); log("\n"); - log("Numeric 32-bit parameter and attribute values are written as decimal values.\n"); - log("Bit verctors of different sizes, or ones containing 'x' or 'z' bits, are written\n"); - log("as string holding the binary representation of the value. Strings are written\n"); - log("as strings, with an appended blank in cases of strings of the form /[01xz]* */.\n"); + log("Bit vectors (including integers) are written as string holding the binary"); + log("representation of the value. Strings are written as strings, with an appended"); + log("blank in cases of strings of the form /[01xz]* */.\n"); log("\n"); log("For example the following Verilog code:\n"); log("\n"); @@ -495,6 +499,7 @@ struct JsonBackend : public Backend { void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE { bool aig_mode = false; + bool compat_int_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -503,13 +508,17 @@ struct JsonBackend : public Backend { aig_mode = true; continue; } + if (args[argidx] == "-compat-int") { + compat_int_mode = true; + continue; + } break; } extra_args(f, filename, args, argidx); log_header(design, "Executing JSON backend.\n"); - JsonWriter json_writer(*f, false, aig_mode); + JsonWriter json_writer(*f, false, aig_mode, compat_int_mode); json_writer.write_design(design); } } JsonBackend; @@ -530,6 +539,10 @@ struct JsonPass : public Pass { log(" -aig\n"); log(" also include AIG models for the different gate types\n"); log("\n"); + log(" -compat-int\n"); + log(" emit 32-bit fully-defined parameter values directly\n"); + log(" as JSON numbers (for compatibility with old parsers)\n"); + log("\n"); log("See 'help write_json' for a description of the JSON format used.\n"); log("\n"); } @@ -537,6 +550,7 @@ struct JsonPass : public Pass { { std::string filename; bool aig_mode = false; + bool compat_int_mode = false; size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) @@ -549,6 +563,10 @@ struct JsonPass : public Pass { aig_mode = true; continue; } + if (args[argidx] == "-compat-int") { + compat_int_mode = true; + continue; + } break; } extra_args(args, argidx, design); @@ -569,7 +587,7 @@ struct JsonPass : public Pass { f = &buf; } - JsonWriter json_writer(*f, true, aig_mode); + JsonWriter json_writer(*f, true, aig_mode, compat_int_mode); json_writer.write_design(design); if (!filename.empty()) { diff --git a/backends/smt2/smtio.py b/backends/smt2/smtio.py index 1df996aa7..34bf7ef38 100644 --- a/backends/smt2/smtio.py +++ b/backends/smt2/smtio.py @@ -304,7 +304,11 @@ class SmtIo: def p_open(self): assert self.p is None - self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + try: + self.p = subprocess.Popen(self.popen_vargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except FileNotFoundError: + print("%s SMT Solver '%s' not found in path." % (self.timestamp(), self.popen_vargs[0]), flush=True) + sys.exit(1) running_solvers[self.p_index] = self.p self.p_running = True self.p_next = None diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 418fd722c..a42569301 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -422,6 +422,8 @@ void AigerReader::parse_xaiger() log_assert(wire); input_sig.append(wire); } + // Reverse input order as fastest input is returned first + input_sig.reverse(); // TODO: Compute LUT mask from AIG in less than O(2 ** input_sig.size()) ce.clear(); ce.compute_deps(output_sig, input_sig.to_sigbit_pool()); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 918d178c7..14e1cec5e 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -244,6 +244,7 @@ namespace AST void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); AstNode *eval_const_function(AstNode *fcall); bool is_simple_const_expr(); + std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); // create a human-readable text representation of the AST (for debugging) void dumpAst(FILE *f, std::string indent) const; diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index b94a8d710..8855d9954 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -41,6 +41,103 @@ YOSYS_NAMESPACE_BEGIN using namespace AST; using namespace AST_INTERNAL; +// Process a format string and arguments for $display, $write, $sprintf, etc + +std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) { + // Other arguments are placeholders. Process the string as we go through it + std::string sout; + for (size_t i = 0; i < sformat.length(); i++) + { + // format specifier + if (sformat[i] == '%') + { + // If there's no next character, that's a problem + if (i+1 >= sformat.length()) + log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str()); + + char cformat = sformat[++i]; + + // %% is special, does not need a matching argument + if (cformat == '%') + { + sout += '%'; + continue; + } + + // Simplify the argument + AstNode *node_arg = nullptr; + + // Everything from here on depends on the format specifier + switch (cformat) + { + case 's': + case 'S': + case 'd': + case 'D': + case 'x': + case 'X': + if (next_arg >= GetSize(children)) + log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n", + cformat, str.c_str()); + + node_arg = children[next_arg++]; + while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_arg->type != AST_CONSTANT) + log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); + break; + + case 'm': + case 'M': + break; + + default: + log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); + break; + } + + switch (cformat) + { + case 's': + case 'S': + sout += node_arg->bitsAsConst().decode_string(); + break; + + case 'd': + case 'D': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'x': + case 'X': + { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); + sout += tmp; + } + break; + + case 'm': + case 'M': + sout += log_id(current_module->name); + break; + + default: + log_abort(); + } + } + + // not a format specifier + else + sout += sformat[i]; + } + return sout; +} + + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -216,99 +313,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (node_string->type != AST_CONSTANT) log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant 1st argument.\n", str.c_str()); std::string sformat = node_string->bitsAsConst().decode_string(); - - // Other arguments are placeholders. Process the string as we go through it - std::string sout; - int next_arg = 1; - for (size_t i = 0; i < sformat.length(); i++) - { - // format specifier - if (sformat[i] == '%') - { - // If there's no next character, that's a problem - if (i+1 >= sformat.length()) - log_file_error(filename, linenum, "System task `%s' called with `%%' at end of string.\n", str.c_str()); - - char cformat = sformat[++i]; - - // %% is special, does not need a matching argument - if (cformat == '%') - { - sout += '%'; - continue; - } - - // Simplify the argument - AstNode *node_arg = nullptr; - - // Everything from here on depends on the format specifier - switch (cformat) - { - case 's': - case 'S': - case 'd': - case 'D': - case 'x': - case 'X': - if (next_arg >= GetSize(children)) - log_file_error(filename, linenum, "Missing argument for %%%c format specifier in system task `%s'.\n", - cformat, str.c_str()); - - node_arg = children[next_arg++]; - while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_arg->type != AST_CONSTANT) - log_file_error(filename, linenum, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); - break; - - case 'm': - case 'M': - break; - - default: - log_file_error(filename, linenum, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); - break; - } - - switch (cformat) - { - case 's': - case 'S': - sout += node_arg->bitsAsConst().decode_string(); - break; - - case 'd': - case 'D': - { - char tmp[128]; - snprintf(tmp, sizeof(tmp), "%d", node_arg->bitsAsConst().as_int()); - sout += tmp; - } - break; - - case 'x': - case 'X': - { - char tmp[128]; - snprintf(tmp, sizeof(tmp), "%x", node_arg->bitsAsConst().as_int()); - sout += tmp; - } - break; - - case 'm': - case 'M': - sout += log_id(current_module->name); - break; - - default: - log_abort(); - } - } - - // not a format specifier - else - sout += sformat[i]; - } - + std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); // Finally, print the message (only include a \n for $display, not for $write) log("%s", sout.c_str()); if (str == "$display") @@ -2244,6 +2249,17 @@ skip_dynamic_range_lvalue_expansion:; goto apply_newNode; } + if (str == "\\$sformatf") { + AstNode *node_string = children[0]; + while (node_string->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + if (node_string->type != AST_CONSTANT) + log_file_error(filename, linenum, "Failed to evaluate system function `%s' with non-constant 1st argument.\n", str.c_str()); + std::string sformat = node_string->bitsAsConst().decode_string(); + std::string sout = process_format_str(sformat, 1, stage, width_hint, sign_hint); + newNode = AstNode::mkconst_str(sout); + goto apply_newNode; + } + if (current_scope.count(str) != 0 && current_scope[str]->type == AST_DPI_FUNCTION) { AstNode *dpi_decl = current_scope[str]; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 9274cf5ca..ae5815f8e 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -539,6 +539,14 @@ bool VerificImporter::import_netlist_instance_cells(Instance *inst, RTLIL::IdStr return true; } + if (inst->Type() == OPER_REDUCE_NAND) { + Wire *tmp = module->addWire(NEW_ID); + cell = module->addReduceAnd(inst_name, IN, tmp, SIGNED); + module->addNot(NEW_ID, tmp, net_map_at(inst->GetOutput())); + import_attributes(cell->attributes, inst); + return true; + } + if (inst->Type() == OPER_REDUCE_OR) { cell = module->addReduceOr(inst_name, IN, net_map_at(inst->GetOutput()), SIGNED); import_attributes(cell->attributes, inst); @@ -1891,6 +1899,9 @@ void verific_import(Design *design, const std::map<std::string,std::string> &par if (!verific_error_msg.empty()) log_error("%s\n", verific_error_msg.c_str()); + for (auto nl : nl_todo) + nl->ChangePortBusStructures(1 /* hierarchical */); + VerificExtNets worker; for (auto nl : nl_todo) worker.run(nl); @@ -2408,7 +2419,7 @@ struct VerificPass : public Pass { else { if (argidx == GetSize(args)) - log_cmd_error("No top module specified.\n"); + cmd_error(args, argidx, "No top module specified.\n"); Array veri_modules, vhdl_units; for (; argidx < GetSize(args); argidx++) @@ -2470,6 +2481,9 @@ struct VerificPass : public Pass { worker.run(nl); } + for (auto nl : nl_todo) + nl->ChangePortBusStructures(1 /* hierarchical */); + if (!dumpfile.empty()) { VeriWrite veri_writer; veri_writer.WriteFile(dumpfile.c_str(), Netlist::PresentDesign()); @@ -2495,7 +2509,7 @@ struct VerificPass : public Pass { goto check_error; } - log_cmd_error("Missing or unsupported mode parameter.\n"); + cmd_error(args, argidx, "Missing or unsupported mode parameter.\n"); check_error: if (!verific_error_msg.empty()) @@ -2568,14 +2582,14 @@ struct ReadPass : public Pass { static bool use_verific = verific_available; if (args.size() < 2 || args[1][0] != '-') - log_cmd_error("Missing mode parameter.\n"); + cmd_error(args, 1, "Missing mode parameter.\n"); if (args[1] == "-verific" || args[1] == "-noverific") { if (args.size() != 2) - log_cmd_error("Additional arguments to -verific/-noverific.\n"); + cmd_error(args, 1, "Additional arguments to -verific/-noverific.\n"); if (args[1] == "-verific") { if (!verific_available) - log_cmd_error("This version of Yosys is built without Verific support.\n"); + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); use_verific = true; } else { use_verific = false; @@ -2584,7 +2598,7 @@ struct ReadPass : public Pass { } if (args.size() < 3) - log_cmd_error("Missing file name parameter.\n"); + cmd_error(args, 3, "Missing file name parameter.\n"); if (args[1] == "-vlog95" || args[1] == "-vlog2k") { if (use_verific) { @@ -2616,7 +2630,7 @@ struct ReadPass : public Pass { args[0] = "verific"; Pass::call(design, args); } else { - log_cmd_error("This version of Yosys is built without Verific support.\n"); + cmd_error(args, 1, "This version of Yosys is built without Verific support.\n"); } return; } @@ -2663,7 +2677,7 @@ struct ReadPass : public Pass { return; } - log_cmd_error("Missing or unsupported mode parameter.\n"); + cmd_error(args, 1, "Missing or unsupported mode parameter.\n"); } } ReadPass; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index ca23df3e8..9b43c250e 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -431,6 +431,8 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } +".*" { return TOK_WILDCARD_CONNECT; } + [-+]?[=*]> { if (!specify_mode) REJECT; frontend_verilog_yylval.string = new std::string(yytext); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index a30935e0a..2c7304cc4 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -138,7 +138,7 @@ struct specify_rise_fall { %token ATTR_BEGIN ATTR_END DEFATTR_BEGIN DEFATTR_END %token TOK_MODULE TOK_ENDMODULE TOK_PARAMETER TOK_LOCALPARAM TOK_DEFPARAM %token TOK_PACKAGE TOK_ENDPACKAGE TOK_PACKAGESEP -%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR +%token TOK_INTERFACE TOK_ENDINTERFACE TOK_MODPORT TOK_VAR TOK_WILDCARD_CONNECT %token TOK_INPUT TOK_OUTPUT TOK_INOUT TOK_WIRE TOK_WAND TOK_WOR TOK_REG TOK_LOGIC %token TOK_INTEGER TOK_SIGNED TOK_ASSIGN TOK_ALWAYS TOK_INITIAL %token TOK_ALWAYS_FF TOK_ALWAYS_COMB TOK_ALWAYS_LATCH @@ -1580,6 +1580,11 @@ cell_port: node->children.back()->str = *$3; delete $3; free_attr($1); + } | + attr TOK_WILDCARD_CONNECT { + if (!sv_mode) + frontend_verilog_yyerror("Wildcard port connections are only supported in SystemVerilog mode."); + astbuf2->attributes[ID(wildcard_port_conns)] = AstNode::mkconst_int(1, false); }; always_comb_or_latch: diff --git a/kernel/driver.cc b/kernel/driver.cc index f0d495b46..9040408bc 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -295,6 +295,9 @@ int main(int argc, char **argv) printf(" -E <depsfile>\n"); printf(" write a Makefile dependencies file with in- and output file names\n"); printf("\n"); + printf(" -x <feature>\n"); + printf(" do not print warnings for the specified experimental feature\n"); + printf("\n"); printf(" -g\n"); printf(" globally enable debug log messages\n"); printf("\n"); @@ -317,8 +320,14 @@ int main(int argc, char **argv) exit(0); } + if (argc == 2 && (!strcmp(argv[1], "-V") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version"))) + { + printf("%s\n", yosys_version_str); + exit(0); + } + int opt; - while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:")) != -1) + while ((opt = getopt(argc, argv, "MXAQTVSgm:f:Hh:b:o:p:l:L:qv:tds:c:W:w:e:D:P:E:x:")) != -1) { switch (opt) { @@ -449,6 +458,9 @@ int main(int argc, char **argv) case 'E': depsfile = optarg; break; + case 'x': + log_experimentals_ignored.insert(optarg); + break; default: fprintf(stderr, "Run '%s -h' for help.\n", argv[0]); exit(1); @@ -561,6 +573,10 @@ int main(int argc, char **argv) if (log_warnings_count) log("Warnings: %d unique messages, %d total\n", GetSize(log_warnings), log_warnings_count); + + if (!log_experimentals.empty()) + log("Warnings: %d experimental features used (not excluded with -x).\n", GetSize(log_experimentals)); + #ifdef _WIN32 log("End of script. Logfile hash: %s\n", hash.c_str()); #else diff --git a/kernel/log.cc b/kernel/log.cc index c5ba0d10d..f5d6c488e 100644 --- a/kernel/log.cc +++ b/kernel/log.cc @@ -42,7 +42,7 @@ std::vector<FILE*> log_files; std::vector<std::ostream*> log_streams; std::map<std::string, std::set<std::string>> log_hdump; std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; -std::set<std::string> log_warnings; +std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; int log_warnings_count = 0; bool log_hdump_all = false; FILE *log_errfile = NULL; @@ -377,6 +377,19 @@ void log_warning(const char *format, ...) va_end(ap); } +void log_experimental(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + string s = vstringf(format, ap); + va_end(ap); + + if (log_experimentals_ignored.count(s) == 0 && log_experimentals.count(s) == 0) { + log_warning("Feature '%s' is experimental.\n", s.c_str()); + log_experimentals.insert(s); + } +} + void log_warning_noprefix(const char *format, ...) { va_list ap; diff --git a/kernel/log.h b/kernel/log.h index 1f15f3459..9db8efaa5 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -50,7 +50,7 @@ extern std::vector<FILE*> log_files; extern std::vector<std::ostream*> log_streams; extern std::map<std::string, std::set<std::string>> log_hdump; extern std::vector<std::regex> log_warn_regexes, log_nowarn_regexes, log_werror_regexes; -extern std::set<std::string> log_warnings; +extern std::set<std::string> log_warnings, log_experimentals, log_experimentals_ignored; extern int log_warnings_count; extern bool log_hdump_all; extern FILE *log_errfile; @@ -77,6 +77,7 @@ YS_NORETURN void logv_error(const char *format, va_list ap) YS_ATTRIBUTE(noretur void log(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(format(printf, 2, 3)); void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); +void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2)); // Log with filename to report a problem in a source file. void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4)); diff --git a/kernel/register.cc b/kernel/register.cc index 5d0fb3c8c..e59d59654 100644 --- a/kernel/register.cc +++ b/kernel/register.cc @@ -304,6 +304,9 @@ void Pass::call(RTLIL::Design *design, std::vector<std::string> args) if (pass_register.count(args[0]) == 0) log_cmd_error("No such command: %s (type 'help' for a command overview)\n", args[0].c_str()); + if (pass_register[args[0]]->experimental_flag) + log_experimental("%s", args[0].c_str()); + size_t orig_sel_stack_pos = design->selection_stack.size(); auto state = pass_register[args[0]]->pre_execute(); pass_register[args[0]]->execute(args, design); @@ -824,6 +827,11 @@ struct HelpPass : public Pass { log("="); log("\n"); it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } } } else if (args[1] == "-cells") { @@ -846,6 +854,11 @@ struct HelpPass : public Pass { std::ostringstream buf; log_streams.push_back(&buf); it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } log_streams.pop_back(); write_tex(f, it.first, it.second->short_help, buf.str()); } @@ -858,6 +871,11 @@ struct HelpPass : public Pass { std::ostringstream buf; log_streams.push_back(&buf); it.second->help(); + if (it.second->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", it.first.c_str()); + log("\n"); + } log_streams.pop_back(); write_html(f, it.first, it.second->short_help, buf.str()); } @@ -865,6 +883,11 @@ struct HelpPass : public Pass { } else if (pass_register.count(args[1])) { pass_register.at(args[1])->help(); + if (pass_register.at(args[1])->experimental_flag) { + log("\n"); + log("WARNING: THE '%s' COMMAND IS EXPERIMENTAL.\n", args[1].c_str()); + log("\n"); + } } else if (cell_help_messages.cell_help.count(args[1])) { log("%s", cell_help_messages.cell_help.at(args[1]).c_str()); diff --git a/kernel/register.h b/kernel/register.h index 821faff3e..4622845b6 100644 --- a/kernel/register.h +++ b/kernel/register.h @@ -36,6 +36,11 @@ struct Pass int call_counter; int64_t runtime_ns; + bool experimental_flag = false; + + void experimental() { + experimental_flag = true; + } struct pre_post_exec_state_t { Pass *parent_pass; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 6251d265d..58c5d9674 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -851,6 +851,8 @@ public: RTLIL::SigSpec repeat(int num) const; + void reverse() { inline_unpack(); std::reverse(bits_.begin(), bits_.end()); } + bool operator <(const RTLIL::SigSpec &other) const; bool operator ==(const RTLIL::SigSpec &other) const; inline bool operator !=(const RTLIL::SigSpec &other) const { return !(*this == other); } diff --git a/manual/CHAPTER_CellLib.tex b/manual/CHAPTER_CellLib.tex index 00a88cc82..55abd9b96 100644 --- a/manual/CHAPTER_CellLib.tex +++ b/manual/CHAPTER_CellLib.tex @@ -198,8 +198,8 @@ calculated signal and a constant zero with an {\tt \$and} gate). \subsection{Registers} -D-Type Flip-Flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK}, -an input port \B{D} and an output port \B{Q}. The following parameters are available for \$dff +D-type flip-flops are represented by {\tt \$dff} cells. These cells have a clock port \B{CLK}, +an input port \B{D} and an output port \B{Q}. The following parameters are available for {\tt \$dff} cells: \begin{itemize} @@ -211,13 +211,23 @@ Clock is active on the positive edge if this parameter has the value {\tt 1'b1} edge if this parameter is {\tt 1'b0}. \end{itemize} -D-Type Flip-Flops with asynchronous resets are represented by {\tt \$adff} cells. As the {\tt \$dff} +D-type flip-flops with enable are represented by {\tt \$dffe} cells. As the {\tt \$dff} +cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{EN} +input port for the enable pin and the following parameter: + +\begin{itemize} +\item \B{EN\_POLARITY} \\ +The enable input is active-high if this parameter has the value {\tt 1'b1} and active-low +if this parameter is {\tt 1'b0}. +\end{itemize} + +D-type flip-flops with asynchronous reset are represented by {\tt \$adff} cells. As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have a single-bit \B{ARST} input port for the reset pin and the following additional two parameters: \begin{itemize} \item \B{ARST\_POLARITY} \\ -The asynchronous reset is high-active if this parameter has the value {\tt 1'b1} and low-active +The asynchronous reset is active-high if this parameter has the value {\tt 1'b1} and active-low if this parameter is {\tt 1'b0}. \item \B{ARST\_VALUE} \\ @@ -231,8 +241,27 @@ Usually these cells are generated by the {\tt proc} pass using the information in the designs RTLIL::Process objects. \end{sloppypar} +D-type flip-flops with asynchronous set and reset are represented by {\tt \$dffsr} cells. +As the {\tt \$dff} cells they have \B{CLK}, \B{D} and \B{Q} ports. In addition they also have +a single-bit \B{SET} input port for the set pin, a single-bit \B{CLR} input port for the reset pin, +and the following two parameters: + +\begin{itemize} +\item \B{SET\_POLARITY} \\ +The set input is active-high if this parameter has the value {\tt 1'b1} and active-low +if this parameter is {\tt 1'b0}. + +\item \B{CLR\_POLARITY} \\ +The reset input is active-high if this parameter has the value {\tt 1'b1} and active-low +if this parameter is {\tt 1'b0}. +\end{itemize} + +When both the set and reset inputs of a {\tt \$dffsr} cell are active, the reset input takes +precedence. + \begin{fixme} -Add information about {\tt \$sr} cells (set-reset flip-flops) and d-type latches. +Add information about {\tt \$sr} cells (set-reset flip-flops), {\tt \$dlatch} cells (d-type latches), +and {\tt \$dlatchsr} cells (d-type latches with set/reset). \end{fixme} \subsection{Memories} @@ -451,6 +480,30 @@ $ClkEdge$ & $RstLvl$ & $RstVal$ & Cell Type \\ \lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFF\_PP0\_} \\ \lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFF\_PP1\_} \\ \end{tabular} +% FIXME: the layout of this is broken and I have no idea how to fix it +\hfil +\begin{tabular}[t]{lll} +$ClkEdge$ & $EnLvl$ & Cell Type \\ +\hline +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_NN\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_NP\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & {\tt \$\_DFFE\_PN\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & {\tt \$\_DFFE\_PP\_} \\ +\end{tabular} +% FIXME: the layout of this is broken too +\hfil +\begin{tabular}[t]{llll} +$ClkEdge$ & $SetLvl$ & $RstLvl$ & Cell Type \\ +\hline +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NNN\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NNP\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_NPN\_} \\ +\lstinline[language=Verilog];negedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_NPP\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PNN\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];0; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PNP\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];0; & {\tt \$\_DFFSR\_PPN\_} \\ +\lstinline[language=Verilog];posedge; & \lstinline[language=Verilog];1; & \lstinline[language=Verilog];1; & {\tt \$\_DFFSR\_PPP\_} \\ +\end{tabular} \caption{Cell types for gate level logic networks} \label{tab:CellLib_gates} \end{table} @@ -459,11 +512,22 @@ Table~\ref{tab:CellLib_gates} lists all cell types used for gate level logic. Th {\tt \$\_NOT\_}, {\tt \$\_AND\_}, {\tt \$\_NAND\_}, {\tt \$\_ANDNOT\_}, {\tt \$\_OR\_}, {\tt \$\_NOR\_}, {\tt \$\_ORNOT\_}, {\tt \$\_XOR\_}, {\tt \$\_XNOR\_} and {\tt \$\_MUX\_} are used to model combinatorial logic. The cell type {\tt \$\_TBUF\_} is used to model tristate logic. + The cell types {\tt \$\_DFF\_N\_} and {\tt \$\_DFF\_P\_} represent d-type flip-flops. +The cell types {\tt \$\_DFFE\_NN\_}, {\tt \$\_DFFE\_NP\_}, {\tt \$\_DFFE\_PN\_} and {\tt \$\_DFFE\_PP\_} +implement d-type flip-flops with enable. The values in the table for these cell types relate to the +following Verilog code template. + +\begin{lstlisting}[mathescape,language=Verilog] + always @($ClkEdge$ C) + if (EN == $EnLvl$) + Q <= D; +\end{lstlisting} + The cell types {\tt \$\_DFF\_NN0\_}, {\tt \$\_DFF\_NN1\_}, {\tt \$\_DFF\_NP0\_}, {\tt \$\_DFF\_NP1\_}, {\tt \$\_DFF\_PN0\_}, {\tt \$\_DFF\_PN1\_}, {\tt \$\_DFF\_PP0\_} and {\tt \$\_DFF\_PP1\_} implement -d-type flip-flops with asynchronous resets. The values in the table for these cell types relate to the +d-type flip-flops with asynchronous reset. The values in the table for these cell types relate to the following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, and \lstinline[language=Verilog];negedge; otherwise. @@ -476,6 +540,25 @@ otherwise. Q <= D; \end{lstlisting} +The cell types {\tt \$\_DFFSR\_NNN\_}, {\tt \$\_DFFSR\_NNP\_}, {\tt \$\_DFFSR\_NPN\_}, {\tt \$\_DFFSR\_NPP\_}, +{\tt \$\_DFFSR\_PNN\_}, {\tt \$\_DFFSR\_PNP\_}, {\tt \$\_DFFSR\_PPN\_} and {\tt \$\_DFFSR\_PPP\_} implement +d-type flip-flops with asynchronous set and reset. The values in the table for these cell types relate to the +following Verilog code template, where \lstinline[mathescape,language=Verilog];$RstEdge$; is \lstinline[language=Verilog];posedge; +if \lstinline[mathescape,language=Verilog];$RstLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; +otherwise, and \lstinline[mathescape,language=Verilog];$SetEdge$; is \lstinline[language=Verilog];posedge; +if \lstinline[mathescape,language=Verilog];$SetLvl$; if \lstinline[language=Verilog];1;, \lstinline[language=Verilog];negedge; +otherwise. + +\begin{lstlisting}[mathescape,language=Verilog] + always @($ClkEdge$ C, $RstEdge$ R, $SetEdge$ S) + if (R == $RstLvl$) + Q <= 0; + else if (S == $SetLvl$) + Q <= 1; + else + Q <= D; +\end{lstlisting} + In most cases gate level logic networks are created from RTL networks using the {\tt techmap} pass. The flip-flop cells from the gate level logic network can be mapped to physical flip-flop cells from a Liberty file using the {\tt dfflibmap} pass. The combinatorial logic cells can be mapped to physical cells from a Liberty file via ABC \citeweblink{ABC} @@ -507,11 +590,7 @@ Add information about {\tt \$ff} and {\tt \$\_FF\_} cells. \end{fixme} \begin{fixme} -Add information about {\tt \$dffe}, {\tt \$dffsr}, {\tt \$dlatch}, and {\tt \$dlatchsr} cells. -\end{fixme} - -\begin{fixme} -Add information about {\tt \$\_DFFE\_??\_}, {\tt \$\_DFFSR\_???\_}, {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells. +Add information about {\tt \$\_DLATCH\_?\_}, and {\tt \$\_DLATCHSR\_???\_} cells. \end{fixme} \begin{fixme} diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index c58c3f66a..9b4e644c0 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -1935,6 +1935,19 @@ def parse_header(source): line = source_text[i].replace("YOSYS_NAMESPACE_BEGIN", " namespace YOSYS_NAMESPACE{").replace("YOSYS_NAMESPACE_END"," }") ugly_line = unpretty_string(line) + # for anonymous unions, ignore union enclosure by skipping start line and replacing end line with new line + if 'union {' in line: + j = i+1 + while j < len(source_text): + union_line = source_text[j] + if '};' in union_line: + source_text[j] = '\n' + break + j += 1 + if j != len(source_text): + i += 1 + continue + if str.startswith(ugly_line, "namespace "):# and ugly_line.find("std") == -1 and ugly_line.find("__") == -1: namespace_name = ugly_line[10:].replace("{","").strip() namespaces.append((namespace_name, ugly_line.count("{"))) diff --git a/passes/cmds/show.cc b/passes/cmds/show.cc index a3e969ef1..eeef24bde 100644 --- a/passes/cmds/show.cc +++ b/passes/cmds/show.cc @@ -873,7 +873,7 @@ struct ShowPass : public Pass { #ifdef __APPLE__ std::string cmd = stringf("ps -fu %d | grep -q '[ ]%s' || xdot '%s' &", getuid(), dot_file.c_str(), dot_file.c_str()); #else - std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid'; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); + std::string cmd = stringf("{ test -f '%s.pid' && fuser -s '%s.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '%s'; ) 3> '%s.pid' &", dot_file.c_str(), dot_file.c_str(), dot_file.c_str(), dot_file.c_str()); #endif log("Exec: %s\n", cmd.c_str()); if (run_command(cmd) != 0) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index d8a628448..fa4a8ea29 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -548,6 +548,19 @@ RTLIL::Module *check_if_top_has_changed(Design *design, Module *top_mod) return NULL; } +// Find a matching wire for an implicit port connection; traversing generate block scope +RTLIL::Wire *find_implicit_port_wire(Module *module, Cell *cell, const std::string& port) +{ + const std::string &cellname = cell->name.str(); + size_t idx = cellname.size(); + while ((idx = cellname.find_last_of('.', idx-1)) != std::string::npos) { + Wire *found = module->wire(cellname.substr(0, idx+1) + port.substr(1)); + if (found != nullptr) + return found; + } + return module->wire(port); +} + struct HierarchyPass : public Pass { HierarchyPass() : Pass("hierarchy", "check, expand and clean up design hierarchy") { } void help() YS_OVERRIDE @@ -970,15 +983,71 @@ struct HierarchyPass : public Pass { } } + // Determine default values + dict<IdString, dict<IdString, Const>> defaults_db; if (!nodefaults) { - dict<IdString, dict<IdString, Const>> defaults_db; - for (auto module : design->modules()) for (auto wire : module->wires()) if (wire->port_input && wire->attributes.count("\\defaultvalue")) defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue"); + } + // Process SV implicit wildcard port connections + std::set<Module*> blackbox_derivatives; + std::vector<Module*> design_modules = design->modules(); + for (auto module : design_modules) + { + for (auto cell : module->cells()) + { + if (!cell->get_bool_attribute(ID(wildcard_port_conns))) + continue; + Module *m = design->module(cell->type); + + if (m == nullptr) + log_error("Cell %s.%s (%s) has implicit port connections but the module it instantiates is unknown.\n", + RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + + // Need accurate port widths for error checking; so must derive blackboxes with dynamic port widths + if (m->get_blackbox_attribute() && !cell->parameters.empty() && m->get_bool_attribute("\\dynports")) { + IdString new_m_name = m->derive(design, cell->parameters, true); + if (new_m_name.empty()) + continue; + if (new_m_name != m->name) { + m = design->module(new_m_name); + blackbox_derivatives.insert(m); + } + } + + auto old_connections = cell->connections(); + for (auto wire : m->wires()) { + // Find ports of the module that aren't explicitly connected + if (!wire->port_input && !wire->port_output) + continue; + if (old_connections.count(wire->name)) + continue; + // Make sure a wire of correct name exists in the parent + Wire* parent_wire = find_implicit_port_wire(module, cell, wire->name.str()); + + // Missing wires are OK when a default value is set + if (!nodefaults && parent_wire == nullptr && defaults_db.count(cell->type) && defaults_db.at(cell->type).count(wire->name)) + continue; + + if (parent_wire == nullptr) + log_error("No matching wire for implicit port connection `%s' of cell %s.%s (%s).\n", + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + if (parent_wire->width != wire->width) + log_error("Width mismatch between wire (%d bits) and port (%d bits) for implicit port connection `%s' of cell %s.%s (%s).\n", + parent_wire->width, wire->width, + RTLIL::id2cstr(wire->name), RTLIL::id2cstr(module->name), RTLIL::id2cstr(cell->name), RTLIL::id2cstr(cell->type)); + cell->setPort(wire->name, parent_wire); + } + cell->attributes.erase(ID(wildcard_port_conns)); + } + } + + if (!nodefaults) + { for (auto module : design->modules()) for (auto cell : module->cells()) { @@ -1000,9 +1069,6 @@ struct HierarchyPass : public Pass { } } - std::set<Module*> blackbox_derivatives; - std::vector<Module*> design_modules = design->modules(); - for (auto module : design_modules) { pool<Wire*> wand_wor_index; diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index 002c1a6a1..3133927bb 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -15,6 +15,7 @@ OBJS += passes/opt/wreduce.o OBJS += passes/opt/opt_demorgan.o OBJS += passes/opt/rmports.o OBJS += passes/opt/opt_lut.o +OBJS += passes/opt/opt_lut_ins.o OBJS += passes/opt/pmux2shiftx.o OBJS += passes/opt/muxpack.o endif diff --git a/passes/opt/opt_lut_ins.cc b/passes/opt/opt_lut_ins.cc new file mode 100644 index 000000000..cf5248ced --- /dev/null +++ b/passes/opt/opt_lut_ins.cc @@ -0,0 +1,278 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/yosys.h" +#include "kernel/sigtools.h" + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +struct OptLutInsPass : public Pass { + OptLutInsPass() : Pass("opt_lut_ins", "discard unused LUT inputs") { } + void help() YS_OVERRIDE + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_lut_ins [options] [selection]\n"); + log("\n"); + log("This pass removes unused inputs from LUT cells (that is, inputs that can not\n"); + log("influence the output signal given this LUT's value). While such LUTs cannot\n"); + log("be directly emitted by ABC, they can be a result of various post-ABC\n"); + log("transformations, such as mapping wide LUTs (not all sub-LUTs will use the\n"); + log("full set of inputs) or optimizations such as xilinx_dffopt.\n"); + log("\n"); + log(" -tech <technology>\n"); + log(" Instead of generic $lut cells, operate on LUT cells specific\n"); + log(" to the given technology. Valid values are: xilinx, ecp5, gowin.\n"); + log("\n"); + } + void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE + { + log_header(design, "Executing OPT_LUT_INS pass (discard unused LUT inputs).\n"); + string techname; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) + { + if (args[argidx] == "-tech" && argidx+1 < args.size()) { + techname = args[++argidx]; + continue; + } + break; + } + extra_args(args, argidx, design); + + if (techname != "" && techname != "xilinx" && techname != "ecp5" && techname != "gowin") + log_cmd_error("Unsupported technology: '%s'\n", techname.c_str()); + + for (auto module : design->selected_modules()) + { + log("Optimizing LUTs in %s.\n", log_id(module)); + + std::vector<Cell *> remove_cells; + // Gather LUTs. + for (auto cell : module->selected_cells()) + { + if (cell->get_bool_attribute(ID::keep)) + continue; + Const lut; + std::vector<SigBit> inputs; + std::vector<SigBit> output; + bool ignore_const = false; + if (techname == "") { + if (cell->type != ID($lut)) + continue; + inputs = cell->getPort(ID::A).bits(); + output = cell->getPort(ID::Y); + lut = cell->getParam(ID(LUT)); + } else if (techname == "xilinx" || techname == "gowin") { + if (cell->type == ID(LUT1)) { + inputs = { + cell->getPort(ID(I0)), + }; + } else if (cell->type == ID(LUT2)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + }; + } else if (cell->type == ID(LUT3)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + }; + } else if (cell->type == ID(LUT4)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + }; + } else if (cell->type == ID(LUT5)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + cell->getPort(ID(I4)), + }; + } else if (cell->type == ID(LUT6)) { + inputs = { + cell->getPort(ID(I0)), + cell->getPort(ID(I1)), + cell->getPort(ID(I2)), + cell->getPort(ID(I3)), + cell->getPort(ID(I4)), + cell->getPort(ID(I5)), + }; + } else { + // Not a LUT. + continue; + } + lut = cell->getParam(ID(INIT)); + if (techname == "xilinx") + output = cell->getPort(ID(O)); + else + output = cell->getPort(ID(F)); + } else if (techname == "ecp5") { + if (cell->type == ID(LUT4)) { + inputs = { + cell->getPort(ID::A), + cell->getPort(ID::B), + cell->getPort(ID(C)), + cell->getPort(ID(D)), + }; + lut = cell->getParam(ID(INIT)); + output = cell->getPort(ID(Z)); + ignore_const = true; + } else { + // Not a LUT. + continue; + } + } + std::vector<int> swizzle; + std::vector<SigBit> new_inputs; + bool doit = false; + for (int i = 0; i < GetSize(inputs); i++) { + SigBit input = inputs[i]; + if (!input.wire) { + if (input.data == State::S1) + swizzle.push_back(-2); + else + swizzle.push_back(-1); + // For ECP5, smaller LUTs are + // implemented as LUT4s with + // extra const inputs. Do not + // consider that to be a reason + // to redo a LUT. + if (!ignore_const) + doit = true; + } else { + bool redundant = true; + for (int j = 0; j < GetSize(lut); j++) { + if (lut[j] != lut[j ^ 1 << i]) + redundant = false; + } + if (redundant) { + swizzle.push_back(-1); + doit = true; + } else { + swizzle.push_back(GetSize(new_inputs)); + new_inputs.push_back(input); + } + } + } + if (!doit) + continue; + log(" Optimizing lut %s (%d -> %d)\n", log_id(cell), GetSize(inputs), GetSize(new_inputs)); + if (techname == "ecp5") { + // Pad the LUT to 4 inputs, adding consts from the front. + int extra = 4 - GetSize(new_inputs); + log_assert(extra >= 0); + if (extra) { + for (int i = 0; i < extra; i++) + new_inputs.insert(new_inputs.begin(), State::S0); + for (auto &swz : swizzle) + if (swz >= 0) + swz += extra; + } + } + Const new_lut(0, 1 << GetSize(new_inputs)); + for (int i = 0; i < GetSize(new_lut); i++) { + int lidx = 0; + for (int j = 0; j < GetSize(inputs); j++) { + int val; + if (swizzle[j] == -2) { + val = 1; + } else if (swizzle[j] == -1) { + val = 0; + } else { + val = (i >> swizzle[j]) & 1; + } + lidx |= val << j; + } + new_lut[i] = lut[lidx]; + } + // For ecp5, do not replace with a const driver — the nextpnr + // packer requires a complete set of LUTs for wide LUT muxes. + if (new_inputs.empty() && techname != "ecp5") { + // const driver. + remove_cells.push_back(cell); + module->connect(output, new_lut[0]); + } else { + if (techname == "") { + cell->setParam(ID(LUT), new_lut); + cell->setParam(ID(WIDTH), GetSize(new_inputs)); + cell->setPort(ID::A, new_inputs); + } else if (techname == "ecp5") { + log_assert(GetSize(new_inputs) == 4); + cell->setParam(ID(INIT), new_lut); + cell->setPort(ID::A, new_inputs[0]); + cell->setPort(ID::B, new_inputs[1]); + cell->setPort(ID(C), new_inputs[2]); + cell->setPort(ID(D), new_inputs[3]); + } else { + // xilinx, gowin + cell->setParam(ID(INIT), new_lut); + if (techname == "xilinx") + log_assert(GetSize(new_inputs) <= 6); + else + log_assert(GetSize(new_inputs) <= 4); + if (GetSize(new_inputs) == 1) + cell->type = ID(LUT1); + else if (GetSize(new_inputs) == 2) + cell->type = ID(LUT2); + else if (GetSize(new_inputs) == 3) + cell->type = ID(LUT3); + else if (GetSize(new_inputs) == 4) + cell->type = ID(LUT4); + else if (GetSize(new_inputs) == 5) + cell->type = ID(LUT5); + else if (GetSize(new_inputs) == 6) + cell->type = ID(LUT6); + else + log_assert(0); + cell->unsetPort(ID(I0)); + cell->unsetPort(ID(I1)); + cell->unsetPort(ID(I2)); + cell->unsetPort(ID(I3)); + cell->unsetPort(ID(I4)); + cell->unsetPort(ID(I5)); + cell->setPort(ID(I0), new_inputs[0]); + if (GetSize(new_inputs) >= 2) + cell->setPort(ID(I1), new_inputs[1]); + if (GetSize(new_inputs) >= 3) + cell->setPort(ID(I2), new_inputs[2]); + if (GetSize(new_inputs) >= 4) + cell->setPort(ID(I3), new_inputs[3]); + if (GetSize(new_inputs) >= 5) + cell->setPort(ID(I4), new_inputs[4]); + if (GetSize(new_inputs) >= 6) + cell->setPort(ID(I5), new_inputs[5]); + } + } + } + for (auto cell : remove_cells) + module->remove(cell); + } + } +} XilinxDffOptPass; + +PRIVATE_NAMESPACE_END + diff --git a/passes/opt/opt_reduce.cc b/passes/opt/opt_reduce.cc index 6a8d8cabd..f74655d1c 100644 --- a/passes/opt/opt_reduce.cc +++ b/passes/opt/opt_reduce.cc @@ -44,9 +44,10 @@ struct OptReduceWorker cells.erase(cell); RTLIL::SigSpec sig_a = assign_map(cell->getPort(ID::A)); + sig_a.sort_and_unify(); pool<RTLIL::SigBit> new_sig_a_bits; - for (auto &bit : sig_a.to_sigbit_set()) + for (auto &bit : sig_a) { if (bit == RTLIL::State::S0) { if (cell->type == ID($reduce_and)) { @@ -86,6 +87,7 @@ struct OptReduceWorker } RTLIL::SigSpec new_sig_a(new_sig_a_bits); + new_sig_a.sort_and_unify(); if (new_sig_a != sig_a || sig_a.size() != cell->getPort(ID::A).size()) { log(" New input vector for %s cell %s: %s\n", cell->type.c_str(), cell->name.c_str(), log_signal(new_sig_a)); @@ -235,7 +237,6 @@ struct OptReduceWorker log(" New connections: %s = %s\n", log_signal(old_sig_conn.first), log_signal(old_sig_conn.second)); module->connect(old_sig_conn); - module->check(); did_something = true; total_count++; @@ -324,6 +325,8 @@ struct OptReduceWorker opt_mux(cell); } } + + module->check(); } }; diff --git a/passes/pmgen/ice40_wrapcarry.cc b/passes/pmgen/ice40_wrapcarry.cc index 6e154147f..0053c8872 100644 --- a/passes/pmgen/ice40_wrapcarry.cc +++ b/passes/pmgen/ice40_wrapcarry.cc @@ -42,11 +42,19 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm) cell->setPort("\\A", st.carry->getPort("\\I0")); cell->setPort("\\B", st.carry->getPort("\\I1")); - cell->setPort("\\CI", st.carry->getPort("\\CI")); + auto CI = st.carry->getPort("\\CI"); + cell->setPort("\\CI", CI); cell->setPort("\\CO", st.carry->getPort("\\CO")); cell->setPort("\\I0", st.lut->getPort("\\I0")); - cell->setPort("\\I3", st.lut->getPort("\\I3")); + auto I3 = st.lut->getPort("\\I3"); + if (pm.sigmap(CI) == pm.sigmap(I3)) { + cell->setParam("\\I3_IS_CI", State::S1); + I3 = State::Sx; + } + else + cell->setParam("\\I3_IS_CI", State::S0); + cell->setPort("\\I3", I3); cell->setPort("\\O", st.lut->getPort("\\O")); cell->setParam("\\LUT", st.lut->getParam("\\LUT_INIT")); @@ -118,7 +126,8 @@ struct Ice40WrapCarryPass : public Pass { auto lut = module->addCell(lut_name, ID($lut)); lut->setParam(ID(WIDTH), 4); lut->setParam(ID(LUT), cell->getParam(ID(LUT))); - lut->setPort(ID(A), {cell->getPort(ID(I0)), cell->getPort(ID(A)), cell->getPort(ID(B)), cell->getPort(ID(I3)) }); + auto I3 = cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3)); + lut->setPort(ID(A), { I3, cell->getPort(ID(B)), cell->getPort(ID(A)), cell->getPort(ID(I0)) }); lut->setPort(ID(Y), cell->getPort(ID(O))); Const src; diff --git a/passes/pmgen/xilinx_dsp.cc b/passes/pmgen/xilinx_dsp.cc index 81c3c57c4..ae7967d7c 100644 --- a/passes/pmgen/xilinx_dsp.cc +++ b/passes/pmgen/xilinx_dsp.cc @@ -767,6 +767,9 @@ struct XilinxDspPass : public Pass { log("to a maximum length of 20 cells, corresponding to the smallest Xilinx 7 Series\n"); log("device.\n"); log("\n"); + log("This pass is a no-op if the scratchpad variable 'xilinx_dsp.multonly' is set\n"); + log("to 1.\n"); + log("\n"); log("\n"); log("Experimental feature: addition/subtractions less than 12 or 24 bits with the\n"); log("'(* use_dsp=\"simd\" *)' attribute attached to the output wire or attached to\n"); @@ -805,6 +808,10 @@ struct XilinxDspPass : public Pass { family = "xcu"; for (auto module : design->selected_modules()) { + + if (design->scratchpad_get_bool("xilinx_dsp.multonly")) + continue; + // Experimental feature: pack $add/$sub cells with // (* use_dsp48="simd" *) into DSP48E1's using its // SIMD feature diff --git a/passes/sat/sat.cc b/passes/sat/sat.cc index 430bba1e8..436ac1b01 100644 --- a/passes/sat/sat.cc +++ b/passes/sat/sat.cc @@ -269,7 +269,8 @@ struct SatHelper for (int i = 0; i < lhs.size(); i++) { RTLIL::SigSpec bit = lhs.extract(i, 1); if (rhs[i] == State::Sx || !satgen.initial_state.check_all(bit)) { - removed_bits.append(bit); + if (rhs[i] != State::Sx) + removed_bits.append(bit); lhs.remove(i, 1); rhs.remove(i, 1); i--; diff --git a/passes/techmap/Makefile.inc b/passes/techmap/Makefile.inc index 369c9de64..c16db0d57 100644 --- a/passes/techmap/Makefile.inc +++ b/passes/techmap/Makefile.inc @@ -13,6 +13,7 @@ OBJS += passes/techmap/abc9_ops.o ifneq ($(ABCEXTERNAL),) passes/techmap/abc.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' passes/techmap/abc9.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' +passes/techmap/abc9_exe.o: CXXFLAGS += -DABCEXTERNAL='"$(ABCEXTERNAL)"' endif endif diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc index 41fdc8f3d..decf9a202 100644 --- a/passes/techmap/tribuf.cc +++ b/passes/techmap/tribuf.cc @@ -86,6 +86,7 @@ struct TribufWorker { cell->unsetPort(ID(S)); cell->type = tri_type; tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); + module->design->scratchpad_set_bool("tribuf.added_something", true); continue; } @@ -95,6 +96,7 @@ struct TribufWorker { cell->unsetPort(ID(S)); cell->type = tri_type; tribuf_cells[sigmap(cell->getPort(ID::Y))].push_back(cell); + module->design->scratchpad_set_bool("tribuf.added_something", true); continue; } } @@ -130,8 +132,10 @@ struct TribufWorker { if (no_tribuf) module->connect(it.first, muxout); - else + else { module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first); + module->design->scratchpad_set_bool("tribuf.added_something", true); + } } } } diff --git a/techlibs/ecp5/cells_map.v b/techlibs/ecp5/cells_map.v index 10e89a3e0..c031703a9 100644 --- a/techlibs/ecp5/cells_map.v +++ b/techlibs/ecp5/cells_map.v @@ -73,102 +73,80 @@ module \$lut (A, Y); input [WIDTH-1:0] A; output Y; - // Need to swap input ordering, and fix init accordingly, - // to match ABC's expectation of LUT inputs in non-decreasing - // delay order - localparam P_WIDTH = WIDTH < 4 ? 4 : WIDTH; - function [P_WIDTH-1:0] permute_index; - input [P_WIDTH-1:0] i; - integer j; - begin - permute_index = 0; - for (j = 0; j < P_WIDTH; j = j + 1) - permute_index[P_WIDTH-1 - j] = i[j]; - end - endfunction - - function [2**P_WIDTH-1:0] permute_init; - integer i; - begin - permute_init = 0; - for (i = 0; i < 2**P_WIDTH; i = i + 1) - permute_init[i] = LUT[permute_index(i)]; - end - endfunction - - parameter [2**P_WIDTH-1:0] P_LUT = permute_init(); - generate if (WIDTH == 1) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), + localparam [15:0] INIT = {{8{LUT[1]}}, {8{LUT[0]}}}; + LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y), .A(1'b0), .B(1'b0), .C(1'b0), .D(A[0])); end else if (WIDTH == 2) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(1'b0), .B(1'b0), .C(A[1]), .D(A[0])); + localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}}; + LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(1'b0), .C(A[0]), .D(A[1])); end else if (WIDTH == 3) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(1'b0), .B(A[2]), .C(A[1]), .D(A[0])); + localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}}; + LUT4 #(.INIT(INIT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(1'b0), .B(A[0]), .C(A[1]), .D(A[2])); end else if (WIDTH == 4) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.Z(Y), - .A(A[3]), .B(A[2]), .C(A[1]), .D(A[0])); + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Z(Y), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); `ifndef NO_PFUMUX end else if (WIDTH == 5) begin wire f0, f1; - LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), - .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1])); - LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), - .A(A[4]), .B(A[3]), .C(A[2]), .D(A[1])); - PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[0]), .Z(Y)); + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + PFUMX mux5(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(Y)); end else if (WIDTH == 6) begin wire f0, f1, f2, f3, g0, g1; - LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - - LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3), - .A(A[5]), .B(A[4]), .C(A[3]), .D(A[2])); - - PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[1]), .Z(g0)); - PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[1]), .Z(g1)); - L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[0]), .Z(Y)); + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); + L6MUX21 mux6 (.D0(g0), .D1(g1), .SD(A[5]), .Z(Y)); end else if (WIDTH == 7) begin wire f0, f1, f2, f3, f4, f5, f6, f7, g0, g1, g2, g3, h0, h1; - LUT4 #(.INIT(P_LUT[15: 0])) lut0 (.Z(f0), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[31:16])) lut1 (.Z(f1), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - LUT4 #(.INIT(P_LUT[47:32])) lut2 (.Z(f2), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[63:48])) lut3 (.Z(f3), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - LUT4 #(.INIT(P_LUT[79:64])) lut4 (.Z(f4), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[95:80])) lut5 (.Z(f5), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - LUT4 #(.INIT(P_LUT[111: 96])) lut6 (.Z(f6), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - LUT4 #(.INIT(P_LUT[127:112])) lut7 (.Z(f7), - .A(A[6]), .B(A[5]), .C(A[4]), .D(A[3])); - - PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[2]), .Z(g0)); - PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[2]), .Z(g1)); - PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[2]), .Z(g2)); - PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[2]), .Z(g3)); - L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[1]), .Z(h0)); - L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[1]), .Z(h1)); - L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[0]), .Z(Y)); + LUT4 #(.INIT(LUT[15: 0])) lut0 (.Z(f0), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[31:16])) lut1 (.Z(f1), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[47:32])) lut2 (.Z(f2), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[63:48])) lut3 (.Z(f3), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[79:64])) lut4 (.Z(f4), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[95:80])) lut5 (.Z(f5), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + LUT4 #(.INIT(LUT[111: 96])) lut6 (.Z(f6), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + LUT4 #(.INIT(LUT[127:112])) lut7 (.Z(f7), + .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3])); + + PFUMX mux50(.ALUT(f1), .BLUT(f0), .C0(A[4]), .Z(g0)); + PFUMX mux51(.ALUT(f3), .BLUT(f2), .C0(A[4]), .Z(g1)); + PFUMX mux52(.ALUT(f5), .BLUT(f4), .C0(A[4]), .Z(g2)); + PFUMX mux53(.ALUT(f7), .BLUT(f6), .C0(A[4]), .Z(g3)); + L6MUX21 mux60 (.D0(g0), .D1(g1), .SD(A[5]), .Z(h0)); + L6MUX21 mux61 (.D0(g2), .D1(g3), .SD(A[5]), .Z(h1)); + L6MUX21 mux7 (.D0(h0), .D1(h1), .SD(A[6]), .Z(Y)); `endif end else begin wire _TECHMAP_FAIL_ = 1; diff --git a/techlibs/ecp5/synth_ecp5.cc b/techlibs/ecp5/synth_ecp5.cc index 6583f43fd..bce20f604 100644 --- a/techlibs/ecp5/synth_ecp5.cc +++ b/techlibs/ecp5/synth_ecp5.cc @@ -343,6 +343,7 @@ struct SynthEcp5Pass : public ScriptPass else run("techmap -map +/ecp5/cells_map.v", "(with -D NO_LUT in vpr mode)"); + run("opt_lut_ins -tech ecp5"); run("clean"); } diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index c5b41b503..99dd3834b 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -246,6 +246,7 @@ struct SynthGowinPass : public ScriptPass if (check_label("map_cells")) { run("techmap -map +/gowin/cells_map.v"); + run("opt_lut_ins -tech gowin"); run("setundef -undriven -params -zero"); run("hilomap -singleton -hicell VCC V -locell GND G"); if (!noiopads || help_mode) diff --git a/techlibs/ice40/abc9_model.v b/techlibs/ice40/abc9_model.v index 26cf6cc22..a5e5f4372 100644 --- a/techlibs/ice40/abc9_model.v +++ b/techlibs/ice40/abc9_model.v @@ -9,6 +9,8 @@ module \$__ICE40_CARRY_WRAPPER ( input I0, I3 ); parameter LUT = 0; + parameter I3_IS_CI = 0; + wire I3_OR_CI = I3_IS_CI ? CI : I3; SB_CARRY carry ( .I0(A), .I1(B), @@ -21,7 +23,7 @@ module \$__ICE40_CARRY_WRAPPER ( .I0(I0), .I1(A), .I2(B), - .I3(I3), + .I3(I3_OR_CI), .O(O) ); endmodule diff --git a/techlibs/ice40/abc9_u.box b/techlibs/ice40/abc9_u.box index 48a51463e..3d4b93834 100644 --- a/techlibs/ice40/abc9_u.box +++ b/techlibs/ice40/abc9_u.box @@ -6,13 +6,12 @@ # Box 1 : $__ICE40_CARRY_WRAPPER (private cell used to preserve # SB_LUT4+SB_CARRY) -# Outputs: O, CO # (Exception: carry chain input/output must be the # last input and output and the entire bus has been # moved there overriding the otherwise # alphabetical ordering) # name ID w/b ins outs $__ICE40_CARRY_WRAPPER 1 1 5 2 -#A B I0 I3 CI +#A B I0 I3 CI 1231 1205 1285 874 874 # O 675 609 - - 278 # CO diff --git a/techlibs/ice40/arith_map.v b/techlibs/ice40/arith_map.v index 00a07247b..ed4140e44 100644 --- a/techlibs/ice40/arith_map.v +++ b/techlibs/ice40/arith_map.v @@ -49,13 +49,14 @@ module _80_ice40_alu (A, B, CI, BI, X, Y, CO); // A[1]: 1100 1100 1100 1100 // A[2]: 1111 0000 1111 0000 // A[3]: 1111 1111 0000 0000 - .LUT(16'b 0110_1001_1001_0110) + .LUT(16'b 0110_1001_1001_0110), + .I3_IS_CI(1'b1) ) carry ( .A(AA[i]), .B(BB[i]), .CI(C[i]), .I0(1'b0), - .I3(C[i]), + .I3(1'bx), .CO(CO[i]), .O(Y[i]) ); diff --git a/techlibs/ice40/cells_map.v b/techlibs/ice40/cells_map.v index 759549e30..d5362eb83 100644 --- a/techlibs/ice40/cells_map.v +++ b/techlibs/ice40/cells_map.v @@ -42,19 +42,18 @@ module \$lut (A, Y); .I0(1'b0), .I1(1'b0), .I2(1'b0), .I3(A[0])); end else if (WIDTH == 2) begin - localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[1]}}, {4{LUT[2]}}, {4{LUT[0]}}}; + localparam [15:0] INIT = {{4{LUT[3]}}, {4{LUT[2]}}, {4{LUT[1]}}, {4{LUT[0]}}}; SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(1'b0), .I1(1'b0), .I2(A[1]), .I3(A[0])); + .I0(1'b0), .I1(1'b0), .I2(A[0]), .I3(A[1])); end else if (WIDTH == 3) begin - localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[3]}}, {2{LUT[5]}}, {2{LUT[1]}}, {2{LUT[6]}}, {2{LUT[2]}}, {2{LUT[4]}}, {2{LUT[0]}}}; + localparam [15:0] INIT = {{2{LUT[7]}}, {2{LUT[6]}}, {2{LUT[5]}}, {2{LUT[4]}}, {2{LUT[3]}}, {2{LUT[2]}}, {2{LUT[1]}}, {2{LUT[0]}}}; SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(1'b0), .I1(A[2]), .I2(A[1]), .I3(A[0])); + .I0(1'b0), .I1(A[0]), .I2(A[1]), .I3(A[2])); end else if (WIDTH == 4) begin - localparam [15:0] INIT = {LUT[15], LUT[7], LUT[11], LUT[3], LUT[13], LUT[5], LUT[9], LUT[1], LUT[14], LUT[6], LUT[10], LUT[2], LUT[12], LUT[4], LUT[8], LUT[0]}; - SB_LUT4 #(.LUT_INIT(INIT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[3]), .I1(A[2]), .I2(A[1]), .I3(A[0])); + SB_LUT4 #(.LUT_INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), .I3(A[3])); end else begin wire _TECHMAP_FAIL_ = 1; end diff --git a/techlibs/ice40/cells_sim.v b/techlibs/ice40/cells_sim.v index 7d1b37fd6..50eab5dde 100644 --- a/techlibs/ice40/cells_sim.v +++ b/techlibs/ice40/cells_sim.v @@ -1126,6 +1126,7 @@ module SB_SPRAM256KA ( input [15:0] DATAIN, input [3:0] MASKWREN, input WREN, CHIPSELECT, CLOCK, STANDBY, SLEEP, POWEROFF, + `ABC9_ARRIVAL_U(1821) // https://github.com/cliffordwolf/icestorm/blob/95949315364f8d9b0c693386aefadf44b28e2cf6/icefuzz/timings_up5k.txt#L13207 output reg [15:0] DATAOUT ); `ifndef BLACKBOX diff --git a/techlibs/ice40/ice40_opt.cc b/techlibs/ice40/ice40_opt.cc index 9bee0444b..925ab31bb 100644 --- a/techlibs/ice40/ice40_opt.cc +++ b/techlibs/ice40/ice40_opt.cc @@ -139,7 +139,8 @@ static void run_ice40_opts(Module *module) log("Optimized $__ICE40_CARRY_WRAPPER cell back to logic (without SB_CARRY) %s.%s: CO=%s\n", log_id(module), log_id(cell), log_signal(replacement_output)); cell->type = "$lut"; - cell->setPort("\\A", { cell->getPort("\\I0"), inbit[0], inbit[1], cell->getPort("\\I3") }); + auto I3 = get_bit_or_zero(cell->getPort(cell->getParam(ID(I3_IS_CI)).as_bool() ? ID(CI) : ID(I3))); + cell->setPort("\\A", { I3, inbit[1], inbit[0], get_bit_or_zero(cell->getPort("\\I0")) }); cell->setPort("\\Y", cell->getPort("\\O")); cell->unsetPort("\\B"); cell->unsetPort("\\CI"); @@ -148,6 +149,7 @@ static void run_ice40_opts(Module *module) cell->unsetPort("\\CO"); cell->unsetPort("\\O"); cell->setParam("\\WIDTH", 4); + cell->unsetParam("\\I3_IS_CI"); } continue; } diff --git a/techlibs/intel/synth_intel.cc b/techlibs/intel/synth_intel.cc index 2ebb8bf50..3689df70e 100644 --- a/techlibs/intel/synth_intel.cc +++ b/techlibs/intel/synth_intel.cc @@ -26,7 +26,7 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN struct SynthIntelPass : public ScriptPass { - SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") {} + SynthIntelPass() : ScriptPass("synth_intel", "synthesis for Intel (Altera) FPGAs.") { experimental(); } void help() YS_OVERRIDE { diff --git a/techlibs/xilinx/cells_sim.v b/techlibs/xilinx/cells_sim.v index c8cbf8522..4692eba33 100644 --- a/techlibs/xilinx/cells_sim.v +++ b/techlibs/xilinx/cells_sim.v @@ -2309,7 +2309,235 @@ assign PCOUT = P; endmodule -// TODO: DSP48 (Virtex 4). +module DSP48 ( + input signed [17:0] A, + input signed [17:0] B, + input signed [47:0] C, + input signed [17:0] BCIN, + input signed [47:0] PCIN, + input CARRYIN, + input [6:0] OPMODE, + input SUBTRACT, + input [1:0] CARRYINSEL, + output signed [47:0] P, + output signed [17:0] BCOUT, + output signed [47:0] PCOUT, + (* clkbuf_sink *) + input CLK, + input CEA, + input CEB, + input CEC, + input CEM, + input CECARRYIN, + input CECINSUB, + input CECTRL, + input CEP, + input RSTA, + input RSTB, + input RSTC, + input RSTM, + input RSTCARRYIN, + input RSTCTRL, + input RSTP +); + +parameter integer AREG = 1; +parameter integer BREG = 1; +parameter integer CREG = 1; +parameter integer MREG = 1; +parameter integer PREG = 1; +parameter integer CARRYINREG = 1; +parameter integer CARRYINSELREG = 1; +parameter integer OPMODEREG = 1; +parameter integer SUBTRACTREG = 1; +parameter B_INPUT = "DIRECT"; +parameter LEGACY_MODE = "MULT18X18S"; + +wire signed [17:0] A_OUT; +wire signed [17:0] B_OUT; +wire signed [47:0] C_OUT; +wire signed [35:0] M_MULT; +wire signed [35:0] M_OUT; +wire signed [47:0] P_IN; +wire [6:0] OPMODE_OUT; +wire [1:0] CARRYINSEL_OUT; +wire CARRYIN_OUT; +wire SUBTRACT_OUT; +reg INT_CARRYIN_XY; +reg INT_CARRYIN_Z; +reg signed [47:0] XMUX; +reg signed [47:0] YMUX; +wire signed [47:0] XYMUX; +reg signed [47:0] ZMUX; +reg CIN; + +// The B input multiplexer. +wire signed [17:0] B_MUX; +assign B_MUX = (B_INPUT == "DIRECT") ? B : BCIN; + +// The cascade output. +assign BCOUT = B_OUT; +assign PCOUT = P; + +// The registers. +reg signed [17:0] A0_REG; +reg signed [17:0] A1_REG; +reg signed [17:0] B0_REG; +reg signed [17:0] B1_REG; +reg signed [47:0] C_REG; +reg signed [35:0] M_REG; +reg signed [47:0] P_REG; +reg [6:0] OPMODE_REG; +reg [1:0] CARRYINSEL_REG; +reg SUBTRACT_REG; +reg CARRYIN_REG; +reg INT_CARRYIN_XY_REG; + +initial begin + A0_REG = 0; + A1_REG = 0; + B0_REG = 0; + B1_REG = 0; + C_REG = 0; + M_REG = 0; + P_REG = 0; + OPMODE_REG = 0; + CARRYINSEL_REG = 0; + SUBTRACT_REG = 0; + CARRYIN_REG = 0; + INT_CARRYIN_XY_REG = 0; +end + +always @(posedge CLK) begin + if (RSTA) begin + A0_REG <= 0; + A1_REG <= 0; + end else if (CEA) begin + A0_REG <= A; + A1_REG <= A0_REG; + end + if (RSTB) begin + B0_REG <= 0; + B1_REG <= 0; + end else if (CEB) begin + B0_REG <= B_MUX; + B1_REG <= B0_REG; + end + if (RSTC) begin + C_REG <= 0; + end else if (CEC) begin + C_REG <= C; + end + if (RSTM) begin + M_REG <= 0; + end else if (CEM) begin + M_REG <= M_MULT; + end + if (RSTP) begin + P_REG <= 0; + end else if (CEP) begin + P_REG <= P_IN; + end + if (RSTCTRL) begin + OPMODE_REG <= 0; + CARRYINSEL_REG <= 0; + SUBTRACT_REG <= 0; + end else begin + if (CECTRL) begin + OPMODE_REG <= OPMODE; + CARRYINSEL_REG <= CARRYINSEL; + end + if (CECINSUB) + SUBTRACT_REG <= SUBTRACT; + end + if (RSTCARRYIN) begin + CARRYIN_REG <= 0; + INT_CARRYIN_XY_REG <= 0; + end else begin + if (CECINSUB) + CARRYIN_REG <= CARRYIN; + if (CECARRYIN) + INT_CARRYIN_XY_REG <= INT_CARRYIN_XY; + end +end + +// The register enables. +assign A_OUT = (AREG == 2) ? A1_REG : (AREG == 1) ? A0_REG : A; +assign B_OUT = (BREG == 2) ? B1_REG : (BREG == 1) ? B0_REG : B_MUX; +assign C_OUT = (CREG == 1) ? C_REG : C; +assign M_OUT = (MREG == 1) ? M_REG : M_MULT; +assign P = (PREG == 1) ? P_REG : P_IN; +assign OPMODE_OUT = (OPMODEREG == 1) ? OPMODE_REG : OPMODE; +assign SUBTRACT_OUT = (SUBTRACTREG == 1) ? SUBTRACT_REG : SUBTRACT; +assign CARRYINSEL_OUT = (CARRYINSELREG == 1) ? CARRYINSEL_REG : CARRYINSEL; +assign CARRYIN_OUT = (CARRYINREG == 1) ? CARRYIN_REG : CARRYIN; + +// The multiplier. +assign M_MULT = A_OUT * B_OUT; + +// The post-adder inputs. +always @* begin + case (OPMODE_OUT[1:0]) + 2'b00: XMUX <= 0; + 2'b10: XMUX <= P; + 2'b11: XMUX <= {{12{A_OUT[17]}}, A_OUT, B_OUT}; + default: XMUX <= 48'hxxxxxxxxxxxx; + endcase + case (OPMODE_OUT[1:0]) + 2'b01: INT_CARRYIN_XY <= A_OUT[17] ~^ B_OUT[17]; + 2'b11: INT_CARRYIN_XY <= ~A_OUT[17]; + // TODO: not tested in hardware. + default: INT_CARRYIN_XY <= A_OUT[17] ~^ B_OUT[17]; + endcase +end + +always @* begin + case (OPMODE_OUT[3:2]) + 2'b00: YMUX <= 0; + 2'b11: YMUX <= C_OUT; + default: YMUX <= 48'hxxxxxxxxxxxx; + endcase +end + +assign XYMUX = (OPMODE_OUT[3:0] == 4'b0101) ? M_OUT : (XMUX + YMUX); + +always @* begin + case (OPMODE_OUT[6:4]) + 3'b000: ZMUX <= 0; + 3'b001: ZMUX <= PCIN; + 3'b010: ZMUX <= P; + 3'b011: ZMUX <= C_OUT; + 3'b101: ZMUX <= {{17{PCIN[47]}}, PCIN[47:17]}; + 3'b110: ZMUX <= {{17{P[47]}}, P[47:17]}; + default: ZMUX <= 48'hxxxxxxxxxxxx; + endcase + // TODO: check how all this works on actual hw. + if (OPMODE_OUT[1:0] == 2'b10) + INT_CARRYIN_Z <= ~P[47]; + else + case (OPMODE_OUT[6:4]) + 3'b001: INT_CARRYIN_Z <= ~PCIN[47]; + 3'b010: INT_CARRYIN_Z <= ~P[47]; + 3'b101: INT_CARRYIN_Z <= ~PCIN[47]; + 3'b110: INT_CARRYIN_Z <= ~P[47]; + default: INT_CARRYIN_Z <= 1'bx; + endcase +end + +always @* begin + case (CARRYINSEL_OUT) + 2'b00: CIN <= CARRYIN_OUT; + 2'b01: CIN <= INT_CARRYIN_Z; + 2'b10: CIN <= INT_CARRYIN_XY; + 2'b11: CIN <= INT_CARRYIN_XY_REG; + default: CIN <= 1'bx; + endcase +end + +// The post-adder. +assign P_IN = SUBTRACT_OUT ? (ZMUX - (XYMUX + CIN)) : (ZMUX + XYMUX + CIN); + +endmodule // TODO: DSP48E (Virtex 5). diff --git a/techlibs/xilinx/cells_xtra.py b/techlibs/xilinx/cells_xtra.py index cb873a044..75646f594 100644 --- a/techlibs/xilinx/cells_xtra.py +++ b/techlibs/xilinx/cells_xtra.py @@ -249,7 +249,7 @@ CELLS = [ # Cell('MULT18X18SIO', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 3E # Cell('DSP48A', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 3A DSP # Cell('DSP48A1', port_attrs={'CLK': ['clkbuf_sink']}), # Spartan 6 - Cell('DSP48', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 4 + # Cell('DSP48', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 4 Cell('DSP48E', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 5 #Cell('DSP48E1', port_attrs={'CLK': ['clkbuf_sink']}), # Virtex 6 / Series 7 Cell('DSP48E2', port_attrs={'CLK': ['clkbuf_sink']}), # Ultrascale diff --git a/techlibs/xilinx/cells_xtra.v b/techlibs/xilinx/cells_xtra.v index beed07e0a..e87f4ec76 100644 --- a/techlibs/xilinx/cells_xtra.v +++ b/techlibs/xilinx/cells_xtra.v @@ -5492,49 +5492,6 @@ module URAM288_BASE (...); input SLEEP; endmodule -module DSP48 (...); - parameter integer AREG = 1; - parameter integer BREG = 1; - parameter B_INPUT = "DIRECT"; - parameter integer CARRYINREG = 1; - parameter integer CARRYINSELREG = 1; - parameter integer CREG = 1; - parameter LEGACY_MODE = "MULT18X18S"; - parameter integer MREG = 1; - parameter integer OPMODEREG = 1; - parameter integer PREG = 1; - parameter integer SUBTRACTREG = 1; - output [17:0] BCOUT; - output [47:0] P; - output [47:0] PCOUT; - input [17:0] A; - input [17:0] B; - input [17:0] BCIN; - input [47:0] C; - input CARRYIN; - input [1:0] CARRYINSEL; - input CEA; - input CEB; - input CEC; - input CECARRYIN; - input CECINSUB; - input CECTRL; - input CEM; - input CEP; - (* clkbuf_sink *) - input CLK; - input [6:0] OPMODE; - input [47:0] PCIN; - input RSTA; - input RSTB; - input RSTC; - input RSTCARRYIN; - input RSTCTRL; - input RSTM; - input RSTP; - input SUBTRACT; -endmodule - module DSP48E (...); parameter SIM_MODE = "SAFE"; parameter integer ACASCREG = 1; diff --git a/techlibs/xilinx/lut_map.v b/techlibs/xilinx/lut_map.v index 62d501632..718ec42f1 100644 --- a/techlibs/xilinx/lut_map.v +++ b/techlibs/xilinx/lut_map.v @@ -29,90 +29,65 @@ module \$lut (A, Y); input [WIDTH-1:0] A; output Y; - // Need to swap input ordering, and fix init accordingly, - // to match ABC's expectation of LUT inputs in non-decreasing - // delay order - function [WIDTH-1:0] permute_index; - input [WIDTH-1:0] i; - integer j; - begin - permute_index = 0; - for (j = 0; j < WIDTH; j = j + 1) - permute_index[WIDTH-1 - j] = i[j]; - end - endfunction - - function [2**WIDTH-1:0] permute_init; - input [2**WIDTH-1:0] orig; - integer i; - begin - permute_init = 0; - for (i = 0; i < 2**WIDTH; i = i + 1) - permute_init[i] = orig[permute_index(i)]; - end - endfunction - - parameter [2**WIDTH-1:0] P_LUT = permute_init(LUT); - generate if (WIDTH == 1) begin - if (P_LUT == 2'b01) begin + if (LUT == 2'b01) begin INV _TECHMAP_REPLACE_ (.O(Y), .I(A[0])); end else begin - LUT1 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), + LUT1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), .I0(A[0])); end end else if (WIDTH == 2) begin - LUT2 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[1]), .I1(A[0])); + LUT2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1])); end else if (WIDTH == 3) begin - LUT3 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[2]), .I1(A[1]), .I2(A[0])); + LUT3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2])); end else if (WIDTH == 4) begin - LUT4 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[3]), .I1(A[2]), .I2(A[1]), - .I3(A[0])); + LUT4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3])); end else if (WIDTH == 5) begin - LUT5 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[4]), .I1(A[3]), .I2(A[2]), - .I3(A[1]), .I4(A[0])); + LUT5 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4])); end else if (WIDTH == 6) begin - LUT6 #(.INIT(P_LUT)) _TECHMAP_REPLACE_ (.O(Y), - .I0(A[5]), .I1(A[4]), .I2(A[3]), - .I3(A[2]), .I4(A[1]), .I5(A[0])); + LUT6 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.O(Y), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); end else if (WIDTH == 7) begin wire T0, T1; - LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0), - .I0(A[6]), .I1(A[5]), .I2(A[4]), - .I3(A[3]), .I4(A[2]), .I5(A[1])); - LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1), - .I0(A[6]), .I1(A[5]), .I2(A[4]), - .I3(A[3]), .I4(A[2]), .I5(A[1])); - MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[0])); + LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + MUXF7 fpga_mux_0 (.O(Y), .I0(T0), .I1(T1), .S(A[6])); end else if (WIDTH == 8) begin wire T0, T1, T2, T3, T4, T5; - LUT6 #(.INIT(P_LUT[63:0])) fpga_lut_0 (.O(T0), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - LUT6 #(.INIT(P_LUT[127:64])) fpga_lut_1 (.O(T1), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - LUT6 #(.INIT(P_LUT[191:128])) fpga_lut_2 (.O(T2), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - LUT6 #(.INIT(P_LUT[255:192])) fpga_lut_3 (.O(T3), - .I0(A[7]), .I1(A[6]), .I2(A[5]), - .I3(A[4]), .I4(A[3]), .I5(A[2])); - MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[1])); - MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[1])); - MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[0])); + LUT6 #(.INIT(LUT[63:0])) fpga_lut_0 (.O(T0), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[127:64])) fpga_lut_1 (.O(T1), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[191:128])) fpga_lut_2 (.O(T2), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + LUT6 #(.INIT(LUT[255:192])) fpga_lut_3 (.O(T3), + .I0(A[0]), .I1(A[1]), .I2(A[2]), + .I3(A[3]), .I4(A[4]), .I5(A[5])); + MUXF7 fpga_mux_0 (.O(T4), .I0(T0), .I1(T1), .S(A[6])); + MUXF7 fpga_mux_1 (.O(T5), .I0(T2), .I1(T3), .S(A[6])); + MUXF8 fpga_mux_2 (.O(Y), .I0(T4), .I1(T5), .S(A[7])); end else begin wire _TECHMAP_FAIL_ = 1; end diff --git a/techlibs/xilinx/lutrams.txt b/techlibs/xilinx/lutrams.txt index 29f6b05cc..faf66bc18 100644 --- a/techlibs/xilinx/lutrams.txt +++ b/techlibs/xilinx/lutrams.txt @@ -153,7 +153,7 @@ endmatch match $__XILINX_RAM32X2Q min bits 5 - min rports 3 + min rports 2 min wports 1 make_outreg or_next_if_better @@ -161,7 +161,7 @@ endmatch match $__XILINX_RAM64X1Q min bits 5 - min rports 3 + min rports 2 min wports 1 make_outreg endmatch diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 5c3b5179d..5a28bb139 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -316,7 +316,11 @@ struct SynthXilinxPass : public ScriptPass run("proc"); if (flatten || help_mode) run("flatten", "(with '-flatten')"); + if (active_design) + active_design->scratchpad_unset("tribuf.added_something"); run("tribuf -logic"); + if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something")) + log_error("Tristate buffers are unsupported without the '-iopad' option.\n"); run("deminout"); run("opt_expr"); run("opt_clean"); @@ -526,7 +530,7 @@ struct SynthXilinxPass : public ScriptPass if (check_label("map_cells")) { // Needs to be done before logic optimization, so that inverters (OE vs T) are handled. if (help_mode || !noiopad) - run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(only if not '-noiopad')"); + run("iopadmap -bits -outpad OBUF I:O -inpad IBUF O:I -toutpad $__XILINX_TOUTPAD OE:I:O -tinoutpad $__XILINX_TINOUTPAD OE:O:I:IO A:top", "(skip if '-noiopad')"); std::string techmap_args = "-map +/techmap.v -map +/xilinx/cells_map.v"; if (widemux > 0) techmap_args += stringf(" -D MIN_MUX_INPUTS=%d", widemux); @@ -589,17 +593,16 @@ struct SynthXilinxPass : public ScriptPass if (!nosrl || help_mode) run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')"); std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v"; - if (help_mode) - techmap_args += stringf("[-map %s]", ff_map_file.c_str()); - else if (!abc9) + if (help_mode || !abc9) techmap_args += stringf(" -map %s", ff_map_file.c_str()); - run("techmap " + techmap_args, "(only if '-abc9')"); + run("techmap " + techmap_args); run("xilinx_dffopt"); + run("opt_lut_ins -tech xilinx"); } if (check_label("finalize")) { if (help_mode || !noclkbuf) - run("clkbufmap -buf BUFG O:I ", "(skip if '-noclkbuf')"); + run("clkbufmap -buf BUFG O:I", "(skip if '-noclkbuf')"); if (help_mode || ise) run("extractinv -inv INV O:I", "(only if '-ise')"); run("clean"); diff --git a/techlibs/xilinx/tests/.gitignore b/techlibs/xilinx/tests/.gitignore index 848f88d53..0d9c28fde 100644 --- a/techlibs/xilinx/tests/.gitignore +++ b/techlibs/xilinx/tests/.gitignore @@ -12,4 +12,7 @@ test_dsp48a_model_ref.v test_dsp48a1_model_ref.v test_dsp48a1_model_uut.v test_dsp48a1_model +test_dsp48_model_ref.v +test_dsp48_model_uut.v +test_dsp48_model *.vcd diff --git a/techlibs/xilinx/tests/test_dsp48_model.sh b/techlibs/xilinx/tests/test_dsp48_model.sh new file mode 100644 index 000000000..9a73f9b0c --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp48_model.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -ex +if [ -z $ISE_DIR ]; then + ISE_DIR=/opt/Xilinx/ISE/14.7 +fi +sed 's/DSP48 /DSP48_UUT /; /DSP48_UUT/,/endmodule/ p; d;' < ../cells_sim.v > test_dsp48_model_uut.v +if [ ! -f "test_dsp48_model_ref.v" ]; then + cp $ISE_DIR/ISE_DS/ISE/verilog/src/unisims/DSP48.v test_dsp48_model_ref.v +fi +for tb in mult_allreg mult_noreg mult_inreg +do + iverilog -s $tb -s glbl -o test_dsp48_model test_dsp48_model.v test_dsp48_model_uut.v test_dsp48_model_ref.v $ISE_DIR/ISE_DS/ISE/verilog/src/glbl.v + vvp -N ./test_dsp48_model +done diff --git a/techlibs/xilinx/tests/test_dsp48_model.v b/techlibs/xilinx/tests/test_dsp48_model.v new file mode 100644 index 000000000..d69c00e93 --- /dev/null +++ b/techlibs/xilinx/tests/test_dsp48_model.v @@ -0,0 +1,287 @@ +`timescale 1ns / 1ps + +module testbench; + parameter integer AREG = 1; + parameter integer BREG = 1; + parameter integer CREG = 1; + parameter integer MREG = 1; + parameter integer PREG = 1; + parameter integer CARRYINREG = 1; + parameter integer CARRYINSELREG = 1; + parameter integer OPMODEREG = 1; + parameter integer SUBTRACTREG = 1; + parameter B_INPUT = "DIRECT"; + parameter LEGACY_MODE = "NONE"; + + reg CLK; + reg CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL; + reg RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL; + reg [17:0] A; + reg [17:0] B; + reg [47:0] C; + reg [17:0] BCIN; + reg [47:0] PCIN; + reg CARRYIN; + reg [6:0] OPMODE; + reg SUBTRACT; + reg [1:0] CARRYINSEL; + + output [47:0] P, REF_P; + output [17:0] BCOUT, REF_BCOUT; + output [47:0] PCOUT, REF_PCOUT; + + integer errcount = 0; + + reg ERROR_FLAG = 0; + + task clkcycle; + begin + #5; + CLK = ~CLK; + #10; + CLK = ~CLK; + #2; + ERROR_FLAG = 0; + if (REF_BCOUT !== BCOUT) begin + $display("ERROR at %1t: REF_BCOUT=%b UUT_BCOUT=%b DIFF=%b", $time, REF_BCOUT, BCOUT, REF_BCOUT ^ BCOUT); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + if (REF_P !== P) begin + $display("ERROR at %1t: REF_P=%b UUT_P=%b DIFF=%b", $time, REF_P, P, REF_P ^ P); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + if (REF_PCOUT !== PCOUT) begin + $display("ERROR at %1t: REF_PCOUT=%b UUT_PCOUT=%b DIFF=%b", $time, REF_PCOUT, PCOUT, REF_PCOUT ^ PCOUT); + errcount = errcount + 1; + ERROR_FLAG = 1; + end + #3; + end + endtask + + reg config_valid = 0; + task drc; + begin + config_valid = 1; + + if (OPMODE[1:0] == 2'b10 && PREG != 1) config_valid = 0; + if (OPMODE[1:0] == 2'b00 && CARRYINSEL == 2'b10) config_valid = 0; + if (OPMODE[1:0] == 2'b10 && CARRYINSEL == 2'b10) config_valid = 0; + if (OPMODE[1:0] == 2'b00 && CARRYINSEL == 2'b11) config_valid = 0; + if (OPMODE[1:0] == 2'b10 && CARRYINSEL == 2'b11) config_valid = 0; + if (OPMODE[3:2] == 2'b10) config_valid = 0; + if ((OPMODE[3:2] == 2'b01) ^ (OPMODE[1:0] == 2'b01) == 1'b1) config_valid = 0; + if ((OPMODE[6:4] == 3'b010 || OPMODE[6:4] == 3'b110) && PREG != 1) config_valid = 0; + if (OPMODE[6:4] == 3'b100) config_valid = 0; + if (OPMODE[6:4] == 3'b111) config_valid = 0; + if (OPMODE[6:4] == 3'b000 && CARRYINSEL == 2'b01) config_valid = 0; + if (OPMODE[6:4] == 3'b011 && CARRYINSEL == 2'b01) config_valid = 0; + + // Xilinx models consider these combinations invalid for an unknown reason. + if (CARRYINSEL == 2'b01 && OPMODE[3:2] == 2'b00) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0000011) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0000101) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0100011) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b0111111) config_valid = 0; + if (CARRYINSEL == 2'b10 && OPMODE == 7'b1100011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0000011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0000101) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0011111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0010011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0100011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0100101) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0101111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0110011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b0111111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1010011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1011111) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1100011) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1100101) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE == 7'b1101111) config_valid = 0; + + if (CARRYINSEL == 2'b10 && OPMODE[3:0] == 4'b0101 && MREG == 1) config_valid = 0; + if (CARRYINSEL == 2'b11 && OPMODE[3:0] == 4'b0101 && MREG == 0) config_valid = 0; + end + endtask + + initial begin + $dumpfile("test_dsp48_model.vcd"); + $dumpvars(0, testbench); + + #2; + CLK = 1'b0; + {CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL} = 8'b11111111; + {A, B, C, PCIN, OPMODE, SUBTRACT, CARRYIN, CARRYINSEL} = 0; + {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = 7'b1111111; + repeat (10) begin + #10; + CLK = 1'b1; + #10; + CLK = 1'b0; + #10; + CLK = 1'b1; + #10; + CLK = 1'b0; + end + {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = 0; + + repeat (100000) begin + clkcycle; + config_valid = 0; + while (!config_valid) begin + A = $urandom; + B = $urandom; + C = {$urandom, $urandom}; + BCIN = $urandom; + PCIN = {$urandom, $urandom}; + + {CEA, CEB, CEC, CEM, CEP, CECARRYIN, CECINSUB, CECTRL} = $urandom | $urandom | $urandom; + {RSTA, RSTB, RSTC, RSTM, RSTP, RSTCARRYIN, RSTCTRL} = $urandom & $urandom & $urandom & $urandom & $urandom & $urandom; + {CARRYIN, CARRYINSEL, OPMODE, SUBTRACT} = $urandom; + + drc; + end + end + + if (errcount == 0) begin + $display("All tests passed."); + $finish; + end else begin + $display("Caught %1d errors.", errcount); + $stop; + end + end + + DSP48 #( + .AREG (AREG), + .BREG (BREG), + .CREG (CREG), + .MREG (MREG), + .PREG (PREG), + .CARRYINREG (CARRYINREG), + .CARRYINSELREG (CARRYINSELREG), + .OPMODEREG (OPMODEREG), + .SUBTRACTREG (SUBTRACTREG), + .B_INPUT (B_INPUT), + .LEGACY_MODE (LEGACY_MODE) + ) ref ( + .A (A), + .B (B), + .C (C), + .BCIN (BCIN), + .PCIN (PCIN), + .CARRYIN (CARRYIN), + .OPMODE (OPMODE), + .SUBTRACT (SUBTRACT), + .CARRYINSEL (CARRYINSEL), + .BCOUT (REF_BCOUT), + .P (REF_P), + .PCOUT (REF_PCOUT), + .CEA (CEA), + .CEB (CEB), + .CEC (CEC), + .CEM (CEM), + .CEP (CEP), + .CECARRYIN (CECARRYIN), + .CECINSUB (CECINSUB), + .CECTRL (CECTRL), + .CLK (CLK), + .RSTA (RSTA), + .RSTB (RSTB), + .RSTC (RSTC), + .RSTM (RSTM), + .RSTP (RSTP), + .RSTCARRYIN (RSTCARRYIN), + .RSTCTRL (RSTCTRL) + ); + + DSP48_UUT #( + .AREG (AREG), + .BREG (BREG), + .CREG (CREG), + .MREG (MREG), + .PREG (PREG), + .CARRYINREG (CARRYINREG), + .CARRYINSELREG (CARRYINSELREG), + .OPMODEREG (OPMODEREG), + .SUBTRACTREG (SUBTRACTREG), + .B_INPUT (B_INPUT), + .LEGACY_MODE (LEGACY_MODE) + ) uut ( + .A (A), + .B (B), + .C (C), + .BCIN (BCIN), + .PCIN (PCIN), + .CARRYIN (CARRYIN), + .OPMODE (OPMODE), + .SUBTRACT (SUBTRACT), + .CARRYINSEL (CARRYINSEL), + .BCOUT (BCOUT), + .P (P), + .PCOUT (PCOUT), + .CEA (CEA), + .CEB (CEB), + .CEC (CEC), + .CEM (CEM), + .CEP (CEP), + .CECARRYIN (CECARRYIN), + .CECINSUB (CECINSUB), + .CECTRL (CECTRL), + .CLK (CLK), + .RSTA (RSTA), + .RSTB (RSTB), + .RSTC (RSTC), + .RSTM (RSTM), + .RSTP (RSTP), + .RSTCARRYIN (RSTCARRYIN), + .RSTCTRL (RSTCTRL) + ); +endmodule + +module mult_noreg; + testbench #( + .AREG (0), + .BREG (0), + .CREG (0), + .MREG (0), + .PREG (0), + .CARRYINREG (0), + .CARRYINSELREG (0), + .OPMODEREG (0), + .SUBTRACTREG (0), + .B_INPUT ("DIRECT") + ) testbench (); +endmodule + +module mult_allreg; + testbench #( + .AREG (1), + .BREG (1), + .CREG (1), + .MREG (1), + .PREG (1), + .CARRYINREG (1), + .CARRYINSELREG (1), + .OPMODEREG (1), + .SUBTRACTREG (1), + .B_INPUT ("CASCADE") + ) testbench (); +endmodule + +module mult_inreg; + testbench #( + .AREG (1), + .BREG (1), + .CREG (1), + .MREG (0), + .PREG (0), + .CARRYINREG (1), + .CARRYINSELREG (0), + .OPMODEREG (0), + .SUBTRACTREG (0), + .B_INPUT ("DIRECT") + ) testbench (); +endmodule diff --git a/tests/arch/ecp5/opt_lut_ins.ys b/tests/arch/ecp5/opt_lut_ins.ys new file mode 100644 index 000000000..2bc546912 --- /dev/null +++ b/tests/arch/ecp5/opt_lut_ins.ys @@ -0,0 +1,32 @@ +read_ilang << EOF + +module \top + + wire input 1 \A + wire input 2 \B + wire input 3 \C + wire input 4 \D + + wire output 5 \Z + + cell \LUT4 $0 + parameter \INIT 16'1111110011000000 + connect \A \A + connect \B \B + connect \C \C + connect \D \D + connect \Z \Z + end +end + +EOF + +read_verilog -lib +/ecp5/cells_sim.v + +equiv_opt -assert -map +/ecp5/cells_sim.v opt_lut_ins -tech ecp5 + +design -load postopt + +select -assert-count 1 top/t:LUT4 +select -assert-count 0 top/w:A %co top/t:LUT4 %i +select -assert-count 1 top/w:B %co top/t:LUT4 %i diff --git a/tests/arch/efinix/mux.ys b/tests/arch/efinix/mux.ys index b46f641e1..a5ab80d8b 100644 --- a/tests/arch/efinix/mux.ys +++ b/tests/arch/efinix/mux.ys @@ -36,6 +36,6 @@ proc equiv_opt -assert -map +/efinix/cells_sim.v synth_efinix # 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 12 t:EFX_LUT4 +select -assert-max 12 t:EFX_LUT4 select -assert-none t:EFX_LUT4 %% t:* %D diff --git a/tests/arch/gowin/mux.ys b/tests/arch/gowin/mux.ys index afad29a89..33b092284 100644 --- a/tests/arch/gowin/mux.ys +++ b/tests/arch/gowin/mux.ys @@ -18,13 +18,13 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux4 # Constrain all select calls below inside the top module -select -assert-count 4 t:LUT4 +select -assert-count 4 t:LUT* select -assert-count 2 t:MUX2_LUT5 select -assert-count 1 t:MUX2_LUT6 select -assert-count 6 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D design -load read hierarchy -top mux8 @@ -35,7 +35,7 @@ cd mux8 # Constrain all select calls below inside the top module select -assert-count 11 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D design -load read hierarchy -top mux16 @@ -46,4 +46,4 @@ cd mux16 # Constrain all select calls below inside the top module select -assert-count 20 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT4 t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% t:* %D +select -assert-none t:GND t:VCC t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:MUX2_LUT6 t:MUX2_LUT7 t:MUX2_LUT8 t:IBUF t:OBUF %% t:* %D diff --git a/tests/arch/ice40/bug1597.ys b/tests/arch/ice40/bug1597.ys new file mode 100644 index 000000000..b7983cfa4 --- /dev/null +++ b/tests/arch/ice40/bug1597.ys @@ -0,0 +1,72 @@ +read_verilog <<EOT +module top ( + input CLK, PIN_1, PIN_2, PIN_3, PIN_4, PIN_5, + PIN_6, PIN_7, PIN_8, PIN_9, PIN_10, PIN_11, PIN_12, PIN_13, PIN_25, + output USBPU, PIN_14, PIN_15, PIN_16, PIN_17, PIN_18, + PIN_19, PIN_20, PIN_21, PIN_22, PIN_23, PIN_24, +); + assign USBPU = 0; + + wire[5:0] parOut; + wire[5:0] chrg; + + assign PIN_14 = parOut[0]; + assign PIN_15 = parOut[1]; + assign PIN_16 = parOut[2]; + assign PIN_17 = parOut[3]; + assign PIN_18 = parOut[4]; + assign PIN_19 = parOut[5]; + assign chrg[0] = PIN_3; + assign chrg[1] = PIN_4; + assign chrg[2] = PIN_5; + assign chrg[3] = PIN_6; + assign chrg[4] = PIN_7; + assign chrg[5] = PIN_8; + + SSCounter6o sc6(PIN_1, CLK, PIN_2, PIN_9, chrg, parOut); + +endmodule + +module SSCounter6 (input wire rst, clk, adv, jmp, input wire [5:0] in, output reg[5:0] out); + always @(posedge clk, posedge rst) + if (rst) out <= 0; + else if (adv || jmp) out <= jmp ? in : out + 1; +endmodule + +// Optimized 6 bit counter, it should takes 7 cells. +/* b[5:1] /* b[0] +1010101010101010 in 1010101010101010 in +1100110011001100 jmp 1100110011001100 jmp +1111000011110000 loop 1111000011110000 loop +1111111100000000 carry 1111111100000000 - +---------------------- ---------------------- +1000101110111000 out 1000101110001011 out + 8 B B 8 8 B 8 B +*/ +module SSCounter6o (input wire rst, clk, adv, jmp, input wire [5:0] in, output wire[5:0] out); + wire[4:0] co; + wire[5:0] lo; + wire ien; + SB_LUT4 #(.LUT_INIT(16'hFFF0)) lien (ien, 0, 0, adv, jmp); + SB_CARRY c0 (co[0], jmp, out[0], 1), + c1 (co[1], jmp, out[1], co[0]), + c2 (co[2], jmp, out[2], co[1]), + c3 (co[3], jmp, out[3], co[2]), + c4 (co[4], jmp, out[4], co[3]); + SB_DFFER d0 (out[0], clk, ien, rst, lo[0]), + d1 (out[1], clk, ien, rst, lo[1]), + d2 (out[2], clk, ien, rst, lo[2]), + d3 (out[3], clk, ien, rst, lo[3]), + d4 (out[4], clk, ien, rst, lo[4]), + d5 (out[5], clk, ien, rst, lo[5]); + SB_LUT4 #(.LUT_INIT(16'h8B8B)) l0 (lo[0], in[0], jmp, out[0], 0); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l1 (lo[1], in[1], jmp, out[1], co[0]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l2 (lo[2], in[2], jmp, out[2], co[1]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l3 (lo[3], in[3], jmp, out[3], co[2]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l4 (lo[4], in[4], jmp, out[4], co[3]); + SB_LUT4 #(.LUT_INIT(16'h8BB8)) l5 (lo[5], in[5], jmp, out[5], co[4]); +endmodule +EOT +hierarchy -top top +flatten +equiv_opt -multiclock -map +/ice40/cells_sim.v synth_ice40 diff --git a/tests/arch/ice40/ice40_opt.ys b/tests/arch/ice40/ice40_opt.ys index 5186d4800..71b68431e 100644 --- a/tests/arch/ice40/ice40_opt.ys +++ b/tests/arch/ice40/ice40_opt.ys @@ -1,24 +1,4 @@ read_verilog -icells -formal <<EOT -module \$__ICE40_CARRY_WRAPPER (output CO, O, input A, B, CI, I0, I3); - parameter LUT = 0; - SB_CARRY carry ( - .I0(A), - .I1(B), - .CI(CI), - .CO(CO) - ); - \$lut #( - .WIDTH(4), - .LUT(LUT) - ) lut ( - .A({I0,A,B,I3}), - .Y(O) - ); -endmodule -EOT -design -stash unmap - -read_verilog -icells -formal <<EOT module top(input CI, I0, output [1:0] CO, output O); wire A = 1'b0, B = 1'b0; \$__ICE40_CARRY_WRAPPER #( @@ -26,13 +6,14 @@ module top(input CI, I0, output [1:0] CO, output O); // A[1]: 1100 1100 1100 1100 // A[2]: 1111 0000 1111 0000 // A[3]: 1111 1111 0000 0000 - .LUT(~16'b 0110_1001_1001_0110) + .LUT(~16'b 0110_1001_1001_0110), + .I3_IS_CI(1'b1) ) u0 ( .A(A), .B(B), .CI(CI), .I0(I0), - .I3(CI), + .I3(1'bx), .CO(CO[0]), .O(O) ); @@ -40,7 +21,7 @@ module top(input CI, I0, output [1:0] CO, output O); endmodule EOT -equiv_opt -assert -map %unmap -map +/ice40/cells_sim.v ice40_opt +equiv_opt -assert -map +/ice40/abc9_model.v -map +/ice40/cells_sim.v ice40_opt design -load postopt select -assert-count 1 t:* select -assert-count 1 t:$lut @@ -105,3 +86,33 @@ select -assert-count 1 t:SB_LUT4 select -assert-count 1 t:SB_CARRY select -assert-count 1 t:SB_CARRY a:keep %i select -assert-count 1 t:SB_CARRY c:carry %i + + +design -reset +read_verilog -icells <<EOT +module top(input I3, I2, I1, I0, output O, O2); + SB_LUT4 #( + .LUT_INIT(8'b 1001_0110) + ) u0 ( + .I0(I0), + .I1(I1), + .I2(I2), + .I3(), + .O(O) + ); + wire CO; + \$__ICE40_CARRY_WRAPPER #( + .LUT(~8'b 1001_0110), + .I3_IS_CI(1'b0) + ) u1 ( + .A(1'b0), + .B(1'b0), + .CI(1'b0), + .I0(), + .I3(), + .CO(CO), + .O(O2) + ); +endmodule +EOT +ice40_opt diff --git a/tests/arch/xilinx/opt_lut_ins.ys b/tests/arch/xilinx/opt_lut_ins.ys new file mode 100644 index 000000000..a01d02179 --- /dev/null +++ b/tests/arch/xilinx/opt_lut_ins.ys @@ -0,0 +1,25 @@ +read_ilang << EOF + +module \top + + wire width 4 input 1 \A + + wire output 2 \O + + cell \LUT4 $0 + parameter \INIT 16'1111110011000000 + connect \I0 \A [0] + connect \I1 \A [1] + connect \I2 \A [2] + connect \I3 \A [3] + connect \O \O + end +end + +EOF + +equiv_opt -assert -map +/xilinx/cells_sim.v opt_lut_ins -tech xilinx + +design -load postopt + +select -assert-count 1 t:LUT3 diff --git a/tests/arch/xilinx/tribuf.sh b/tests/arch/xilinx/tribuf.sh new file mode 100644 index 000000000..636aed12a --- /dev/null +++ b/tests/arch/xilinx/tribuf.sh @@ -0,0 +1,5 @@ +! ../../../yosys ../common/tribuf.v -qp "synth_xilinx" +../../../yosys ../common/tribuf.v -qp "synth_xilinx -iopad; \ +select -assert-count 2 t:IBUF; \ +select -assert-count 1 t:INV; \ +select -assert-count 1 t:OBUFT" diff --git a/tests/opt/opt_lut_ins.ys b/tests/opt/opt_lut_ins.ys new file mode 100644 index 000000000..82460b164 --- /dev/null +++ b/tests/opt/opt_lut_ins.ys @@ -0,0 +1,23 @@ +read_ilang << EOF + +module \top + + wire width 4 input 1 \A + + wire output 2 \Y + + cell $lut \lut + parameter \LUT 16'1111110011000000 + parameter \WIDTH 4 + connect \A \A + connect \Y \Y + end +end + +EOF + +equiv_opt -assert opt_lut_ins + +design -load postopt + +select -assert-count 1 t:$lut r:WIDTH=3 %i diff --git a/tests/sat/initval.ys b/tests/sat/initval.ys index 2079d2f34..1436724b0 100644 --- a/tests/sat/initval.ys +++ b/tests/sat/initval.ys @@ -2,3 +2,14 @@ read_verilog -sv initval.v proc;; sat -seq 10 -prove-asserts + +design -reset +read_verilog -icells <<EOT +module top(input clk, i, output [1:0] o); +(* init = 2'bx0 *) +wire [1:0] o; +assign o[1] = o[0]; +$_DFF_P_ dff (.C(clk), .D(i), .Q(o[0])); +endmodule +EOT +sat -seq 1 diff --git a/tests/various/help.ys b/tests/various/help.ys new file mode 100644 index 000000000..9283ce8f1 --- /dev/null +++ b/tests/various/help.ys @@ -0,0 +1,2 @@ +help -all +help -celltypes diff --git a/tests/various/sformatf.ys b/tests/various/sformatf.ys new file mode 100644 index 000000000..66d6b0dbe --- /dev/null +++ b/tests/various/sformatf.ys @@ -0,0 +1,12 @@ +read_verilog <<EOT + +module top; + localparam a = $sformatf("0x%x", 8'h5A); + localparam b = $sformatf("%d", 4'b011); + generate + if (a != "0x5a") $error("a incorrect!"); + if (b != "3") $error("b incorrect!"); + endgenerate +endmodule + +EOT diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh new file mode 100755 index 000000000..9a01447f7 --- /dev/null +++ b/tests/various/sv_implicit_ports.sh @@ -0,0 +1,124 @@ +#!/bin/bash + +trap 'echo "ERROR in sv_implicit_ports.sh" >&2; exit 1' ERR + +# Simple case +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + wire [7:0] b = 8'd42; + add add_i(.*); +endmodule +EOT + +# Generate block +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); +assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + generate + if (1) begin:ablock + wire [7:0] b = 8'd42; + add add_i(.*); + end + endgenerate +endmodule +EOT + +# Missing wire +((../../yosys -f "verilog -sv" -qp "hierarchy -top top" - || true) <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add add_i(.*); +endmodule +EOT +) 2>&1 | grep -F "ERROR: No matching wire for implicit port connection \`b' of cell top.add_i (add)." > /dev/null + +# Incorrectly sized wire +((../../yosys -f "verilog -sv" -qp "hierarchy -top top" - || true) <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + wire [6:0] b = 6'd42; + add add_i(.*); +endmodule +EOT +) 2>&1 | grep -F "ERROR: Width mismatch between wire (7 bits) and port (8 bits) for implicit port connection \`b' of cell top.add_i (add)." > /dev/null + +# Defaults +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a = 8'd00, input [7:0] b = 8'd01, output [7:0] q); +assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add add_i(.*); +endmodule +EOT + +# Parameterised module +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add #(parameter N=3) (input [N-1:0] a = 8'd00, input [N-1:0] b = 8'd01, output [N-1:0] q); +assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add #(.N(8)) add_i(.*); +endmodule +EOT + +# Parameterised blackbox module +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:add" - <<EOT +(* blackbox *) +module add #(parameter N=3) (input [N-1:0] a, b, output [N-1:0] q); +endmodule + +module top(input [7:0] a, b, output [7:0] q); + add #(.N(8)) add_i(.*); +endmodule +EOT + +# Parameterised blackbox module - incorrect width +((../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:add" - || true) <<EOT +(* blackbox *) +module add #(parameter N=3) (input [N-1:0] a, b, output [N-1:0] q); +endmodule + +module top(input [7:0] a, b, output [7:0] q); + add #(.N(6)) add_i(.*); +endmodule +EOT +) 2>&1 | grep -F "ERROR: Width mismatch between wire (8 bits) and port (6 bits) for implicit port connection \`q' of cell top.add_i (add)." > /dev/null + +# Mixed implicit and explicit 1 +../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, output [7:0] q); + add add_i(.b(8'd42), .*); +endmodule +EOT + +# Mixed implicit and explicit 2 +(../../yosys -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <<EOT +module add(input [7:0] a, input [7:0] b, output [7:0] q); + assign q = a + b; +endmodule + +module top(input [7:0] a, input [9:0] b, output [7:0] q); + add add_i(.b, .*); +endmodule +EOT +) 2>&1 | grep -F "Warning: Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null |