mirror of https://github.com/YosysHQ/yosys.git
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:
parent
98afe2b758
commit
fe74b0cd95
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue