verilog: significant block scoping improvements
authorZachary Snow <zach@zachjs.com>
Wed, 27 Jan 2021 18:30:22 +0000 (13:30 -0500)
committerZachary Snow <zach@zachjs.com>
Sun, 31 Jan 2021 14:42:09 +0000 (09:42 -0500)
This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.

Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.

1. Unlabled generate blocks are now implicitly named according to the LRM in
   `label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
   synthetic unnamed generate block to avoid creating extra hierarchy levels
   where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
   of the topmost scope, which is necessary because such wires and cells often
   appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
   invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
   scope, completely deferring the inspection and elaboration of nested scopes;
   names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
   to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
   in largely the same manner as other blocks
     before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
      after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
   than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
   prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
   or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode

Addresses the following issues: 656, 2423, 2493

33 files changed:
frontends/ast/ast.h
frontends/ast/simplify.cc
frontends/verilog/verilog_parser.y
kernel/rtlil.h
passes/techmap/techmap.cc
techlibs/common/cmp2lcu.v
techlibs/common/cmp2lut.v
techlibs/common/mul2dsp.v
techlibs/ice40/brams_map.v
techlibs/xilinx/arith_map.v
tests/simple/func_block.v [new file with mode: 0644]
tests/simple/func_recurse.v [new file with mode: 0644]
tests/simple/func_width_scope.v [new file with mode: 0644]
tests/simple/genblk_collide.v [new file with mode: 0644]
tests/simple/genblk_dive.v [new file with mode: 0644]
tests/simple/genblk_order.v [new file with mode: 0644]
tests/simple/generate.v
tests/simple/local_loop_var.sv [new file with mode: 0644]
tests/simple/loop_var_shadow.v [new file with mode: 0644]
tests/simple/named_genblk.v [new file with mode: 0644]
tests/simple/nested_genblk_resolve.v [new file with mode: 0644]
tests/simple/unnamed_block_decl.sv [new file with mode: 0644]
tests/various/gen_if_null.v
tests/various/gen_if_null.ys
tests/verilog/bug2493.ys [new file with mode: 0644]
tests/verilog/bug656.v [new file with mode: 0644]
tests/verilog/bug656.ys [new file with mode: 0644]
tests/verilog/genblk_case.v [new file with mode: 0644]
tests/verilog/genblk_case.ys [new file with mode: 0644]
tests/verilog/hidden_decl.ys [new file with mode: 0644]
tests/verilog/unnamed_block.ys [new file with mode: 0644]
tests/verilog/unnamed_genblk.sv [new file with mode: 0644]
tests/verilog/unnamed_genblk.ys [new file with mode: 0644]

index 90739216679569362917423e57ec37a1e02f0aa1..6f173ca22f2f11c9bae6cbdf50f0b06040adfe74 100644 (file)
@@ -252,8 +252,8 @@ namespace AST
                bool simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param);
                void replace_result_wire_name_in_function(const std::string &from, const std::string &to);
                AstNode *readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init);
-               void expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope = true);
-               void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules);
+               void expand_genblock(const std::string &prefix);
+               void label_genblks(std::set<std::string>& existing, int &counter);
                void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places,
                                dict<AstNode*, uint32_t> &mem2reg_flags, dict<AstNode*, uint32_t> &proc_flags, uint32_t &status_flags);
                bool mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block);
index fc2976c83e8833a7f9d22496dbfe1d8b218cf774..77911e96620b23742e8f05997246c8a4fb836105 100644 (file)
@@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var)
        return true;
 }
 
+static std::string prefix_id(const std::string &prefix, const std::string &str)
+{
+       log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\'));
+       log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\'));
+       log_assert(prefix.back() == '.');
+       if (str.front() == '\\')
+               return prefix + str.substr(1);
+       return prefix + str;
+}
+
 // 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().
@@ -748,6 +758,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")
        if (type == AST_MODULE) {
                current_scope.clear();
+               std::set<std::string> existing;
+               int counter = 0;
+               label_genblks(existing, counter);
                std::map<std::string, AstNode*> this_wire_scope;
                for (size_t i = 0; i < children.size(); i++) {
                        AstNode *node = children[i];
@@ -1855,19 +1868,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
 
                        // expand body
                        int index = varbuf->children[0]->integer;
-                       if (body_ast->type == AST_GENBLOCK)
-                               buf = body_ast->clone();
-                       else
-                               buf = new AstNode(AST_GENBLOCK, body_ast->clone());
-                       if (buf->str.empty()) {
-                               std::stringstream sstr;
-                               sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++);
-                               buf->str = sstr.str();
-                       }
-                       std::map<std::string, std::string> name_map;
+                       log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK);
+                       log_assert(!body_ast->str.empty());
+                       buf = body_ast->clone();
+
                        std::stringstream sstr;
                        sstr << buf->str << "[" << index << "].";
-                       buf->expand_genblock(varbuf->str, sstr.str(), name_map);
+                       std::string prefix = sstr.str();
+
+                       // create a scoped localparam for the current value of the loop variable
+                       AstNode *local_index = varbuf->clone();
+                       size_t pos = local_index->str.rfind('.');
+                       if (pos != std::string::npos) // remove outer prefix
+                               local_index->str = "\\" + local_index->str.substr(pos + 1);
+                       local_index->str = prefix_id(prefix, local_index->str);
+                       current_scope[local_index->str] = local_index;
+                       current_ast_mod->children.push_back(local_index);
+
+                       buf->expand_genblock(prefix);
 
                        if (type == AST_GENFOR) {
                                for (size_t i = 0; i < buf->children.size(); i++) {
@@ -1915,14 +1933,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        {
                for (size_t i = 0; i < children.size(); i++)
                        if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF)
-                               log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n");
+                       {
+                               log_assert(!VERILOG_FRONTEND::sv_mode);
+                               log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n");
+                       }
        }
 
        // transform block with name
        if (type == AST_BLOCK && !str.empty())
        {
-               std::map<std::string, std::string> name_map;
-               expand_genblock(std::string(), str + ".", name_map);
+               expand_genblock(str + ".");
 
                std::vector<AstNode*> new_children;
                for (size_t i = 0; i < children.size(); i++)
@@ -1942,8 +1962,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
        if (type == AST_GENBLOCK && children.size() != 0)
        {
                if (!str.empty()) {
-                       std::map<std::string, std::string> name_map;
-                       expand_genblock(std::string(), str + ".", name_map);
+                       expand_genblock(str + ".");
                }
 
                for (size_t i = 0; i < children.size(); i++) {
@@ -1979,8 +1998,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                                buf = new AstNode(AST_GENBLOCK, buf);
 
                        if (!buf->str.empty()) {
-                               std::map<std::string, std::string> name_map;
-                               buf->expand_genblock(std::string(), buf->str + ".", name_map);
+                               buf->expand_genblock(buf->str + ".");
                        }
 
                        for (size_t i = 0; i < buf->children.size(); i++) {
@@ -2058,8 +2076,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage,
                        buf = selected_case->clone();
 
                        if (!buf->str.empty()) {
-                               std::map<std::string, std::string> name_map;
-                               buf->expand_genblock(std::string(), buf->str + ".", name_map);
+                               buf->expand_genblock(buf->str + ".");
                        }
 
                        for (size_t i = 0; i < buf->children.size(); i++) {
@@ -3159,12 +3176,16 @@ skip_dynamic_range_lvalue_expansion:;
                                log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str());
                }
 
-               AstNode *decl = current_scope[str];
 
                std::stringstream sstr;
-               sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$";
+               sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.';
                std::string prefix = sstr.str();
 
+               AstNode *decl = current_scope[str];
+               decl = decl->clone();
+               decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion
+               decl->expand_genblock(prefix);
+
                bool recommend_const_eval = false;
                bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval);
                if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype))
@@ -3177,11 +3198,11 @@ skip_dynamic_range_lvalue_expansion:;
                        }
 
                        if (all_args_const) {
-                               AstNode *func_workspace = current_scope[str]->clone();
-                               func_workspace->str = NEW_ID.str();
-                               func_workspace->replace_result_wire_name_in_function(str, func_workspace->str);
+                               AstNode *func_workspace = decl->clone();
+                               func_workspace->str = prefix_id(prefix, "$result");
                                newNode = func_workspace->eval_const_function(this);
                                delete func_workspace;
+                               delete decl;
                                goto apply_newNode;
                        }
 
@@ -3192,8 +3213,6 @@ skip_dynamic_range_lvalue_expansion:;
                }
 
                size_t arg_count = 0;
-               std::map<std::string, std::string> replace_rules;
-               vector<AstNode*> added_mod_children;
                dict<std::string, AstNode*> wire_cache;
                vector<AstNode*> new_stmts;
                vector<AstNode*> output_assignments;
@@ -3203,16 +3222,17 @@ skip_dynamic_range_lvalue_expansion:;
                        log_assert(type == AST_FCALL);
 
                        AstNode *wire = NULL;
+                       std::string res_name = prefix_id(prefix, "$result");
                        for (auto child : decl->children)
-                               if (child->type == AST_WIRE && child->str == str)
+                               if (child->type == AST_WIRE && child->str == res_name)
                                        wire = child->clone();
                        log_assert(wire != NULL);
 
-                       wire->str = prefix + str;
                        wire->port_id = 0;
                        wire->is_input = false;
                        wire->is_output = false;
 
+                       current_scope[wire->str] = wire;
                        current_ast_mod->children.push_back(wire);
                        while (wire->simplify(true, false, false, 1, -1, false, false)) { }
 
@@ -3256,7 +3276,6 @@ skip_dynamic_range_lvalue_expansion:;
                                if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))
                                {
                                        AstNode *wire = child->clone();
-                                       wire->str = prefix + wire->str;
                                        wire->port_id = 0;
                                        wire->is_input = false;
                                        wire->is_output = false;
@@ -3318,7 +3337,6 @@ skip_dynamic_range_lvalue_expansion:;
                                else
                                {
                                        wire = child->clone();
-                                       wire->str = prefix + wire->str;
                                        wire->port_id = 0;
                                        wire->is_input = false;
                                        wire->is_output = false;
@@ -3329,15 +3347,11 @@ skip_dynamic_range_lvalue_expansion:;
 
                                        wire_cache[child->str] = wire;
 
+                                       current_scope[wire->str] = wire;
                                        current_ast_mod->children.push_back(wire);
-                                       added_mod_children.push_back(wire);
                                }
 
-                               if (child->type == AST_WIRE)
-                                       while (wire->simplify(true, false, false, 1, -1, false, false)) { }
-
-                               replace_rules[child->str] = wire->str;
-                               current_scope[wire->str] = wire;
+                               while (wire->simplify(true, false, false, 1, -1, false, false)) { }
 
                                if ((child->is_input || child->is_output) && arg_count < children.size())
                                {
@@ -3381,18 +3395,9 @@ skip_dynamic_range_lvalue_expansion:;
                                }
                        }
 
-               for (auto child : added_mod_children) {
-                       child->replace_ids(prefix, replace_rules);
-                       while (child->simplify(true, false, false, 1, -1, false, false)) { }
-               }
-
                for (auto child : decl->children)
                        if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM)
-                       {
-                               AstNode *stmt = child->clone();
-                               stmt->replace_ids(prefix, replace_rules);
-                               new_stmts.push_back(stmt);
-                       }
+                               new_stmts.push_back(child->clone());
 
                new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end());
 
@@ -3405,10 +3410,11 @@ skip_dynamic_range_lvalue_expansion:;
                }
 
        replace_fcall_with_id:
+               delete decl;
                if (type == AST_FCALL) {
                        delete_children();
                        type = AST_IDENTIFIER;
-                       str = prefix + str;
+                       str = prefix_id(prefix, "$result");
                }
                if (type == AST_TCALL)
                        str = "";
@@ -3859,63 +3865,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
        return block;
 }
 
-// annotate the names of all wires and other named objects in a generate block
-void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope)
+// annotate the names of all wires and other named objects in a named generate
+// or procedural block; nested blocks are themselves annotated such that the
+// prefix is carried forward, but resolution of their children is deferred
+void AstNode::expand_genblock(const std::string &prefix)
 {
-       // `original_scope` defaults to false, and is used to prevent the premature
-       // prefixing of items in named sub-blocks
-
-       if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) {
-               if (children.empty()) {
-                       current_scope[index_var]->children[0]->cloneInto(this);
-               } else {
-                       AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone());
-                       p->str = stringf("$genval$%d", autoidx++);
-                       current_ast_mod->children.push_back(p);
-                       str = p->str;
-                       id2ast = p;
-               }
-       }
-
        if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) {
-               if (name_map.count(str) > 0) {
-                       str = name_map[str];
-               } else {
-                       // remap the prefix of this ident if it is a local generate scope
-                       size_t pos = str.rfind('.');
-                       if (pos != std::string::npos) {
-                               std::string existing_prefix = str.substr(0, pos);
-                               if (name_map.count(existing_prefix) > 0) {
-                                       str = name_map[existing_prefix] + str.substr(pos);
-                               }
+               log_assert(!str.empty());
+
+               // search starting in the innermost scope and then stepping outward
+               for (size_t ppos = prefix.size() - 1; ppos; --ppos) {
+                       if (prefix.at(ppos) != '.') continue;
+
+                       std::string new_prefix = prefix.substr(0, ppos + 1);
+                       auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string {
+                               std::string new_name = prefix_id(new_prefix, ident);
+                               if (current_scope.count(new_name))
+                                       return new_name;
+                               return {};
+                       };
+
+                       // attempt to resolve the full identifier
+                       std::string resolved = attempt_resolve(str);
+                       if (!resolved.empty()) {
+                               str = resolved;
+                               break;
                        }
-               }
-       }
 
-       std::map<std::string, std::string> backup_name_map;
-
-       auto prefix_node = [&](AstNode* child) {
-               if (backup_name_map.size() == 0)
-                       backup_name_map = name_map;
+                       // attempt to resolve hierarchical prefixes within the identifier,
+                       // as the prefix could refer to a local scope which exists but
+                       // hasn't yet been elaborated
+                       for (size_t spos = str.size() - 1; spos; --spos) {
+                               if (str.at(spos) != '.') continue;
+                               resolved = attempt_resolve(str.substr(0, spos));
+                               if (!resolved.empty()) {
+                                       str = resolved + str.substr(spos);
+                                       ppos = 1; // break outer loop
+                                       break;
+                               }
+                       }
 
-               // if within a nested scope
-               if (!original_scope) {
-                       // this declaration shadows anything in the parent scope(s)
-                       name_map[child->str] = child->str;
-                       return;
                }
+       }
 
-               std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix;
-               size_t pos = child->str.rfind('.');
-               if (pos == std::string::npos)
-                       pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0;
-               else
-                       pos = pos + 1;
-               new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos);
-               if (new_name[0] != '$' && new_name[0] != '\\')
-                       new_name = prefix[0] + new_name;
-
-               name_map[child->str] = new_name;
+       auto prefix_node = [&prefix](AstNode* child) {
+               if (child->str.empty()) return;
+               std::string new_name = prefix_id(prefix, child->str);
                if (child->type == AST_FUNCTION)
                        child->replace_result_wire_name_in_function(child->str, new_name);
                else
@@ -3967,43 +3962,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
                        continue;
                // functions/tasks may reference wires, constants, etc. in this scope
                if (child->type == AST_FUNCTION || child->type == AST_TASK)
-                       child->expand_genblock(index_var, prefix, name_map, false);
-               // continue prefixing if this child block is anonymous
-               else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK)
-                       child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty());
-               else
-                       child->expand_genblock(index_var, prefix, name_map, original_scope);
-       }
-
+                       continue;
+               // named blocks pick up the current prefix and will expanded later
+               if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty())
+                       continue;
 
-       if (backup_name_map.size() > 0)
-               name_map.swap(backup_name_map);
+               child->expand_genblock(prefix);
+       }
 }
 
-// rename stuff (used when tasks of functions are instantiated)
-void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules)
+// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or
+// IEEE 1800-2017 Section 27.6
+void AstNode::label_genblks(std::set<std::string>& existing, int &counter)
 {
-       if (type == AST_BLOCK)
-       {
-               std::map<std::string, std::string> new_rules = rules;
-               std::string new_prefix = prefix + str;
-
-               for (auto child : children)
-                       if (child->type == AST_WIRE) {
-                               new_rules[child->str] = new_prefix + child->str;
-                               child->str = new_prefix + child->str;
-                       }
+       switch (type) {
+       case AST_GENIF:
+       case AST_GENFOR:
+       case AST_GENCASE:
+               // seeing a proper generate control flow construct increments the
+               // counter once
+               ++counter;
+               for (AstNode *child : children)
+                       child->label_genblks(existing, counter);
+               break;
 
-               for (auto child : children)
-                       if (child->type != AST_WIRE)
-                               child->replace_ids(new_prefix, new_rules);
+       case AST_GENBLOCK: {
+               // if this block is unlabeled, generate its corresponding unique name
+               for (int padding = 0; str.empty(); ++padding) {
+                       std::string candidate = "\\genblk";
+                       for (int i = 0; i < padding; ++i)
+                               candidate += '0';
+                       candidate += std::to_string(counter);
+                       if (!existing.count(candidate))
+                               str = candidate;
+               }
+               // within a genblk, the counter starts fresh
+               std::set<std::string> existing_local = existing;
+               int counter_local = 0;
+               for (AstNode *child : children)
+                       child->label_genblks(existing_local, counter_local);
+               break;
        }
-       else
-       {
-               if (type == AST_IDENTIFIER && rules.count(str) > 0)
-                       str = rules.at(str);
-               for (auto child : children)
-                       child->replace_ids(prefix, rules);
+
+       default:
+               // track names which could conflict with implicit genblk names
+               if (str.rfind("\\genblk", 0) == 0)
+                       existing.insert(str);
+               for (AstNode *child : children)
+                       child->label_genblks(existing, counter);
+               break;
        }
 }
 
@@ -4773,6 +4780,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall)
 
                if (stmt->type == AST_BLOCK)
                {
+                       if (!stmt->str.empty())
+                               stmt->expand_genblock(stmt->str + ".");
+
                        block->children.erase(block->children.begin());
                        block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());
                        stmt->children.clear();
index 8bd58d24c8cb0ec089c3a09ebdb6a0ecbf6655d7..6255a4204d0b748c9dc7fe0607ac057adef3c224 100644 (file)
@@ -770,6 +770,7 @@ module_body:
        module_body module_body_stmt |
        /* the following line makes the generate..endgenrate keywords optional */
        module_body gen_stmt |
+       module_body gen_block |
        module_body ';' |
        %empty;
 
@@ -2459,6 +2460,16 @@ behavioral_stmt:
                exitTypeScope();
                if ($4 != NULL && $8 != NULL && *$4 != *$8)
                        frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1);
+               AstNode *node = ast_stack.back();
+               // In SystemVerilog, unnamed blocks with block item declarations
+               // create an implicit hierarchy scope
+               if (sv_mode && node->str.empty())
+                   for (const AstNode* child : node->children)
+                       if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER
+                               || child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) {
+                           node->str = "$unnamed_block$" + std::to_string(autoidx++);
+                           break;
+                       }
                SET_AST_NODE_LOC(ast_stack.back(), @2, @8);
                delete $4;
                delete $8;
@@ -2473,6 +2484,7 @@ behavioral_stmt:
                ast_stack.back()->children.push_back($7);
        } ';' simple_behavioral_stmt ')' {
                AstNode *block = new AstNode(AST_BLOCK);
+               block->str = "$for_loop$" + std::to_string(autoidx++);
                ast_stack.back()->children.push_back(block);
                ast_stack.push_back(block);
        } behavioral_stmt {
@@ -2722,6 +2734,7 @@ single_arg:
 
 module_gen_body:
        module_gen_body gen_stmt_or_module_body_stmt |
+       module_gen_body gen_block |
        %empty;
 
 gen_stmt_or_module_body_stmt:
@@ -2747,12 +2760,7 @@ gen_stmt:
                ast_stack.back()->children.push_back(node);
                ast_stack.push_back(node);
                ast_stack.back()->children.push_back($3);
-               AstNode *block = new AstNode(AST_GENBLOCK);
-               ast_stack.back()->children.push_back(block);
-               ast_stack.push_back(block);
-       } gen_stmt_block {
-               ast_stack.pop_back();
-       } opt_gen_else {
+       } gen_stmt_block opt_gen_else {
                SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
                ast_stack.pop_back();
        } |
@@ -2765,6 +2773,18 @@ gen_stmt:
                SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
                ast_stack.pop_back();
        } |
+       TOK_MSG_TASKS {
+               AstNode *node = new AstNode(AST_TECALL);
+               node->str = *$1;
+               delete $1;
+               ast_stack.back()->children.push_back(node);
+               ast_stack.push_back(node);
+       } opt_arg_list ';'{
+               SET_AST_NODE_LOC(ast_stack.back(), @1, @3);
+               ast_stack.pop_back();
+       };
+
+gen_block:
        TOK_BEGIN {
                enterTypeScope();
        } opt_label {
@@ -2778,18 +2798,9 @@ gen_stmt:
                delete $7;
                SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
                ast_stack.pop_back();
-       } |
-       TOK_MSG_TASKS {
-               AstNode *node = new AstNode(AST_TECALL);
-               node->str = *$1;
-               delete $1;
-               ast_stack.back()->children.push_back(node);
-               ast_stack.push_back(node);
-       } opt_arg_list ';'{
-               SET_AST_NODE_LOC(ast_stack.back(), @1, @3);
-               ast_stack.pop_back();
        };
 
+// result is wrapped in a genblock only if necessary
 gen_stmt_block:
        {
                AstNode *node = new AstNode(AST_GENBLOCK);
@@ -2798,7 +2809,7 @@ gen_stmt_block:
        } gen_stmt_or_module_body_stmt {
                SET_AST_NODE_LOC(ast_stack.back(), @2, @2);
                ast_stack.pop_back();
-       };
+       } | gen_block;
 
 opt_gen_else:
        TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN;
index cd966b815222440601e25db982cee9aaadf3850f..4dad3c428739ccf96baacc708fe27d933a39e436 100644 (file)
@@ -334,6 +334,10 @@ namespace RTLIL
                        return compare(size()-len, len, suffix) == 0;
                }
 
+               bool contains(const char* str) const {
+                       return strstr(c_str(), str);
+               }
+
                size_t size() const {
                        return strlen(c_str());
                }
index d43737c8dc0174342feb410ed79342aa22cae496..96843d7108db1bf16661ac941ae885ee717c6248 100644 (file)
@@ -118,19 +118,14 @@ struct TechmapWorker
                        return result;
 
                for (auto w : module->wires()) {
-                       const char *p = w->name.c_str();
-                       if (*p == '$')
+                       if (*w->name.c_str() == '$')
                                continue;
 
-                       const char *q = strrchr(p+1, '.');
-                       if (q)
-                               p = q;
-
-                       if (!strncmp(p, "\\_TECHMAP_", 10)) {
+                       if (w->name.contains("_TECHMAP_") && !w->name.contains("_TECHMAP_REPLACE_")) {
                                TechmapWireData record;
                                record.wire = w;
                                record.value = w;
-                               result[p].push_back(record);
+                               result[w->name].push_back(record);
                                w->set_bool_attribute(ID::keep);
                                w->set_bool_attribute(ID::_techmap_special_);
                        }
@@ -165,7 +160,7 @@ struct TechmapWorker
 
                orig_cell_name = cell->name.str();
                for (auto tpl_cell : tpl->cells())
-                       if (tpl_cell->name == ID::_TECHMAP_REPLACE_) {
+                       if (tpl_cell->name.ends_with("_TECHMAP_REPLACE_")) {
                                module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str());
                                break;
                        }
@@ -226,8 +221,8 @@ struct TechmapWorker
                        }
                        design->select(module, w);
 
-                       if (tpl_w->name.begins_with("\\_TECHMAP_REPLACE_.")) {
-                               IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), tpl_w->name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
+                       if (const char *p = strstr(tpl_w->name.c_str(), "_TECHMAP_REPLACE_.")) {
+                               IdString replace_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_"));
                                Wire *replace_w = module->addWire(replace_name, tpl_w);
                                module->connect(replace_w, w);
                        }
@@ -327,12 +322,12 @@ struct TechmapWorker
                for (auto tpl_cell : tpl->cells())
                {
                        IdString c_name = tpl_cell->name;
-                       bool techmap_replace_cell = (c_name == ID::_TECHMAP_REPLACE_);
+                       bool techmap_replace_cell = c_name.ends_with("_TECHMAP_REPLACE_");
 
                        if (techmap_replace_cell)
                                c_name = orig_cell_name;
-                       else if (tpl_cell->name.begins_with("\\_TECHMAP_REPLACE_."))
-                               c_name = stringf("%s%s", orig_cell_name.c_str(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_"));
+                       else if (const char *p = strstr(tpl_cell->name.c_str(), "_TECHMAP_REPLACE_."))
+                               c_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_"));
                        else
                                apply_prefix(cell->name, c_name);
 
@@ -730,12 +725,16 @@ struct TechmapWorker
                                                for (auto &it : twd)
                                                        techmap_wire_names.insert(it.first);
 
-                                               for (auto &it : twd[ID::_TECHMAP_FAIL_]) {
-                                                       RTLIL::SigSpec value = it.value;
-                                                       if (value.is_fully_const() && value.as_bool()) {
-                                                               log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n",
-                                                                               derived_name.c_str(), log_id(it.wire->name), log_signal(value));
-                                                               techmap_do_cache[tpl] = false;
+                                               for (auto &it : twd) {
+                                                       if (!it.first.ends_with("_TECHMAP_FAIL_"))
+                                                               continue;
+                                                       for (const TechmapWireData &elem : it.second) {
+                                                               RTLIL::SigSpec value = elem.value;
+                                                               if (value.is_fully_const() && value.as_bool()) {
+                                                                       log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n",
+                                                                                       derived_name.c_str(), log_id(elem.wire->name), log_signal(value));
+                                                                       techmap_do_cache[tpl] = false;
+                                                               }
                                                        }
                                                }
 
@@ -744,7 +743,7 @@ struct TechmapWorker
 
                                                for (auto &it : twd)
                                                {
-                                                       if (!it.first.begins_with("\\_TECHMAP_DO_") || it.second.empty())
+                                                       if (!it.first.contains("_TECHMAP_DO_") || it.second.empty())
                                                                continue;
 
                                                        auto &data = it.second.front();
@@ -756,7 +755,7 @@ struct TechmapWorker
 
                                                        const char *p = data.wire->name.c_str();
                                                        const char *q = strrchr(p+1, '.');
-                                                       q = q ? q : p+1;
+                                                       q = q ? q+1 : p+1;
 
                                                        std::string cmd_string = data.value.as_const().decode_string();
 
@@ -873,7 +872,7 @@ struct TechmapWorker
 
                                        TechmapWires twd = techmap_find_special_wires(tpl);
                                        for (auto &it : twd) {
-                                               if (it.first != ID::_TECHMAP_FAIL_ && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.begins_with("\\_TECHMAP_DO_") && !it.first.begins_with("\\_TECHMAP_DONE_"))
+                                               if (!it.first.ends_with("_TECHMAP_FAIL_") && (!it.first.begins_with("\\_TECHMAP_REMOVEINIT_") || !it.first.ends_with("_")) && !it.first.contains("_TECHMAP_DO_") && !it.first.contains("_TECHMAP_DONE_"))
                                                        log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first));
                                                if (techmap_do_cache[tpl])
                                                        for (auto &it2 : it.second)
index a221727e7744dc72e305211dfee7711966cc2646..4e62039e9d4b7366fc8bacda6a2e162e42439eae 100644 (file)
@@ -41,10 +41,7 @@ generate
         wire [WIDTH-1:0] BB = {{(WIDTH-B_WIDTH){B_SIGNED ? B[B_WIDTH-1] : 1'b0}}, B};
         // For $ge operation, start with the assumption that A and B are
         //   equal (propagating this equality if A and B turn out to be so)
-        if (_TECHMAP_CELLTYPE_ == "$ge")
-            localparam CI = 1'b1;
-        else
-            localparam CI = 1'b0;
+        localparam CI = _TECHMAP_CELLTYPE_ == "$ge";
         $__CMP2LCU #(.AB_WIDTH(WIDTH), .AB_SIGNED(A_SIGNED && B_SIGNED), .LCU_WIDTH(1), .BUDGET(`LUT_WIDTH), .CI(CI))
             _TECHMAP_REPLACE_ (.A(AA), .B(BB), .P(1'b1), .G(1'b0), .Y(Y));
     end
