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