67#include "mlir/Pass/Pass.h"
68#include "llvm/Support/Debug.h"
70#define DEBUG_TYPE "firrtl-inject-dut-hier"
74#define GEN_PASS_DEF_INJECTDUTHIERARCHY
75#include "circt/Dialect/FIRRTL/Passes.h.inc"
83struct InjectDUTHierarchy
84 :
public circt::firrtl::impl::InjectDUTHierarchyBase<InjectDUTHierarchy> {
85 void runOnOperation()
override;
107 InstanceOp wrapperInst, StringAttr oldDutNameAttr) {
109 auto namepath = path.getNamepath().getValue();
112 SmallVector<Attribute> newNamepath;
113 newNamepath.reserve(namepath.size() + 1);
114 while (path.modPart(nlaIdx) != oldDutNameAttr)
115 newNamepath.push_back(namepath[nlaIdx++]);
116 newNamepath.push_back(hw::InnerRefAttr::get(dut.getModuleNameAttr(),
120 if (
auto dutRef = dyn_cast<hw::InnerRefAttr>(namepath[nlaIdx]))
121 newNamepath.push_back(hw::InnerRefAttr::get(
122 wrapperInst.getModuleNameAttr().getAttr(), dutRef.getName()));
124 newNamepath.push_back(
125 FlatSymbolRefAttr::get(wrapperInst.getModuleNameAttr().getAttr()));
128 auto back = namepath.drop_front(nlaIdx + 1);
129 newNamepath.append(back.begin(), back.end());
130 path.setNamepathAttr(ArrayAttr::get(dut.getContext(), newNamepath));
133void InjectDUTHierarchy::runOnOperation() {
136 CircuitOp circuit = getOperation();
142 StringAttr wrapperName;
145 StringRef wrapperInstanceName;
148 bool moveDut =
false;
156 for (Annotation anno : AnnotationSet(circuit)) {
157 if (!anno.isClass(injectDUTHierarchyAnnoClass))
160 auto name = anno.getMember<StringAttr>(
"name");
162 emitError(circuit->getLoc())
163 <<
"contained a malformed "
164 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
165 "annotation that did not contain a 'name' field";
171 emitError(circuit->getLoc())
172 <<
"contained multiple "
173 "'sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation' "
174 "annotations when at most one is allowed";
180 if (
auto instanceName = anno.getMember<StringAttr>(
"instanceName"))
181 wrapperInstanceName = instanceName.getValue();
182 if (
auto moveDutAnnoAttr = anno.getMember<BoolAttr>(
"moveDut"))
183 moveDut = moveDutAnnoAttr.getValue();
187 return signalPassFailure();
192 return markAllAnalysesPreserved();
196 InstanceInfo &instanceInfo = getAnalysis<InstanceInfo>();
197 if (!instanceInfo.
getDut()) {
198 emitError(circuit->getLoc())
199 <<
"contained a '" << injectDUTHierarchyAnnoClass <<
"', but no '"
200 << markDUTAnnoClass <<
"' was provided";
201 return signalPassFailure();
206 FModuleOp dut = cast<FModuleOp>(instanceInfo.
getDut());
215 OpBuilder b(circuit.getContext());
216 CircuitNamespace circuitNS(circuit);
217 b.setInsertionPointAfter(dut);
221 auto oldDutNameAttr = dut.getNameAttr();
223 dut = FModuleOp::create(b, dut.getLoc(), oldDutNameAttr,
224 dut.getConventionAttr(), dut.getPorts());
244 auto emptyArray = b.getArrayAttr({});
245 auto name = circuitNS.newName(wrapperName.getValue());
247 dut.setPortAnnotationsAttr(emptyArray);
248 dut.setName(b.getStringAttr(name));
251 if (circuit.getNameAttr() == wrapper.getNameAttr()) {
252 circuit.setNameAttr(dut.getNameAttr());
259 for (
auto *use : instanceGraph.
lookup(wrapper.getNameAttr())->
uses()) {
260 auto instanceOp = use->getInstance<InstanceOp>();
262 use->getInstance().emitOpError()
263 <<
"instantiates the design-under-test, but "
264 "is not a 'firrtl.instance'";
265 return signalPassFailure();
267 instanceOp.setModuleNameAttr(FlatSymbolRefAttr::get(dut.getNameAttr()));
270 dut.setVisibility(wrapper.getVisibility());
271 dut.setAnnotationsAttr(wrapper.getAnnotationsAttr());
272 dut.setPortAnnotationsAttr(wrapper.getPortAnnotationsAttr());
273 wrapper.setPrivate();
274 wrapper.setAnnotationsAttr(emptyArray);
275 wrapper.setPortAnnotationsAttr(emptyArray);
276 wrapper.setName(name);
280 b.setInsertionPointToStart(dut.getBodyBlock());
281 hw::InnerSymbolNamespace dutNS(dut);
282 auto instanceName = wrapperInstanceName.empty() ? wrapper.getModuleName()
283 : wrapperInstanceName;
284 auto wrapperInst = InstanceOp::create(
285 b, b.getUnknownLoc(), wrapper, instanceName, NameKindEnum::DroppableName,
286 ArrayRef<Attribute>{}, ArrayRef<Attribute>{},
false,
false,
287 hw::InnerSymAttr::get(b.getStringAttr(dutNS.newName(instanceName))));
288 for (
const auto &pair : llvm::enumerate(wrapperInst.getResults())) {
289 Value lhs = dut.getArgument(pair.index());
290 Value rhs = pair.value();
291 if (dut.getPortDirection(pair.index()) == Direction::In)
297 DenseSet<StringAttr> dutPaths, dutPortSyms;
298 for (
auto anno : AnnotationSet(dut)) {
299 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
301 dutPaths.insert(sym.getAttr());
303 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
304 auto portSym = dut.getPortSymbolAttr(i);
306 dutPortSyms.insert(portSym.getSymName());
308 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
310 dutPaths.insert(sym.getAttr());
315 llvm::dbgs() <<
"DUT Symbol Users:\n";
316 for (
auto path : dutPaths)
317 llvm::dbgs() <<
" - " << FlatSymbolRefAttr::get(path) <<
"\n";
318 llvm::dbgs() <<
"Port Symbols:\n";
319 for (
auto sym : dutPortSyms)
320 llvm::dbgs() <<
" - " << FlatSymbolRefAttr::get(sym) <<
"\n";
335 LLVM_DEBUG(llvm::dbgs() <<
"Processing hierarchical paths:\n");
336 auto &nlaTable = getAnalysis<NLATable>();
337 DenseMap<StringAttr, hw::HierPathOp> dutRenames;
338 for (
auto nla : llvm::make_early_inc_range(nlaTable.lookup(oldDutNameAttr))) {
339 LLVM_DEBUG(llvm::dbgs() <<
" - " << nla <<
"\n");
340 auto namepath = nla.getNamepath().getValue();
350 if (nla.root() == oldDutNameAttr) {
351 assert(namepath.size() > 1 &&
"namepath size must be greater than one");
352 SmallVector<Attribute> newNamepath{hw::InnerRefAttr::get(
353 wrapper.getNameAttr(),
354 cast<hw::InnerRefAttr>(namepath.front()).getName())};
355 auto tail = namepath.drop_front();
356 newNamepath.append(tail.begin(), tail.end());
357 nla->setAttr(
"namepath", b.getArrayAttr(newNamepath));
373 if (nla.leafMod() == oldDutNameAttr) {
376 if (nla.isComponent() && dutPortSyms.count(nla.ref()))
382 if (nla.isModule() && dutPaths.contains(nla.getSymNameAttr())) {
383 OpBuilder::InsertionGuard guard(b);
384 b.setInsertionPoint(nla);
385 auto clone = cast<hw::HierPathOp>(b.clone(*nla));
386 clone.setSymNameAttr(b.getStringAttr(
387 circuitNS.newName(clone.getSymNameAttr().getValue())));
388 dutRenames.insert({nla.getSymNameAttr(), clone});
397 SmallVector<Annotation> newAnnotations;
398 auto removeAndUpdateNLAs = [&](Annotation anno) ->
bool {
399 auto sym = anno.getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
402 if (!dutRenames.count(sym.getAttr()))
406 FlatSymbolRefAttr::get(dutRenames[sym.getAttr()].getSymNameAttr()));
407 newAnnotations.push_back(anno);
412 AnnotationSet annotations(dut);
413 annotations.removeAnnotations(removeAndUpdateNLAs);
414 annotations.addAnnotations(newAnnotations);
415 annotations.applyToOperation(dut);
416 for (
size_t i = 0, e = dut.getNumPorts(); i != e; ++i) {
417 newAnnotations.clear();
419 annotations.removeAnnotations(removeAndUpdateNLAs);
420 annotations.addAnnotations(newAnnotations);
421 annotations.applyToPort(dut, i);
425 wrapper.walk([&](RWProbeOp rwp) {
426 rwp.setTargetAttr(hw::InnerRefAttr::get(wrapper.getModuleNameAttr(),
427 rwp.getTarget().getName()));
assert(baseType &&"element must be base type")
static void addHierarchy(hw::HierPathOp path, FModuleOp dut, InstanceOp wrapperInst, StringAttr oldDutNameAttr)
Add an extra level of hierarchy to a hierarchical path that places the wrapper instance after the DUT...
#define CIRCT_DEBUG_SCOPED_PASS_LOGGER(PASS)
static AnnotationSet forPort(FModuleLike op, size_t portNo)
Get an annotation set for the specified port.
igraph::ModuleOpInterface getDut()
Return the design-under-test if one is defined for the circuit, otherwise return null.
llvm::iterator_range< UseIterator > uses()
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
void emitConnect(OpBuilder &builder, Location loc, Value lhs, Value rhs, bool warnOnTruncation=false)
Emit a connect between two values.
StringAttr getInnerSymName(Operation *op)
Return the StringAttr for the inner_sym name, if it exists.
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.