@@ -81,12 +78,12 @@ generate
         assign Y = CO[LCU_WIDTH-1];
     end
     else begin
-        if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0])
-            localparam COST = 0;
-        else if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0])
-            localparam COST = 1;
-        else
-            localparam COST = 2;
+        localparam COST =
+            _TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]
+            ? 0
+            : (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]
+                ? 1
+                : 2);
 
         if (BUDGET < COST)
              $__CMP2LCU #(.AB_WIDTH(AB_WIDTH), .AB_SIGNED(AB_SIGNED), .LCU_WIDTH(LCU_WIDTH+1), .BUDGET(`LUT_WIDTH), .CI(CI))
@@ -104,21 +101,21 @@ generate
                 //   from MSB down, deferring to less significant bits if the
                 //   MSBs are equal
                 assign GG = P[0] & (A[AB_WIDTH-1] & ~B[AB_WIDTH-1]);
+            (* force_downto *)
+            wire [LCU_WIDTH-1:0] P_, G_;
             if (LCU_WIDTH == 1) begin
                 // Propagate only if all pairs are equal
                 //   (inconclusive evidence to say A >= B)
-                wire P_ = P[0] & PP;
+                assign P_ = P[0] & PP;
                 // Generate if any comparisons call for it
-                wire G_ = G[0] | GG;
+                assign G_ = G[0] | GG;
             end
             else begin
                 // Propagate only if all pairs are equal
                 //   (inconclusive evidence to say A >= B)
