25 #include "llvm/Support/Debug.h"
27 #define DEBUG_TYPE "firrtl-inject-dut-hier"
29 using namespace circt;
30 using namespace firrtl;
33 struct InjectDUTHierarchy :
public InjectDUTHierarchyBase<InjectDUTHierarchy> {
34 void runOnOperation()
override;
47 InstanceOp wrapperInst) {
48 auto namepath = path.getNamepath().getValue();
51 SmallVector<Attribute> newNamepath;
52 newNamepath.reserve(namepath.size() + 1);
53 while (path.modPart(nlaIdx) != dut.getNameAttr())
54 newNamepath.push_back(namepath[nlaIdx++]);
59 if (
auto dutRef = dyn_cast<hw::InnerRefAttr>(namepath[nlaIdx]))
61 wrapperInst.getModuleNameAttr().getAttr(), dutRef.getName()));
63 newNamepath.push_back(
67 auto back = namepath.drop_front(nlaIdx + 1);
68 newNamepath.append(back.begin(), back.end());
69 path.setNamepathAttr(
ArrayAttr::get(dut.getContext(), newNamepath));
72 void InjectDUTHierarchy::runOnOperation() {
75 CircuitOp circuit = getOperation();
85 StringAttr wrapperName;
95 auto name = anno.
getMember<StringAttr>(
"name");
97 emitError(circuit->getLoc())
98 <<
"contained a malformed "
99 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
100 "annotation that did not contain a 'name' field";
106 emitError(circuit->getLoc())
107 <<
"contained multiple "
108 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
109 "annotations when at most one is allowed";
119 return signalPassFailure();
124 return markAllAnalysesPreserved();
130 for (
auto mod : circuit.getOps<FModuleOp>()) {
134 auto diag = emitError(mod.getLoc())
136 << dut.getModuleName()
137 <<
"' also had such an annotation (this should "
139 diag.attachNote(dut.getLoc()) <<
"the first DUT was found here";
147 return signalPassFailure();
152 if (wrapperName && !dut) {
153 emitError(circuit->getLoc())
160 return signalPassFailure();
169 OpBuilder b(circuit.getContext());
172 b.setInsertionPointAfter(dut);
173 auto newDUT = b.create<FModuleOp>(dut.getLoc(), dut.getNameAttr(),
174 dut.getConventionAttr(), dut.getPorts(),
175 dut.getAnnotations());
177 SymbolTable::setSymbolVisibility(newDUT, dut.getVisibility());
178 dut.setName(b.getStringAttr(circuitNS.newName(wrapperName.getValue())));
187 [](
auto,
auto) {
return true; });
192 b.setInsertionPointToStart(dut.getBodyBlock());
193 hw::InnerSymbolNamespace dutNS(dut);
195 b.create<InstanceOp>(b.getUnknownLoc(), wrapper, wrapper.getModuleName(),
196 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
197 ArrayRef<Attribute>{},
false,
199 dutNS.newName(wrapper.getModuleName()))));
200 for (
const auto &pair : llvm::enumerate(wrapperInst.getResults())) {
201 Value lhs = dut.getArgument(pair.index());
202 Value rhs = pair.value();
209 DenseSet<StringAttr> dutPaths, dutPortSyms;
211 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
213 dutPaths.insert(sym.getAttr());
215 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
216 auto portSym = dut.getPortSymbolAttr(i);
218 dutPortSyms.insert(portSym.getSymName());
220 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
222 dutPaths.insert(sym.getAttr());
227 llvm::dbgs() <<
"DUT Symbol Users:\n";
228 for (
auto path : dutPaths)
230 llvm::dbgs() <<
"Port Symbols:\n";
231 for (
auto sym : dutPortSyms)
245 LLVM_DEBUG(llvm::dbgs() <<
"Processing hierarchical paths:\n");
246 auto &nlaTable = getAnalysis<NLATable>();
247 DenseMap<StringAttr, hw::HierPathOp> dutRenames;
248 for (
auto nla : llvm::make_early_inc_range(nlaTable.lookup(dut))) {
249 LLVM_DEBUG(llvm::dbgs() <<
" - " << nla <<
"\n");
250 auto namepath = nla.getNamepath().getValue();
254 if (nla.root() == dut.getNameAttr()) {
255 assert(namepath.size() > 1 &&
"namepath size must be greater than one");
257 wrapper.getNameAttr(),
258 cast<hw::InnerRefAttr>(namepath.front()).getName())};
259 auto tail = namepath.drop_front();
260 newNamepath.append(tail.begin(), tail.end());
261 nla->setAttr(
"namepath", b.getArrayAttr(newNamepath));
277 if (nla.leafMod() == dut.getNameAttr()) {
279 if (nla.isComponent() && dutPortSyms.count(nla.ref()))
285 if (nla.isModule() && dutPaths.contains(nla.getSymNameAttr())) {
286 OpBuilder::InsertionGuard guard(b);
287 b.setInsertionPoint(nla);
288 auto clone = cast<hw::HierPathOp>(b.clone(*nla));
289 clone.setSymNameAttr(b.getStringAttr(
290 circuitNS.newName(clone.getSymNameAttr().getValue())));
291 dutRenames.insert({nla.getSymNameAttr(), clone});
300 SmallVector<Annotation> newAnnotations;
301 auto removeAndUpdateNLAs = [&](
Annotation anno) ->
bool {
302 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
305 if (!dutRenames.count(sym.getAttr()))
310 newAnnotations.push_back(anno);
316 annotations.removeAnnotations(removeAndUpdateNLAs);
317 annotations.addAnnotations(newAnnotations);
318 annotations.applyToOperation(dut);
319 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
320 newAnnotations.clear();
322 annotations.removeAnnotations(removeAndUpdateNLAs);
323 annotations.addAnnotations(newAnnotations);
324 annotations.applyToPort(dut, i);
333 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.
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.