circt/lib/Conversion/ImportVerilog/HierarchicalNames.cpp

192 lines
6.8 KiB
C++

//===- 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();
}