-                (* force_downto *)
-                wire [LCU_WIDTH-1:0] P_ = {P[LCU_WIDTH-1:1], P[0] & PP};
+                assign P_ = {P[LCU_WIDTH-1:1], P[0] & PP};
                 // Generate if any comparisons call for it
-                (* force_downto *)
-                wire [LCU_WIDTH-1:0] G_ = {G[LCU_WIDTH-1:1], G[0] | GG};
+                assign G_ = {G[LCU_WIDTH-1:1], G[0] | GG};
             end
             if (AB_WIDTH == 1)
                $__CMP2LCU #(.AB_WIDTH(AB_WIDTH-1), .AB_SIGNED(1'b0), .LCU_WIDTH(LCU_WIDTH), .BUDGET(BUDGET-COST), .CI(CI))
index ec8f98e8d75734b8e2a8888a976c1a6427d2a386..c753bd2f1b7b0b2e1860386130c8996b8ed7de0f 100644 (file)
@@ -66,14 +66,12 @@ function automatic [(1 << `LUT_WIDTH)-1:0] gen_lut;
 endfunction
 
 generate
-       if (_TECHMAP_CELLTYPE_ == "$lt")
-               localparam operation = 0;
-       if (_TECHMAP_CELLTYPE_ == "$le")
-               localparam operation = 1;
-       if (_TECHMAP_CELLTYPE_ == "$gt")
-               localparam operation = 2;
-       if (_TECHMAP_CELLTYPE_ == "$ge")
-               localparam operation = 3;
+       localparam operation =
+               _TECHMAP_CELLTYPE_ == "$lt" ? 0 :
+               _TECHMAP_CELLTYPE_ == "$le" ? 1 :
+               _TECHMAP_CELLTYPE_ == "$gt" ? 2 :
+               _TECHMAP_CELLTYPE_ == "$ge" ? 3 :
+               -1;
 
        if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1)
                wire _TECHMAP_FAIL_ = 1;
index bec47d01f9c2b0c26a91504dad8075e1a1170452..f22f47b4a54de68a6e41e0ca8c28d0f670461c0a 100644 (file)
@@ -121,7 +121,7 @@ module _80_mul (A, B, Y);
                        localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL);\r
                        localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);\r
                        localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;\r
-                       if (A_SIGNED && B_SIGNED) begin\r
+                       if (A_SIGNED && B_SIGNED) begin : blk\r
                                (* force_downto *)\r
                                wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];\r
                                (* force_downto *)\r
@@ -129,7 +129,7 @@ module _80_mul (A, B, Y);
                                (* force_downto *)\r
                                wire signed [Y_WIDTH-1:0] partial_sum [n:0];\r
                        end\r
-                       else begin\r
+                       else begin : blk\r
                                (* force_downto *)\r
                                wire [partial_Y_WIDTH-1:0] partial [n-1:0];\r
                                (* force_downto *)\r
@@ -148,15 +148,15 @@ module _80_mul (A, B, Y);
                                ) mul (\r
                                        .A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),\r
                                        .B(B),\r
-                                       .Y(partial[i])\r
+                                       .Y(blk.partial[i])\r
                                );\r
                                // TODO: Currently a 'cascade' approach to summing the partial\r
                                //       products is taken here, but a more efficient 'binary\r
                                //       reduction' approach also exists...\r
                                if (i == 0)\r
-                                       assign partial_sum[i] = partial[i];\r
+                                       assign blk.partial_sum[i] = blk.partial[i];\r
                                else\r
-                                       assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];\r
+                                       assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1];\r
                        end\r
 \r
                        \$__mul #(\r
@@ -168,17 +168,17 @@ module _80_mul (A, B, Y);
                        ) sliceA.last (\r
                                .A(A[A_WIDTH-1 -: last_A_WIDTH]),\r
                                .B(B),\r
-                               .Y(last_partial)\r
+                               .Y(blk.last_partial)\r
                        );\r
-                       assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];\r
-                       assign Y = partial_sum[n];\r
+                       assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];\r
+                       assign Y = blk.partial_sum[n];\r
                end\r
                else if (B_WIDTH > `DSP_B_MAXWIDTH) begin\r
                        localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);\r
                        localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL);\r
                        localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);\r
                        localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;\r
