systemverilog-plugin: Adjusting simplify.cc, removing unneded code Signed-off-by: Magdalena Andrys <mandrys@antmicro.com>
diff --git a/systemverilog-plugin/third_party/yosys/simplify.cc b/systemverilog-plugin/third_party/yosys/simplify.cc index 78e5768..71d15a4 100644 --- a/systemverilog-plugin/third_party/yosys/simplify.cc +++ b/systemverilog-plugin/third_party/yosys/simplify.cc
@@ -28,8 +28,8 @@ #include "kernel/log.h" #include "libs/sha1/sha1.h" -#include "frontends/verilog/verilog_frontend.h" -#include "ast.h" + +#include "const2ast.h" #include <sstream> #include <stdarg.h> @@ -37,149 +37,22 @@ #include <math.h> YOSYS_NAMESPACE_BEGIN - -using namespace AST; -using namespace AST_INTERNAL; - -// Process a format string and arguments for $display, $write, $sprintf, etc - -std::string AstNode::process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint) { - // Other arguments are placeholders. Process the string as we go through it - std::string sout; - for (size_t i = 0; i < sformat.length(); i++) - { - // format specifier - if (sformat[i] == '%') - { - // If there's no next character, that's a problem - if (i+1 >= sformat.length()) - log_file_error(filename, location.first_line, "System task `%s' called with `%%' at end of string.\n", str.c_str()); - - char cformat = sformat[++i]; - - // %% is special, does not need a matching argument - if (cformat == '%') - { - sout += '%'; - continue; - } - - bool got_len = false; - bool got_zlen = false; - int len_value = 0; - - while ('0' <= cformat && cformat <= '9') - { - if (!got_len && cformat == '0') - got_zlen = true; - - got_len = true; - len_value = 10*len_value + (cformat - '0'); - - cformat = sformat[++i]; - } - - // Simplify the argument - AstNode *node_arg = nullptr; - - // Everything from here on depends on the format specifier - switch (cformat) - { - case 's': - case 'S': - case 'd': - case 'D': - if (got_len && len_value != 0) - goto unsupported_format; - YS_FALLTHROUGH - case 'x': - case 'X': - if (next_arg >= GetSize(children)) - log_file_error(filename, location.first_line, "Missing argument for %%%c format specifier in system task `%s'.\n", - cformat, str.c_str()); - - node_arg = children[next_arg++]; - while (node_arg->simplify(true, false, false, stage, width_hint, sign_hint, false)) { } - if (node_arg->type != AST_CONSTANT) - log_file_error(filename, location.first_line, "Failed to evaluate system task `%s' with non-constant argument.\n", str.c_str()); - break; - - case 'm': - case 'M': - if (got_len) - goto unsupported_format; - break; - - case 'l': - case 'L': - if (got_len) - goto unsupported_format; - break; - - default: - unsupported_format: - log_file_error(filename, location.first_line, "System task `%s' called with invalid/unsupported format specifier.\n", str.c_str()); - break; - } - - switch (cformat) - { - case 's': - case 'S': - sout += node_arg->bitsAsConst().decode_string(); - break; - - case 'd': - case 'D': - sout += stringf("%d", node_arg->bitsAsConst().as_int()); - break; - - case 'x': - case 'X': - { - Const val = node_arg->bitsAsConst(); - - while (GetSize(val) % 4 != 0) - val.bits.push_back(State::S0); - - int len = GetSize(val) / 4; - for (int i = len; i < len_value; i++) - sout += got_zlen ? '0' : ' '; - - for (int i = len-1; i >= 0; i--) { - Const digit = val.extract(4*i, 4); - if (digit.is_fully_def()) - sout += stringf(cformat == 'x' ? "%x" : "%X", digit.as_int()); - else - sout += cformat == 'x' ? "x" : "X"; - } - } - break; - - case 'm': - case 'M': - sout += log_id(current_module->name); - break; - - case 'l': - case 'L': - sout += log_id(current_module->name); - break; - - default: - log_abort(); - } - } - - // not a format specifier - else - sout += sformat[i]; - } - return sout; +namespace VERILOG_FRONTEND +{ +extern bool sv_mode; } +YOSYS_NAMESPACE_END +namespace systemverilog_plugin +{ -void AstNode::annotateTypedEnums(AstNode *template_node) +using namespace ::Yosys; +using namespace ::Yosys::AST; +using namespace ::Yosys::AST_INTERNAL; + +bool simplify(AstNode *ast_node, bool const_fold, bool at_zero, bool in_lvalue, int stage, int width_hint, bool sign_hint, bool in_param); + +void annotateTypedEnums(AstNode *ast_node, AstNode *template_node) { //check if enum if (template_node->attributes.count(ID::enum_type)) { @@ -192,7 +65,7 @@ log_assert(current_scope.count(enum_type) == 1); AstNode *enum_node = current_scope.at(enum_type); log_assert(enum_node->type == AST_ENUM); - while (enum_node->simplify(true, false, false, 1, -1, false, true)) { } + while (simplify(enum_node, true, false, false, 1, -1, false, true)) { } //get width from 1st enum item: log_assert(enum_node->children.size() >= 1); AstNode *enum_item0 = enum_node->children[0]; @@ -233,7 +106,7 @@ RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); enum_item_str.append(val.as_string()); //set attribute for available val to enum item name mappings - attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str); + ast_node->attributes[enum_item_str.c_str()] = AstNode::mkconst_str(enum_item->str); } } } @@ -407,120 +280,7 @@ return (is_union ? packed_width : offset); } -[[noreturn]] static void struct_op_error(AstNode *node) -{ - log_file_error(node->filename, node->location.first_line, "Unsupported operation for struct/union member %s\n", node->str.c_str()+1); -} - -static AstNode *node_int(int ival) -{ - return AstNode::mkconst_int(ival, true); -} - -static AstNode *multiply_by_const(AstNode *expr_node, int stride) -{ - return new AstNode(AST_MUL, expr_node, node_int(stride)); -} - -static AstNode *normalize_struct_index(AstNode *expr, AstNode *member_node, int dimension) -{ - expr = expr->clone(); - - int offset = get_struct_range_offset(member_node, dimension); - if (offset) { - expr = new AstNode(AST_SUB, expr, node_int(offset)); - } - - if (member_node->multirange_swapped[dimension]) { - // The dimension has swapped range; swap index into the struct accordingly. - int msb = get_struct_range_width(member_node, dimension) - 1; - expr = new AstNode(AST_SUB, node_int(msb), expr); - } - - return expr; -} - -static AstNode *struct_index_lsb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int &stride) -{ - stride /= get_struct_range_width(member_node, dimension); - auto right = normalize_struct_index(rnode->children.back(), member_node, dimension); - auto offset = stride > 1 ? multiply_by_const(right, stride) : right; - return lsb_offset ? new AstNode(AST_ADD, lsb_offset, offset) : offset; -} - -static AstNode *struct_index_msb_offset(AstNode *lsb_offset, AstNode *rnode, AstNode *member_node, int dimension, int stride) -{ - log_assert(rnode->children.size() <= 2); - - // Offset to add to LSB - AstNode *offset; - if (rnode->children.size() == 1) { - // Index, e.g. s.a[i] - offset = node_int(stride - 1); - } - else { - // rnode->children.size() == 2 - // Slice, e.g. s.a[i:j] - auto left = normalize_struct_index(rnode->children[0], member_node, dimension); - auto right = normalize_struct_index(rnode->children[1], member_node, dimension); - offset = new AstNode(AST_SUB, left, right); - if (stride > 1) { - // offset = (msb - lsb + 1)*stride - 1 - auto slice_width = new AstNode(AST_ADD, offset, node_int(1)); - offset = new AstNode(AST_SUB, multiply_by_const(slice_width, stride), node_int(1)); - } - } - - return new AstNode(AST_ADD, lsb_offset, offset); -} - - -AstNode *AST::make_struct_member_range(AstNode *node, AstNode *member_node) -{ - // Work out the range in the packed array that corresponds to a struct member - // taking into account any range operations applicable to the current node - // such as array indexing or slicing - int range_left = member_node->range_left; - int range_right = member_node->range_right; - if (node->children.empty()) { - // no range operations apply, return the whole width - return make_range(range_left - range_right, 0); - } - - if (node->children.size() != 1) { - struct_op_error(node); - } - - // Range operations - auto rnode = node->children[0]; - AstNode *lsb_offset = NULL; - int stride = range_left - range_right + 1; - size_t i = 0; - - // Calculate LSB offset for the final index / slice - if (rnode->type == AST_RANGE) { - lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride); - } - else if (rnode->type == AST_MULTIRANGE) { - // Add offset for each dimension - auto mrnode = rnode; - for (i = 0; i < mrnode->children.size(); i++) { - rnode = mrnode->children[i]; - lsb_offset = struct_index_lsb_offset(lsb_offset, rnode, member_node, i, stride); - } - i--; // Step back to the final index / slice - } - else { - struct_op_error(node); - } - - // Calculate MSB offset for the final index / slice - auto msb_offset = struct_index_msb_offset(lsb_offset->clone(), rnode, member_node, i, stride); - - return new AstNode(AST_RANGE, msb_offset, lsb_offset); -} - -AstNode *AST::get_struct_member(const AstNode *node) +AstNode get_struct_member(const AstNode *node) { AST::AstNode *member_node; if (node->attributes.count(ID::wiretype) && (member_node = node->attributes.at(ID::wiretype)) && @@ -606,84 +366,6 @@ return prefix + str; } -// direct access to this global should be limited to the following two functions -static const RTLIL::Design *simplify_design_context = nullptr; - -void AST::set_simplify_design_context(const RTLIL::Design *design) -{ - log_assert(!simplify_design_context || !design); - simplify_design_context = design; -} - -// lookup the module with the given name in the current design context -static const RTLIL::Module* lookup_module(const std::string &name) -{ - return simplify_design_context->module(name); -} - -const RTLIL::Module* AstNode::lookup_cell_module() -{ - log_assert(type == AST_CELL); - - auto reprocess_after = [this] (const std::string &modname) { - if (!attributes.count(ID::reprocess_after)) - attributes[ID::reprocess_after] = AstNode::mkconst_str(modname); - }; - - const AstNode *celltype = nullptr; - for (const AstNode *child : children) - if (child->type == AST_CELLTYPE) { - celltype = child; - break; - } - log_assert(celltype != nullptr); - - const RTLIL::Module *module = lookup_module(celltype->str); - if (!module) - module = lookup_module("$abstract" + celltype->str); - if (!module) { - if (celltype->str.at(0) != '$') - reprocess_after(celltype->str); - return nullptr; - } - - // build a mapping from true param name to param value - size_t para_counter = 0; - dict<RTLIL::IdString, RTLIL::Const> cell_params_map; - for (AstNode *child : children) { - if (child->type != AST_PARASET) - continue; - - if (child->str.empty() && para_counter >= module->avail_parameters.size()) - return nullptr; // let hierarchy handle this error - IdString paraname = child->str.empty() ? module->avail_parameters[para_counter++] : child->str; - - const AstNode *value = child->children[0]; - if (value->type != AST_REALVALUE && value->type != AST_CONSTANT) - return nullptr; // let genrtlil handle this error - cell_params_map[paraname] = value->asParaConst(); - } - - // put the parameters in order and generate the derived module name - std::vector<std::pair<RTLIL::IdString, RTLIL::Const>> named_parameters; - for (RTLIL::IdString param : module->avail_parameters) { - auto it = cell_params_map.find(param); - if (it != cell_params_map.end()) - named_parameters.emplace_back(it->first, it->second); - } - std::string modname = celltype->str; - if (cell_params_map.size()) // not named_parameters to cover hierarchical defparams - modname = derived_module_name(celltype->str, named_parameters); - - // try to find the resolved module - module = lookup_module(modname); - if (!module) { - reprocess_after(modname); - return nullptr; - } - return module; -} - // returns whether an expression contains an unbased unsized literal; does not // check the literal exists in a self-determined context static bool contains_unbased_unsized(const AstNode *node) @@ -837,6 +519,21 @@ check_auto_nosync(child); } +static inline std::string encode_filename(const std::string &filename) +{ + std::stringstream val; + if (!std::any_of(filename.begin(), filename.end(), [](char c) { + return static_cast<unsigned char>(c) < 33 || static_cast<unsigned char>(c) > 126; + })) return filename; + for (unsigned char const c : filename) { + if (c < 33 || c > 126) + val << stringf("$%02x", c); + else + val << c; + } + return val.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(). @@ -1280,7 +977,7 @@ // create the indirection wire std::stringstream sstr; - sstr << "$indirect$" << ref->name.c_str() << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$indirect$" << ref->name.c_str() << "$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string tmp_str = sstr.str(); add_wire_for_ref(ref, tmp_str); @@ -1825,7 +1522,7 @@ attributes[ID::wiretype] = mkconst_str(resolved_type_node->str); // if an enum then add attributes to support simulator tracing - annotateTypedEnums(template_node); + annotateTypedEnums(ast_node, template_node); // Insert clones children from template at beginning for (int i = 0; i < GetSize(template_node->children); i++) @@ -2181,7 +1878,7 @@ std::swap(data_range_left, data_range_right); std::stringstream sstr; - sstr << "$mem2bits$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$mem2bits$" << str << "$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string wire_id = sstr.str(); AstNode *wire = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(data_range_left, true), mkconst_int(data_range_right, true))); @@ -2777,14 +2474,14 @@ // mask and shift operations, disabled for now AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); - wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++); wire_mask->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_mask->is_logic = true; while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { } current_ast_mod->children.push_back(wire_mask); AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); - wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++); wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_data->is_logic = true; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } @@ -2795,7 +2492,7 @@ shift_expr->detectSignWidth(shamt_width_hint, shamt_sign_hint); AstNode *wire_sel = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(shamt_width_hint-1, true), mkconst_int(0, true))); - wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + wire_sel->str = stringf("$bitselwrite$sel$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++); wire_sel->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_sel->is_logic = true; wire_sel->is_signed = shamt_sign_hint; @@ -2874,7 +2571,7 @@ if (stage > 1 && (type == AST_ASSERT || type == AST_ASSUME || type == AST_LIVE || type == AST_FAIR || type == AST_COVER) && current_block != NULL) { std::stringstream sstr; - sstr << "$formal$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$formal$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_check = sstr.str() + "_CHECK", id_en = sstr.str() + "_EN"; AstNode *wire_check = new AstNode(AST_WIRE); @@ -2983,7 +2680,7 @@ newNode = new AstNode(AST_BLOCK); AstNode *wire_tmp = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, autoidx++); + wire_tmp->str = stringf("$splitcmplxassign$%s:%d$%d", encode_filename(filename).c_str(), location.first_line, autoidx++); current_ast_mod->children.push_back(wire_tmp); current_scope[wire_tmp->str] = wire_tmp; wire_tmp->attributes[ID::nosync] = AstNode::mkconst_int(1, false); @@ -3021,7 +2718,7 @@ (children[0]->children.size() == 1 || children[0]->children.size() == 2) && children[0]->children[0]->type == AST_RANGE) { std::stringstream sstr; - sstr << "$memwr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); + sstr << "$memwr$" << children[0]->str << "$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA", id_en = sstr.str() + "_EN"; int mem_width, mem_size, addr_bits; @@ -3293,7 +2990,7 @@ AstNode *reg = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(width_hint-1, true), mkconst_int(0, true))); - reg->str = stringf("$past$%s:%d$%d$%d", RTLIL::encode_filename(filename).c_str(), location.first_line, myidx, i); + reg->str = stringf("$past$%s:%d$%d$%d", encode_filename(filename).c_str(), location.first_line, myidx, i); reg->is_reg = true; reg->is_signed = sign_hint; @@ -3828,7 +3525,7 @@ std::stringstream sstr; - sstr << str << "$func$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; + sstr << str << "$func$" << encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++) << '.'; std::string prefix = sstr.str(); AstNode *decl = current_scope[str]; @@ -4375,1300 +4072,4 @@ return did_something; } -void AstNode::replace_result_wire_name_in_function(const std::string &from, const std::string &to) -{ - for (AstNode *child : children) - child->replace_result_wire_name_in_function(from, to); - if (str == from && type != AST_FCALL && type != AST_TCALL) - str = to; -} - -// replace a readmem[bh] TCALL ast node with a block of memory assignments -AstNode *AstNode::readmem(bool is_readmemh, std::string mem_filename, AstNode *memory, int start_addr, int finish_addr, bool unconditional_init) -{ - int mem_width, mem_size, addr_bits; - memory->meminfo(mem_width, mem_size, addr_bits); - - AstNode *block = new AstNode(AST_BLOCK); - - AstNode *meminit = nullptr; - int next_meminit_cursor=0; - vector<State> meminit_bits; - vector<State> en_bits; - int meminit_size=0; - - for (int i = 0; i < mem_width; i++) - en_bits.push_back(State::S1); - - std::ifstream f; - f.open(mem_filename.c_str()); - if (f.fail()) { -#ifdef _WIN32 - char slash = '\\'; -#else - char slash = '/'; -#endif - std::string path = filename.substr(0, filename.find_last_of(slash)+1); - f.open(path + mem_filename.c_str()); - yosys_input_files.insert(path + mem_filename); - } else { - yosys_input_files.insert(mem_filename); - } - if (f.fail() || GetSize(mem_filename) == 0) - log_file_error(filename, location.first_line, "Can not open file `%s` for %s.\n", mem_filename.c_str(), str.c_str()); - - log_assert(GetSize(memory->children) == 2 && memory->children[1]->type == AST_RANGE && memory->children[1]->range_valid); - int range_left = memory->children[1]->range_left, range_right = memory->children[1]->range_right; - int range_min = min(range_left, range_right), range_max = max(range_left, range_right); - - if (start_addr < 0) - start_addr = range_min; - - if (finish_addr < 0) - finish_addr = range_max + 1; - - bool in_comment = false; - int increment = start_addr <= finish_addr ? +1 : -1; - int cursor = start_addr; - - while (!f.eof()) - { - std::string line, token; - std::getline(f, line); - - for (int i = 0; i < GetSize(line); i++) { - if (in_comment && line.compare(i, 2, "*/") == 0) { - line[i] = ' '; - line[i+1] = ' '; - in_comment = false; - continue; - } - if (!in_comment && line.compare(i, 2, "/*") == 0) - in_comment = true; - if (in_comment) - line[i] = ' '; - } - - while (1) - { - token = next_token(line, " \t\r\n"); - if (token.empty() || token.compare(0, 2, "//") == 0) - break; - - if (token[0] == '@') { - token = token.substr(1); - const char *nptr = token.c_str(); - char *endptr; - cursor = strtol(nptr, &endptr, 16); - if (!*nptr || *endptr) - log_file_error(filename, location.first_line, "Can not parse address `%s` for %s.\n", nptr, str.c_str()); - continue; - } - - AstNode *value = VERILOG_FRONTEND::const2ast(stringf("%d'%c", mem_width, is_readmemh ? 'h' : 'b') + token); - - if (unconditional_init) - { - if (meminit == nullptr || cursor != next_meminit_cursor) - { - if (meminit != nullptr) { - meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[3] = AstNode::mkconst_int(meminit_size, false); - } - - meminit = new AstNode(AST_MEMINIT); - meminit->children.push_back(AstNode::mkconst_int(cursor, false)); - meminit->children.push_back(nullptr); - meminit->children.push_back(AstNode::mkconst_bits(en_bits, false)); - meminit->children.push_back(nullptr); - meminit->str = memory->str; - meminit->id2ast = memory; - meminit_bits.clear(); - meminit_size = 0; - - current_ast_mod->children.push_back(meminit); - next_meminit_cursor = cursor; - } - - meminit_size++; - next_meminit_cursor++; - meminit_bits.insert(meminit_bits.end(), value->bits.begin(), value->bits.end()); - delete value; - } - else - { - block->children.push_back(new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER, new AstNode(AST_RANGE, AstNode::mkconst_int(cursor, false))), value)); - block->children.back()->children[0]->str = memory->str; - block->children.back()->children[0]->id2ast = memory; - block->children.back()->children[0]->was_checked = true; - } - - cursor += increment; - if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) - break; - } - - if ((cursor == finish_addr+increment) || (increment > 0 && cursor > range_max) || (increment < 0 && cursor < range_min)) - break; - } - - if (meminit != nullptr) { - meminit->children[1] = AstNode::mkconst_bits(meminit_bits, false); - meminit->children[3] = AstNode::mkconst_int(meminit_size, false); - } - - return block; -} - -// 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) -{ - if (type == AST_IDENTIFIER || type == AST_FCALL || type == AST_TCALL || type == AST_WIRETYPE || type == AST_PREFIX) { - log_assert(!str.empty()); - - // search starting in the innermost scope and then stepping outward - for (size_t ppos = prefix.size() - 1; ppos; --ppos) { - if (prefix.at(ppos) != '.') continue; - - std::string new_prefix = prefix.substr(0, ppos + 1); - auto attempt_resolve = [&new_prefix](const std::string &ident) -> std::string { - std::string new_name = prefix_id(new_prefix, ident); - if (current_scope.count(new_name)) - return new_name; - return {}; - }; - - // attempt to resolve the full identifier - std::string resolved = attempt_resolve(str); - if (!resolved.empty()) { - str = resolved; - break; - } - - // attempt to resolve hierarchical prefixes within the identifier, - // as the prefix could refer to a local scope which exists but - // hasn't yet been elaborated - for (size_t spos = str.size() - 1; spos; --spos) { - if (str.at(spos) != '.') continue; - resolved = attempt_resolve(str.substr(0, spos)); - if (!resolved.empty()) { - str = resolved + str.substr(spos); - ppos = 1; // break outer loop - break; - } - } - - } - } - - 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 - child->str = new_name; - current_scope[new_name] = child; - }; - - for (size_t i = 0; i < children.size(); i++) { - AstNode *child = children[i]; - - 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); - prefix_node(enode); - } - break; - - default: - break; - } - } - - for (size_t i = 0; i < children.size(); i++) { - AstNode *child = children[i]; - // AST_PREFIX member names should not be prefixed; we recurse into them - // as normal to ensure indices and ranges are properly resolved, and - // then restore the previous string - if (type == AST_PREFIX && i == 1) { - std::string backup_scope_name = child->str; - child->expand_genblock(prefix); - child->str = backup_scope_name; - continue; - } - // functions/tasks may reference wires, constants, etc. in this scope - if (child->type == AST_FUNCTION || child->type == AST_TASK) - continue; - // named blocks pick up the current prefix and will expanded later - if ((child->type == AST_GENBLOCK || child->type == AST_BLOCK) && !child->str.empty()) - continue; - - child->expand_genblock(prefix); - } -} - -// 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) -{ - 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; - - 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; - } - - 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; - } -} - -// helper function for mem2reg_as_needed_pass1 -static void mark_memories_assign_lhs_complex(dict<AstNode*, pool<std::string>> &mem2reg_places, - dict<AstNode*, uint32_t> &mem2reg_candidates, AstNode *that) -{ - for (auto &child : that->children) - mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, child); - - if (that->type == AST_IDENTIFIER && that->id2ast && that->id2ast->type == AST_MEMORY) { - AstNode *mem = that->id2ast; - if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_CMPLX_LHS)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(that->filename).c_str(), that->location.first_line)); - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CMPLX_LHS; - } -} - -// find memories that should be replaced by registers -void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg_places, - dict<AstNode*, uint32_t> &mem2reg_candidates, dict<AstNode*, uint32_t> &proc_flags, uint32_t &flags) -{ - uint32_t children_flags = 0; - int lhs_children_counter = 0; - - if (type == AST_TYPEDEF) - return; // don't touch content of typedefs - - if (type == AST_ASSIGN || type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) - { - // mark all memories that are used in a complex expression on the left side of an assignment - for (auto &lhs_child : children[0]->children) - mark_memories_assign_lhs_complex(mem2reg_places, mem2reg_candidates, lhs_child); - - if (children[0]->type == AST_IDENTIFIER && children[0]->id2ast && children[0]->id2ast->type == AST_MEMORY) - { - AstNode *mem = children[0]->id2ast; - - // activate mem2reg if this is assigned in an async proc - if (flags & AstNode::MEM2REG_FL_ASYNC) { - if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ASYNC)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ASYNC; - } - - // remember if this is assigned blocking (=) - if (type == AST_ASSIGN_EQ) { - if (!(proc_flags[mem] & AstNode::MEM2REG_FL_EQ1)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); - proc_flags[mem] |= AstNode::MEM2REG_FL_EQ1; - } - - // for proper (non-init) writes: remember if this is a constant index or not - if ((flags & MEM2REG_FL_INIT) == 0) { - if (children[0]->children.size() && children[0]->children[0]->type == AST_RANGE && children[0]->children[0]->children.size()) { - if (children[0]->children[0]->children[0]->type == AST_CONSTANT) - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_CONST_LHS; - else - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_VAR_LHS; - } - } - - // remember where this is - if (flags & MEM2REG_FL_INIT) { - if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_INIT)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_INIT; - } else { - if (!(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_SET_ELSE)) - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_SET_ELSE; - } - } - - lhs_children_counter = 1; - } - - if (type == AST_IDENTIFIER && id2ast && id2ast->type == AST_MEMORY) - { - AstNode *mem = id2ast; - - // flag if used after blocking assignment (in same proc) - if ((proc_flags[mem] & AstNode::MEM2REG_FL_EQ1) && !(mem2reg_candidates[mem] & AstNode::MEM2REG_FL_EQ2)) { - mem2reg_places[mem].insert(stringf("%s:%d", RTLIL::encode_filename(filename).c_str(), location.first_line)); - mem2reg_candidates[mem] |= AstNode::MEM2REG_FL_EQ2; - } - } - - // also activate if requested, either by using mem2reg attribute or by declaring array as 'wire' instead of 'reg' or 'logic' - if (type == AST_MEMORY && (get_bool_attribute(ID::mem2reg) || (flags & AstNode::MEM2REG_FL_ALL) || !(is_reg || is_logic))) - mem2reg_candidates[this] |= AstNode::MEM2REG_FL_FORCED; - - if ((type == AST_MODULE || type == AST_INTERFACE) && get_bool_attribute(ID::mem2reg)) - children_flags |= AstNode::MEM2REG_FL_ALL; - - dict<AstNode*, uint32_t> *proc_flags_p = NULL; - - if (type == AST_ALWAYS) { - int count_edge_events = 0; - for (auto child : children) - if (child->type == AST_POSEDGE || child->type == AST_NEGEDGE) - count_edge_events++; - if (count_edge_events != 1) - children_flags |= AstNode::MEM2REG_FL_ASYNC; - proc_flags_p = new dict<AstNode*, uint32_t>; - } - else if (type == AST_INITIAL) { - children_flags |= AstNode::MEM2REG_FL_INIT; - proc_flags_p = new dict<AstNode*, uint32_t>; - } - - uint32_t backup_flags = flags; - flags |= children_flags; - log_assert((flags & ~0x000000ff) == 0); - - for (auto child : children) - { - if (lhs_children_counter > 0) { - lhs_children_counter--; - if (child->children.size() && child->children[0]->type == AST_RANGE && child->children[0]->children.size()) { - for (auto c : child->children[0]->children) { - if (proc_flags_p) - c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); - else - c->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); - } - } - } else - if (proc_flags_p) - child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, *proc_flags_p, flags); - else - child->mem2reg_as_needed_pass1(mem2reg_places, mem2reg_candidates, proc_flags, flags); - } - - flags &= ~children_flags | backup_flags; - - if (proc_flags_p) { -#ifndef NDEBUG - for (auto it : *proc_flags_p) - log_assert((it.second & ~0xff000000) == 0); -#endif - delete proc_flags_p; - } -} - -bool AstNode::mem2reg_check(pool<AstNode*> &mem2reg_set) -{ - if (type != AST_IDENTIFIER || !id2ast || !mem2reg_set.count(id2ast)) - return false; - - if (children.empty() || children[0]->type != AST_RANGE || GetSize(children[0]->children) != 1) - log_file_error(filename, location.first_line, "Invalid array access.\n"); - - return true; -} - -void AstNode::mem2reg_remove(pool<AstNode*> &mem2reg_set, vector<AstNode*> &delnodes) -{ - log_assert(mem2reg_set.count(this) == 0); - - if (mem2reg_set.count(id2ast)) - id2ast = nullptr; - - for (size_t i = 0; i < children.size(); i++) { - if (mem2reg_set.count(children[i]) > 0) { - delnodes.push_back(children[i]); - children.erase(children.begin() + (i--)); - } else { - children[i]->mem2reg_remove(mem2reg_set, delnodes); - } - } -} - -// actually replace memories with registers -bool AstNode::mem2reg_as_needed_pass2(pool<AstNode*> &mem2reg_set, AstNode *mod, AstNode *block, AstNode *&async_block) -{ - bool did_something = false; - - if (type == AST_BLOCK) - block = this; - - if (type == AST_FUNCTION || type == AST_TASK) - return false; - - if (type == AST_TYPEDEF) - return false; - - if (type == AST_MEMINIT && id2ast && mem2reg_set.count(id2ast)) - { - log_assert(children[0]->type == AST_CONSTANT); - log_assert(children[1]->type == AST_CONSTANT); - log_assert(children[2]->type == AST_CONSTANT); - log_assert(children[3]->type == AST_CONSTANT); - - int cursor = children[0]->asInt(false); - Const data = children[1]->bitsAsConst(); - Const en = children[2]->bitsAsConst(); - int length = children[3]->asInt(false); - - if (length != 0) - { - AstNode *block = new AstNode(AST_INITIAL, new AstNode(AST_BLOCK)); - mod->children.push_back(block); - block = block->children[0]; - - int wordsz = GetSize(data) / length; - - for (int i = 0; i < length; i++) { - int pos = 0; - while (pos < wordsz) { - if (en[pos] != State::S1) { - pos++; - } else { - int epos = pos + 1; - while (epos < wordsz && en[epos] == State::S1) - epos++; - int clen = epos - pos; - AstNode *range = new AstNode(AST_RANGE, AstNode::mkconst_int(cursor+i, false)); - if (pos != 0 || epos != wordsz) { - int left; - int right; - AstNode *mrange = id2ast->children[0]; - if (mrange->range_left < mrange->range_right) { - right = mrange->range_right - pos; - left = mrange->range_right - epos + 1; - } else { - right = mrange->range_right + pos; - left = mrange->range_right + epos - 1; - } - range = new AstNode(AST_MULTIRANGE, range, new AstNode(AST_RANGE, AstNode::mkconst_int(left, true), AstNode::mkconst_int(right, true))); - } - AstNode *target = new AstNode(AST_IDENTIFIER, range); - target->str = str; - target->id2ast = id2ast; - target->was_checked = true; - block->children.push_back(new AstNode(AST_ASSIGN_EQ, target, mkconst_bits(data.extract(i*wordsz + pos, clen).bits, false))); - pos = epos; - } - } - } - } - - AstNode *newNode = new AstNode(AST_NONE); - newNode->cloneInto(this); - delete newNode; - - did_something = true; - } - - if (type == AST_ASSIGN && block == NULL && children[0]->mem2reg_check(mem2reg_set)) - { - if (async_block == NULL) { - async_block = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); - mod->children.push_back(async_block); - } - - AstNode *newNode = clone(); - newNode->type = AST_ASSIGN_EQ; - newNode->children[0]->was_checked = true; - async_block->children[0]->children.push_back(newNode); - - newNode = new AstNode(AST_NONE); - newNode->cloneInto(this); - delete newNode; - - did_something = true; - } - - if ((type == AST_ASSIGN_LE || type == AST_ASSIGN_EQ) && children[0]->mem2reg_check(mem2reg_set) && - children[0]->children[0]->children[0]->type != AST_CONSTANT) - { - std::stringstream sstr; - sstr << "$mem2reg_wr$" << children[0]->str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); - std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; - - int mem_width, mem_size, addr_bits; - bool mem_signed = children[0]->id2ast->is_signed; - children[0]->id2ast->meminfo(mem_width, mem_size, addr_bits); - - AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); - wire_addr->str = id_addr; - wire_addr->is_reg = true; - wire_addr->was_checked = true; - wire_addr->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - mod->children.push_back(wire_addr); - while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } - - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_data->str = id_data; - wire_data->is_reg = true; - wire_data->was_checked = true; - wire_data->is_signed = mem_signed; - wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - mod->children.push_back(wire_data); - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - - log_assert(block != NULL); - size_t assign_idx = 0; - while (assign_idx < block->children.size() && block->children[assign_idx] != this) - assign_idx++; - log_assert(assign_idx < block->children.size()); - - AstNode *assign_addr = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), children[0]->children[0]->children[0]->clone()); - assign_addr->children[0]->str = id_addr; - assign_addr->children[0]->was_checked = true; - block->children.insert(block->children.begin()+assign_idx+1, assign_addr); - - AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); - case_node->children[0]->str = id_addr; - for (int i = 0; i < mem_size; i++) { - if (children[0]->children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->children[0]->integer) != i) - continue; - AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(type, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); - if (children[0]->children.size() == 2) - assign_reg->children[0]->children.push_back(children[0]->children[1]->clone()); - assign_reg->children[0]->str = stringf("%s[%d]", children[0]->str.c_str(), i); - assign_reg->children[1]->str = id_data; - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); - } - block->children.insert(block->children.begin()+assign_idx+2, case_node); - - children[0]->delete_children(); - children[0]->range_valid = false; - children[0]->id2ast = NULL; - children[0]->str = id_data; - type = AST_ASSIGN_EQ; - children[0]->was_checked = true; - - did_something = true; - } - - if (mem2reg_check(mem2reg_set)) - { - AstNode *bit_part_sel = NULL; - if (children.size() == 2) - bit_part_sel = children[1]->clone(); - - if (children[0]->children[0]->type == AST_CONSTANT) - { - int id = children[0]->children[0]->integer; - int left = id2ast->children[1]->children[0]->integer; - int right = id2ast->children[1]->children[1]->integer; - bool valid_const_access = - (left <= id && id <= right) || - (right <= id && id <= left); - if (valid_const_access) - { - str = stringf("%s[%d]", str.c_str(), id); - delete_children(); - range_valid = false; - id2ast = NULL; - } - else - { - int width; - if (bit_part_sel) - { - bit_part_sel->dumpAst(nullptr, "? "); - if (bit_part_sel->children.size() == 1) - width = 0; - else - width = bit_part_sel->children[0]->integer - - bit_part_sel->children[1]->integer; - delete bit_part_sel; - bit_part_sel = nullptr; - } - else - { - width = id2ast->children[0]->children[0]->integer - - id2ast->children[0]->children[1]->integer; - } - width = abs(width) + 1; - - delete_children(); - - std::vector<RTLIL::State> x_bits; - for (int i = 0; i < width; i++) - x_bits.push_back(RTLIL::State::Sx); - AstNode *constant = AstNode::mkconst_bits(x_bits, false); - constant->cloneInto(this); - delete constant; - } - } - else - { - std::stringstream sstr; - sstr << "$mem2reg_rd$" << str << "$" << RTLIL::encode_filename(filename) << ":" << location.first_line << "$" << (autoidx++); - std::string id_addr = sstr.str() + "_ADDR", id_data = sstr.str() + "_DATA"; - - int mem_width, mem_size, addr_bits; - bool mem_signed = id2ast->is_signed; - id2ast->meminfo(mem_width, mem_size, addr_bits); - - AstNode *wire_addr = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(addr_bits-1, true), mkconst_int(0, true))); - wire_addr->str = id_addr; - wire_addr->is_reg = true; - wire_addr->was_checked = true; - if (block) - wire_addr->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - mod->children.push_back(wire_addr); - while (wire_addr->simplify(true, false, false, 1, -1, false, false)) { } - - AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(mem_width-1, true), mkconst_int(0, true))); - wire_data->str = id_data; - wire_data->is_reg = true; - wire_data->was_checked = true; - wire_data->is_signed = mem_signed; - if (block) - wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); - mod->children.push_back(wire_data); - while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } - - AstNode *assign_addr = new AstNode(block ? AST_ASSIGN_EQ : AST_ASSIGN, new AstNode(AST_IDENTIFIER), children[0]->children[0]->clone()); - assign_addr->children[0]->str = id_addr; - assign_addr->children[0]->was_checked = true; - - AstNode *case_node = new AstNode(AST_CASE, new AstNode(AST_IDENTIFIER)); - case_node->children[0]->str = id_addr; - - for (int i = 0; i < mem_size; i++) { - if (children[0]->children[0]->type == AST_CONSTANT && int(children[0]->children[0]->integer) != i) - continue; - AstNode *cond_node = new AstNode(AST_COND, AstNode::mkconst_int(i, false, addr_bits), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), new AstNode(AST_IDENTIFIER)); - assign_reg->children[0]->str = id_data; - assign_reg->children[0]->was_checked = true; - assign_reg->children[1]->str = stringf("%s[%d]", str.c_str(), i); - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); - } - - std::vector<RTLIL::State> x_bits; - for (int i = 0; i < mem_width; i++) - x_bits.push_back(RTLIL::State::Sx); - - AstNode *cond_node = new AstNode(AST_COND, new AstNode(AST_DEFAULT), new AstNode(AST_BLOCK)); - AstNode *assign_reg = new AstNode(AST_ASSIGN_EQ, new AstNode(AST_IDENTIFIER), AstNode::mkconst_bits(x_bits, false)); - assign_reg->children[0]->str = id_data; - assign_reg->children[0]->was_checked = true; - cond_node->children[1]->children.push_back(assign_reg); - case_node->children.push_back(cond_node); - - if (block) - { - size_t assign_idx = 0; - while (assign_idx < block->children.size() && !block->children[assign_idx]->contains(this)) - assign_idx++; - log_assert(assign_idx < block->children.size()); - block->children.insert(block->children.begin()+assign_idx, case_node); - block->children.insert(block->children.begin()+assign_idx, assign_addr); - } - else - { - AstNode *proc = new AstNode(AST_ALWAYS, new AstNode(AST_BLOCK)); - proc->children[0]->children.push_back(case_node); - mod->children.push_back(proc); - mod->children.push_back(assign_addr); - } - - delete_children(); - range_valid = false; - id2ast = NULL; - str = id_data; - } - - if (bit_part_sel) - children.push_back(bit_part_sel); - - did_something = true; - } - - log_assert(id2ast == NULL || mem2reg_set.count(id2ast) == 0); - - auto children_list = children; - for (size_t i = 0; i < children_list.size(); i++) - if (children_list[i]->mem2reg_as_needed_pass2(mem2reg_set, mod, block, async_block)) - did_something = true; - - return did_something; -} - -// calculate memory dimensions -void AstNode::meminfo(int &mem_width, int &mem_size, int &addr_bits) -{ - log_assert(type == AST_MEMORY); - - mem_width = children[0]->range_left - children[0]->range_right + 1; - mem_size = children[1]->range_left - children[1]->range_right; - - if (mem_size < 0) - mem_size *= -1; - mem_size += min(children[1]->range_left, children[1]->range_right) + 1; - - addr_bits = 1; - while ((1 << addr_bits) < mem_size) - 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() -{ - if (type == AST_WHILE || type == AST_REPEAT) - return true; - for (auto child : children) - if (child->has_const_only_constructs()) - return true; - return false; -} - -bool AstNode::is_simple_const_expr() -{ - if (type == AST_IDENTIFIER) - return false; - for (auto child : children) - if (!child->is_simple_const_expr()) - return false; - return true; -} - -// helper function for AstNode::eval_const_function() -bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed) -{ - if (type == AST_IDENTIFIER && variables.count(str)) { - int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); - if (!children.empty()) { - if (children.size() != 1 || children.at(0)->type != AST_RANGE) { - if (!must_succeed) - return false; - log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s: ...called from here.\n", - fcall->loc_string().c_str()); - } - if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) - return false; - while (simplify(true, false, false, 1, -1, false, true)) { } - if (!children.at(0)->range_valid) { - if (!must_succeed) - return false; - log_file_error(filename, location.first_line, "Non-constant range\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - offset = min(children.at(0)->range_left, children.at(0)->range_right); - width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); - } - offset -= variables.at(str).offset; - if (variables.at(str).range_swapped) - offset = -offset; - std::vector<RTLIL::State> &var_bits = variables.at(str).val.bits; - std::vector<RTLIL::State> new_bits(var_bits.begin() + offset, var_bits.begin() + offset + width); - AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); - newNode->cloneInto(this); - delete newNode; - return true; - } - - for (auto &child : children) - if (!child->replace_variables(variables, fcall, must_succeed)) - return false; - return true; -} - -// attempt to statically evaluate a functions with all-const arguments -AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) -{ - std::map<std::string, AstNode*> backup_scope = current_scope; - std::map<std::string, AstNode::varinfo_t> variables; - std::vector<AstNode*> to_delete; - AstNode *block = new AstNode(AST_BLOCK); - AstNode *result = nullptr; - - size_t argidx = 0; - for (auto child : children) - { - block->children.push_back(child->clone()); - } - - while (!block->children.empty()) - { - AstNode *stmt = block->children.front(); - -#if 0 - log("-----------------------------------\n"); - for (auto &it : variables) - log("%20s %40s\n", it.first.c_str(), log_signal(it.second.val)); - stmt->dumpAst(NULL, "stmt> "); -#endif - - if (stmt->type == AST_WIRE) - { - while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - if (!stmt->range_valid) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s: ... called from here.\n", - stmt->str.c_str(), fcall->loc_string().c_str()); - } - AstNode::varinfo_t &variable = variables[stmt->str]; - int width = abs(stmt->range_left - stmt->range_right) + 1; - // if this variable has already been declared as an input, check the - // sizes match if it already had an explicit size - if (variable.arg && variable.explicitly_sized && variable.val.size() != width) { - log_file_error(filename, location.first_line, "Incompatible re-declaration of constant function wire %s.\n", stmt->str.c_str()); - } - variable.val = RTLIL::Const(RTLIL::State::Sx, width); - variable.offset = stmt->range_swapped ? stmt->range_left : stmt->range_right; - variable.range_swapped = stmt->range_swapped; - variable.is_signed = stmt->is_signed; - variable.explicitly_sized = stmt->children.size() && - stmt->children.back()->type == AST_RANGE; - // identify the argument corresponding to this wire, if applicable - if (stmt->is_input && argidx < fcall->children.size()) { - variable.arg = fcall->children.at(argidx++); - } - // load the constant arg's value into this variable - if (variable.arg) { - if (variable.arg->type == AST_CONSTANT) { - variable.val = variable.arg->bitsAsConst(width); - } else { - log_assert(variable.arg->type == AST_REALVALUE); - variable.val = variable.arg->realAsConst(width); - } - } - current_scope[stmt->str] = stmt; - - block->children.erase(block->children.begin()); - to_delete.push_back(stmt); - continue; - } - - log_assert(variables.count(str) != 0); - - if (stmt->type == AST_LOCALPARAM) - { - while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - - current_scope[stmt->str] = stmt; - - block->children.erase(block->children.begin()); - to_delete.push_back(stmt); - continue; - } - - if (stmt->type == AST_ASSIGN_EQ) - { - if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && - stmt->children.at(0)->children.at(0)->type == AST_RANGE) - if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) - goto finished; - if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) - goto finished; - while (stmt->simplify(true, false, false, 1, -1, false, true)) { } - - if (stmt->type != AST_ASSIGN_EQ) - continue; - - if (stmt->children.at(1)->type != AST_CONSTANT) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here. X\n", - fcall->loc_string().c_str()); - } - - if (stmt->children.at(0)->type != AST_IDENTIFIER) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - - if (!variables.count(stmt->children.at(0)->str)) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - - if (stmt->children.at(0)->children.empty()) { - variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); - } else { - AstNode *range = stmt->children.at(0)->children.at(0); - if (!range->range_valid) { - if (!must_succeed) - goto finished; - log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - int offset = min(range->range_left, range->range_right); - int width = std::abs(range->range_left - range->range_right) + 1; - varinfo_t &v = variables[stmt->children.at(0)->str]; - RTLIL::Const r = stmt->children.at(1)->bitsAsConst(v.val.bits.size()); - for (int i = 0; i < width; i++) { - int index = i + offset - v.offset; - if (v.range_swapped) - index = -index; - v.val.bits.at(index) = r.bits.at(i); - } - } - - delete block->children.front(); - block->children.erase(block->children.begin()); - continue; - } - - if (stmt->type == AST_FOR) - { - block->children.insert(block->children.begin(), stmt->children.at(0)); - stmt->children.at(3)->children.push_back(stmt->children.at(2)); - stmt->children.erase(stmt->children.begin() + 2); - stmt->children.erase(stmt->children.begin()); - stmt->type = AST_WHILE; - continue; - } - - if (stmt->type == AST_WHILE) - { - AstNode *cond = stmt->children.at(0)->clone(); - if (!cond->replace_variables(variables, fcall, must_succeed)) - goto finished; - while (cond->simplify(true, false, false, 1, -1, false, true)) { } - - if (cond->type != AST_CONSTANT) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - - if (cond->asBool()) { - block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); - } else { - delete block->children.front(); - block->children.erase(block->children.begin()); - } - - delete cond; - continue; - } - - if (stmt->type == AST_REPEAT) - { - AstNode *num = stmt->children.at(0)->clone(); - if (!num->replace_variables(variables, fcall, must_succeed)) - goto finished; - while (num->simplify(true, false, false, 1, -1, false, true)) { } - - if (num->type != AST_CONSTANT) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - - block->children.erase(block->children.begin()); - for (int i = 0; i < num->bitsAsConst().as_int(); i++) - block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); - - delete stmt; - delete num; - continue; - } - - if (stmt->type == AST_CASE) - { - AstNode *expr = stmt->children.at(0)->clone(); - if (!expr->replace_variables(variables, fcall, must_succeed)) - goto finished; - while (expr->simplify(true, false, false, 1, -1, false, true)) { } - - AstNode *sel_case = NULL; - for (size_t i = 1; i < stmt->children.size(); i++) - { - bool found_match = false; - log_assert(stmt->children.at(i)->type == AST_COND || stmt->children.at(i)->type == AST_CONDX || stmt->children.at(i)->type == AST_CONDZ); - - if (stmt->children.at(i)->children.front()->type == AST_DEFAULT) { - sel_case = stmt->children.at(i)->children.back(); - continue; - } - - for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) - { - AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); - if (!cond->replace_variables(variables, fcall, must_succeed)) - goto finished; - - cond = new AstNode(AST_EQ, expr->clone(), cond); - while (cond->simplify(true, false, false, 1, -1, false, true)) { } - - if (cond->type != AST_CONSTANT) { - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - } - - found_match = cond->asBool(); - delete cond; - } - - if (found_match) { - sel_case = stmt->children.at(i)->children.back(); - break; - } - } - - block->children.erase(block->children.begin()); - if (sel_case) - block->children.insert(block->children.begin(), sel_case->clone()); - delete stmt; - delete expr; - continue; - } - - 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(); - delete stmt; - continue; - } - - if (!must_succeed) - goto finished; - log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s: ... called from here.\n", - fcall->loc_string().c_str()); - log_abort(); - } - - result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); - -finished: - delete block; - current_scope = backup_scope; - - for (auto it : to_delete) { - delete it; - } - to_delete.clear(); - - return result; -} - -void AstNode::allocateDefaultEnumValues() -{ - log_assert(type==AST_ENUM); - log_assert(children.size() > 0); - if (children.front()->attributes.count(ID::enum_base_type)) - return; // already elaborated - int last_enum_int = -1; - for (auto node : children) { - log_assert(node->type==AST_ENUM_ITEM); - node->attributes[ID::enum_base_type] = mkconst_str(str); - for (size_t i = 0; i < node->children.size(); i++) { - switch (node->children[i]->type) { - case AST_NONE: - // replace with auto-incremented constant - delete node->children[i]; - node->children[i] = AstNode::mkconst_int(++last_enum_int, true); - break; - case AST_CONSTANT: - // explicit constant (or folded expression) - // TODO: can't extend 'x or 'z item - last_enum_int = node->children[i]->integer; - break; - default: - // ignore ranges - break; - } - // TODO: range check - } - } -} - -bool AstNode::is_recursive_function() const -{ - std::set<const AstNode *> visited; - std::function<bool(const AstNode *node)> visit = [&](const AstNode *node) { - if (visited.count(node)) - return node == this; - visited.insert(node); - if (node->type == AST_FCALL) { - auto it = current_scope.find(node->str); - if (it != current_scope.end() && visit(it->second)) - return true; - } - for (const AstNode *child : node->children) { - if (visit(child)) - return true; - } - return false; - }; - - log_assert(type == AST_FUNCTION); - return visit(this); -} - -std::pair<AstNode*, AstNode*> AstNode::get_tern_choice() -{ - if (!children[0]->isConst()) - return {}; - - bool found_sure_true = false; - bool found_maybe_true = false; - - if (children[0]->type == AST_CONSTANT) - for (auto &bit : children[0]->bits) { - if (bit == RTLIL::State::S1) - found_sure_true = true; - if (bit > RTLIL::State::S1) - found_maybe_true = true; - } - else - found_sure_true = children[0]->asReal(true) != 0; - - AstNode *choice = nullptr, *not_choice = nullptr; - if (found_sure_true) - choice = children[1], not_choice = children[2]; - else if (!found_maybe_true) - choice = children[2], not_choice = children[1]; - - return {choice, not_choice}; -} - -std::string AstNode::try_pop_module_prefix() const -{ - AstNode *current_scope_ast = (current_ast_mod == nullptr) ? current_ast : current_ast_mod; - size_t pos = str.find('.', 1); - if (str[0] == '\\' && pos != std::string::npos) { - std::string new_str = "\\" + str.substr(pos + 1); - if (current_scope.count(new_str)) { - std::string prefix = str.substr(0, pos); - auto it = current_scope_ast->attributes.find(ID::hdlname); - if ((it != current_scope_ast->attributes.end() && it->second->str == prefix) - || prefix == current_scope_ast->str) - return new_str; - } - } - return str; -} - -YOSYS_NAMESPACE_END +} // namespace systemverilog_plugin