25#include "mlir/Pass/Pass.h"
26#include "llvm/Support/Debug.h"
28#define DEBUG_TYPE "firrtl-inject-dut-hier"
32#define GEN_PASS_DEF_INJECTDUTHIERARCHY
33#include "circt/Dialect/FIRRTL/Passes.h.inc"
38using namespace firrtl;
41struct InjectDUTHierarchy
42 :
public circt::firrtl::impl::InjectDUTHierarchyBase<InjectDUTHierarchy> {
43 void runOnOperation()
override;
56 InstanceOp wrapperInst) {
57 auto namepath = path.getNamepath().getValue();
60 SmallVector<Attribute> newNamepath;
61 newNamepath.reserve(namepath.size() + 1);
62 while (path.modPart(nlaIdx) != dut.getNameAttr())
63 newNamepath.push_back(namepath[nlaIdx++]);
64 newNamepath.push_back(hw::InnerRefAttr::get(dut.getModuleNameAttr(),
68 if (
auto dutRef = dyn_cast<hw::InnerRefAttr>(namepath[nlaIdx]))
69 newNamepath.push_back(hw::InnerRefAttr::get(
70 wrapperInst.getModuleNameAttr().getAttr(), dutRef.getName()));
72 newNamepath.push_back(
73 FlatSymbolRefAttr::get(wrapperInst.getModuleNameAttr().getAttr()));
76 auto back = namepath.drop_front(nlaIdx + 1);
77 newNamepath.append(back.begin(), back.end());
78 path.setNamepathAttr(ArrayAttr::get(dut.getContext(), newNamepath));
81void InjectDUTHierarchy::runOnOperation() {
84 CircuitOp circuit = getOperation();
90 StringAttr wrapperName;
100 auto name = anno.
getMember<StringAttr>(
"name");
102 emitError(circuit->getLoc())
103 <<
"contained a malformed "
104 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
105 "annotation that did not contain a 'name' field";
111 emitError(circuit->getLoc())
112 <<
"contained multiple "
113 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
114 "annotations when at most one is allowed";
124 return signalPassFailure();
129 return markAllAnalysesPreserved();
133 InstanceInfo &instanceInfo = getAnalysis<InstanceInfo>();
134 if (!instanceInfo.
getDut()) {
135 emitError(circuit->getLoc())
138 return signalPassFailure();
143 FModuleOp dut = cast<FModuleOp>(instanceInfo.
getDut());
152 OpBuilder b(circuit.getContext());
155 b.setInsertionPointAfter(dut);
156 auto newDUT = b.create<FModuleOp>(dut.getLoc(), dut.getNameAttr(),
157 dut.getConventionAttr(), dut.getPorts(),
158 dut.getAnnotations());
160 SymbolTable::setSymbolVisibility(newDUT, dut.getVisibility());
161 dut.setName(b.getStringAttr(circuitNS.newName(wrapperName.getValue())));
170 [](
auto,
auto) {
return true; });
175 b.setInsertionPointToStart(dut.getBodyBlock());
178 b.create<InstanceOp>(b.getUnknownLoc(), wrapper, wrapper.getModuleName(),
179 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
180 ArrayRef<Attribute>{},
false,
181 hw::InnerSymAttr::get(b.getStringAttr(
182 dutNS.newName(wrapper.getModuleName()))));
183 for (
const auto &pair :
llvm::enumerate(wrapperInst.getResults())) {
184 Value lhs = dut.getArgument(pair.index());
185 Value rhs = pair.value();
186 if (dut.getPortDirection(pair.index()) == Direction::In)
192 DenseSet<StringAttr> dutPaths, dutPortSyms;
194 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
196 dutPaths.insert(sym.getAttr());
198 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
199 auto portSym = dut.getPortSymbolAttr(i);
201 dutPortSyms.insert(portSym.getSymName());
203 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
205 dutPaths.insert(sym.getAttr());
210 llvm::dbgs() <<
"DUT Symbol Users:\n";
211 for (
auto path : dutPaths)
212 llvm::dbgs() <<
" - " << FlatSymbolRefAttr::
get(path) <<
"\n";
213 llvm::dbgs() <<
"Port Symbols:\n";
214 for (
auto sym : dutPortSyms)
215 llvm::dbgs() <<
" - " << FlatSymbolRefAttr::
get(sym) <<
"\n";
228 LLVM_DEBUG(llvm::dbgs() <<
"Processing hierarchical paths:\n");
229 auto &nlaTable = getAnalysis<NLATable>();
230 DenseMap<StringAttr, hw::HierPathOp> dutRenames;
231 for (
auto nla :
llvm::make_early_inc_range(nlaTable.lookup(dut))) {
232 LLVM_DEBUG(llvm::dbgs() <<
" - " << nla <<
"\n");
233 auto namepath = nla.getNamepath().getValue();
237 if (nla.root() == dut.getNameAttr()) {
238 assert(namepath.size() > 1 &&
"namepath size must be greater than one");
239 SmallVector<Attribute> newNamepath{hw::InnerRefAttr::get(
240 wrapper.getNameAttr(),
241 cast<hw::InnerRefAttr>(namepath.front()).getName())};
242 auto tail = namepath.drop_front();
243 newNamepath.append(tail.begin(), tail.end());
244 nla->setAttr(
"namepath", b.getArrayAttr(newNamepath));
260 if (nla.leafMod() == dut.getNameAttr()) {
262 if (nla.isComponent() && dutPortSyms.count(nla.ref()))
268 if (nla.isModule() && dutPaths.contains(nla.getSymNameAttr())) {
269 OpBuilder::InsertionGuard guard(b);
270 b.setInsertionPoint(nla);
271 auto clone = cast<hw::HierPathOp>(b.clone(*nla));
272 clone.setSymNameAttr(b.getStringAttr(
273 circuitNS.newName(clone.getSymNameAttr().getValue())));
274 dutRenames.insert({nla.getSymNameAttr(), clone});
283 SmallVector<Annotation> newAnnotations;
284 auto removeAndUpdateNLAs = [&](
Annotation anno) ->
bool {
285 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
288 if (!dutRenames.count(sym.getAttr()))
292 FlatSymbolRefAttr::get(dutRenames[sym.getAttr()].getSymNameAttr()));
293 newAnnotations.push_back(anno);
299 annotations.removeAnnotations(removeAndUpdateNLAs);
300 annotations.addAnnotations(newAnnotations);
301 annotations.applyToOperation(dut);
302 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
303 newAnnotations.clear();
305 annotations.removeAnnotations(removeAndUpdateNLAs);
306 annotations.addAnnotations(newAnnotations);
307 annotations.applyToPort(dut, i);
316 return std::make_unique<InjectDUTHierarchy>();
assert(baseType &&"element must be base type")
static AnnotationSet forPort(Operation *op, size_t portNo)
static void addHierarchy(hw::HierPathOp path, FModuleOp dut, InstanceOp wrapperInst)
Add an extra level of hierarchy to a hierarchical path that places the wrapper instance after the DUT...
This class provides a read-only projection over the MLIR attributes that represent a set of annotatio...
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
static bool removePortAnnotations(Operation *module, llvm::function_ref< bool(unsigned, Annotation)> predicate)
Remove all port annotations from a module or extmodule for which predicate returns true.
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
void setMember(StringAttr name, Attribute value)
Add or set a member of the annotation to a value.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * injectDUTHierarchyAnnoClass
constexpr const char * dutAnnoClass
std::unique_ptr< mlir::Pass > createInjectDUTHierarchyPass()
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs)
Emit a connect between two values.
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
llvm::raw_ostream & debugPassHeader(const mlir::Pass *pass, int width=80)
Write a boilerplate header for a pass to the debug stream.
The namespace of a CircuitOp, generally inhabited by modules.