-                       if (A_SIGNED && B_SIGNED) begin\r
+                       if (A_SIGNED && B_SIGNED) begin : blk\r
                                (* force_downto *)\r
                                wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];\r
                                (* force_downto *)\r
@@ -186,7 +186,7 @@ module _80_mul (A, B, Y);
                                (* force_downto *)\r
                                wire signed [Y_WIDTH-1:0] partial_sum [n:0];\r
                        end\r
-                       else begin\r
+                       else begin : blk\r
                                (* force_downto *)\r
                                wire [partial_Y_WIDTH-1:0] partial [n-1:0];\r
                                (* force_downto *)\r
@@ -205,15 +205,15 @@ module _80_mul (A, B, Y);
                                ) mul (\r
                                        .A(A),\r
                                        .B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}),\r
-                                       .Y(partial[i])\r
+                                       .Y(blk.partial[i])\r
                                );\r
                                // TODO: Currently a 'cascade' approach to summing the partial\r
                                //       products is taken here, but a more efficient 'binary\r
                                //       reduction' approach also exists...\r
                                if (i == 0)\r
-                                       assign partial_sum[i] = partial[i];\r
+                                       assign blk.partial_sum[i] = blk.partial[i];\r
                                else\r
-                                       assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];\r
+                                       assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1];\r
                        end\r
 \r
                        \$__mul #(\r
