16 #include "mlir/Pass/Pass.h"
27 #include "llvm/Support/Debug.h"
29 #define DEBUG_TYPE "firrtl-inject-dut-hier"
33 #define GEN_PASS_DEF_INJECTDUTHIERARCHY
34 #include "circt/Dialect/FIRRTL/Passes.h.inc"
38 using namespace circt;
39 using namespace firrtl;
42 struct InjectDUTHierarchy
43 :
public circt::firrtl::impl::InjectDUTHierarchyBase<InjectDUTHierarchy> {
44 void runOnOperation()
override;
57 InstanceOp wrapperInst) {
58 auto namepath = path.getNamepath().getValue();
61 SmallVector<Attribute> newNamepath;
62 newNamepath.reserve(namepath.size() + 1);
63 while (path.modPart(nlaIdx) != dut.getNameAttr())
64 newNamepath.push_back(namepath[nlaIdx++]);
69 if (
auto dutRef = dyn_cast<hw::InnerRefAttr>(namepath[nlaIdx]))
71 wrapperInst.getModuleNameAttr().getAttr(), dutRef.getName()));
73 newNamepath.push_back(
77 auto back = namepath.drop_front(nlaIdx + 1);
78 newNamepath.append(back.begin(), back.end());
79 path.setNamepathAttr(
ArrayAttr::get(dut.getContext(), newNamepath));
82 void InjectDUTHierarchy::runOnOperation() {
85 CircuitOp circuit = getOperation();
95 StringAttr wrapperName;
105 auto name = anno.
getMember<StringAttr>(
"name");
107 emitError(circuit->getLoc())
108 <<
"contained a malformed "
109 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
110 "annotation that did not contain a 'name' field";
116 emitError(circuit->getLoc())
117 <<
"contained multiple "
118 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
119 "annotations when at most one is allowed";
129 return signalPassFailure();
134 return markAllAnalysesPreserved();
140 for (
auto mod : circuit.getOps<FModuleOp>()) {
144 auto diag = emitError(mod.getLoc())
146 << dut.getModuleName()
147 <<
"' also had such an annotation (this should "
149 diag.attachNote(dut.getLoc()) <<
"the first DUT was found here";
157 return signalPassFailure();
162 if (wrapperName && !dut) {
163 emitError(circuit->getLoc())
170 return signalPassFailure();
179 OpBuilder b(circuit.getContext());
182 b.setInsertionPointAfter(dut);
183 auto newDUT = b.create<FModuleOp>(dut.getLoc(), dut.getNameAttr(),
184 dut.getConventionAttr(), dut.getPorts(),
185 dut.getAnnotations());
187 SymbolTable::setSymbolVisibility(newDUT, dut.getVisibility());
188 dut.setName(b.getStringAttr(circuitNS.newName(wrapperName.getValue())));
197 [](
auto,
auto) {
return true; });
202 b.setInsertionPointToStart(dut.getBodyBlock());
203 hw::InnerSymbolNamespace dutNS(dut);
205 b.create<InstanceOp>(b.getUnknownLoc(), wrapper, wrapper.getModuleName(),
206 NameKindEnum::DroppableName, ArrayRef<Attribute>{},
207 ArrayRef<Attribute>{},
false,
209 dutNS.newName(wrapper.getModuleName()))));
210 for (
const auto &pair : llvm::enumerate(wrapperInst.getResults())) {
211 Value lhs = dut.getArgument(pair.index());
212 Value rhs = pair.value();
219 DenseSet<StringAttr> dutPaths, dutPortSyms;
221 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
223 dutPaths.insert(sym.getAttr());
225 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
226 auto portSym = dut.getPortSymbolAttr(i);
228 dutPortSyms.insert(portSym.getSymName());
230 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
232 dutPaths.insert(sym.getAttr());
237 llvm::dbgs() <<
"DUT Symbol Users:\n";
238 for (
auto path : dutPaths)
240 llvm::dbgs() <<
"Port Symbols:\n";
241 for (
auto sym : dutPortSyms)
255 LLVM_DEBUG(llvm::dbgs() <<
"Processing hierarchical paths:\n");
256 auto &nlaTable = getAnalysis<NLATable>();
257 DenseMap<StringAttr, hw::HierPathOp> dutRenames;
258 for (
auto nla : llvm::make_early_inc_range(nlaTable.lookup(dut))) {
259 LLVM_DEBUG(llvm::dbgs() <<
" - " << nla <<
"\n");
260 auto namepath = nla.getNamepath().getValue();
264 if (nla.root() == dut.getNameAttr()) {
265 assert(namepath.size() > 1 &&
"namepath size must be greater than one");
267 wrapper.getNameAttr(),
268 cast<hw::InnerRefAttr>(namepath.front()).getName())};
269 auto tail = namepath.drop_front();
270 newNamepath.append(tail.begin(), tail.end());
271 nla->setAttr(
"namepath", b.getArrayAttr(newNamepath));
287 if (nla.leafMod() == dut.getNameAttr()) {
289 if (nla.isComponent() && dutPortSyms.count(nla.ref()))
295 if (nla.isModule() && dutPaths.contains(nla.getSymNameAttr())) {
296 OpBuilder::InsertionGuard guard(b);
297 b.setInsertionPoint(nla);
298 auto clone = cast<hw::HierPathOp>(b.clone(*nla));
299 clone.setSymNameAttr(b.getStringAttr(
300 circuitNS.newName(clone.getSymNameAttr().getValue())));
301 dutRenames.insert({nla.getSymNameAttr(), clone});
310 SmallVector<Annotation> newAnnotations;
311 auto removeAndUpdateNLAs = [&](
Annotation anno) ->
bool {
312 auto sym = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
315 if (!dutRenames.count(sym.getAttr()))
320 newAnnotations.push_back(anno);
326 annotations.removeAnnotations(removeAndUpdateNLAs);
327 annotations.addAnnotations(newAnnotations);
328 annotations.applyToOperation(dut);
329 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
330 newAnnotations.clear();
332 annotations.removeAnnotations(removeAndUpdateNLAs);
333 annotations.addAnnotations(newAnnotations);
334 annotations.applyToPort(dut, i);
343 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.