Skip to content

Commit 9cdd345

Browse files
joennlaeJannis Schoenleber
authored andcommitted
systemverilog-plugin: add parameter type propagation through hierarchy
Signed-off-by: Jannis Schönleber <joennlae@gmail.com>
1 parent e0a923c commit 9cdd345

File tree

3 files changed

+168
-4
lines changed

3 files changed

+168
-4
lines changed

systemverilog-plugin/UhdmAst.cc

Lines changed: 160 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,37 @@ static void visitEachDescendant(AST::AstNode *node, const std::function<void(AST
188188
}
189189
}
190190

191+
static void visitEachDescendantIdentifier(AST::AstNode *node, std::unordered_set<std::string> &identifiers,
192+
const std::function<void(AST::AstNode *, std::unordered_set<std::string> &)> &f)
193+
{
194+
for (auto child : node->children) {
195+
f(child, identifiers);
196+
visitEachDescendantIdentifier(child, identifiers, f);
197+
}
198+
}
199+
200+
static void check_range_for_identifier(AST::AstNode *node, std::unordered_set<std::string> &identifiers)
201+
{
202+
if (node->attributes.count(UhdmAst::packed_ranges())) {
203+
for (auto r : node->attributes[UhdmAst::packed_ranges()]->children) {
204+
visitEachDescendantIdentifier(r, identifiers, [](AST::AstNode *node, std::unordered_set<std::string> &identifiers) {
205+
if (node->type == AST::AST_IDENTIFIER) {
206+
identifiers.insert(node->str);
207+
}
208+
});
209+
}
210+
}
211+
if (node->attributes.count(UhdmAst::unpacked_ranges())) {
212+
for (auto r : node->attributes[UhdmAst::unpacked_ranges()]->children) {
213+
visitEachDescendantIdentifier(r, identifiers, [](AST::AstNode *node, std::unordered_set<std::string> &identifiers) {
214+
if (node->type == AST::AST_IDENTIFIER) {
215+
identifiers.insert(node->str);
216+
}
217+
});
218+
}
219+
}
220+
}
221+
191222
static void add_multirange_wire(AST::AstNode *node, std::vector<AST::AstNode *> packed_ranges, std::vector<AST::AstNode *> unpacked_ranges,
192223
bool reverse = true)
193224
{
@@ -345,10 +376,15 @@ static void resolve_wiretype(AST::AstNode *wire_node)
345376
AST::AstNode *wiretype_ast = nullptr;
346377
log_assert(AST_INTERNAL::current_scope.count(wiretype_node->str));
347378
wiretype_ast = AST_INTERNAL::current_scope[wiretype_node->str];
379+
380+
auto wiretype_ast_clone = wiretype_ast->clone();
348381
// we need to setup current top ast as this simplify
349382
// needs to have access to all already defined ids
350383
while (wire_node->simplify(true, false, false, 1, -1, false, false)) {
351384
}
385+
// retain original type if type is reused multiple times as yosys will simplify it to a unusable type later or set even a nullptr
386+
AST_INTERNAL::current_scope[wiretype_ast_clone->str] = wiretype_ast_clone;
387+
352388
if (wiretype_ast->children[0]->type == AST::AST_STRUCT && wire_node->type == AST::AST_WIRE) {
353389
auto struct_width = get_max_offset_struct(wiretype_ast->children[0]);
354390
wire_node->range_left = struct_width;
@@ -767,6 +803,10 @@ static void setup_current_scope(std::unordered_map<std::string, AST::AstNode *>
767803
}
768804
for (auto &o : current_top_node->children) {
769805
if (o->type == AST::AST_TYPEDEF || o->type == AST::AST_PARAMETER || o->type == AST::AST_LOCALPARAM) {
806+
// debatable if needed :-)
807+
// if (AST_INTERNAL::current_scope.count(o->str)) {
808+
// log_warning("multiple typedefs for %s, %d, %d\n", o->str.c_str(), o->type, AST_INTERNAL::current_scope[o->str]->type);
809+
// }
770810
AST_INTERNAL::current_scope[o->str] = o;
771811
} else if (o->type == AST::AST_ENUM) {
772812
AST_INTERNAL::current_scope[o->str] = o;
@@ -1193,6 +1233,25 @@ void UhdmAst::visit_one_to_one(const std::vector<int> child_node_types, vpiHandl
11931233
}
11941234
}
11951235

1236+
void UhdmAst::visit_one_to_two_levels(int child_node_type_level1, const std::vector<int> child_node_types, vpiHandle parent_handle,
1237+
const std::function<void(AST::AstNode *)> &f)
1238+
{
1239+
vpiHandle first_level = vpi_iterate(child_node_type_level1, parent_handle);
1240+
while (vpiHandle vpi_child_obj = vpi_scan(first_level)) {
1241+
for (auto child : child_node_types) {
1242+
vpiHandle itr = vpi_handle(child, vpi_child_obj);
1243+
if (itr) {
1244+
UhdmAst uhdm_ast(this, shared, indent + " ");
1245+
auto *child_node = uhdm_ast.process_object(itr);
1246+
f(child_node);
1247+
}
1248+
vpi_release_handle(itr);
1249+
}
1250+
vpi_release_handle(vpi_child_obj);
1251+
}
1252+
vpi_release_handle(first_level);
1253+
}
1254+
11961255
void UhdmAst::visit_range(vpiHandle obj_h, const std::function<void(AST::AstNode *)> &f)
11971256
{
11981257
std::vector<AST::AstNode *> range_nodes;
@@ -1690,15 +1749,36 @@ void UhdmAst::move_type_to_new_typedef(AST::AstNode *current_node, AST::AstNode
16901749
typedef_node->location = type_node->location;
16911750
typedef_node->filename = type_node->filename;
16921751
typedef_node->str = strip_package_name(type_node->str);
1752+
bool isReplace = false;
1753+
AST::AstNode *replaceNode = nullptr;
16931754
for (auto c : current_node->children) {
16941755
if (c->str == typedef_node->str) {
1695-
return;
1756+
log_assert(c->children[0]);
1757+
if (c->children[0]->type < type_node->type) {
1758+
if (type_node->type == AST::AST_ENUM && type_node->attributes.count("\\enum_base_type") == 0) {
1759+
// redefine enum we can skip
1760+
return;
1761+
}
1762+
// replace type used for parameter propagation
1763+
isReplace = true;
1764+
replaceNode = c;
1765+
log_experimental("Info: will be replacing type %s, %d, new %d\n", c->str.c_str(), c->children[c->children.size() - 1]->type,
1766+
type_node->type);
1767+
continue;
1768+
} else {
1769+
// assume it the same, maybe add warning for type parameter propagation through hierarchy
1770+
return;
1771+
}
16961772
}
16971773
}
16981774
if (type_node->type == AST::AST_STRUCT) {
16991775
type_node->str.clear();
17001776
typedef_node->children.push_back(type_node);
1701-
current_node->children.push_back(typedef_node);
1777+
if (isReplace) {
1778+
*replaceNode = *typedef_node;
1779+
} else {
1780+
current_node->children.push_back(typedef_node);
1781+
}
17021782
} else if (type_node->type == AST::AST_ENUM) {
17031783
if (type_node->attributes.count("\\enum_base_type")) {
17041784
auto base_type = type_node->attributes["\\enum_base_type"];
@@ -1718,7 +1798,11 @@ void UhdmAst::move_type_to_new_typedef(AST::AstNode *current_node, AST::AstNode
17181798
wire_node->attributes[enum_item_str.c_str()] = AST::AstNode::mkconst_str(c->str);
17191799
}
17201800
typedef_node->children.push_back(wire_node);
1721-
current_node->children.push_back(typedef_node);
1801+
if (isReplace) {
1802+
*replaceNode = *typedef_node;
1803+
} else {
1804+
current_node->children.push_back(typedef_node);
1805+
}
17221806
delete type_node;
17231807
} else {
17241808
type_node->str = "$enum" + std::to_string(shared.next_enum_id());
@@ -1738,7 +1822,11 @@ void UhdmAst::move_type_to_new_typedef(AST::AstNode *current_node, AST::AstNode
17381822
} else {
17391823
type_node->str.clear();
17401824
typedef_node->children.push_back(type_node);
1741-
current_node->children.push_back(typedef_node);
1825+
if (isReplace) {
1826+
*replaceNode = *typedef_node;
1827+
} else {
1828+
current_node->children.push_back(typedef_node);
1829+
}
17421830
}
17431831
}
17441832

@@ -1918,6 +2006,12 @@ void UhdmAst::process_module()
19182006
delete node;
19192007
}
19202008
});
2009+
// adding type parameter name to instantiated module name
2010+
visit_one_to_two_levels(vpiParameter, {vpiTypespec}, obj_h, [&](AST::AstNode *node) {
2011+
if (node) {
2012+
parameters.push_back(std::make_pair(node->str, RTLIL::Const::from_string(node->str)));
2013+
}
2014+
});
19212015
// We need to rename module to prevent name collision with the same module, but with different parameters
19222016
std::string module_name = !parameters.empty() ? AST::derived_module_name(type, parameters).c_str() : type;
19232017
auto module_node = shared.top_nodes[module_name];
@@ -1972,6 +2066,54 @@ void UhdmAst::process_module()
19722066
current_node->children.insert(current_node->children.begin(), typeNode);
19732067
auto old_top = shared.current_top_node;
19742068
shared.current_top_node = module_node;
2069+
shared.elaborated_nodes.push_back(old_top);
2070+
2071+
std::unordered_set<std::string> visited_identifiers;
2072+
visitEachDescendant(module_node,
2073+
[&](AST::AstNode *current_scope_node) { check_range_for_identifier(current_scope_node, visited_identifiers); });
2074+
2075+
visit_one_to_two_levels(vpiParameter, {vpiTypespec}, obj_h, [&](AST::AstNode *node) {
2076+
if (node && node->str.size()) {
2077+
move_type_to_new_typedef(module_node, node);
2078+
}
2079+
});
2080+
2081+
// vpiVariables need to be checked after vpiParameter as they are processed in that order and pointers are swapped in surelog
2082+
visit_one_to_two_levels(vpiVariables, {vpiTypespec}, obj_h, [&](AST::AstNode *node) {
2083+
if (node && node->str.size()) {
2084+
move_type_to_new_typedef(module_node, node);
2085+
}
2086+
});
2087+
2088+
visitEachDescendant(module_node,
2089+
[&](AST::AstNode *current_scope_node) { check_range_for_identifier(current_scope_node, visited_identifiers); });
2090+
2091+
visitEachDescendant(module_node, [&](AST::AstNode *current_scope_node) {
2092+
if (visited_identifiers.count(current_scope_node->str) > 0) {
2093+
// identifier is already defined in this scope does not need to be copied
2094+
visited_identifiers.erase(current_scope_node->str);
2095+
}
2096+
});
2097+
2098+
if (visited_identifiers.size() > 0) {
2099+
// couldn't copy all identifiers copied
2100+
for (int i = shared.elaborated_nodes.size() - 1; i >= 0; i--) {
2101+
// trying to copy from elaborated nodes before hand
2102+
auto parent_of_parent = shared.elaborated_nodes.at(i);
2103+
visitEachDescendant(parent_of_parent, [&](AST::AstNode *current_scope_node) {
2104+
if (visited_identifiers.count(current_scope_node->str) > 0) {
2105+
// copy identifier and replace
2106+
visited_identifiers.erase(current_scope_node->str);
2107+
add_or_replace_child(module_node, current_scope_node->clone());
2108+
}
2109+
});
2110+
if (visited_identifiers.size() == 0) {
2111+
break;
2112+
}
2113+
}
2114+
}
2115+
log_assert(visited_identifiers.size() == 0);
2116+
19752117
visit_one_to_many({vpiVariables, vpiNet, vpiArrayNet, vpiInterface, vpiModule, vpiPort, vpiGenScopeArray, vpiContAssign, vpiTaskFunc}, obj_h,
19762118
[&](AST::AstNode *node) {
19772119
if (node) {
@@ -1980,6 +2122,7 @@ void UhdmAst::process_module()
19802122
});
19812123
make_cell(obj_h, current_node, module_node);
19822124
shared.current_top_node = old_top;
2125+
shared.elaborated_nodes.pop_back();
19832126
}
19842127
}
19852128

@@ -4415,6 +4558,16 @@ void UhdmAst::process_unsupported_stmt(const UHDM::BaseClass *object, bool is_er
44154558
log_func("%sCurrently not supported object of type '%s'\n", prefix.c_str(), UHDM::VpiTypeName(obj_h).c_str());
44164559
}
44174560

4561+
void UhdmAst::process_type_parameter()
4562+
{
4563+
visit_one_to_one({vpiTypespec}, obj_h, [&](AST::AstNode *node) {
4564+
if (node) {
4565+
current_node = make_ast_node(AST::AST_TYPEDEF);
4566+
current_node->children.push_back(node);
4567+
}
4568+
});
4569+
}
4570+
44184571
AST::AstNode *UhdmAst::process_object(vpiHandle obj_handle)
44194572
{
44204573
obj_h = obj_handle;
@@ -4687,6 +4840,9 @@ AST::AstNode *UhdmAst::process_object(vpiHandle obj_handle)
46874840
process_unsupported_stmt(object);
46884841
break;
46894842
case vpiTypeParameter:
4843+
// for type parameter propagation we need to instantiate a base type (often logic)
4844+
// that will be replaced later
4845+
process_type_parameter();
46904846
// Instances in an `uhdmTopModules` tree already have all parameter references
46914847
// substituted with the parameter type/value by Surelog,
46924848
// so the plugin doesn't need to process the parameter itself.

systemverilog-plugin/UhdmAst.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ class UhdmAst
2424
// ChildrenNodeTypes that are present in the given object.
2525
void visit_one_to_one(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f);
2626

27+
// Walks through two-level relationships from given parent
28+
// mainly used for type parameter propagation
29+
void visit_one_to_two_levels(int child_node_type_level1, const std::vector<int> child_node_types, vpiHandle parent_handle,
30+
const std::function<void(::Yosys::AST::AstNode *)> &f);
31+
2732
// Visit children of type vpiRange that belong to the given parent node.
2833
void visit_range(vpiHandle obj_h, const std::function<void(::Yosys::AST::AstNode *)> &f);
2934

@@ -152,6 +157,7 @@ class UhdmAst
152157
void process_primterm();
153158
void simplify_parameter(::Yosys::AST::AstNode *parameter, ::Yosys::AST::AstNode *module_node = nullptr);
154159
void process_unsupported_stmt(const UHDM::BaseClass *object, bool is_error = true);
160+
void process_type_parameter();
155161

156162
UhdmAst(UhdmAst *p, UhdmAstShared &s, const std::string &i) : parent(p), shared(s), indent(i)
157163
{

systemverilog-plugin/uhdmastshared.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class UhdmAstShared
6666
std::unordered_map<std::string, ::Yosys::AST::AstNode *> param_types;
6767

6868
::Yosys::AST::AstNode *current_top_node = nullptr;
69+
70+
std::vector<::Yosys::AST::AstNode *> elaborated_nodes;
6971
// Set of non-synthesizable objects to skip in current design;
7072
std::set<const UHDM::BaseClass *> nonSynthesizableObjects;
7173
};

0 commit comments

Comments
 (0)