@@ -225,20 +225,24 @@ module _80_mul (A, B, Y);
                        ) mul_sliceB_last (\r
                                .A(A),\r
                                .B(B[B_WIDTH-1 -: last_B_WIDTH]),\r
-                               .Y(last_partial)\r
+                               .Y(blk.last_partial)\r
                        );\r
-                       assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];\r
-                       assign Y = partial_sum[n];\r
+                       assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];\r
+                       assign Y = blk.partial_sum[n];\r
                end\r
                else begin\r
-                       if (A_SIGNED)\r
+                       if (A_SIGNED) begin : blkA\r
                                wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);\r
-                       else\r
+                       end\r
+                       else begin : blkA\r
                                wire [`DSP_A_MAXWIDTH-1:0] Aext = A;\r
-                       if (B_SIGNED)\r
+                       end\r
+                       if (B_SIGNED) begin : blkB\r
                                wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);\r
-                       else\r
+                       end\r
+                       else begin : blkB\r
                                wire [`DSP_B_MAXWIDTH-1:0] Bext = B;\r
+                       end\r
 \r
                        `DSP_NAME #(\r
                                .A_SIGNED(A_SIGNED),\r
@@ -247,8 +251,8 @@ module _80_mul (A, B, Y);
                                .B_WIDTH(`DSP_B_MAXWIDTH),\r
                                .Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),\r
                        ) _TECHMAP_REPLACE_ (\r
-                               .A(Aext),\r
-                               .B(Bext),\r
+                               .A(blkA.Aext),\r
+                               .B(blkB.Bext),\r
                                .Y(Y)\r
                        );\r
                end\r
index ad3bccd21f45c7015ab94101c4d1942a479c36c4..db9f5d8ce5965cd74a4d1d9515b79b6ca12fa865 100644 (file)
@@ -254,6 +254,41 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B
 
        wire [15:0] A1DATA_16, B1DATA_16;
 
