Merge pull request #2529 from zachjs/unnamed-genblk

verilog: significant block scoping improvements
This commit is contained in:
whitequark 2021-02-04 09:57:28 +00:00 committed by GitHub
commit baf1875307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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); 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); 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); 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 expand_genblock(const std::string &prefix);
void replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules); void label_genblks(std::set<std::string>& existing, int &counter);
void mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, 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); 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); 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; 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 // 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 // values, unrolled for-loops, expanded generate blocks, etc. when this function
// is done with an AST it can be converted into RTLIL using genRTLIL(). // 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;") // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;")
if (type == AST_MODULE) { if (type == AST_MODULE) {
current_scope.clear(); current_scope.clear();
std::set<std::string> existing;
int counter = 0;
label_genblks(existing, counter);
std::map<std::string, AstNode*> this_wire_scope; std::map<std::string, AstNode*> this_wire_scope;
for (size_t i = 0; i < children.size(); i++) { for (size_t i = 0; i < children.size(); i++) {
AstNode *node = children[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 // expand body
int index = varbuf->children[0]->integer; int index = varbuf->children[0]->integer;
if (body_ast->type == AST_GENBLOCK) log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK);
buf = body_ast->clone(); log_assert(!body_ast->str.empty());
else buf = body_ast->clone();
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;
std::stringstream sstr; std::stringstream sstr;
sstr << buf->str << "[" << index << "]."; 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) { if (type == AST_GENFOR) {
for (size_t i = 0; i < buf->children.size(); i++) { 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++) 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) 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 // transform block with name
if (type == AST_BLOCK && !str.empty()) if (type == AST_BLOCK && !str.empty())
{ {
std::map<std::string, std::string> name_map; expand_genblock(str + ".");
expand_genblock(std::string(), str + ".", name_map);
std::vector<AstNode*> new_children; std::vector<AstNode*> new_children;
for (size_t i = 0; i < children.size(); i++) 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 (type == AST_GENBLOCK && children.size() != 0)
{ {
if (!str.empty()) { if (!str.empty()) {
std::map<std::string, std::string> name_map; expand_genblock(str + ".");
expand_genblock(std::string(), str + ".", name_map);
} }
for (size_t i = 0; i < children.size(); i++) { 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); buf = new AstNode(AST_GENBLOCK, buf);
if (!buf->str.empty()) { if (!buf->str.empty()) {
std::map<std::string, std::string> name_map; buf->expand_genblock(buf->str + ".");
buf->expand_genblock(std::string(), buf->str + ".", name_map);
} }
for (size_t i = 0; i < buf->children.size(); i++) { 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(); buf = selected_case->clone();
if (!buf->str.empty()) { if (!buf->str.empty()) {
std::map<std::string, std::string> name_map; buf->expand_genblock(buf->str + ".");
buf->expand_genblock(std::string(), buf->str + ".", name_map);
} }
for (size_t i = 0; i < buf->children.size(); i++) { 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()); 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; std::stringstream sstr;
sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.';
std::string prefix = sstr.str(); 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 recommend_const_eval = false;
bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval);
if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) if ((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) { if (all_args_const) {
AstNode *func_workspace = current_scope[str]->clone(); AstNode *func_workspace = decl->clone();
func_workspace->str = NEW_ID.str(); func_workspace->str = prefix_id(prefix, "$result");
func_workspace->replace_result_wire_name_in_function(str, func_workspace->str);
newNode = func_workspace->eval_const_function(this); newNode = func_workspace->eval_const_function(this);
delete func_workspace; delete func_workspace;
delete decl;
goto apply_newNode; goto apply_newNode;
} }
@ -3192,8 +3213,6 @@ skip_dynamic_range_lvalue_expansion:;
} }
size_t arg_count = 0; size_t arg_count = 0;
std::map<std::string, std::string> replace_rules;
vector<AstNode*> added_mod_children;
dict<std::string, AstNode*> wire_cache; dict<std::string, AstNode*> wire_cache;
vector<AstNode*> new_stmts; vector<AstNode*> new_stmts;
vector<AstNode*> output_assignments; vector<AstNode*> output_assignments;
@ -3203,16 +3222,17 @@ skip_dynamic_range_lvalue_expansion:;
log_assert(type == AST_FCALL); log_assert(type == AST_FCALL);
AstNode *wire = NULL; AstNode *wire = NULL;
std::string res_name = prefix_id(prefix, "$result");
for (auto child : decl->children) 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(); wire = child->clone();
log_assert(wire != NULL); log_assert(wire != NULL);
wire->str = prefix + str;
wire->port_id = 0; wire->port_id = 0;
wire->is_input = false; wire->is_input = false;
wire->is_output = false; wire->is_output = false;
current_scope[wire->str] = wire;
current_ast_mod->children.push_back(wire); current_ast_mod->children.push_back(wire);
while (wire->simplify(true, false, false, 1, -1, false, false)) { } 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))) if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str)))
{ {
AstNode *wire = child->clone(); AstNode *wire = child->clone();
wire->str = prefix + wire->str;
wire->port_id = 0; wire->port_id = 0;
wire->is_input = false; wire->is_input = false;
wire->is_output = false; wire->is_output = false;
@ -3318,7 +3337,6 @@ skip_dynamic_range_lvalue_expansion:;
else else
{ {
wire = child->clone(); wire = child->clone();
wire->str = prefix + wire->str;
wire->port_id = 0; wire->port_id = 0;
wire->is_input = false; wire->is_input = false;
wire->is_output = false; wire->is_output = false;
@ -3329,15 +3347,11 @@ skip_dynamic_range_lvalue_expansion:;
wire_cache[child->str] = wire; wire_cache[child->str] = wire;
current_scope[wire->str] = wire;
current_ast_mod->children.push_back(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)) { }
while (wire->simplify(true, false, false, 1, -1, false, false)) { }
replace_rules[child->str] = wire->str;
current_scope[wire->str] = wire;
if ((child->is_input || child->is_output) && arg_count < children.size()) 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) for (auto child : decl->children)
if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM)
{ new_stmts.push_back(child->clone());
AstNode *stmt = child->clone();
stmt->replace_ids(prefix, replace_rules);
new_stmts.push_back(stmt);
}
new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); 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: replace_fcall_with_id:
delete decl;
if (type == AST_FCALL) { if (type == AST_FCALL) {
delete_children(); delete_children();
type = AST_IDENTIFIER; type = AST_IDENTIFIER;
str = prefix + str; str = prefix_id(prefix, "$result");
} }
if (type == AST_TCALL) if (type == AST_TCALL)
str = ""; str = "";
@ -3859,63 +3865,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m
return block; return block;
} }
// annotate the names of all wires and other named objects in a generate block // annotate the names of all wires and other named objects in a named generate
void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) // 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 (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) {
if (name_map.count(str) > 0) { log_assert(!str.empty());
str = name_map[str];
} else { // search starting in the innermost scope and then stepping outward
// remap the prefix of this ident if it is a local generate scope for (size_t ppos = prefix.size() - 1; ppos; --ppos) {
size_t pos = str.rfind('.'); if (prefix.at(ppos) != '.') continue;
if (pos != std::string::npos) {
std::string existing_prefix = str.substr(0, pos); std::string new_prefix = prefix.substr(0, ppos + 1);
if (name_map.count(existing_prefix) > 0) { auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string {
str = name_map[existing_prefix] + str.substr(pos); 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 = [&prefix](AstNode* child) {
if (child->str.empty()) return;
auto prefix_node = [&](AstNode* child) { std::string new_name = prefix_id(prefix, child->str);
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;
if (child->type == AST_FUNCTION) if (child->type == AST_FUNCTION)
child->replace_result_wire_name_in_function(child->str, new_name); child->replace_result_wire_name_in_function(child->str, new_name);
else else
@ -3967,43 +3962,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma
continue; continue;
// functions/tasks may reference wires, constants, etc. in this scope // functions/tasks may reference wires, constants, etc. in this scope
if (child->type == AST_FUNCTION || child->type == AST_TASK) if (child->type == AST_FUNCTION || child->type == AST_TASK)
child->expand_genblock(index_var, prefix, name_map, false); continue;
// continue prefixing if this child block is anonymous // named blocks pick up the current prefix and will expanded later
else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty())
child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); continue;
else
child->expand_genblock(index_var, prefix, name_map, original_scope); 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) // add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or
void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) // IEEE 1800-2017 Section 27.6
void AstNode::label_genblks(std::set<std::string>& existing, int &counter)
{ {
if (type == AST_BLOCK) switch (type) {
{ case AST_GENIF:
std::map<std::string, std::string> new_rules = rules; case AST_GENFOR:
std::string new_prefix = prefix + str; 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) case AST_GENBLOCK: {
if (child->type == AST_WIRE) { // if this block is unlabeled, generate its corresponding unique name
new_rules[child->str] = new_prefix + child->str; for (int padding = 0; str.empty(); ++padding) {
child->str = new_prefix + child->str; std::string candidate = "\\genblk";
} for (int i = 0; i < padding; ++i)
candidate += '0';
for (auto child : children) candidate += std::to_string(counter);
if (child->type != AST_WIRE) if (!existing.count(candidate))
child->replace_ids(new_prefix, new_rules); 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
{ default:
if (type == AST_IDENTIFIER && rules.count(str) > 0) // track names which could conflict with implicit genblk names
str = rules.at(str); if (str.rfind("\\genblk", 0) == 0)
for (auto child : children) existing.insert(str);
child->replace_ids(prefix, rules); 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->type == AST_BLOCK)
{ {
if (!stmt->str.empty())
stmt->expand_genblock(stmt->str + ".");
block->children.erase(block->children.begin()); block->children.erase(block->children.begin());
block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end());
stmt->children.clear(); stmt->children.clear();

View File

@ -770,6 +770,7 @@ module_body:
module_body module_body_stmt | module_body module_body_stmt |
/* the following line makes the generate..endgenrate keywords optional */ /* the following line makes the generate..endgenrate keywords optional */
module_body gen_stmt | module_body gen_stmt |
module_body gen_block |
module_body ';' | module_body ';' |
%empty; %empty;
@ -2459,6 +2460,16 @@ behavioral_stmt:
exitTypeScope(); exitTypeScope();
if ($4 != NULL && $8 != NULL && *$4 != *$8) 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); 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); SET_AST_NODE_LOC(ast_stack.back(), @2, @8);
delete $4; delete $4;
delete $8; delete $8;
@ -2473,6 +2484,7 @@ behavioral_stmt:
ast_stack.back()->children.push_back($7); ast_stack.back()->children.push_back($7);
} ';' simple_behavioral_stmt ')' { } ';' simple_behavioral_stmt ')' {
AstNode *block = new AstNode(AST_BLOCK); AstNode *block = new AstNode(AST_BLOCK);
block->str = "$for_loop$" + std::to_string(autoidx++);
ast_stack.back()->children.push_back(block); ast_stack.back()->children.push_back(block);
ast_stack.push_back(block); ast_stack.push_back(block);
} behavioral_stmt { } behavioral_stmt {
@ -2722,6 +2734,7 @@ single_arg:
module_gen_body: module_gen_body:
module_gen_body gen_stmt_or_module_body_stmt | module_gen_body gen_stmt_or_module_body_stmt |
module_gen_body gen_block |
%empty; %empty;
gen_stmt_or_module_body_stmt: gen_stmt_or_module_body_stmt:
@ -2747,12 +2760,7 @@ gen_stmt:
ast_stack.back()->children.push_back(node); ast_stack.back()->children.push_back(node);
ast_stack.push_back(node); ast_stack.push_back(node);
ast_stack.back()->children.push_back($3); ast_stack.back()->children.push_back($3);
AstNode *block = new AstNode(AST_GENBLOCK); } gen_stmt_block opt_gen_else {
ast_stack.back()->children.push_back(block);
ast_stack.push_back(block);
} gen_stmt_block {
ast_stack.pop_back();
} opt_gen_else {
SET_AST_NODE_LOC(ast_stack.back(), @1, @7); SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
ast_stack.pop_back(); ast_stack.pop_back();
} | } |
@ -2765,20 +2773,6 @@ gen_stmt:
SET_AST_NODE_LOC(ast_stack.back(), @1, @7); SET_AST_NODE_LOC(ast_stack.back(), @1, @7);
ast_stack.pop_back(); 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 { TOK_MSG_TASKS {
AstNode *node = new AstNode(AST_TECALL); AstNode *node = new AstNode(AST_TECALL);
node->str = *$1; node->str = *$1;
@ -2790,6 +2784,23 @@ gen_stmt:
ast_stack.pop_back(); 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: gen_stmt_block:
{ {
AstNode *node = new AstNode(AST_GENBLOCK); AstNode *node = new AstNode(AST_GENBLOCK);
@ -2798,7 +2809,7 @@ gen_stmt_block:
} gen_stmt_or_module_body_stmt { } gen_stmt_or_module_body_stmt {
SET_AST_NODE_LOC(ast_stack.back(), @2, @2); SET_AST_NODE_LOC(ast_stack.back(), @2, @2);
ast_stack.pop_back(); ast_stack.pop_back();
}; } | gen_block;
opt_gen_else: opt_gen_else:
TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN;

View File

@ -334,6 +334,10 @@ namespace RTLIL
return compare(size()-len, len, suffix) == 0; return compare(size()-len, len, suffix) == 0;
} }
bool contains(const char* str) const {
return strstr(c_str(), str);
}
size_t size() const { size_t size() const {
return strlen(c_str()); return strlen(c_str());
} }

View File

@ -118,19 +118,14 @@ struct TechmapWorker
return result; return result;
for (auto w : module->wires()) { for (auto w : module->wires()) {
const char *p = w->name.c_str(); if (*w->name.c_str() == '$')
if (*p == '$')
continue; continue;
const char *q = strrchr(p+1, '.'); if (w->name.contains("_TECHMAP_") && !w->name.contains("_TECHMAP_REPLACE_")) {
if (q)
p = q;
if (!strncmp(p, "\\_TECHMAP_", 10)) {
TechmapWireData record; TechmapWireData record;
record.wire = w; record.wire = w;
record.value = 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::keep);
w->set_bool_attribute(ID::_techmap_special_); w->set_bool_attribute(ID::_techmap_special_);
} }
@ -165,7 +160,7 @@ struct TechmapWorker
orig_cell_name = cell->name.str(); orig_cell_name = cell->name.str();
for (auto tpl_cell : tpl->cells()) 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()); module->rename(cell, stringf("$techmap%d", autoidx++) + cell->name.str());
break; break;
} }
@ -226,8 +221,8 @@ struct TechmapWorker
} }
design->select(module, w); design->select(module, w);
if (tpl_w->name.begins_with("\\_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(), tpl_w->name.c_str() + strlen("\\_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); Wire *replace_w = module->addWire(replace_name, tpl_w);
module->connect(replace_w, w); module->connect(replace_w, w);
} }
@ -327,12 +322,12 @@ struct TechmapWorker
for (auto tpl_cell : tpl->cells()) for (auto tpl_cell : tpl->cells())
{ {
IdString c_name = tpl_cell->name; 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) if (techmap_replace_cell)
c_name = orig_cell_name; c_name = orig_cell_name;
else if (tpl_cell->name.begins_with("\\_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(), c_name.c_str() + strlen("\\_TECHMAP_REPLACE_")); c_name = stringf("%s%s", orig_cell_name.c_str(), p + strlen("_TECHMAP_REPLACE_"));
else else
apply_prefix(cell->name, c_name); apply_prefix(cell->name, c_name);
@ -730,12 +725,16 @@ struct TechmapWorker
for (auto &it : twd) for (auto &it : twd)
techmap_wire_names.insert(it.first); techmap_wire_names.insert(it.first);
for (auto &it : twd[ID::_TECHMAP_FAIL_]) { for (auto &it : twd) {
RTLIL::SigSpec value = it.value; if (!it.first.ends_with("_TECHMAP_FAIL_"))
if (value.is_fully_const() && value.as_bool()) { continue;
log("Not using module `%s' from techmap as it contains a %s marker wire with non-zero value %s.\n", for (const TechmapWireData &elem : it.second) {
derived_name.c_str(), log_id(it.wire->name), log_signal(value)); RTLIL::SigSpec value = elem.value;
techmap_do_cache[tpl] = false; 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) for (auto &it : twd)
{ {
if (!it.first.begins_with("\\_TECHMAP_DO_") || it.second.empty()) if (!it.first.contains("_TECHMAP_DO_") || it.second.empty())
continue; continue;
auto &data = it.second.front(); auto &data = it.second.front();
@ -756,7 +755,7 @@ struct TechmapWorker
const char *p = data.wire->name.c_str(); const char *p = data.wire->name.c_str();
const char *q = strrchr(p+1, '.'); 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(); std::string cmd_string = data.value.as_const().decode_string();
@ -873,7 +872,7 @@ struct TechmapWorker
TechmapWires twd = techmap_find_special_wires(tpl); TechmapWires twd = techmap_find_special_wires(tpl);
for (auto &it : twd) { 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)); log_error("Techmap yielded unknown config wire %s.\n", log_id(it.first));
if (techmap_do_cache[tpl]) if (techmap_do_cache[tpl])
for (auto &it2 : it.second) 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}; 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 // 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) // equal (propagating this equality if A and B turn out to be so)
if (_TECHMAP_CELLTYPE_ == "$ge") localparam CI = _TECHMAP_CELLTYPE_ == "$ge";
localparam CI = 1'b1;
else
localparam CI = 1'b0;
$__CMP2LCU #(.AB_WIDTH(WIDTH), .AB_SIGNED(A_SIGNED && B_SIGNED), .LCU_WIDTH(1), .BUDGET(`LUT_WIDTH), .CI(CI)) $__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)); _TECHMAP_REPLACE_ (.A(AA), .B(BB), .P(1'b1), .G(1'b0), .Y(Y));
end end
@ -81,12 +78,12 @@ generate
assign Y = CO[LCU_WIDTH-1]; assign Y = CO[LCU_WIDTH-1];
end end
else begin else begin
if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]) localparam COST =
localparam COST = 0; _TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] && _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]
else if (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]) ? 0
localparam COST = 1; : (_TECHMAP_CONSTMSK_A_[AB_WIDTH-1:0] || _TECHMAP_CONSTMSK_B_[AB_WIDTH-1:0]
else ? 1
localparam COST = 2; : 2);
if (BUDGET < COST) if (BUDGET < COST)
$__CMP2LCU #(.AB_WIDTH(AB_WIDTH), .AB_SIGNED(AB_SIGNED), .LCU_WIDTH(LCU_WIDTH+1), .BUDGET(`LUT_WIDTH), .CI(CI)) $__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 // from MSB down, deferring to less significant bits if the
// MSBs are equal // MSBs are equal
assign GG = P[0] & (A[AB_WIDTH-1] & ~B[AB_WIDTH-1]); 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 if (LCU_WIDTH == 1) begin
// Propagate only if all pairs are equal // Propagate only if all pairs are equal
// (inconclusive evidence to say A >= B) // (inconclusive evidence to say A >= B)
wire P_ = P[0] & PP; assign P_ = P[0] & PP;
// Generate if any comparisons call for it // Generate if any comparisons call for it
wire G_ = G[0] | GG; assign G_ = G[0] | GG;
end end
else begin else begin
// Propagate only if all pairs are equal // Propagate only if all pairs are equal
// (inconclusive evidence to say A >= B) // (inconclusive evidence to say A >= B)
(* force_downto *) assign P_ = {P[LCU_WIDTH-1:1], P[0] & PP};
wire [LCU_WIDTH-1:0] P_ = {P[LCU_WIDTH-1:1], P[0] & PP};
// Generate if any comparisons call for it // Generate if any comparisons call for it
(* force_downto *) assign G_ = {G[LCU_WIDTH-1:1], G[0] | GG};
wire [LCU_WIDTH-1:0] G_ = {G[LCU_WIDTH-1:1], G[0] | GG};
end end
if (AB_WIDTH == 1) if (AB_WIDTH == 1)
$__CMP2LCU #(.AB_WIDTH(AB_WIDTH-1), .AB_SIGNED(1'b0), .LCU_WIDTH(LCU_WIDTH), .BUDGET(BUDGET-COST), .CI(CI)) $__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 endfunction
generate generate
if (_TECHMAP_CELLTYPE_ == "$lt") localparam operation =
localparam operation = 0; _TECHMAP_CELLTYPE_ == "$lt" ? 0 :
if (_TECHMAP_CELLTYPE_ == "$le") _TECHMAP_CELLTYPE_ == "$le" ? 1 :
localparam operation = 1; _TECHMAP_CELLTYPE_ == "$gt" ? 2 :
if (_TECHMAP_CELLTYPE_ == "$gt") _TECHMAP_CELLTYPE_ == "$ge" ? 3 :
localparam operation = 2; -1;
if (_TECHMAP_CELLTYPE_ == "$ge")
localparam operation = 3;
if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1) if (A_WIDTH > `LUT_WIDTH || B_WIDTH > `LUT_WIDTH || Y_WIDTH != 1)
wire _TECHMAP_FAIL_ = 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 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_A_WIDTH = A_WIDTH-n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom);
localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH; localparam last_Y_WIDTH = B_WIDTH+last_A_WIDTH;
if (A_SIGNED && B_SIGNED) begin if (A_SIGNED && B_SIGNED) begin : blk
(* force_downto *) (* force_downto *)
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *) (* force_downto *)
@ -129,7 +129,7 @@ module _80_mul (A, B, Y);
(* force_downto *) (* force_downto *)
wire signed [Y_WIDTH-1:0] partial_sum [n:0]; wire signed [Y_WIDTH-1:0] partial_sum [n:0];
end end
else begin else begin : blk
(* force_downto *) (* force_downto *)
wire [partial_Y_WIDTH-1:0] partial [n-1:0]; wire [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *) (* force_downto *)
@ -148,15 +148,15 @@ module _80_mul (A, B, Y);
) mul ( ) mul (
.A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}), .A({{sign_headroom{1'b0}}, A[i*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_A_MAXWIDTH_PARTIAL-sign_headroom]}),
.B(B), .B(B),
.Y(partial[i]) .Y(blk.partial[i])
); );
// TODO: Currently a 'cascade' approach to summing the partial // TODO: Currently a 'cascade' approach to summing the partial
// products is taken here, but a more efficient 'binary // products is taken here, but a more efficient 'binary
// reduction' approach also exists... // reduction' approach also exists...
if (i == 0) if (i == 0)
assign partial_sum[i] = partial[i]; assign blk.partial_sum[i] = blk.partial[i];
else 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 end
\$__mul #( \$__mul #(
@ -168,17 +168,17 @@ module _80_mul (A, B, Y);
) sliceA.last ( ) sliceA.last (
.A(A[A_WIDTH-1 -: last_A_WIDTH]), .A(A[A_WIDTH-1 -: last_A_WIDTH]),
.B(B), .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 blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_A_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];
assign Y = partial_sum[n]; assign Y = blk.partial_sum[n];
end end
else if (B_WIDTH > `DSP_B_MAXWIDTH) begin 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 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 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_B_WIDTH = B_WIDTH-n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom);
localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH; localparam last_Y_WIDTH = A_WIDTH+last_B_WIDTH;
if (A_SIGNED && B_SIGNED) begin if (A_SIGNED && B_SIGNED) begin : blk
(* force_downto *) (* force_downto *)
wire signed [partial_Y_WIDTH-1:0] partial [n-1:0]; wire signed [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *) (* force_downto *)
@ -186,7 +186,7 @@ module _80_mul (A, B, Y);
(* force_downto *) (* force_downto *)
wire signed [Y_WIDTH-1:0] partial_sum [n:0]; wire signed [Y_WIDTH-1:0] partial_sum [n:0];
end end
else begin else begin : blk
(* force_downto *) (* force_downto *)
wire [partial_Y_WIDTH-1:0] partial [n-1:0]; wire [partial_Y_WIDTH-1:0] partial [n-1:0];
(* force_downto *) (* force_downto *)
@ -205,15 +205,15 @@ module _80_mul (A, B, Y);
) mul ( ) mul (
.A(A), .A(A),
.B({{sign_headroom{1'b0}}, B[i*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom) +: `DSP_B_MAXWIDTH_PARTIAL-sign_headroom]}), .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 // TODO: Currently a 'cascade' approach to summing the partial
// products is taken here, but a more efficient 'binary // products is taken here, but a more efficient 'binary
// reduction' approach also exists... // reduction' approach also exists...
if (i == 0) if (i == 0)
assign partial_sum[i] = partial[i]; assign blk.partial_sum[i] = blk.partial[i];
else 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 end
\$__mul #( \$__mul #(
@ -225,20 +225,24 @@ module _80_mul (A, B, Y);
) mul_sliceB_last ( ) mul_sliceB_last (
.A(A), .A(A),
.B(B[B_WIDTH-1 -: last_B_WIDTH]), .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 blk.partial_sum[n] = (blk.last_partial << (* mul2dsp *) n*(`DSP_B_MAXWIDTH_PARTIAL-sign_headroom)) + (* mul2dsp *) blk.partial_sum[n-1];
assign Y = partial_sum[n]; assign Y = blk.partial_sum[n];
end end
else begin else begin
if (A_SIGNED) if (A_SIGNED) begin : blkA
wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A); wire signed [`DSP_A_MAXWIDTH-1:0] Aext = $signed(A);
else end
else begin : blkA
wire [`DSP_A_MAXWIDTH-1:0] Aext = A; 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); wire signed [`DSP_B_MAXWIDTH-1:0] Bext = $signed(B);
else end
else begin : blkB
wire [`DSP_B_MAXWIDTH-1:0] Bext = B; wire [`DSP_B_MAXWIDTH-1:0] Bext = B;
end
`DSP_NAME #( `DSP_NAME #(
.A_SIGNED(A_SIGNED), .A_SIGNED(A_SIGNED),
@ -247,8 +251,8 @@ module _80_mul (A, B, Y);
.B_WIDTH(`DSP_B_MAXWIDTH), .B_WIDTH(`DSP_B_MAXWIDTH),
.Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)), .Y_WIDTH(`MIN(Y_WIDTH,`DSP_A_MAXWIDTH+`DSP_B_MAXWIDTH)),
) _TECHMAP_REPLACE_ ( ) _TECHMAP_REPLACE_ (
.A(Aext), .A(blkA.Aext),
.B(Bext), .B(blkB.Bext),
.Y(Y) .Y(Y)
); );
end 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; 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 generate
if (MODE == 1) begin if (MODE == 1) begin
assign A1DATA = {A1DATA_16[14], A1DATA_16[12], A1DATA_16[10], A1DATA_16[ 8], 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], 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; B1DATA_16[ 6], B1DATA_16[ 4], B1DATA_16[ 2], B1DATA_16[ 0]} = B1DATA;
`include "brams_init1.vh" `include "brams_init1.vh"
`INSTANCE
end end
if (MODE == 2) begin if (MODE == 2) begin
assign A1DATA = {A1DATA_16[13], A1DATA_16[9], A1DATA_16[5], A1DATA_16[1]}; 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; assign {B1DATA_16[13], B1DATA_16[9], B1DATA_16[5], B1DATA_16[1]} = B1DATA;
`include "brams_init2.vh" `include "brams_init2.vh"
`INSTANCE
end end
if (MODE == 3) begin if (MODE == 3) begin
assign A1DATA = {A1DATA_16[11], A1DATA_16[3]}; assign A1DATA = {A1DATA_16[11], A1DATA_16[3]};
assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA; assign {B1DATA_16[11], B1DATA_16[3]} = B1DATA;
`include "brams_init3.vh" `include "brams_init3.vh"
`INSTANCE
end end
endgenerate endgenerate
\$__ICE40_RAM4K #( `undef INSTANCE
.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)
);
endmodule endmodule

View File

@ -151,6 +151,8 @@ generate if (`LUT_SIZE == 4) begin
); );
end endgenerate end endgenerate
assign X = S;
end else begin end else begin
localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4;
@ -193,8 +195,8 @@ end else begin
end end
end endgenerate end endgenerate
end endgenerate
assign X = S; assign X = S;
end endgenerate
endmodule 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.C.x == 1)
`ASSERT(gen_test8.A.B.x == 0) `ASSERT(gen_test8.A.B.x == 0)
endmodule 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; localparam OFF = 0;
generate generate
if (OFF) ; if (OFF) ;
else input x; else wire x;
if (!OFF) input y; if (!OFF) wire y;
else ; else ;
if (OFF) ; if (OFF) ;
else ; else ;
if (OFF) ; if (OFF) ;
input z; wire z;
endgenerate endgenerate
assign genblk1.x = 0;
assign genblk2.y = 0;
assign z = 0;
endmodule endmodule

View File

@ -1,4 +1,4 @@
read_verilog gen_if_null.v read_verilog gen_if_null.v
select -assert-count 1 test/x select -assert-count 1 test/genblk1.x
select -assert-count 1 test/y select -assert-count 1 test/genblk2.y
select -assert-count 1 test/z 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