verilog: significant block scoping improvements

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
This commit is contained in:
Zachary Snow 2021-01-27 13:30:22 -05:00
parent 98afe2b758
commit fe74b0cd95
33 changed files with 783 additions and 262 deletions

View 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);

View 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;
}
// 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;
}
}
}
}
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;
// 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;
child->expand_genblock(prefix);
}
if (backup_name_map.size() > 0)
name_map.swap(backup_name_map);
}
// 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;
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) {
new_rules[child->str] = new_prefix + child->str;
child->str = new_prefix + child->str;
}
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();

View 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,20 +2773,6 @@ gen_stmt:
SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
ast_stack.pop_back();
} |
TOK_BEGIN {
enterTypeScope();
} opt_label {
AstNode *node = new AstNode(AST_GENBLOCK);
node->str = $3 ? *$3 : std::string();
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
} module_gen_body TOK_END opt_label {
exitTypeScope();
delete $3;
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;
@ -2790,6 +2784,23 @@ gen_stmt:
ast_stack.pop_back();
};
gen_block:
TOK_BEGIN {
enterTypeScope();
} opt_label {
AstNode *node = new AstNode(AST_GENBLOCK);
node->str = $3 ? *$3 : std::string();
ast_stack.back()->children.push_back(node);
ast_stack.push_back(node);
} module_gen_body TOK_END opt_label {
exitTypeScope();
delete $3;
delete $7;
SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
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;

View 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());
}

View 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)

View 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))

View 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;

View File

@ -121,7 +121,7 @@ module _80_mul (A, B, Y);
localparam partial_Y_WIDTH = `MIN(Y_WIDTH, B_WIDTH+`DSP_A_MAXWIDTH_PARTIAL);
localparam last_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;
if (A_SIGNED && B_SIGNED) begin
if (A_SIGNED && B_SIGNED) begin : blk
(* force_downto *)
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@ -129,7 +129,7 @@ module _80_mul (A, B, Y);
(* force_downto *)
wire signed [Y_WIDTH-1:0] partial_sum [n:0];
end
else begin
else begin : blk
(* force_downto *)
wire [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@ -148,15 +148,15 @@ module _80_mul (A, B, Y);
) mul (
.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),
.B(B),
.Y(partial[i])
.Y(blk.partial[i])
);
// TODO: Currently a 'cascade' approach to summing the partial
// products is taken here, but a more efficient 'binary
// reduction' approach also exists...
if (i == 0)
assign partial_sum[i] = partial[i];
assign blk.partial_sum[i] = blk.partial[i];
else
assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];
assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1];
end
\$__mul #(
@ -168,17 +168,17 @@ module _80_mul (A, B, Y);
) sliceA.last (
.A(A[A_WIDTH-1 -: last_A_WIDTH]),
.B(B),
.Y(last_partial)
.Y(blk.last_partial)
);
assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];
assign Y = partial_sum[n];
assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];
assign Y = blk.partial_sum[n];
end
else if (B_WIDTH > `DSP_B_MAXWIDTH) begin
localparam n = (B_WIDTH-`DSP_B_MAXWIDTH+`DSP_B_MAXWIDTH_PARTIAL-sign_headroom-1) / (`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
localparam partial_Y_WIDTH = `MIN(Y_WIDTH, A_WIDTH+`DSP_B_MAXWIDTH_PARTIAL);
localparam last_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;
if (A_SIGNED && B_SIGNED) begin
if (A_SIGNED && B_SIGNED) begin : blk
(* force_downto *)
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@ -186,7 +186,7 @@ module _80_mul (A, B, Y);
(* force_downto *)
wire signed [Y_WIDTH-1:0] partial_sum [n:0];
end
else begin
else begin : blk
(* force_downto *)
wire [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *)
@ -205,15 +205,15 @@ module _80_mul (A, B, Y);
) mul (
.A(A),
.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}),
.Y(partial[i])
.Y(blk.partial[i])
);
// TODO: Currently a 'cascade' approach to summing the partial
// products is taken here, but a more efficient 'binary
// reduction' approach also exists...
if (i == 0)
assign partial_sum[i] = partial[i];
assign blk.partial_sum[i] = blk.partial[i];
else
assign partial_sum[i] = (partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[i-1];
assign blk.partial_sum[i] = (blk.partial[i] << (* mul2dsp *) i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[i-1];
end
\$__mul #(
@ -225,20 +225,24 @@ module _80_mul (A, B, Y);
) mul_sliceB_last (
.A(A),
.B(B[B_WIDTH-1 -: last_B_WIDTH]),
.Y(last_partial)
.Y(blk.last_partial)
);
assign partial_sum[n] = (last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) partial_sum[n-1];
assign Y = partial_sum[n];
assign blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];
assign Y = blk.partial_sum[n];
end
else begin
if (A_SIGNED)
if (A_SIGNED) begin : blkA
wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
else
end
else begin : blkA
wire [`DSP_A_MAXWIDTH-1:0] Aext = A;
if (B_SIGNED)
end
if (B_SIGNED) begin : blkB
wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
else
end
else begin : blkB
wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
end
`DSP_NAME #(
.A_SIGNED(A_SIGNED),
@ -247,8 +251,8 @@ module _80_mul (A, B, Y);
.B_WIDTH(`DSP_B_MAXWIDTH),
.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
) _TECHMAP_REPLACE_ (
.A(Aext),
.B(Bext),
.A(blkA.Aext),
.B(blkB.Bext),
.Y(Y)
);
end

View 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

View 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

33
tests/simple/func_block.v Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View 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

12
tests/verilog/bug2493.ys Normal file
View File

@ -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

21
tests/verilog/bug656.v Normal file
View File

@ -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

13
tests/verilog/bug656.ys Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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