diff options
33 files changed, 779 insertions, 258 deletions
diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 907392166..6f173ca22 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -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); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index fc2976c83..77911e966 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -549,6 +549,16 @@ static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) return true; } +static std::string prefix_id(const std::string &prefix, const std::string &str) +{ + log_assert(!prefix.empty() && (prefix.front() == '$' || prefix.front() == '\\')); + log_assert(!str.empty() && (str.front() == '$' || str.front() == '\\')); + log_assert(prefix.back() == '.'); + if (str.front() == '\\') + return prefix + str.substr(1); + return prefix + str; +} + // convert the AST into a simpler AST that has all parameters substituted by their // values, unrolled for-loops, expanded generate blocks, etc. when this function // is done with an AST it can be converted into RTLIL using genRTLIL(). @@ -748,6 +758,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // also merge multiple declarations for the same wire (e.g. "output foobar; reg foobar;") if (type == AST_MODULE) { current_scope.clear(); + std::set<std::string> existing; + int counter = 0; + label_genblks(existing, counter); std::map<std::string, AstNode*> this_wire_scope; for (size_t i = 0; i < children.size(); i++) { AstNode *node = children[i]; @@ -1855,19 +1868,24 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, // expand body int index = varbuf->children[0]->integer; - if (body_ast->type == AST_GENBLOCK) - buf = body_ast->clone(); - else - buf = new AstNode(AST_GENBLOCK, body_ast->clone()); - if (buf->str.empty()) { - std::stringstream sstr; - sstr << "$genblock$" << filename << ":" << location.first_line << "$" << (autoidx++); - buf->str = sstr.str(); - } - std::map<std::string, std::string> name_map; + log_assert(body_ast->type == AST_GENBLOCK || body_ast->type == AST_BLOCK); + log_assert(!body_ast->str.empty()); + buf = body_ast->clone(); + std::stringstream sstr; sstr << buf->str << "[" << index << "]."; - buf->expand_genblock(varbuf->str, sstr.str(), name_map); + std::string prefix = sstr.str(); + + // create a scoped localparam for the current value of the loop variable + AstNode *local_index = varbuf->clone(); + size_t pos = local_index->str.rfind('.'); + if (pos != std::string::npos) // remove outer prefix + local_index->str = "\\" + local_index->str.substr(pos + 1); + local_index->str = prefix_id(prefix, local_index->str); + current_scope[local_index->str] = local_index; + current_ast_mod->children.push_back(local_index); + + buf->expand_genblock(prefix); if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { @@ -1915,14 +1933,16 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, { for (size_t i = 0; i < children.size(); i++) if (children[i]->type == AST_WIRE || children[i]->type == AST_MEMORY || children[i]->type == AST_PARAMETER || children[i]->type == AST_LOCALPARAM || children[i]->type == AST_TYPEDEF) - log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is an unsupported SystemVerilog feature!\n"); + { + log_assert(!VERILOG_FRONTEND::sv_mode); + log_file_error(children[i]->filename, children[i]->location.first_line, "Local declaration in unnamed block is only supported in SystemVerilog mode!\n"); + } } // transform block with name if (type == AST_BLOCK && !str.empty()) { - std::map<std::string, std::string> name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); std::vector<AstNode*> new_children; for (size_t i = 0; i < children.size(); i++) @@ -1942,8 +1962,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENBLOCK && children.size() != 0) { if (!str.empty()) { - std::map<std::string, std::string> name_map; - expand_genblock(std::string(), str + ".", name_map); + expand_genblock(str + "."); } for (size_t i = 0; i < children.size(); i++) { @@ -1979,8 +1998,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = new AstNode(AST_GENBLOCK, buf); if (!buf->str.empty()) { - std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { @@ -2058,8 +2076,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, buf = selected_case->clone(); if (!buf->str.empty()) { - std::map<std::string, std::string> name_map; - buf->expand_genblock(std::string(), buf->str + ".", name_map); + buf->expand_genblock(buf->str + "."); } for (size_t i = 0; i < buf->children.size(); i++) { @@ -3159,12 +3176,16 @@ skip_dynamic_range_lvalue_expansion:; log_file_error(filename, location.first_line, "Can't resolve task name `%s'.\n", str.c_str()); } - AstNode *decl = current_scope[str]; std::stringstream sstr; - sstr << "$func$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++) << "$"; + sstr << str << "$func$" << filename << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); + AstNode *decl = current_scope[str]; + decl = decl->clone(); + decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion + decl->expand_genblock(prefix); + bool recommend_const_eval = false; bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) @@ -3177,11 +3198,11 @@ skip_dynamic_range_lvalue_expansion:; } if (all_args_const) { - AstNode *func_workspace = current_scope[str]->clone(); - func_workspace->str = NEW_ID.str(); - func_workspace->replace_result_wire_name_in_function(str, func_workspace->str); + AstNode *func_workspace = decl->clone(); + func_workspace->str = prefix_id(prefix, "$result"); newNode = func_workspace->eval_const_function(this); delete func_workspace; + delete decl; goto apply_newNode; } @@ -3192,8 +3213,6 @@ skip_dynamic_range_lvalue_expansion:; } size_t arg_count = 0; - std::map<std::string, std::string> replace_rules; - vector<AstNode*> added_mod_children; dict<std::string, AstNode*> wire_cache; vector<AstNode*> new_stmts; vector<AstNode*> output_assignments; @@ -3203,16 +3222,17 @@ skip_dynamic_range_lvalue_expansion:; log_assert(type == AST_FCALL); AstNode *wire = NULL; + std::string res_name = prefix_id(prefix, "$result"); for (auto child : decl->children) - if (child->type == AST_WIRE && child->str == str) + if (child->type == AST_WIRE && child->str == res_name) wire = child->clone(); log_assert(wire != NULL); - wire->str = prefix + str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); while (wire->simplify(true, false, false, 1, -1, false, false)) { } @@ -3256,7 +3276,6 @@ skip_dynamic_range_lvalue_expansion:; if (child->type == AST_WIRE && (child->is_input || child->is_output || (type == AST_FCALL && child->str == str))) { AstNode *wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; @@ -3318,7 +3337,6 @@ skip_dynamic_range_lvalue_expansion:; else { wire = child->clone(); - wire->str = prefix + wire->str; wire->port_id = 0; wire->is_input = false; wire->is_output = false; @@ -3329,15 +3347,11 @@ skip_dynamic_range_lvalue_expansion:; wire_cache[child->str] = wire; + current_scope[wire->str] = wire; current_ast_mod->children.push_back(wire); - added_mod_children.push_back(wire); } - if (child->type == AST_WIRE) - while (wire->simplify(true, false, false, 1, -1, false, false)) { } - - replace_rules[child->str] = wire->str; - current_scope[wire->str] = wire; + while (wire->simplify(true, false, false, 1, -1, false, false)) { } if ((child->is_input || child->is_output) && arg_count < children.size()) { @@ -3381,18 +3395,9 @@ skip_dynamic_range_lvalue_expansion:; } } - for (auto child : added_mod_children) { - child->replace_ids(prefix, replace_rules); - while (child->simplify(true, false, false, 1, -1, false, false)) { } - } - for (auto child : decl->children) if (child->type != AST_WIRE && child->type != AST_MEMORY && child->type != AST_PARAMETER && child->type != AST_LOCALPARAM) - { - AstNode *stmt = child->clone(); - stmt->replace_ids(prefix, replace_rules); - new_stmts.push_back(stmt); - } + new_stmts.push_back(child->clone()); new_stmts.insert(new_stmts.end(), output_assignments.begin(), output_assignments.end()); @@ -3405,10 +3410,11 @@ skip_dynamic_range_lvalue_expansion:; } replace_fcall_with_id: + delete decl; if (type == AST_FCALL) { delete_children(); type = AST_IDENTIFIER; - str = prefix + str; + str = prefix_id(prefix, "$result"); } if (type == AST_TCALL) str = ""; @@ -3859,63 +3865,52 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m return block; } -// annotate the names of all wires and other named objects in a generate block -void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) +// annotate the names of all wires and other named objects in a named generate +// or procedural block; nested blocks are themselves annotated such that the +// prefix is carried forward, but resolution of their children is deferred +void AstNode::expand_genblock(const std::string &prefix) { - // `original_scope` defaults to false, and is used to prevent the premature - // prefixing of items in named sub-blocks - - if (!index_var.empty() && type == AST_IDENTIFIER && str == index_var) { - if (children.empty()) { - current_scope[index_var]->children[0]->cloneInto(this); - } else { - AstNode *p = new AstNode(AST_LOCALPARAM, current_scope[index_var]->children[0]->clone()); - p->str = stringf("$genval$%d", autoidx++); - current_ast_mod->children.push_back(p); - str = p->str; - id2ast = p; - } - } - if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) { - if (name_map.count(str) > 0) { - str = name_map[str]; - } else { - // remap the prefix of this ident if it is a local generate scope - size_t pos = str.rfind('.'); - if (pos != std::string::npos) { - std::string existing_prefix = str.substr(0, pos); - if (name_map.count(existing_prefix) > 0) { - str = name_map[existing_prefix] + str.substr(pos); - } + log_assert(!str.empty()); + + // search starting in the innermost scope and then stepping outward + for (size_t ppos = prefix.size() - 1; ppos; --ppos) { + if (prefix.at(ppos) != '.') continue; + + std::string new_prefix = prefix.substr(0, ppos + 1); + auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { + std::string new_name = prefix_id(new_prefix, ident); + if (current_scope.count(new_name)) + return new_name; + return {}; + }; + + // attempt to resolve the full identifier + std::string resolved = attempt_resolve(str); + if (!resolved.empty()) { + str = resolved; + break; } - } - } - std::map<std::string, std::string> backup_name_map; - - auto prefix_node = [&](AstNode* child) { - if (backup_name_map.size() == 0) - backup_name_map = name_map; + // attempt to resolve hierarchical prefixes within the identifier, + // as the prefix could refer to a local scope which exists but + // hasn't yet been elaborated + for (size_t spos = str.size() - 1; spos; --spos) { + if (str.at(spos) != '.') continue; + resolved = attempt_resolve(str.substr(0, spos)); + if (!resolved.empty()) { + str = resolved + str.substr(spos); + ppos = 1; // break outer loop + break; + } + } - // if within a nested scope - if (!original_scope) { - // this declaration shadows anything in the parent scope(s) - name_map[child->str] = child->str; - return; } + } - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = child->str.rfind('.'); - if (pos == std::string::npos) - pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; - else - pos = pos + 1; - new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); - if (new_name[0] != '$' && new_name[0] != '\\') - new_name = prefix[0] + new_name; - - name_map[child->str] = new_name; + auto prefix_node = [&prefix](AstNode* child) { + if (child->str.empty()) return; + std::string new_name = prefix_id(prefix, child->str); if (child->type == AST_FUNCTION) child->replace_result_wire_name_in_function(child->str, new_name); else @@ -3967,43 +3962,55 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma continue; // functions/tasks may reference wires, constants, etc. in this scope if (child->type == AST_FUNCTION || child->type == AST_TASK) - child->expand_genblock(index_var, prefix, name_map, false); - // continue prefixing if this child block is anonymous - else if (child->type == AST_GENBLOCK || child->type == AST_BLOCK) - child->expand_genblock(index_var, prefix, name_map, original_scope && child->str.empty()); - else - child->expand_genblock(index_var, prefix, name_map, original_scope); - } - + continue; + // named blocks pick up the current prefix and will expanded later + if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) + continue; - if (backup_name_map.size() > 0) - name_map.swap(backup_name_map); + child->expand_genblock(prefix); + } } -// rename stuff (used when tasks of functions are instantiated) -void AstNode::replace_ids(const std::string &prefix, const std::map<std::string, std::string> &rules) +// add implicit AST_GENBLOCK names according to IEEE 1364-2005 Section 12.4.3 or +// IEEE 1800-2017 Section 27.6 +void AstNode::label_genblks(std::set<std::string>& existing, int &counter) { - if (type == AST_BLOCK) - { - std::map<std::string, std::string> new_rules = rules; - std::string new_prefix = prefix + str; - - for (auto child : children) - if (child->type == AST_WIRE) { - new_rules[child->str] = new_prefix + child->str; - child->str = new_prefix + child->str; - } + switch (type) { + case AST_GENIF: + case AST_GENFOR: + case AST_GENCASE: + // seeing a proper generate control flow construct increments the + // counter once + ++counter; + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; - for (auto child : children) - if (child->type != AST_WIRE) - child->replace_ids(new_prefix, new_rules); + case AST_GENBLOCK: { + // if this block is unlabeled, generate its corresponding unique name + for (int padding = 0; str.empty(); ++padding) { + std::string candidate = "\\genblk"; + for (int i = 0; i < padding; ++i) + candidate += '0'; + candidate += std::to_string(counter); + if (!existing.count(candidate)) + str = candidate; + } + // within a genblk, the counter starts fresh + std::set<std::string> existing_local = existing; + int counter_local = 0; + for (AstNode *child : children) + child->label_genblks(existing_local, counter_local); + break; } - else - { - if (type == AST_IDENTIFIER && rules.count(str) > 0) - str = rules.at(str); - for (auto child : children) - child->replace_ids(prefix, rules); + + default: + // track names which could conflict with implicit genblk names + if (str.rfind("\\genblk", 0) == 0) + existing.insert(str); + for (AstNode *child : children) + child->label_genblks(existing, counter); + break; } } @@ -4773,6 +4780,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) if (stmt->type == AST_BLOCK) { + if (!stmt->str.empty()) + stmt->expand_genblock(stmt->str + "."); + block->children.erase(block->children.begin()); block->children.insert(block->children.begin(), stmt->children.begin(), stmt->children.end()); stmt->children.clear(); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 8bd58d24c..6255a4204 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -770,6 +770,7 @@ module_body: module_body module_body_stmt | /* the following line makes the generate..endgenrate keywords optional */ module_body gen_stmt | + module_body gen_block | module_body ';' | %empty; @@ -2459,6 +2460,16 @@ behavioral_stmt: exitTypeScope(); if ($4 != NULL && $8 != NULL && *$4 != *$8) frontend_verilog_yyerror("Begin label (%s) and end label (%s) don't match.", $4->c_str()+1, $8->c_str()+1); + AstNode *node = ast_stack.back(); + // In SystemVerilog, unnamed blocks with block item declarations + // create an implicit hierarchy scope + if (sv_mode && node->str.empty()) + for (const AstNode* child : node->children) + if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER + || child->type == AST_LOCALPARAM || child->type == AST_TYPEDEF) { + node->str = "$unnamed_block$" + std::to_string(autoidx++); + break; + } SET_AST_NODE_LOC(ast_stack.back(), @2, @8); delete $4; delete $8; @@ -2473,6 +2484,7 @@ behavioral_stmt: ast_stack.back()->children.push_back($7); } ';' simple_behavioral_stmt ')' { AstNode *block = new AstNode(AST_BLOCK); + block->str = "$for_loop$" + std::to_string(autoidx++); ast_stack.back()->children.push_back(block); ast_stack.push_back(block); } behavioral_stmt { @@ -2722,6 +2734,7 @@ single_arg: module_gen_body: module_gen_body gen_stmt_or_module_body_stmt | + module_gen_body gen_block | %empty; gen_stmt_or_module_body_stmt: @@ -2747,12 +2760,7 @@ gen_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); ast_stack.back()->children.push_back($3); - AstNode *block = new AstNode(AST_GENBLOCK); - ast_stack.back()->children.push_back(block); - ast_stack.push_back(block); - } gen_stmt_block { - ast_stack.pop_back(); - } opt_gen_else { + } gen_stmt_block opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | @@ -2765,6 +2773,18 @@ gen_stmt: SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | + TOK_MSG_TASKS { + AstNode *node = new AstNode(AST_TECALL); + node->str = *$1; + delete $1; + ast_stack.back()->children.push_back(node); + ast_stack.push_back(node); + } opt_arg_list ';'{ + SET_AST_NODE_LOC(ast_stack.back(), @1, @3); + ast_stack.pop_back(); + }; + +gen_block: TOK_BEGIN { enterTypeScope(); } opt_label { @@ -2778,18 +2798,9 @@ gen_stmt: delete $7; SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); - } | - TOK_MSG_TASKS { - AstNode *node = new AstNode(AST_TECALL); - node->str = *$1; - delete $1; - ast_stack.back()->children.push_back(node); - ast_stack.push_back(node); - } opt_arg_list ';'{ - SET_AST_NODE_LOC(ast_stack.back(), @1, @3); - ast_stack.pop_back(); }; +// result is wrapped in a genblock only if necessary gen_stmt_block: { AstNode *node = new AstNode(AST_GENBLOCK); @@ -2798,7 +2809,7 @@ gen_stmt_block: } gen_stmt_or_module_body_stmt { SET_AST_NODE_LOC(ast_stack.back(), @2, @2); ast_stack.pop_back(); - }; + } | gen_block; opt_gen_else: TOK_ELSE gen_stmt_block | %empty %prec FAKE_THEN; diff --git a/kernel/rtlil.h b/kernel/rtlil.h index cd966b815..4dad3c428 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -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()); } diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index d43737c8d..96843d710 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -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) diff --git a/techlibs/common/cmp2lcu.v b/techlibs/common/cmp2lcu.v index a221727e7..4e62039e9 100644 --- a/techlibs/common/cmp2lcu.v +++ b/techlibs/common/cmp2lcu.v @@ -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)) diff --git a/techlibs/common/cmp2lut.v b/techlibs/common/cmp2lut.v index ec8f98e8d..c753bd2f1 100644 --- a/techlibs/common/cmp2lut.v +++ b/techlibs/common/cmp2lut.v @@ -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; diff --git a/techlibs/common/mul2dsp.v b/techlibs/common/mul2dsp.v index bec47d01f..f22f47b4a 100644 --- a/techlibs/common/mul2dsp.v +++ b/techlibs/common/mul2dsp.v @@ -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
diff --git a/techlibs/ice40/brams_map.v b/techlibs/ice40/brams_map.v index ad3bccd21..db9f5d8ce 100644 --- a/techlibs/ice40/brams_map.v +++ b/techlibs/ice40/brams_map.v @@ -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 diff --git a/techlibs/xilinx/arith_map.v b/techlibs/xilinx/arith_map.v index eb8a04bde..63be7563e 100644 --- a/techlibs/xilinx/arith_map.v +++ b/techlibs/xilinx/arith_map.v @@ -151,6 +151,8 @@ generate if (`LUT_SIZE == 4) begin ); end endgenerate + assign X = S; + end else begin localparam CARRY4_COUNT = (Y_WIDTH + 3) / 4; @@ -193,8 +195,8 @@ end else begin end end endgenerate -end endgenerate - assign X = S; + +end endgenerate endmodule diff --git a/tests/simple/func_block.v b/tests/simple/func_block.v new file mode 100644 index 000000000..be759d1a9 --- /dev/null +++ b/tests/simple/func_block.v @@ -0,0 +1,33 @@ +`default_nettype none + +module top(inp, out1, out2, out3); + input wire [31:0] inp; + + function automatic [31:0] func1; + input [31:0] inp; + reg [31:0] idx; + for (idx = 0; idx < 32; idx = idx + 1) begin : blk + func1[idx] = (idx & 1'b1) ^ inp[idx]; + end + endfunction + + function automatic [31:0] func2; + input [31:0] inp; + reg [31:0] idx; + for (idx = 0; idx < 32; idx = idx + 1) begin : blk + func2[idx] = (idx & 1'b1) ^ inp[idx]; + end + endfunction + + function automatic [31:0] func3; + localparam A = 32 - 1; + parameter B = 1 - 0; + input [31:0] inp; + func3[A:B] = inp[A:B]; + endfunction + + output wire [31:0] out1, out2, out3; + assign out1 = func1(inp); + assign out2 = func2(inp); + assign out3 = func3(inp); +endmodule diff --git a/tests/simple/func_recurse.v b/tests/simple/func_recurse.v new file mode 100644 index 000000000..d61c8cc06 --- /dev/null +++ b/tests/simple/func_recurse.v @@ -0,0 +1,25 @@ +module top( + input wire [3:0] inp, + output wire [3:0] out1, out2 +); + function automatic [3:0] pow_a; + input [3:0] base, exp; + begin + pow_a = 1; + if (exp > 0) + pow_a = base * pow_a(base, exp - 1); + end + endfunction + + function automatic [3:0] pow_b; + input [3:0] base, exp; + begin + pow_b = 1; + if (exp > 0) + pow_b = base * pow_b(base, exp - 1); + end + endfunction + + assign out1 = pow_a(inp, 3); + assign out2 = pow_b(2, 2); +endmodule diff --git a/tests/simple/func_width_scope.v b/tests/simple/func_width_scope.v new file mode 100644 index 000000000..ce81e894e --- /dev/null +++ b/tests/simple/func_width_scope.v @@ -0,0 +1,41 @@ +module top(inp, out1, out2); + input wire signed inp; + + localparam WIDTH_A = 5; + function automatic [WIDTH_A-1:0] func1; + input reg [WIDTH_A-1:0] inp; + func1 = ~inp; + endfunction + wire [func1(0)-1:0] xc; + assign xc = 1'sb1; + wire [WIDTH_A-1:0] xn; + assign xn = func1(inp); + + generate + if (1) begin : blk + localparam WIDTH_A = 6; + function automatic [WIDTH_A-1:0] func2; + input reg [WIDTH_A-1:0] inp; + func2 = ~inp; + endfunction + wire [func2(0)-1:0] yc; + assign yc = 1'sb1; + wire [WIDTH_A-1:0] yn; + assign yn = func2(inp); + + localparam WIDTH_B = 7; + function automatic [WIDTH_B-1:0] func3; + input reg [WIDTH_B-1:0] inp; + func3 = ~inp; + endfunction + wire [func3(0)-1:0] zc; + assign zc = 1'sb1; + wire [WIDTH_B-1:0] zn; + assign zn = func3(inp); + end + endgenerate + + output wire [1023:0] out1, out2; + assign out1 = {xc, 1'b0, blk.yc, 1'b0, blk.zc}; + assign out2 = {xn, 1'b0, blk.yn, 1'b0, blk.zn}; +endmodule diff --git a/tests/simple/genblk_collide.v b/tests/simple/genblk_collide.v new file mode 100644 index 000000000..f42dd2cfc --- /dev/null +++ b/tests/simple/genblk_collide.v @@ -0,0 +1,27 @@ +`default_nettype none + +module top1; + generate + if (1) begin : foo + if (1) begin : bar + wire x; + end + assign bar.x = 1; + wire y; + end + endgenerate +endmodule + +module top2; + genvar i; + generate + if (1) begin : foo + wire x; + for (i = 0; i < 1; i = i + 1) begin : foo + if (1) begin : foo + assign x = 1; + end + end + end + endgenerate +endmodule diff --git a/tests/simple/genblk_dive.v b/tests/simple/genblk_dive.v new file mode 100644 index 000000000..98d0e1f4b --- /dev/null +++ b/tests/simple/genblk_dive.v @@ -0,0 +1,21 @@ +`default_nettype none +module top(output wire x); + generate + if (1) begin : Z + if (1) begin : A + wire x; + if (1) begin : B + wire x; + if (1) begin : C + wire x; + assign B.x = 0; + wire z = A.B.C.x; + end + assign A.x = A.B.C.x; + end + assign B.C.x = B.x; + end + end + endgenerate + assign x = Z.A.x; +endmodule diff --git a/tests/simple/genblk_order.v b/tests/simple/genblk_order.v new file mode 100644 index 000000000..7c3a7a756 --- /dev/null +++ b/tests/simple/genblk_order.v @@ -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 diff --git a/tests/simple/generate.v b/tests/simple/generate.v index 667f40096..445c88ba8 100644 --- a/tests/simple/generate.v +++ b/tests/simple/generate.v @@ -260,3 +260,66 @@ module gen_test8; `ASSERT(gen_test8.A.C.x == 1) `ASSERT(gen_test8.A.B.x == 0) endmodule + +// ------------------------------------------ + +module gen_test9; + +// `define VERIFY +`ifdef VERIFY + `define ASSERT(expr) assert property (expr); +`else + `define ASSERT(expr) +`endif + + wire [1:0] w = 2'b11; + generate + begin : A + wire [1:0] x; + begin : B + wire [1:0] y = 2'b00; + `ASSERT(w == 3) + `ASSERT(x == 2) + `ASSERT(y == 0) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) + end + begin : C + wire [1:0] z = 2'b01; + `ASSERT(w == 3) + `ASSERT(x == 2) + `ASSERT(z == 1) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) + end + assign x = B.y ^ 2'b11 ^ C.z; + `ASSERT(x == 2) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) + end + endgenerate + + `ASSERT(w == 3) + `ASSERT(A.x == 2) + `ASSERT(A.C.z == 1) + `ASSERT(A.B.y == 0) + `ASSERT(gen_test9.w == 3) + `ASSERT(gen_test9.A.x == 2) + `ASSERT(gen_test9.A.C.z == 1) + `ASSERT(gen_test9.A.B.y == 0) +endmodule diff --git a/tests/simple/local_loop_var.sv b/tests/simple/local_loop_var.sv new file mode 100644 index 000000000..46b4e5c22 --- /dev/null +++ b/tests/simple/local_loop_var.sv @@ -0,0 +1,11 @@ +module top(out); + output integer out; + initial begin + integer i; + for (i = 0; i < 5; i = i + 1) + if (i == 0) + out = 1; + else + out += 2 ** i; + end +endmodule diff --git a/tests/simple/loop_var_shadow.v b/tests/simple/loop_var_shadow.v new file mode 100644 index 000000000..0222a4493 --- /dev/null +++ b/tests/simple/loop_var_shadow.v @@ -0,0 +1,15 @@ +module top(out); + genvar i; + generate + for (i = 0; i < 2; i = i + 1) begin : loop + localparam j = i + 1; + if (1) begin : blk + localparam i = j + 1; + wire [i:0] x; + assign x = 1'sb1; + end + end + endgenerate + output wire [63:0] out; + assign out = {loop[0].blk.x, loop[1].blk.x}; +endmodule diff --git a/tests/simple/named_genblk.v b/tests/simple/named_genblk.v new file mode 100644 index 000000000..b8300fc4d --- /dev/null +++ b/tests/simple/named_genblk.v @@ -0,0 +1,27 @@ +`default_nettype none +module top; + generate + if (1) begin + wire t; + begin : foo + wire x; + end + wire u; + end + begin : bar + wire x; + wire y; + begin : baz + wire x; + wire z; + end + end + endgenerate + assign genblk1.t = 1; + assign genblk1.foo.x = 1; + assign genblk1.u = 1; + assign bar.x = 1; + assign bar.y = 1; + assign bar.baz.x = 1; + assign bar.baz.z = 1; +endmodule diff --git a/tests/simple/nested_genblk_resolve.v b/tests/simple/nested_genblk_resolve.v new file mode 100644 index 000000000..da5593f8a --- /dev/null +++ b/tests/simple/nested_genblk_resolve.v @@ -0,0 +1,14 @@ +`default_nettype none +module top; + generate + if (1) begin + wire x; + genvar i; + for (i = 0; i < 1; i = i + 1) begin + if (1) begin + assign x = 1; + end + end + end + endgenerate +endmodule diff --git a/tests/simple/unnamed_block_decl.sv b/tests/simple/unnamed_block_decl.sv new file mode 100644 index 000000000..e81b457a8 --- /dev/null +++ b/tests/simple/unnamed_block_decl.sv @@ -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 diff --git a/tests/various/gen_if_null.v b/tests/various/gen_if_null.v index a12ac6288..992bc68b3 100644 --- a/tests/various/gen_if_null.v +++ b/tests/various/gen_if_null.v @@ -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 diff --git a/tests/various/gen_if_null.ys b/tests/various/gen_if_null.ys index 31dfc444b..0733e3a94 100644 --- a/tests/various/gen_if_null.ys +++ b/tests/various/gen_if_null.ys @@ -1,4 +1,4 @@ read_verilog gen_if_null.v -select -assert-count 1 test/x -select -assert-count 1 test/y +select -assert-count 1 test/genblk1.x +select -assert-count 1 test/genblk2.y select -assert-count 1 test/z diff --git a/tests/verilog/bug2493.ys b/tests/verilog/bug2493.ys new file mode 100644 index 000000000..380d2a823 --- /dev/null +++ b/tests/verilog/bug2493.ys @@ -0,0 +1,12 @@ +logger -expect error "Failed to detect width for identifier \\genblk1\.y!" 1 +read_verilog <<EOT +module top1; + wire x; + generate + if (1) begin + mod y(); + assign x = y; + end + endgenerate +endmodule +EOT diff --git a/tests/verilog/bug656.v b/tests/verilog/bug656.v new file mode 100644 index 000000000..068d045fd --- /dev/null +++ b/tests/verilog/bug656.v @@ -0,0 +1,21 @@ +module top #( + parameter WIDTH = 6 +) ( + input [WIDTH-1:0] a_i, + input [WIDTH-1:0] b_i, + output [WIDTH-1:0] z_o +); + genvar g; + generate + for (g = 0; g < WIDTH; g = g + 1) begin + if (g > 2) begin + wire tmp; + assign tmp = a_i[g] || b_i[g]; + assign z_o[g] = tmp; + end + else begin + assign z_o[g] = a_i[g] && b_i[g]; + end + end + endgenerate +endmodule diff --git a/tests/verilog/bug656.ys b/tests/verilog/bug656.ys new file mode 100644 index 000000000..7f367341a --- /dev/null +++ b/tests/verilog/bug656.ys @@ -0,0 +1,13 @@ +read_verilog bug656.v + +select -assert-count 1 top/a_i +select -assert-count 1 top/b_i +select -assert-count 1 top/z_o + +select -assert-none top/genblk1[0].genblk1.tmp +select -assert-none top/genblk1[1].genblk1.tmp +select -assert-none top/genblk1[2].genblk1.tmp + +select -assert-count 1 top/genblk1[3].genblk1.tmp +select -assert-count 1 top/genblk1[4].genblk1.tmp +select -assert-count 1 top/genblk1[5].genblk1.tmp diff --git a/tests/verilog/genblk_case.v b/tests/verilog/genblk_case.v new file mode 100644 index 000000000..081fb09d3 --- /dev/null +++ b/tests/verilog/genblk_case.v @@ -0,0 +1,26 @@ +module top; + parameter YES = 1; + generate + if (YES) wire y; + else wire n; + + if (!YES) wire n; + else wire y; + + case (YES) + 1: wire y; + 0: wire n; + endcase + + case (!YES) + 0: wire y; + 1: wire n; + endcase + + if (YES) wire y; + else wire n; + + if (!YES) wire n; + else wire y; + endgenerate +endmodule diff --git a/tests/verilog/genblk_case.ys b/tests/verilog/genblk_case.ys new file mode 100644 index 000000000..3c1bb51f9 --- /dev/null +++ b/tests/verilog/genblk_case.ys @@ -0,0 +1,15 @@ +read_verilog genblk_case.v + +select -assert-count 0 top/genblk1.n +select -assert-count 0 top/genblk2.n +select -assert-count 0 top/genblk3.n +select -assert-count 0 top/genblk4.n +select -assert-count 0 top/genblk5.n +select -assert-count 0 top/genblk6.n + +select -assert-count 1 top/genblk1.y +select -assert-count 1 top/genblk2.y +select -assert-count 1 top/genblk3.y +select -assert-count 1 top/genblk4.y +select -assert-count 1 top/genblk5.y +select -assert-count 1 top/genblk6.y diff --git a/tests/verilog/hidden_decl.ys b/tests/verilog/hidden_decl.ys new file mode 100644 index 000000000..aed7847dc --- /dev/null +++ b/tests/verilog/hidden_decl.ys @@ -0,0 +1,11 @@ +logger -expect error "Identifier `\\y' is implicitly declared and `default_nettype is set to none" 1 +read_verilog <<EOT +`default_nettype none +module top1; + wire x; + generate + if (1) wire y; + endgenerate + assign x = y; +endmodule +EOT diff --git a/tests/verilog/unnamed_block.ys b/tests/verilog/unnamed_block.ys new file mode 100644 index 000000000..0f209a089 --- /dev/null +++ b/tests/verilog/unnamed_block.ys @@ -0,0 +1,28 @@ +read_verilog <<EOT +module top; + initial begin : blk + integer x; + end +endmodule +EOT + +delete + +read_verilog -sv <<EOT +module top; + initial begin + integer x; + end +endmodule +EOT + +delete + +logger -expect error "Local declaration in unnamed block is only supported in SystemVerilog mode!" 1 +read_verilog <<EOT +module top; + initial begin + integer x; + end +endmodule +EOT diff --git a/tests/verilog/unnamed_genblk.sv b/tests/verilog/unnamed_genblk.sv new file mode 100644 index 000000000..41828b1b0 --- /dev/null +++ b/tests/verilog/unnamed_genblk.sv @@ -0,0 +1,39 @@ +// This test is taken directly from Section 27.6 of IEEE 1800-2017 + +module top; + parameter genblk2 = 0; + genvar i; + + // The following generate block is implicitly named genblk1 + + if (genblk2) logic a; // top.genblk1.a + else logic b; // top.genblk1.b + + // The following generate block is implicitly named genblk02 + // as genblk2 is already a declared identifier + + if (genblk2) logic a; // top.genblk02.a + else logic b; // top.genblk02.b + + // The following generate block would have been named genblk3 + // but is explicitly named g1 + + for (i = 0; i < 1; i = i + 1) begin : g1 // block name + // The following generate block is implicitly named genblk1 + // as the first nested scope inside g1 + if (1) logic a; // top.g1[0].genblk1.a + end + + // The following generate block is implicitly named genblk4 since + // it belongs to the fourth generate construct in scope "top". + // The previous generate block would have been + // named genblk3 if it had not been explicitly named g1 + + for (i = 0; i < 1; i = i + 1) + // The following generate block is implicitly named genblk1 + // as the first nested generate block in genblk4 + if (1) logic a; // top.genblk4[0].genblk1.a + + // The following generate block is implicitly named genblk5 + if (1) logic a; // top.genblk5.a +endmodule diff --git a/tests/verilog/unnamed_genblk.ys b/tests/verilog/unnamed_genblk.ys new file mode 100644 index 000000000..2b9aa9d69 --- /dev/null +++ b/tests/verilog/unnamed_genblk.ys @@ -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 |