+`define INSTANCE \
+       \$__ICE40_RAM4K #( \
+               .READ_MODE(MODE), \
+               .WRITE_MODE(MODE), \
+               .NEGCLK_R(!CLKPOL2), \
+               .NEGCLK_W(!CLKPOL3), \
+               .INIT_0(INIT_0), \
+               .INIT_1(INIT_1), \
+               .INIT_2(INIT_2), \
+               .INIT_3(INIT_3), \
+               .INIT_4(INIT_4), \
+               .INIT_5(INIT_5), \
+               .INIT_6(INIT_6), \
+               .INIT_7(INIT_7), \
+               .INIT_8(INIT_8), \
+               .INIT_9(INIT_9), \
+               .INIT_A(INIT_A), \
+               .INIT_B(INIT_B), \
+               .INIT_C(INIT_C), \
+               .INIT_D(INIT_D), \
+               .INIT_E(INIT_E), \
+               .INIT_F(INIT_F) \
+       ) _TECHMAP_REPLACE_ ( \
+               .RDATA(A1DATA_16), \
+               .RADDR(A1ADDR_11), \
+               .RCLK(CLK2), \
+               .RCLKE(A1EN), \
+               .RE(1'b1), \
+               .WDATA(B1DATA_16), \
+               .WADDR(B1ADDR_11), \
+               .WCLK(CLK3), \
+               .WCLKE(|B1EN), \
+               .WE(1'b1) \
+       );
+
        generate
                if (MODE == 1) begin
                        assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8],
@@ -261,51 +296,23 @@ module \$__ICE40_RAM4K_M123 (CLK2, CLK3, A1ADDR, A1DATA, A1EN, B1ADDR, B1DATA, B
                        assign {B1DATA_16[14], B1DATA_16[12], B1DATA_16[10], B1DATA_16[ 8],
                                B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA;
                        `include "brams_init1.vh"
+                       `INSTANCE
                end
                if (MODE == 2) begin
                        assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]};
                        assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA;
                        `include "brams_init2.vh"
+                       `INSTANCE
                end
                if (MODE == 3) begin
                        assign A1DATA = {A1DATA_16[11], A1DATA_16[3]};
                        assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA;
                        `include "brams_init3.vh"
+                       `INSTANCE
                end
        endgenerate
 
-       \$__ICE40_RAM4K #(
-               .READ_MODE(MODE),
-               .WRITE_MODE(MODE),
-               .NEGCLK_R(!CLKPOL2),
-               .NEGCLK_W(!CLKPOL3),
-               .INIT_0(INIT_0),
-               .INIT_1(INIT_1),
-               .INIT_2(INIT_2),
-               .INIT_3(INIT_3),
-               .INIT_4(INIT_4),
-               .INIT_5(INIT_5),
-               .INIT_6(INIT_6),
-               .INIT_7(INIT_7),
-               .INIT_8(INIT_8),
-               .INIT_9(INIT_9),
-               .INIT_A(INIT_A),
-               .INIT_B(INIT_B),
-               .INIT_C(INIT_C),
-               .INIT_D(INIT_D),
-               .INIT_E(INIT_E),
-               .INIT_F(INIT_F)
-       ) _TECHMAP_REPLACE_ (
-               .RDATA(A1DATA_16),
-               .RADDR(A1ADDR_11),
-               .RCLK(CLK2),
-               .RCLKE(A1EN),
-               .RE(1'b1),
-               .WDATA(B1DATA_16),
-               .WADDR(B1ADDR_11),
-               .WCLK(CLK3),
-               .WCLKE(|B1EN),
-               .WE(1'b1)
-       );
+`undef INSTANCE
+
 endmodule
 
