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);
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().
// 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];
// 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++) {
{
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++)
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++) {
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++) {
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++) {
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))
}
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;
}
}
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;
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)) { }
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;
else
{
wire = child->clone();
- wire->str = prefix + wire->str;
wire->port_id = 0;
wire->is_input = false;
wire->is_output = false;
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())
{
}
}
- 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());
}
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 = "";
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
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;
}
}
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();
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;
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;
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 {
module_gen_body:
module_gen_body gen_stmt_or_module_body_stmt |
+ module_gen_body gen_block |
%empty;
gen_stmt_or_module_body_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();
} |
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 {
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);
} 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;
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());
}
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_);
}
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;
}
}
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);
}
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);
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;
+ }
}
}
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();
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();
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)
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
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))
// 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))
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;
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
(* 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
) 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
) 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
(* 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
) 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
) 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
.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
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],
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
);
end endgenerate
+ assign X = S;
+
end else begin
localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
end
end endgenerate
-end endgenerate
-
assign X = S;
+
+end endgenerate
endmodule
--- /dev/null
+`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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+`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
`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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+`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
--- /dev/null
+`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
--- /dev/null
+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
-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
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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+// 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
--- /dev/null
+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