systemverilog-plugin: Support memories in nested genblocks Signed-off-by: Magdalena Andrys <mandrys@antmicro.com>
diff --git a/systemverilog-plugin/UhdmAst.cc b/systemverilog-plugin/UhdmAst.cc index 8d2527f..0b465f0 100644 --- a/systemverilog-plugin/UhdmAst.cc +++ b/systemverilog-plugin/UhdmAst.cc
@@ -399,42 +399,61 @@ } } -static void add_force_convert_attribute(AST::AstNode *wire_node, int val = 1) +static void add_force_convert_attribute(AST::AstNode *wire_node, uint32_t val = 1) { - wire_node->attributes[UhdmAst::force_convert()] = AST::AstNode::mkconst_int(val, true); + AST::AstNode *&attr = wire_node->attributes[UhdmAst::force_convert()]; + if (!attr) { + attr = AST::AstNode::mkconst_int(val, true); + } else if (attr->integer != val) { + attr->integer = val; + } } -static void check_memories(AST::AstNode *module_node) +static void check_memories(AST::AstNode *node, std::string scope, std::map<std::string, AST::AstNode *> &memories) { - std::map<std::string, AST::AstNode *> memories; - std::string current_scope_str = ""; - visitEachDescendant(module_node, [&](AST::AstNode *node) { - if (node->type == AST::AST_GENBLOCK) { - current_scope_str = "." + node->str; + for (auto *child : node->children) { + check_memories(child, node->type == AST::AST_GENBLOCK ? scope + "." + node->str : scope, memories); + } + + if (node->str == "\\$readmemh") { + if (node->children.size() != 2 || node->children[1]->str.empty() || node->children[1]->type != AST::AST_IDENTIFIER) { + log_error("%s:%d: Wrong usage of '\\$readmemh'\n", node->filename.c_str(), node->location.first_line); } - if (node->str == "\\$readmemh") { - if (node->children.size() != 2 || node->children[1]->str.empty() || node->children[1]->type != AST::AST_IDENTIFIER) { - log_error("%s:%d: Wrong usage of '\\$readmemh'\n", node->filename.c_str(), node->location.first_line); - } - if (memories[current_scope_str + node->children[1]->str]) - add_force_convert_attribute(memories[node->children[1]->str], 0); + // TODO: Look for the memory in all other scope levels, like we do in case of AST::AST_IDENTIFIER, + // as here the memory can also be defined before before the current scope. + std::string name = scope + "." + node->children[1]->str; + const auto iter = memories.find(name); + if (iter != memories.end()) { + add_force_convert_attribute(iter->second, 0); } - if (node->type == AST::AST_WIRE) { - const std::vector<AST::AstNode *> packed_ranges = - node->attributes.count(UhdmAst::packed_ranges()) ? node->attributes[UhdmAst::packed_ranges()]->children : std::vector<AST::AstNode *>(); - const std::vector<AST::AstNode *> unpacked_ranges = node->attributes.count(UhdmAst::unpacked_ranges()) - ? node->attributes[UhdmAst::unpacked_ranges()]->children - : std::vector<AST::AstNode *>(); - if (packed_ranges.size() == 1 && unpacked_ranges.size() == 1) { - log_assert(!memories.count(current_scope_str + node->str)); - memories[current_scope_str + node->str] = node; - } + } + + if (node->type == AST::AST_WIRE) { + const std::size_t packed_ranges_count = + node->attributes.count(UhdmAst::packed_ranges()) ? node->attributes[UhdmAst::packed_ranges()]->children.size() : 0; + const std::size_t unpacked_ranges_count = + node->attributes.count(UhdmAst::unpacked_ranges()) ? node->attributes[UhdmAst::unpacked_ranges()]->children.size() : 0; + + if (packed_ranges_count == 1 && unpacked_ranges_count == 1) { + std::string name = scope + "." + node->str; + auto [iter, did_insert] = memories.insert_or_assign(std::move(name), node); + log_assert(did_insert); } - if (node->type == AST::AST_IDENTIFIER) { - std::string name = current_scope_str + node->str; - bool force_convert = false; - if (memories.count(name)) { - if (!memories[name]->attributes.count(UhdmAst::force_convert())) { + return; + } + + if (node->type == AST::AST_IDENTIFIER) { + std::string full_id = scope; + std::size_t scope_end_pos = scope.size(); + + for (;;) { + full_id += "." + node->str; + const auto iter = memories.find(full_id); + if (iter != memories.end()) { + // Memory node found! + if (!iter->second->attributes.count(UhdmAst::force_convert())) { + const bool is_full_memory_access = (node->children.size() == 0); + const bool is_slice_memory_access = (node->children.size() == 1 && node->children[0]->children.size() != 1); // convert memory to list of registers // in case of access to whole memory // or slice of memory @@ -447,36 +466,34 @@ // don't convert in case of accessing // memory using address, e.g. // mem[0] <= '{default:0} - // - // Access to whole memory - if (node->children.size() == 0) { - force_convert = true; - } - // Access to slice of memory - if (node->children.size() == 1 && node->children[0]->children.size() != 1) { - force_convert = true; + if (is_full_memory_access || is_slice_memory_access) { + add_force_convert_attribute(iter->second); } } + break; } else { - name = node->str; - if (memories.count(name)) { - if (!memories[name]->attributes.count(UhdmAst::force_convert())) { - // Access to whole memory - if (node->children.size() == 0) { - force_convert = true; - } - // Access to slice of memory - if (node->children.size() == 1 && node->children[0]->children.size() != 1) { - force_convert = true; - } + if (scope_end_pos == 0) { + // We reached the top scope and the memory node wasn't found. + break; + } else { + // Memory node wasn't found. + // Erase node name and last segment of the scope to check the previous scope. + // FIXME: This doesn't work with escaped identifiers containing a dot. + scope_end_pos = full_id.find_last_of('.', scope_end_pos - 1); + if (scope_end_pos == std::string::npos) { + scope_end_pos = 0; } + full_id.erase(scope_end_pos); } } - if (force_convert) { - add_force_convert_attribute(memories[name]); - } } - }); + } +} + +static void check_memories(AST::AstNode *node) +{ + std::map<std::string, AST::AstNode *> memories; + check_memories(node, "", memories); } // This function is workaround missing support for multirange (with n-ranges) packed/unpacked nodes