diff options
Diffstat (limited to 'frontends/ast/simplify.cc')
-rw-r--r-- | frontends/ast/simplify.cc | 321 |
1 files changed, 237 insertions, 84 deletions
diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index f11383b96..7f9795d29 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -89,7 +89,7 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg case 'S': case 'd': case 'D': - if (got_len) + if (got_len && len_value != 0) goto unsupported_format; YS_FALLTHROUGH case 'x': @@ -517,6 +517,27 @@ static AstNode *make_packed_struct(AstNode *template_node, std::string &name) return wnode; } +// check if a node or its children contains an assignment to the given variable +static bool node_contains_assignment_to(const AstNode* node, const AstNode* var) +{ + if (node->type == AST_ASSIGN_EQ || node->type == AST_ASSIGN_LE) { + // current node is iteslf an assignment + log_assert(node->children.size() >= 2); + const AstNode* lhs = node->children[0]; + if (lhs->type == AST_IDENTIFIER && lhs->str == var->str) + return false; + } + for (const AstNode* child : node->children) { + // if this child shadows the given variable + if (child != var && child->str == var->str && child->type == AST_WIRE) + break; // skip the remainder of this block/scope + // depth-first short circuit + if (!node_contains_assignment_to(child, var)) + return false; + } + return true; +} + // 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(). @@ -812,7 +833,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, while (node->simplify(true, false, false, 1, -1, false, node->type == AST_PARAMETER || node->type == AST_LOCALPARAM)) did_something = true; if (node->type == AST_ENUM) { - for (auto enode YS_ATTRIBUTE(unused) : node->children){ + for (auto enode : node->children){ log_assert(enode->type==AST_ENUM_ITEM); while (node->simplify(true, false, false, 1, -1, false, in_param)) did_something = true; @@ -984,6 +1005,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_TO_SIGNED: case AST_TO_UNSIGNED: case AST_SELFSZ: + case AST_CAST_SIZE: case AST_CONCAT: case AST_REPLICATE: case AST_REDUCE_AND: @@ -1160,6 +1182,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, bool in_param_here = in_param; if (i == 0 && (type == AST_REPLICATE || type == AST_WIRE)) const_fold_here = true, in_param_here = true; + if (i == 0 && (type == AST_GENIF || type == AST_GENCASE)) + in_param_here = true; + if (i == 1 && (type == AST_FOR || type == AST_GENFOR)) + in_param_here = true; if (type == AST_PARAMETER || type == AST_LOCALPARAM) const_fold_here = true; if (i == 0 && (type == AST_ASSIGN || type == AST_ASSIGN_EQ || type == AST_ASSIGN_LE)) @@ -1716,25 +1742,27 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, body_ast->children.size() == 1 && body_ast->children.at(0)->type == AST_GENBLOCK) body_ast = body_ast->children.at(0); + const char* loop_type_str = "procedural"; + const char* var_type_str = "register"; + AstNodeType var_type = AST_WIRE; + if (type == AST_GENFOR) { + loop_type_str = "generate"; + var_type_str = "genvar"; + var_type = AST_GENVAR; + } + if (init_ast->type != AST_ASSIGN_EQ) - log_file_error(filename, location.first_line, "Unsupported 1st expression of generate for-loop!\n"); + log_file_error(filename, location.first_line, "Unsupported 1st expression of %s for-loop!\n", loop_type_str); if (next_ast->type != AST_ASSIGN_EQ) - log_file_error(filename, location.first_line, "Unsupported 3rd expression of generate for-loop!\n"); + log_file_error(filename, location.first_line, "Unsupported 3rd expression of %s for-loop!\n", loop_type_str); - if (type == AST_GENFOR) { - if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_GENVAR) - log_file_error(filename, location.first_line, "Left hand side of 1st expression of generate for-loop is not a gen var!\n"); - if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_GENVAR) - log_file_error(filename, location.first_line, "Left hand side of 3rd expression of generate for-loop is not a gen var!\n"); - } else { - if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != AST_WIRE) - log_file_error(filename, location.first_line, "Left hand side of 1st expression of generate for-loop is not a register!\n"); - if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != AST_WIRE) - log_file_error(filename, location.first_line, "Left hand side of 3rd expression of generate for-loop is not a register!\n"); - } + if (init_ast->children[0]->id2ast == NULL || init_ast->children[0]->id2ast->type != var_type) + log_file_error(filename, location.first_line, "Left hand side of 1st expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); + if (next_ast->children[0]->id2ast == NULL || next_ast->children[0]->id2ast->type != var_type) + log_file_error(filename, location.first_line, "Left hand side of 3rd expression of %s for-loop is not a %s!\n", loop_type_str, var_type_str); if (init_ast->children[0]->id2ast != next_ast->children[0]->id2ast) - log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of generate for-loop!\n"); + log_file_error(filename, location.first_line, "Incompatible left-hand sides in 1st and 3rd expression of %s for-loop!\n", loop_type_str); // eval 1st expression AstNode *varbuf = init_ast->children[1]->clone(); @@ -1746,7 +1774,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (varbuf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right hand side of 1st expression of generate for-loop is not constant!\n"); + log_file_error(filename, location.first_line, "Right hand side of 1st expression of %s for-loop is not constant!\n", loop_type_str); auto resolved = current_scope.at(init_ast->children[0]->str); if (resolved->range_valid) { @@ -1787,7 +1815,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "2nd expression of generate for-loop is not constant!\n"); + log_file_error(filename, location.first_line, "2nd expression of %s for-loop is not constant!\n", loop_type_str); if (buf->integer == 0) { delete buf; @@ -1813,7 +1841,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, if (type == AST_GENFOR) { for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(false, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } } else { @@ -1833,7 +1861,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (buf->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Right hand side of 3rd expression of generate for-loop is not constant (%s)!\n", type2str(buf->type).c_str()); + log_file_error(filename, location.first_line, "Right hand side of 3rd expression of %s for-loop is not constant (%s)!\n", loop_type_str, type2str(buf->type).c_str()); delete varbuf->children[0]; varbuf->children[0] = buf; @@ -1889,7 +1917,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < children.size(); i++) { - children[i]->simplify(false, false, false, stage, -1, false, false); + children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(children[i]); } @@ -1926,7 +1954,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(false, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -1976,7 +2004,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, continue; buf = child->clone(); - while (buf->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } + while (buf->simplify(true, false, false, stage, width_hint, sign_hint, true)) { } if (buf->type != AST_CONSTANT) { // for (auto f : log_files) // dumpAst(f, "verilog-ast> "); @@ -2005,7 +2033,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } for (size_t i = 0; i < buf->children.size(); i++) { - buf->children[i]->simplify(false, false, false, stage, -1, false, false); + buf->children[i]->simplify(const_fold, false, false, stage, -1, false, false); current_ast_mod->children.push_back(buf->children[i]); } @@ -2170,6 +2198,9 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, use_case_method = true; } + if (!use_case_method && current_always->detect_latch(children[0]->str)) + use_case_method = true; + if (use_case_method) { // big case block @@ -3055,7 +3086,7 @@ skip_dynamic_range_lvalue_expansion:; bool all_args_const = true; for (auto child : children) { while (child->simplify(true, false, false, 1, -1, false, true)) { } - if (child->type != AST_CONSTANT) + if (child->type != AST_CONSTANT && child->type != AST_REALVALUE) all_args_const = false; } @@ -3222,6 +3253,13 @@ skip_dynamic_range_lvalue_expansion:; if ((child->is_input || child->is_output) && arg_count < children.size()) { AstNode *arg = children[arg_count++]->clone(); + // convert purely constant arguments into localparams + if (child->is_input && child->type == AST_WIRE && arg->type == AST_CONSTANT && node_contains_assignment_to(decl, child)) { + wire->type = AST_LOCALPARAM; + wire->attributes.erase(ID::nosync); + wire->children.insert(wire->children.begin(), arg->clone()); + continue; + } AstNode *wire_id = new AstNode(AST_IDENTIFIER); wire_id->str = wire->str; AstNode *assign = child->is_input ? @@ -3514,6 +3552,13 @@ replace_fcall_later:; } } break; + case AST_CAST_SIZE: + if (children.at(0)->type == AST_CONSTANT && children.at(1)->type == AST_CONSTANT) { + int width = children[0]->bitsAsConst().as_int(); + RTLIL::Const val = children[1]->bitsAsConst(width); + newNode = mkconst_bits(val.bits, children[1]->is_signed); + } + break; case AST_CONCAT: string_op = !children.empty(); for (auto it = children.begin(); it != children.end(); it++) { @@ -3700,8 +3745,11 @@ AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *m } // 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) +void AstNode::expand_genblock(std::string index_var, std::string prefix, std::map<std::string, std::string> &name_map, bool original_scope) { + // `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); @@ -3714,53 +3762,85 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma } } - if ((type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE) && name_map.count(str) > 0) - str = name_map[str]; + 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); + } + } + } + } std::map<std::string, std::string> backup_name_map; + auto prefix_node = [&](AstNode* child) { + if (backup_name_map.size() == 0) + backup_name_map = name_map; + + // if within a nested scope + if (!original_scope) { + // this declaration shadows anything in the parent scope(s) + name_map[child->str] = child->str; + return; + } + + std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; + size_t pos = child->str.rfind('.'); + if (pos == std::string::npos) + pos = child->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; + else + pos = pos + 1; + new_name = child->str.substr(0, pos) + new_name + child->str.substr(pos); + if (new_name[0] != '$' && new_name[0] != '\\') + new_name = prefix[0] + new_name; + + name_map[child->str] = new_name; + if (child->type == AST_FUNCTION) + replace_result_wire_name_in_function(child, child->str, new_name); + else + child->str = new_name; + current_scope[new_name] = child; + }; + for (size_t i = 0; i < children.size(); i++) { AstNode *child = children[i]; - if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || - child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF || child->type == AST_ENUM_ITEM) { - if (backup_name_map.size() == 0) - backup_name_map = name_map; - 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) - replace_result_wire_name_in_function(child, child->str, new_name); - else - child->str = new_name; - current_scope[new_name] = child; - } - if (child->type == AST_ENUM){ + + switch (child->type) { + case AST_WIRE: + case AST_MEMORY: + case AST_PARAMETER: + case AST_LOCALPARAM: + case AST_FUNCTION: + case AST_TASK: + case AST_CELL: + case AST_TYPEDEF: + case AST_ENUM_ITEM: + case AST_GENVAR: + prefix_node(child); + break; + + case AST_BLOCK: + case AST_GENBLOCK: + if (!child->str.empty()) + prefix_node(child); + break; + + case AST_ENUM: current_scope[child->str] = child; for (auto enode : child->children){ log_assert(enode->type == AST_ENUM_ITEM); - if (backup_name_map.size() == 0) - backup_name_map = name_map; - std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; - size_t pos = enode->str.rfind('.'); - if (pos == std::string::npos) - pos = enode->str[0] == '\\' && prefix[0] == '\\' ? 1 : 0; - else - pos = pos + 1; - new_name = enode->str.substr(0, pos) + new_name + enode->str.substr(pos); - if (new_name[0] != '$' && new_name[0] != '\\') - new_name = prefix[0] + new_name; - name_map[enode->str] = new_name; - - enode->str = new_name; - current_scope[new_name] = enode; + prefix_node(enode); } + break; + + default: + break; } } @@ -3770,8 +3850,14 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma // still needs to recursed-into if (type == AST_PREFIX && i == 1 && child->type == AST_IDENTIFIER) continue; - if (child->type != AST_FUNCTION && child->type != AST_TASK) - child->expand_genblock(index_var, prefix, name_map); + // 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); } @@ -4238,6 +4324,62 @@ void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) addr_bits++; } +bool AstNode::detect_latch(const std::string &var) +{ + switch (type) + { + case AST_ALWAYS: + for (auto &c : children) + { + switch (c->type) + { + case AST_POSEDGE: + case AST_NEGEDGE: + return false; + case AST_EDGE: + break; + case AST_BLOCK: + if (!c->detect_latch(var)) + return false; + break; + default: + log_abort(); + } + } + return true; + case AST_BLOCK: + for (auto &c : children) + if (!c->detect_latch(var)) + return false; + return true; + case AST_CASE: + { + bool r = true; + for (auto &c : children) { + if (c->type == AST_COND) { + if (c->children.at(1)->detect_latch(var)) + return true; + r = false; + } + if (c->type == AST_DEFAULT) { + if (c->children.at(0)->detect_latch(var)) + return true; + r = false; + } + } + return r; + } + case AST_ASSIGN_EQ: + case AST_ASSIGN_LE: + if (children.at(0)->type == AST_IDENTIFIER && + children.at(0)->children.empty() && children.at(0)->str == var) + return false; + return true; + default: + return true; + } +} + bool AstNode::has_const_only_constructs(bool &recommend_const_eval) { if (type == AST_FOR) @@ -4303,27 +4445,9 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) size_t argidx = 0; for (auto child : children) { - if (child->type == AST_WIRE) - { - while (child->simplify(true, false, false, 1, -1, false, true)) { } - if (!child->range_valid) - log_file_error(child->filename, child->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", - child->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); - variables[child->str].val = RTLIL::Const(RTLIL::State::Sx, abs(child->range_left - child->range_right)+1); - variables[child->str].offset = min(child->range_left, child->range_right); - variables[child->str].is_signed = child->is_signed; - if (child->is_input && argidx < fcall->children.size()) - variables[child->str].val = fcall->children.at(argidx++)->bitsAsConst(variables[child->str].val.bits.size()); - backup_scope[child->str] = current_scope[child->str]; - current_scope[child->str] = child; - continue; - } - block->children.push_back(child->clone()); } - log_assert(variables.count(str) != 0); - while (!block->children.empty()) { AstNode *stmt = block->children.front(); @@ -4335,6 +4459,35 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) stmt->dumpAst(NULL, "stmt> "); #endif + if (stmt->type == AST_WIRE) + { + while (stmt->simplify(true, false, false, 1, -1, false, true)) { } + if (!stmt->range_valid) + log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", + stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); + variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); + variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); + variables[stmt->str].is_signed = stmt->is_signed; + if (stmt->is_input && argidx < fcall->children.size()) { + int width = variables[stmt->str].val.bits.size(); + auto* arg_node = fcall->children.at(argidx++); + if (arg_node->type == AST_CONSTANT) { + variables[stmt->str].val = arg_node->bitsAsConst(width); + } else { + log_assert(arg_node->type == AST_REALVALUE); + variables[stmt->str].val = arg_node->realAsConst(width); + } + } + if (!backup_scope.count(stmt->str)) + backup_scope[stmt->str] = current_scope[stmt->str]; + current_scope[stmt->str] = stmt; + + block->children.erase(block->children.begin()); + continue; + } + + log_assert(variables.count(str) != 0); + if (stmt->type == AST_ASSIGN_EQ) { if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && |