-
Notifications
You must be signed in to change notification settings - Fork 309
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ImportVerilog] Add HierarchicalNames.cpp to support hierarchical nam…
…es. (#7382) * [ImportVerilog] Add HierarchicalNames.cpp to support hierarchical names. * [ImportVerilog] Distinguish hierarchical names into upward and downward. Based on the SystemVerilog IEEE Std 1800-2017 § 23.6 Hierarchical names. Hierarchical names are separated into upward and downward. For example: module Top; int x; SubA subA(); assign x = subA.a; // upward: The Sub module's variable is used at the Top module. endmodule module SubA; int a; assign a = Top.x; // downward: The Top module's variable is used at the Sub module. endmodule Therefore, we mark upward as outputs and downward as inputs, meanwhile, all hierarchical names are marked as RefType. Unsupported cases: However, we don't support hierarchical names invoked by two irrelevant modules at the same level. For example: module A; int a = B.b; endmodule module B; int b = A.a; endmodule And we also don't support hierarchical names existing in the repeat modules. For example: module Bar; SubC subC1(); SubC subC2(); int u = subC1.a; assign subC2.b = u; endmodule module SubC(); int a, b; endmodule Co-authored-by: Fabian Schuiki <fabian@schuiki.ch>
- Loading branch information
1 parent
b5141b7
commit 4a73177
Showing
6 changed files
with
405 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
//===- Expressions.cpp - Slang expression conversion ----------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "ImportVerilogInternals.h" | ||
|
||
using namespace circt; | ||
using namespace ImportVerilog; | ||
|
||
namespace { | ||
struct HierPathValueExprVisitor { | ||
Context &context; | ||
Location loc; | ||
OpBuilder &builder; | ||
|
||
// Such as `sub.a`, the `sub` is the outermost module for the hierarchical | ||
// variable `a`. | ||
const slang::ast::Symbol &outermostModule; | ||
|
||
HierPathValueExprVisitor(Context &context, Location loc, | ||
const slang::ast::Symbol &outermostModule) | ||
: context(context), loc(loc), builder(context.builder), | ||
outermostModule(outermostModule) {} | ||
|
||
// Handle hierarchical values | ||
LogicalResult visit(const slang::ast::HierarchicalValueExpression &expr) { | ||
auto *currentInstBody = | ||
expr.symbol.getParentScope()->getContainingInstance(); | ||
auto *outermostInstBody = | ||
outermostModule.as_if<slang::ast::InstanceBodySymbol>(); | ||
|
||
// Like module Foo; int a; Foo.a; endmodule. | ||
// Ignore "Foo.a" invoked by this module itself. | ||
if (currentInstBody == outermostInstBody) | ||
return success(); | ||
|
||
auto hierName = builder.getStringAttr(expr.symbol.name); | ||
const slang::ast::InstanceBodySymbol *parentInstBody = nullptr; | ||
|
||
// Collect hierarchical names that are added to the port list. | ||
std::function<void(const slang::ast::InstanceBodySymbol *, bool)> | ||
collectHierarchicalPaths = [&](auto sym, bool isUpward) { | ||
// Here we use "sameHierPaths" to avoid collecting the repeat | ||
// hierarchical names on the same path. | ||
if (!context.sameHierPaths.contains(hierName) || | ||
!context.hierPaths.contains(sym)) { | ||
context.hierPaths[sym].push_back( | ||
HierPathInfo{hierName, | ||
{}, | ||
isUpward ? slang::ast::ArgumentDirection::Out | ||
: slang::ast::ArgumentDirection::In, | ||
&expr.symbol}); | ||
context.sameHierPaths.insert(hierName); | ||
} | ||
|
||
// Iterate up from the current instance body symbol until meeting the | ||
// outermost module. | ||
parentInstBody = | ||
sym->parentInstance->getParentScope()->getContainingInstance(); | ||
if (!parentInstBody) | ||
return; | ||
|
||
if (isUpward) { | ||
// Avoid collecting hierarchical names into the outermost module. | ||
if (parentInstBody && parentInstBody != outermostInstBody) { | ||
hierName = | ||
builder.getStringAttr(sym->parentInstance->name + | ||
llvm::Twine(".") + hierName.getValue()); | ||
collectHierarchicalPaths(parentInstBody, isUpward); | ||
} | ||
} else { | ||
if (parentInstBody && parentInstBody != currentInstBody) | ||
collectHierarchicalPaths(parentInstBody, isUpward); | ||
} | ||
}; | ||
|
||
// Determine whether hierarchical names are upward or downward. | ||
auto *tempInstBody = currentInstBody; | ||
while (tempInstBody) { | ||
tempInstBody = tempInstBody->parentInstance->getParentScope() | ||
->getContainingInstance(); | ||
if (tempInstBody == outermostInstBody) { | ||
collectHierarchicalPaths(currentInstBody, true); | ||
return success(); | ||
} | ||
} | ||
|
||
hierName = builder.getStringAttr(currentInstBody->parentInstance->name + | ||
llvm::Twine(".") + hierName.getValue()); | ||
collectHierarchicalPaths(outermostInstBody, false); | ||
return success(); | ||
} | ||
|
||
/// TODO:Skip all others. | ||
/// But we should output a warning to display which symbol had been skipped. | ||
/// However, to ensure we can test smoothly, we didn't do that. | ||
template <typename T> | ||
LogicalResult visit(T &&node) { | ||
return success(); | ||
} | ||
|
||
LogicalResult visitInvalid(const slang::ast::Expression &expr) { | ||
mlir::emitError(loc, "invalid expression"); | ||
return failure(); | ||
} | ||
}; | ||
} // namespace | ||
|
||
LogicalResult | ||
Context::collectHierarchicalValues(const slang::ast::Expression &expr, | ||
const slang::ast::Symbol &outermostModule) { | ||
auto loc = convertLocation(expr.sourceRange); | ||
return expr.visit(HierPathValueExprVisitor(*this, loc, outermostModule)); | ||
} | ||
|
||
/// Traverse the instance body. | ||
namespace { | ||
struct InstBodyVisitor { | ||
Context &context; | ||
Location loc; | ||
|
||
InstBodyVisitor(Context &context, Location loc) | ||
: context(context), loc(loc) {} | ||
|
||
// Handle instances. | ||
LogicalResult visit(const slang::ast::InstanceSymbol &instNode) { | ||
return context.traverseInstanceBody(instNode.body); | ||
} | ||
|
||
// Handle variables. | ||
LogicalResult visit(const slang::ast::VariableSymbol &varNode) { | ||
auto &outermostModule = varNode.getParentScope()->asSymbol(); | ||
if (const auto *init = varNode.getInitializer()) | ||
if (failed(context.collectHierarchicalValues(*init, outermostModule))) | ||
return failure(); | ||
return success(); | ||
} | ||
|
||
// Handle nets. | ||
LogicalResult visit(const slang::ast::NetSymbol &netNode) { | ||
auto &outermostModule = netNode.getParentScope()->asSymbol(); | ||
if (const auto *init = netNode.getInitializer()) | ||
if (failed(context.collectHierarchicalValues(*init, outermostModule))) | ||
return failure(); | ||
return success(); | ||
} | ||
|
||
// Handle continuous assignments. | ||
LogicalResult visit(const slang::ast::ContinuousAssignSymbol &assignNode) { | ||
const auto &expr = | ||
assignNode.getAssignment().as<slang::ast::AssignmentExpression>(); | ||
|
||
// Such as `sub.a`, the `sub` is the outermost module for the hierarchical | ||
// variable `a`. | ||
auto &outermostModule = assignNode.getParentScope()->asSymbol(); | ||
if (expr.left().hasHierarchicalReference()) | ||
if (failed( | ||
context.collectHierarchicalValues(expr.left(), outermostModule))) | ||
return failure(); | ||
|
||
if (expr.right().hasHierarchicalReference()) | ||
if (failed( | ||
context.collectHierarchicalValues(expr.right(), outermostModule))) | ||
return failure(); | ||
|
||
return success(); | ||
} | ||
|
||
/// TODO:Skip all others. | ||
/// But we should output a warning to display which symbol had been skipped. | ||
/// However, to ensure we can test smoothly, we didn't do that. | ||
template <typename T> | ||
LogicalResult visit(T &&node) { | ||
return success(); | ||
} | ||
}; | ||
}; // namespace | ||
|
||
LogicalResult Context::traverseInstanceBody(const slang::ast::Symbol &symbol) { | ||
if (auto *instBodySymbol = symbol.as_if<slang::ast::InstanceBodySymbol>()) | ||
for (auto &member : instBodySymbol->members()) { | ||
auto loc = convertLocation(member.location); | ||
if (failed(member.visit(InstBodyVisitor(*this, loc)))) | ||
return failure(); | ||
} | ||
return success(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.