index eb8a04bded08427e3f521442ef39bea417926758..63be7563e8edf397f19369c108275c9ad9ca4a4d 100644 (file)
@@ -151,6 +151,8 @@ generate if (`LUT_SIZE == 4) begin
                );
        end endgenerate
 
+       assign X = S;
+
 end else begin
 
        localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
@@ -193,8 +195,8 @@ end else begin
                end
        end endgenerate
 
-end endgenerate
-
        assign X = S;
+
+end endgenerate
 endmodule
 
diff --git a/tests/simple/func_block.v b/tests/simple/func_block.v
new file mode 100644 (file)
index 0000000..be759d1
--- /dev/null
@@ -0,0 +1,33 @@
+`default_nettype none
+
+module top(inp, out1, out2, out3);
+       input wire [31:0] inp;
+
+       function automatic [31:0] func1;
+               input [31:0] inp;
+               reg [31:0] idx;
+               for (idx = 0; idx < 32; idx = idx + 1) begin : blk
+                       func1[idx] = (idx & 1'b1) ^ inp[idx];
+               end
+       endfunction
+
+       function automatic [31:0] func2;
+               input [31:0] inp;
+               reg [31:0] idx;
+               for (idx = 0; idx < 32; idx = idx + 1) begin : blk
+                       func2[idx] = (idx & 1'b1) ^ inp[idx];
+               end
+       endfunction
+
+       function automatic [31:0] func3;
+               localparam A = 32 - 1;
+               parameter B = 1 - 0;
+               input [31:0] inp;
+               func3[A:B] = inp[A:B];
+       endfunction
+
+       output wire [31:0] out1, out2, out3;
+       assign out1 = func1(inp);
+       assign out2 = func2(inp);
+       assign out3 = func3(inp);
+endmodule
diff --git a/tests/simple/func_recurse.v b/tests/simple/func_recurse.v
new file mode 100644 (file)
index 0000000..d61c8cc
--- /dev/null
@@ -0,0 +1,25 @@
+module top(
+       input wire [3:0] inp,
+       output wire [3:0] out1, out2
+);
+       function automatic [3:0] pow_a;
+               input [3:0] base, exp;
+               begin
+                       pow_a = 1;
+                       if (exp > 0)
+                               pow_a = base * pow_a(base, exp - 1);
+               end
+       endfunction
+
+       function automatic [3:0] pow_b;
+               input [3:0] base, exp;
+               begin
+                       pow_b = 1;
+                       if (exp > 0)
+                               pow_b = base * pow_b(base, exp - 1);
+               end
+       endfunction
+
+       assign out1 = pow_a(inp, 3);
+       assign out2 = pow_b(2, 2);
+endmodule
diff --git a/tests/simple/func_width_scope.v b/tests/simple/func_width_scope.v
new file mode 100644 (file)
index 0000000..ce81e89
--- /dev/null
@@ -0,0 +1,41 @@
+module top(inp, out1, out2);
+       input wire signed inp;
+
+       localparam WIDTH_A = 5;
+       function automatic [WIDTH_A-1:0] func1;
+               input reg [WIDTH_A-1:0] inp;
+               func1 = ~inp;
+       endfunction
+       wire [func1(0)-1:0] xc;
+       assign xc = 1'sb1;
+       wire [WIDTH_A-1:0] xn;
+       assign xn = func1(inp);
+
+       generate
+               if (1) begin : blk
+                       localparam WIDTH_A = 6;
+                       function automatic [WIDTH_A-1:0] func2;
+                               input reg [WIDTH_A-1:0] inp;
+                               func2 = ~inp;
+                       endfunction
+                       wire [func2(0)-1:0] yc;
+                       assign yc = 1'sb1;
+                       wire [WIDTH_A-1:0] yn;
+                       assign yn = func2(inp);
+
+                       localparam WIDTH_B = 7;
+                       function automatic [WIDTH_B-1:0] func3;
+                               input reg [WIDTH_B-1:0] inp;
+                               func3 = ~inp;
+                       endfunction
+                       wire [func3(0)-1:0] zc;
+                       assign zc = 1'sb1;
+                       wire [WIDTH_B-1:0] zn;
+                       assign zn = func3(inp);
+               end
+       endgenerate
+
+       output wire [1023:0] out1, out2;
+       assign out1 = {xc, 1'b0, blk.yc, 1'b0, blk.zc};
+       assign out2 = {xn, 1'b0, blk.yn, 1'b0, blk.zn};
+endmodule
diff --git a/tests/simple/genblk_collide.v b/tests/simple/genblk_collide.v
new file mode 100644 (file)
index 0000000..f42dd2c
--- /dev/null
@@ -0,0 +1,27 @@
+`default_nettype none
+
+module top1;
+       generate
+               if (1) begin : foo
+                       if (1) begin : bar
+                               wire x;
+                       end
+                       assign bar.x = 1;
+                       wire y;
+               end
+       endgenerate
+endmodule
+
+module top2;
+       genvar i;
+       generate
+               if (1) begin : foo
+                       wire x;
+                       for (i = 0; i < 1; i = i + 1) begin : foo
+                               if (1) begin : foo
+                                       assign x = 1;
+                               end
+                       end
+               end
+       endgenerate
+endmodule
diff --git a/tests/simple/genblk_dive.v b/tests/simple/genblk_dive.v
new file mode 100644 (file)
index 0000000..98d0e1f
--- /dev/null
@@ -0,0 +1,21 @@
+`default_nettype none
+module top(output wire x);
+       generate
+               if (1) begin : Z
+                       if (1) begin : A
+                               wire x;
+                               if (1) begin : B
+                                       wire x;
+                                       if (1) begin : C
+                                               wire x;
+                                               assign B.x = 0;
+                                               wire z = A.B.C.x;
+                                       end
+                                       assign A.x = A.B.C.x;
+                               end
+                               assign B.C.x = B.x;
+                       end
+               end
+       endgenerate
+       assign x = Z.A.x;
+endmodule
diff --git a/tests/simple/genblk_order.v b/tests/simple/genblk_order.v
new file mode 100644 (file)
index 0000000..7c3a7a7
--- /dev/null
@@ -0,0 +1,18 @@
+`default_nettype none
+module top(
+       output wire out1,
+       output wire out2
+);
+       generate
+               if (1) begin : outer
+                       if (1) begin : foo
+                               wire x = 0;
+                               if (1) begin : foo
+                                       wire x = 1;
+                                       assign out1 = foo.x;
+                               end
+                               assign out2 = foo.x;
+                       end
+               end
+       endgenerate
+endmodule
index 12327b36eb8426e2ef0d1f18cd26f370eb7efacc..ac4dd81a8b621239c3dea012e15010fff3c4741a 100644 (file)
@@ -260,3 +260,66 @@ module gen_test8;
        `ASSERT(gen_test8.A.C.x == 1)
        `ASSERT(gen_test8.A.B.x == 0)
 endmodule
+
+// ------------------------------------------
+
+module gen_test9;
+
+// `define VERIFY
+`ifdef VERIFY
+       `define ASSERT(expr) assert property (expr);
+`else
+       `define ASSERT(expr)
+`endif
+
+       wire [1:0] w = 2'b11;
+       generate
+               begin : A
+                       wire [1:0] x;
+                       begin : B
+                               wire [1:0] y = 2'b00;
+                               `ASSERT(w == 3)
+                               `ASSERT(x == 2)
+                               `ASSERT(y == 0)
+                               `ASSERT(A.x == 2)
+                               `ASSERT(A.C.z == 1)
+                               `ASSERT(A.B.y == 0)
+                               `ASSERT(gen_test9.w == 3)
+                               `ASSERT(gen_test9.A.x == 2)
+                               `ASSERT(gen_test9.A.C.z == 1)
+                               `ASSERT(gen_test9.A.B.y == 0)
+                       end
+                       begin : C
+                               wire [1:0] z = 2'b01;
+                               `ASSERT(w == 3)
+                               `ASSERT(x == 2)
+                               `ASSERT(z == 1)
+                               `ASSERT(A.x == 2)
+                               `ASSERT(A.C.z == 1)
+                               `ASSERT(A.B.y == 0)
+                               `ASSERT(gen_test9.w == 3)
+                               `ASSERT(gen_test9.A.x == 2)
+                               `ASSERT(gen_test9.A.C.z == 1)
+                               `ASSERT(gen_test9.A.B.y == 0)
+                       end
+                       assign x = B.y ^ 2'b11 ^ C.z;
+                       `ASSERT(x == 2)
+                       `ASSERT(A.x == 2)
+                       `ASSERT(A.C.z == 1)
+                       `ASSERT(A.B.y == 0)
+                       `ASSERT(gen_test9.w == 3)
+                       `ASSERT(gen_test9.A.x == 2)
+                       `ASSERT(gen_test9.A.C.z == 1)
+                       `ASSERT(gen_test9.A.B.y == 0)
+               end
+       endgenerate
+
+       `ASSERT(w == 3)
+       `ASSERT(A.x == 2)
+       `ASSERT(A.C.z == 1)
+       `ASSERT(A.B.y == 0)
+       `ASSERT(gen_test9.w == 3)
+       `ASSERT(gen_test9.A.x == 2)
+       `ASSERT(gen_test9.A.C.z == 1)
+       `ASSERT(gen_test9.A.B.y == 0)
+endmodule
diff --git a/tests/simple/local_loop_var.sv b/tests/simple/local_loop_var.sv
new file mode 100644 (file)
index 0000000..46b4e5c
--- /dev/null
@@ -0,0 +1,11 @@
+module top(out);
+       output integer out;
+       initial begin
+               integer i;
+               for (i = 0; i < 5; i = i + 1)
+                       if (i == 0)
+                               out = 1;
+                       else
+                               out += 2 ** i;
+       end
+endmodule
diff --git a/tests/simple/loop_var_shadow.v b/tests/simple/loop_var_shadow.v
new file mode 100644 (file)
index 0000000..0222a44
--- /dev/null
@@ -0,0 +1,15 @@
+module top(out);
+       genvar i;
+       generate
+               for (i = 0; i < 2; i = i + 1) begin : loop
+                       localparam j = i + 1;
+                       if (1) begin : blk
+                               localparam i = j + 1;
+                               wire [i:0] x;
+                               assign x = 1'sb1;
+                       end
+               end
+       endgenerate
+       output wire [63:0] out;
+       assign out = {loop[0].blk.x, loop[1].blk.x};
+endmodule
diff --git a/tests/simple/named_genblk.v b/tests/simple/named_genblk.v
new file mode 100644 (file)
index 0000000..b8300fc
--- /dev/null
@@ -0,0 +1,27 @@
+`default_nettype none
+module top;
+       generate
+               if (1) begin
+                       wire t;
+                       begin : foo
+                               wire x;
+                       end
+                       wire u;
+               end
+               begin : bar
+                       wire x;
+                       wire y;
+                       begin : baz
+                               wire x;
+                               wire z;
+                       end
+               end
+       endgenerate
+       assign genblk1.t = 1;
+       assign genblk1.foo.x = 1;
+       assign genblk1.u = 1;
+       assign bar.x = 1;
+       assign bar.y = 1;
+       assign bar.baz.x = 1;
+       assign bar.baz.z = 1;
+endmodule
diff --git a/tests/simple/nested_genblk_resolve.v b/tests/simple/nested_genblk_resolve.v
new file mode 100644 (file)
index 0000000..da5593f
--- /dev/null
@@ -0,0 +1,14 @@
+`default_nettype none
+module top;
+    generate
+        if (1) begin
+            wire x;
+            genvar i;
+            for (i = 0; i < 1; i = i + 1) begin
+                if (1) begin
+                    assign x = 1;
+                end
+            end
+        end
+    endgenerate
+endmodule
diff --git a/tests/simple/unnamed_block_decl.sv b/tests/simple/unnamed_block_decl.sv
new file mode 100644 (file)
index 0000000..e81b457
--- /dev/null
@@ -0,0 +1,17 @@
+module top(z);
+       output integer z;
+       initial begin
+               integer x;
+               x = 1;
+               begin
+                       integer y;
+                       y = x + 1;
+                       begin
+                               integer z;
+                               z = y + 1;
+                               y = z + 1;
+                       end
+                       z = y + 1;
+               end
+       end
+endmodule
index a12ac6288689194f412fb2f0f63e14afc3a32e0e..992bc68b3df7e56c7c7a9316dc48e55ce34ad8a2 100644 (file)
@@ -1,13 +1,17 @@
-module test(x, y, z);
+`default_nettype none
+module test;
        localparam OFF = 0;
        generate
                if (OFF) ;
-               else input x;
-               if (!OFF) input y;
+               else wire x;
+               if (!OFF) wire y;
                else ;
                if (OFF) ;
                else ;
                if (OFF) ;
-               input z;
+               wire z;
        endgenerate
+       assign genblk1.x = 0;
+       assign genblk2.y = 0;
+       assign z = 0;
 endmodule
index 31dfc444b6f7b40a70310b263a7d800d3f1fd6b6..0733e3a945d3a6cc7bfb1fbfc035a9c5dcb906c2 100644 (file)
@@ -1,4 +1,4 @@
 read_verilog gen_if_null.v
-select -assert-count 1 test/x
-select -assert-count 1 test/y
+select -assert-count 1 test/genblk1.x
+select -assert-count 1 test/genblk2.y
 select -assert-count 1 test/z
diff --git a/tests/verilog/bug2493.ys b/tests/verilog/bug2493.ys
new file mode 100644 (file)
index 0000000..380d2a8
--- /dev/null
@@ -0,0 +1,12 @@
+logger -expect error "Failed to detect width for identifier \\genblk1\.y!" 1
+read_verilog <<EOT
+module top1;
+    wire x;
+    generate
+        if (1) begin
+            mod y();
+            assign x = y;
+        end
+    endgenerate
+endmodule
+EOT
diff --git a/tests/verilog/bug656.v b/tests/verilog/bug656.v
new file mode 100644 (file)
index 0000000..068d045
--- /dev/null
@@ -0,0 +1,21 @@
+module top #(
+       parameter WIDTH = 6
+) (
+       input [WIDTH-1:0] a_i,
+       input [WIDTH-1:0] b_i,
+       output [WIDTH-1:0] z_o
+);
+       genvar g;
+       generate
+               for (g = 0; g < WIDTH; g = g + 1) begin
+                       if (g > 2) begin
+                               wire tmp;
+                               assign tmp = a_i[g] || b_i[g];
+                               assign z_o[g] = tmp;
+                       end
+                       else begin
+                               assign z_o[g] = a_i[g] && b_i[g];
+                       end
+               end
+       endgenerate
+endmodule
diff --git a/tests/verilog/bug656.ys b/tests/verilog/bug656.ys
new file mode 100644 (file)
index 0000000..7f36734
--- /dev/null
@@ -0,0 +1,13 @@
+read_verilog bug656.v
+
+select -assert-count 1 top/a_i
+select -assert-count 1 top/b_i
+select -assert-count 1 top/z_o
+
+select -assert-none top/genblk1[0].genblk1.tmp
+select -assert-none top/genblk1[1].genblk1.tmp
+select -assert-none top/genblk1[2].genblk1.tmp
+
+select -assert-count 1 top/genblk1[3].genblk1.tmp
+select -assert-count 1 top/genblk1[4].genblk1.tmp
+select -assert-count 1 top/genblk1[5].genblk1.tmp
diff --git a/tests/verilog/genblk_case.v b/tests/verilog/genblk_case.v
new file mode 100644 (file)
index 0000000..081fb09
--- /dev/null
@@ -0,0 +1,26 @@
+module top;
+       parameter YES = 1;
+       generate
+               if (YES) wire y;
+               else wire n;
+
+               if (!YES) wire n;
+               else wire y;
+
+               case (YES)
+                       1: wire y;
+                       0: wire n;
+               endcase
+
+               case (!YES)
+                       0: wire y;
+                       1: wire n;
+               endcase
+
+               if (YES) wire y;
+               else wire n;
+
+               if (!YES) wire n;
+               else wire y;
+       endgenerate
+endmodule
diff --git a/tests/verilog/genblk_case.ys b/tests/verilog/genblk_case.ys
new file mode 100644 (file)
index 0000000..3c1bb51
--- /dev/null
@@ -0,0 +1,15 @@
+read_verilog genblk_case.v
+
+select -assert-count 0 top/genblk1.n
+select -assert-count 0 top/genblk2.n
+select -assert-count 0 top/genblk3.n
+select -assert-count 0 top/genblk4.n
+select -assert-count 0 top/genblk5.n
+select -assert-count 0 top/genblk6.n
+
+select -assert-count 1 top/genblk1.y
+select -assert-count 1 top/genblk2.y
+select -assert-count 1 top/genblk3.y
+select -assert-count 1 top/genblk4.y
+select -assert-count 1 top/genblk5.y
+select -assert-count 1 top/genblk6.y
diff --git a/tests/verilog/hidden_decl.ys b/tests/verilog/hidden_decl.ys
new file mode 100644 (file)
index 0000000..aed7847
--- /dev/null
@@ -0,0 +1,11 @@
+logger -expect error "Identifier `\\y' is implicitly declared and `default_nettype is set to none" 1
+read_verilog <<EOT
+`default_nettype none
+module top1;
+    wire x;
+    generate
+        if (1) wire y;
+    endgenerate
+    assign x = y;
+endmodule
+EOT
diff --git a/tests/verilog/unnamed_block.ys b/tests/verilog/unnamed_block.ys
new file mode 100644 (file)
index 0000000..0f209a0
--- /dev/null
@@ -0,0 +1,28 @@
+read_verilog <<EOT
+module top;
+    initial begin : blk
+        integer x;
+    end
+endmodule
+EOT
+
+delete
+
+read_verilog -sv <<EOT
+module top;
+    initial begin
+        integer x;
+    end
+endmodule
+EOT
+
+delete
+
+logger -expect error "Local declaration in unnamed block is only supported in SystemVerilog mode!" 1
+read_verilog <<EOT
+module top;
+    initial begin
+        integer x;
+    end
+endmodule
+EOT
diff --git a/tests/verilog/unnamed_genblk.sv b/tests/verilog/unnamed_genblk.sv
new file mode 100644 (file)
index 0000000..41828b1
--- /dev/null
@@ -0,0 +1,39 @@
+// This test is taken directly from Section 27.6 of IEEE 1800-2017
+
+module top;
+       parameter genblk2 = 0;
+       genvar i;
+
+       // The following generate block is implicitly named genblk1
+
+       if (genblk2) logic a; // top.genblk1.a
+       else logic b; // top.genblk1.b
+
+       // The following generate block is implicitly named genblk02
+       // as genblk2 is already a declared identifier
+
+       if (genblk2) logic a; // top.genblk02.a
+       else logic b; // top.genblk02.b
+
+       // The following generate block would have been named genblk3
+       // but is explicitly named g1
+
+       for (i = 0; i < 1; i = i + 1) begin : g1 // block name
+               // The following generate block is implicitly named genblk1
+               // as the first nested scope inside g1
+               if (1) logic a; // top.g1[0].genblk1.a
+       end
+
+       // The following generate block is implicitly named genblk4 since
+       // it belongs to the fourth generate construct in scope "top".
+       // The previous generate block would have been
+       // named genblk3 if it had not been explicitly named g1
+
+       for (i = 0; i < 1; i = i + 1)
+               // The following generate block is implicitly named genblk1
+               // as the first nested generate block in genblk4
+               if (1) logic a; // top.genblk4[0].genblk1.a
+
+       // The following generate block is implicitly named genblk5
+       if (1) logic a; // top.genblk5.a
+endmodule
diff --git a/tests/verilog/unnamed_genblk.ys b/tests/verilog/unnamed_genblk.ys
new file mode 100644 (file)
index 0000000..2b9aa9d
--- /dev/null
@@ -0,0 +1,8 @@
+read_verilog -sv unnamed_genblk.sv
+select -assert-count 0 top/genblk1.a
+select -assert-count 1 top/genblk02.b
+select -assert-count 0 top/genblk1.a
+select -assert-count 1 top/genblk02.b
+select -assert-count 1 top/g1[0].genblk1.a
+select -assert-count 1 top/genblk4[0].genblk1.a
+select -assert-count 1 top/genblk5.a