From 22c967e35e23d0688081818f49a11f0ec0853bb1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 19 Jan 2020 21:15:51 +0000 Subject: [PATCH 01/11] ast: Add support for $sformatf system function Signed-off-by: David Shah --- frontends/ast/ast.h | 1 + frontends/ast/simplify.cc | 202 ++++++++++++++++++++------------------ tests/various/sformatf.ys | 12 +++ 3 files changed, 122 insertions(+), 93 deletions(-) create mode 100644 tests/various/sformatf.ys 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 &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/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 < Date: Sat, 1 Feb 2020 10:21:19 +0100 Subject: [PATCH 02/11] json: remove the 32-bit parameter special case Before, the rules for encoding parameters in JSON were as follows: - if the parameter is not a string: - if it is exactly 32 bits long and there are no z or x bits, emit it as an int - otherwise, emit it as a string made of 0/1/x/z characters - if the parameter is a string: - if it contains only 0/1/x/z characters, append a space at the end to distinguish it from a non-string - otherwise, emit it directly However, this caused a problem in the json11 parser used in nextpnr: yosys emits unsigned ints, and nextpnr parses them as signed, using the value of INT_MIN for values that overflow the signed int range. This caused destruction of LUT5 initialization values. Since both nextpnr and yosys parser can also accept 32-bit parameters in the same encoding as other widths, let's just remove that special case. The old behavior is still left behind a `-compat-int` flag, in case someone relies on it. --- backends/json/json.cc | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) 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 sigids; pool 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 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()) { From 65716c998272704c057d846076dc3258c74f5a34 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 27 Jan 2020 11:19:27 +0000 Subject: [PATCH 03/11] xilinx_dsp: Add multonly scratchpad var to bypass Signed-off-by: David Shah --- passes/pmgen/xilinx_dsp.cc | 7 +++++++ 1 file changed, 7 insertions(+) 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 From b44d0e041f09216dd90dccd3f18f146b1dfb7e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Ko=C5=9Bcielnicki?= Date: Sun, 2 Feb 2020 11:26:00 +0100 Subject: [PATCH 04/11] xilinx: use RAM32M/RAM64M for memories with two read ports This fixes inefficient LUT RAM usage for memories with one write and two read ports (commonly used as register files). --- techlibs/xilinx/lutrams.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 50f86c11b2bb9e561f5a0cf10e053b1aa4918abd Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 08:24:01 +0000 Subject: [PATCH 05/11] sv: Add lexing and parsing of .* (wildcard port conns) Signed-off-by: David Shah --- frontends/verilog/verilog_lexer.l | 2 ++ frontends/verilog/verilog_parser.y | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index ca23df3e8..39520bd51 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_AUTOCONNECT_ALL; } + [-+]?[=*]> { 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..cb413e13a 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_AUTOCONNECT_ALL %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,9 @@ cell_port: node->children.back()->str = *$3; delete $3; free_attr($1); + } | + attr TOK_AUTOCONNECT_ALL { + astbuf2->attributes[ID(autoconnect)] = AstNode::mkconst_int(1, false); }; always_comb_or_latch: From 5df591c02309c086229029808c21ab8721278888 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 09:04:54 +0000 Subject: [PATCH 06/11] hierarchy: Resolve SV wildcard port connections Signed-off-by: David Shah --- frontends/verilog/verilog_parser.y | 2 +- passes/hierarchy/hierarchy.cc | 65 ++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index cb413e13a..5ec8e66a6 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -1582,7 +1582,7 @@ cell_port: free_attr($1); } | attr TOK_AUTOCONNECT_ALL { - astbuf2->attributes[ID(autoconnect)] = AstNode::mkconst_int(1, false); + astbuf2->attributes[ID(implicit_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..0704c2651 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,6 +983,55 @@ struct HierarchyPass : public Pass { } } + // Process SV implicit port connections + std::set blackbox_derivatives; + std::vector design_modules = design->modules(); + + for (auto module : design_modules) + { + for (auto cell : module->cells()) + { + if (!cell->get_bool_attribute(ID(implicit_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()); + 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(implicit_port_conns)); + } + } + if (!nodefaults) { dict> defaults_db; @@ -1000,9 +1062,6 @@ struct HierarchyPass : public Pass { } } - std::set blackbox_derivatives; - std::vector design_modules = design->modules(); - for (auto module : design_modules) { pool wand_wor_index; From a210675d71b30e97bad728d7f418c14ea0eb28ba Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 09:16:37 +0000 Subject: [PATCH 07/11] sv: Add tests for wildcard port connections Signed-off-by: David Shah --- tests/various/sv_implicit_ports.sh | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 tests/various/sv_implicit_ports.sh diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh new file mode 100755 index 000000000..13d39cf8b --- /dev/null +++ b/tests/various/sv_implicit_ports.sh @@ -0,0 +1,56 @@ +#!/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" - <&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) <&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 From 7e741714df62338a2037d24721ef99ca8a0c6763 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 09:21:35 +0000 Subject: [PATCH 08/11] hierarchy: Correct handling of wildcard port connections with default values Signed-off-by: David Shah --- passes/hierarchy/hierarchy.cc | 21 ++++++++++++++------- tests/various/sv_implicit_ports.sh | 11 +++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 0704c2651..c298a6600 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -983,6 +983,15 @@ struct HierarchyPass : public Pass { } } + // Determine default values + dict> defaults_db; + if (!nodefaults) + { + 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 port connections std::set blackbox_derivatives; std::vector design_modules = design->modules(); @@ -1019,6 +1028,11 @@ struct HierarchyPass : public Pass { 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)); @@ -1034,13 +1048,6 @@ struct HierarchyPass : public Pass { if (!nodefaults) { - dict> 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"); - for (auto module : design->modules()) for (auto cell : module->cells()) { diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh index 13d39cf8b..2faac2e85 100755 --- a/tests/various/sv_implicit_ports.sh +++ b/tests/various/sv_implicit_ports.sh @@ -54,3 +54,14 @@ module top(input [7:0] a, output [7:0] q); 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" - < Date: Fri, 22 Nov 2019 12:57:51 +0000 Subject: [PATCH 09/11] sv: More tests for wildcard port connections Signed-off-by: David Shah --- tests/various/sv_implicit_ports.sh | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh index 2faac2e85..9a01447f7 100755 --- a/tests/various/sv_implicit_ports.sh +++ b/tests/various/sv_implicit_ports.sh @@ -65,3 +65,60 @@ 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" - <&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" - <&1 | grep -F "Warning: Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null From 4bfd2ef4f328b4a95918ed3e0d7a7e38406c4ae8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 15:07:55 +0000 Subject: [PATCH 10/11] sv: Improve handling of wildcard port connections Signed-off-by: David Shah --- frontends/verilog/verilog_lexer.l | 2 +- frontends/verilog/verilog_parser.y | 8 +++++--- passes/hierarchy/hierarchy.cc | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 39520bd51..9b43c250e 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -431,7 +431,7 @@ import[ \t\r\n]+\"(DPI|DPI-C)\"[ \t\r\n]+function[ \t\r\n]+ { "+:" { return TOK_POS_INDEXED; } "-:" { return TOK_NEG_INDEXED; } -".*" { return TOK_AUTOCONNECT_ALL; } +".*" { return TOK_WILDCARD_CONNECT; } [-+]?[=*]> { if (!specify_mode) REJECT; diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 5ec8e66a6..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 TOK_AUTOCONNECT_ALL +%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 @@ -1581,8 +1581,10 @@ cell_port: delete $3; free_attr($1); } | - attr TOK_AUTOCONNECT_ALL { - astbuf2->attributes[ID(implicit_port_conns)] = AstNode::mkconst_int(1, false); + 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 c298a6600..fa4a8ea29 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -992,7 +992,7 @@ struct HierarchyPass : public Pass { if (wire->port_input && wire->attributes.count("\\defaultvalue")) defaults_db[module->name][wire->name] = wire->attributes.at("\\defaultvalue"); } - // Process SV implicit port connections + // Process SV implicit wildcard port connections std::set blackbox_derivatives; std::vector design_modules = design->modules(); @@ -1000,7 +1000,7 @@ struct HierarchyPass : public Pass { { for (auto cell : module->cells()) { - if (!cell->get_bool_attribute(ID(implicit_port_conns))) + if (!cell->get_bool_attribute(ID(wildcard_port_conns))) continue; Module *m = design->module(cell->type); @@ -1042,7 +1042,7 @@ struct HierarchyPass : public Pass { 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(implicit_port_conns)); + cell->attributes.erase(ID(wildcard_port_conns)); } } From 0488492ad269df9641ab317eac5568353dd61076 Mon Sep 17 00:00:00 2001 From: David Shah Date: Fri, 22 Nov 2019 15:32:46 +0000 Subject: [PATCH 11/11] Update CHANGELOG and README Signed-off-by: David Shah --- CHANGELOG | 1 + README.md | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 481ba266e..241fba9e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -55,6 +55,7 @@ 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" diff --git a/README.md b/README.md index 77e9410da..327d407f9 100644 --- a/README.md +++ b/README.md @@ -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