24 #include "mlir/Pass/Pass.h"
25 #include "llvm/Support/Debug.h"
27 #define DEBUG_TYPE "firrtl-inject-dut-hier"
31 #define GEN_PASS_DEF_INJECTDUTHIERARCHY
32 #include "circt/Dialect/FIRRTL/Passes.h.inc"
36 using namespace circt;
37 using namespace firrtl;
40 struct InjectDUTHierarchy
41 :
public circt::firrtl::impl::InjectDUTHierarchyBase<InjectDUTHierarchy> {
42 void runOnOperation()
override;
55 InstanceOp wrapperInst) {
56 auto namepath = path.getNamepath().getValue();
59 SmallVector<Attribute> newNamepath;
60 newNamepath.reserve(namepath.size() + 1);
61 while (path.modPart(nlaIdx) != dut.getNameAttr())
62 newNamepath.push_back(namepath[nlaIdx++]);
67 if (
auto dutRef = dyn_cast<hw::InnerRefAttr>(namepath[nlaIdx]))
69 wrapperInst.getModuleNameAttr().getAttr(), dutRef.getName()));
71 newNamepath.push_back(
75 auto back = namepath.drop_front(nlaIdx + 1);
76 newNamepath.append(back.begin(), back.end());
77 path.setNamepathAttr(
ArrayAttr::get(dut.getContext(), newNamepath));
80 void InjectDUTHierarchy::runOnOperation() {
83 CircuitOp circuit = getOperation();
93 StringAttr wrapperName;
103 auto name = anno.
getMember<StringAttr>(
"name");
105 emitError(circuit->getLoc())
106 <<
"contained a malformed "
107 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
108 "annotation that did not contain a 'name' field";
114 emitError(circuit->getLoc())
115 <<
"contained multiple "
116 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
117 "annotations when at most one is allowed";
127 return signalPassFailure();
132 return markAllAnalysesPreserved();
138 for (
auto mod : circuit.getOps<FModuleOp>()) {
142 auto diag = emitError(mod.getLoc())
144 << dut.getModuleName()
145 <<
"' also had such an annotation (this should "
147 diag.attachNote(dut.getLoc()) <<
"the first DUT was found here";
155 return signalPassFailure();
160 if (wrapperName && !dut) {
161 emitError(circuit->getLoc())
168 return signalPassFailure();
177 OpBuilder b(circuit.getContext());
180 b.setInsertionPointAfter(dut);
181 auto newDUT = b.create<FModuleOp>(dut.getLoc(), dut.getNameAttr(),
182 dut.getConventionAttr(), dut.getPorts(),
183 dut.getAnnotations());
185 SymbolTable::setSymbolVisibility(newDUT, dut.getVisibility());
186 dut.setName(b.getStringAttr(circuitNS.newName(wrapperName.getValue())));
195 [](
auto,
auto) {
return true; });
200 b.setInsertionPointToStart(dut.getBodyBlock());
201 hw::InnerSymbolNamespace dutNS(dut);
203 b.create<InstanceOp>(b.getUnknownLoc(), wrapper, wrapper.getModuleName(),
204 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
205 ArrayRef<Attribute>{},
false,
207 dutNS.newName(wrapper.getModuleName()))));
208 for (
const auto &pair : llvm::enumerate(wrapperInst.getResults())) {
209 Value lhs = dut.getArgument(pair.index());
210 Value rhs = pair.value();
217 DenseSet<StringAttr> dutPaths, dutPortSyms;
219 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
221 dutPaths.insert(sym.getAttr());
223 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
224 auto portSym = dut.getPortSymbolAttr(i);
226 dutPortSyms.insert(portSym.getSymName());
228 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
230 dutPaths.insert(sym.getAttr());
235 llvm::dbgs() <<
"DUT Symbol Users:\n";
236 for (
auto path : dutPaths)
238 llvm::dbgs() <<
"Port Symbols:\n";
239 for (
auto sym : dutPortSyms)
253 LLVM_DEBUG(llvm::dbgs() <<
"Processing hierarchical paths:\n");
254 auto &nlaTable = getAnalysis<NLATable>();
255 DenseMap<StringAttr, hw::HierPathOp> dutRenames;
256 for (
auto nla : llvm::make_early_inc_range(nlaTable.lookup(dut))) {
257 LLVM_DEBUG(llvm::dbgs() <<
" - " << nla <<
"\n");
258 auto namepath = nla.getNamepath().getValue();
262 if (nla.root() == dut.getNameAttr()) {
263 assert(namepath.size() > 1 &&
"namepath size must be greater than one");
265 wrapper.getNameAttr(),
266 cast<hw::InnerRefAttr>(namepath.front()).getName())};
267 auto tail = namepath.drop_front();
268 newNamepath.append(tail.begin(), tail.end());
269 nla->setAttr(
"namepath", b.getArrayAttr(newNamepath));
285 if (nla.leafMod() == dut.getNameAttr()) {
287 if (nla.isComponent() && dutPortSyms.count(nla.ref()))
293 if (nla.isModule() && dutPaths.contains(nla.getSymNameAttr())) {
294 OpBuilder::InsertionGuard guard(b);
295 b.setInsertionPoint(nla);
296 auto clone = cast<hw::HierPathOp>(b.clone(*nla));
297 clone.setSymNameAttr(b.getStringAttr(
298 circuitNS.newName(clone.getSymNameAttr().getValue())));
299 dutRenames.insert({nla.getSymNameAttr(), clone});
308 SmallVector<Annotation> newAnnotations;
309 auto removeAndUpdateNLAs = [&](
Annotation anno) ->
bool {
310 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
313 if (!dutRenames.count(sym.getAttr()))
318 newAnnotations.push_back(anno);
324 annotations.removeAnnotations(removeAndUpdateNLAs);
325 annotations.addAnnotations(newAnnotations);
326 annotations.applyToOperation(dut);
327 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
328 newAnnotations.clear();
330 annotations.removeAnnotations(removeAndUpdateNLAs);
331 annotations.addAnnotations(newAnnotations);
332 annotations.applyToPort(dut, i);
341 return std::make_unique<InjectDUTHierarchy>();
assert(baseType &&"element must be base type")
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.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
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.
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.