diff options
-rw-r--r-- | CHANGELOG | 4 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | backends/json/json.cc | 38 | ||||
-rw-r--r-- | frontends/ast/ast.h | 1 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 202 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 7 | ||||
-rw-r--r-- | passes/hierarchy/hierarchy.cc | 76 | ||||
-rw-r--r-- | passes/pmgen/xilinx_dsp.cc | 7 | ||||
-rw-r--r-- | techlibs/xilinx/lutrams.txt | 4 | ||||
-rw-r--r-- | tests/various/sformatf.ys | 12 | ||||
-rwxr-xr-x | tests/various/sv_implicit_ports.sh | 124 |
12 files changed, 369 insertions, 112 deletions
@@ -53,7 +53,9 @@ Yosys 0.9 .. Yosys 0.9-dev - Added support for flip-flops with synchronous reset to synth_xilinx - Added support for flip-flops with reset and enable to synth_xilinx - Added "check -mapped" - - Added checking of SystemVerilog always block types (always_comb, always_latch and always_ff) + - 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" @@ -387,6 +387,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/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/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 1b6618cd8..52f157c6e 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/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/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/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/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/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 |