38#include "mlir/IR/Attributes.h"
39#include "mlir/IR/ImplicitLocOpBuilder.h"
40#include "mlir/Pass/Pass.h"
41#include "mlir/Support/FileUtilities.h"
42#include "llvm/Support/Debug.h"
44#define DEBUG_TYPE "firrtl-extract-instances"
48#define GEN_PASS_DEF_EXTRACTINSTANCES
49#include "circt/Dialect/FIRRTL/Passes.h.inc"
54using namespace firrtl;
55using hw::InnerRefAttr;
63struct ExtractionInfo {
65 StringRef traceFilename;
69 StringRef wrapperModule;
75struct ExtractInstancesPass
76 :
public circt::firrtl::impl::ExtractInstancesBase<ExtractInstancesPass> {
77 void runOnOperation()
override;
79 void collectAnno(InstanceOp inst,
Annotation anno);
80 void extractInstances();
81 void groupInstances();
82 void createTraceFiles(ClassOp &sifiveMetadata);
87 return moduleNamespaces.try_emplace(module, module).first->second;
93 return ::getInnerRefTo(op,
95 return getModuleNamespace(mod);
100 hw::HierPathOp cloneWithNewNameAndPath(hw::HierPathOp pathOp,
101 ArrayRef<Attribute> newPath) {
102 OpBuilder builder(pathOp);
103 auto newPathOp = builder.cloneWithoutRegions(pathOp);
104 newPathOp.setSymNameAttr(builder.getStringAttr(
105 circuitNamespace.newName(newPathOp.getSymName())));
106 newPathOp.setNamepathAttr(builder.getArrayAttr(newPath));
111 emit::FileOp getOrCreateFile(StringRef fileName) {
112 auto [it, inserted] = files.try_emplace(fileName, emit::FileOp{});
114 auto builder = ImplicitLocOpBuilder::atBlockEnd(
115 UnknownLoc::get(&getContext()), getOperation().
getBodyBlock());
116 it->second = builder.create<emit::FileOp>(fileName);
121 bool anythingChanged;
127 SymbolTable *symbolTable =
nullptr;
131 DenseMap<Operation *, SmallVector<Annotation, 1>> annotatedModules;
134 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractionWorklist;
137 DenseMap<StringRef, emit::FileOp> files;
143 DenseMap<Operation *, SmallVector<InnerRefAttr>> extractionPaths;
147 DenseMap<Operation *, StringAttr> originalInstanceParents;
151 SmallVector<std::pair<InstanceOp, ExtractionInfo>> extractedInstances;
154 DenseMap<Operation *, std::pair<SmallString<16>, StringAttr>>
160 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
162 ClassOp extractMetadataClass, schemaClass;
163 const unsigned prefixNameFieldId = 0, pathFieldId = 2, fileNameFieldId = 4,
167 DenseMap<InnerRefAttr, InstanceOp> innerRefToInstances;
168 Type stringType, pathType;
178void ExtractInstancesPass::runOnOperation() {
179 circuitOp = getOperation();
180 anythingChanged =
false;
182 annotatedModules.clear();
183 extractionWorklist.clear();
185 extractionPaths.clear();
186 originalInstanceParents.clear();
187 extractedInstances.clear();
188 instPrefixNamesPair.clear();
189 moduleNamespaces.clear();
190 circuitNamespace.clear();
191 circuitNamespace.add(circuitOp);
192 innerRefToInstances.clear();
193 extractMetadataClass = {};
195 auto *context = circuitOp->getContext();
196 stringType = StringType::get(context);
197 pathType = PathType::get(context);
202 instanceGraph = &getAnalysis<InstanceGraph>();
203 instanceInfo = &getAnalysis<InstanceInfo>();
204 symbolTable = &getAnalysis<SymbolTable>();
207 return signalPassFailure();
212 return signalPassFailure();
217 return signalPassFailure();
219 ClassOp sifiveMetadata =
220 dyn_cast_or_null<ClassOp>(symbolTable->lookup(
"SiFive_Metadata"));
223 createTraceFiles(sifiveMetadata);
225 return signalPassFailure();
228 LLVM_DEBUG(llvm::dbgs() <<
"\n");
229 if (!anythingChanged)
230 markAllAnalysesPreserved();
239void ExtractInstancesPass::collectAnnos() {
240 CircuitOp circuit = getOperation();
254 if (
auto moveDutAnnoAttr = anno.
getMember<BoolAttr>(
"moveDut"))
255 moveDut = moveDutAnnoAttr.getValue();
260 StringRef clkgateFileName;
261 StringRef clkgateWrapperModule;
265 LLVM_DEBUG(llvm::dbgs()
266 <<
"Clock gate extraction config: " << anno.
getDict() <<
"\n");
267 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
268 auto groupAttr = anno.
getMember<StringAttr>(
"group");
270 circuit.emitError(
"missing `filename` attribute in `")
271 << anno.
getClass() <<
"` annotation";
276 if (!clkgateFileName.empty()) {
277 circuit.emitError(
"multiple `")
278 << anno.
getClass() <<
"` annotations on circuit";
283 clkgateFileName = filenameAttr.getValue();
285 clkgateWrapperModule = groupAttr.getValue();
290 StringRef memoryFileName;
291 StringRef memoryWrapperModule;
295 LLVM_DEBUG(llvm::dbgs()
296 <<
"Memory extraction config: " << anno.
getDict() <<
"\n");
297 auto filenameAttr = anno.
getMember<StringAttr>(
"filename");
298 auto groupAttr = anno.
getMember<StringAttr>(
"group");
300 circuit.emitError(
"missing `filename` attribute in `")
301 << anno.
getClass() <<
"` annotation";
306 if (!memoryFileName.empty()) {
307 circuit.emitError(
"multiple `")
308 << anno.
getClass() <<
"` annotations on circuit";
313 memoryFileName = filenameAttr.getValue();
315 memoryWrapperModule = groupAttr.getValue();
321 for (
auto module : circuit.getOps<FModuleLike>()) {
325 LLVM_DEBUG(llvm::dbgs() <<
"Annotated module `" << module.getModuleName()
326 <<
"`:\n " << anno.
getDict() <<
"\n");
327 annotatedModules[module].push_back(anno);
333 circuit.walk([&](InstanceOp inst) {
334 SmallVector<Annotation, 1> instAnnos;
335 Operation *
module = inst.getReferencedModule(*instanceGraph);
338 auto it = annotatedModules.find(module);
339 if (it != annotatedModules.end())
340 instAnnos.append(it->second);
346 LLVM_DEBUG(llvm::dbgs() <<
"Annotated instance `" << inst.getName()
347 <<
"`:\n " << anno.
getDict() <<
"\n");
348 instAnnos.push_back(anno);
353 if (instAnnos.empty())
357 if (instAnnos.size() > 1) {
358 auto d = inst.emitError(
"multiple extraction annotations on instance `")
359 << inst.getName() <<
"`";
360 d.attachNote(inst.getLoc()) <<
"instance has the following annotations, "
361 "but at most one is allowed:";
362 for (
auto anno : instAnnos)
363 d.attachNote(inst.
getLoc()) << anno.getDict();
369 collectAnno(inst, instAnnos[0]);
378 if (!clkgateFileName.empty()) {
379 for (
auto module : circuit.getOps<FExtModuleOp>()) {
380 if (!module.getDefnameAttr().getValue().ends_with(
"EICG_wrapper"))
382 LLVM_DEBUG(llvm::dbgs()
383 <<
"Clock gate `" << module.getModuleName() <<
"`\n");
384 if (!instanceInfo->anyInstanceInDesign(module)) {
385 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
390 info.traceFilename = clkgateFileName;
391 info.prefix =
"clock_gate";
392 info.wrapperModule = clkgateWrapperModule;
393 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
394 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
395 LLVM_DEBUG(llvm::dbgs()
397 << inst->getParentOfType<FModuleLike>().getModuleName()
398 <<
"." << inst.getName() <<
"`\n");
399 extractionWorklist.push_back({inst,
info});
408 if (!memoryFileName.empty()) {
412 getOrCreateFile(memoryFileName);
414 for (
auto module : circuit.getOps<FMemModuleOp>()) {
415 LLVM_DEBUG(llvm::dbgs() <<
"Memory `" << module.getModuleName() <<
"`\n");
416 if (!instanceInfo->anyInstanceInDesign(module)) {
417 LLVM_DEBUG(llvm::dbgs() <<
"- Ignored (outside DUT)\n");
422 info.traceFilename = memoryFileName;
423 info.prefix =
"mem_wiring";
424 info.wrapperModule = memoryWrapperModule;
425 for (
auto *instRecord : instanceGraph->lookup(module)->uses()) {
426 if (
auto inst = dyn_cast<InstanceOp>(*instRecord->getInstance())) {
427 LLVM_DEBUG(llvm::dbgs()
429 << inst->getParentOfType<FModuleLike>().getModuleName()
430 <<
"." << inst.getName() <<
"`\n");
431 extractionWorklist.push_back({inst,
info});
440void ExtractInstancesPass::collectAnno(InstanceOp inst,
Annotation anno) {
441 LLVM_DEBUG(llvm::dbgs() <<
"Processing instance `" << inst.getName() <<
"` "
444 auto getStringOrError = [&](StringRef member) {
445 auto attr = anno.
getMember<StringAttr>(member);
447 inst.emitError(
"missing `")
448 << member <<
"` attribute in `" << anno.
getClass() <<
"` annotation";
455 auto filename = getStringOrError(
"filename");
456 auto prefix = getStringOrError(
"prefix");
457 auto dest = anno.
getMember<StringAttr>(
"dest");
462 info.traceFilename = filename;
463 info.prefix = prefix;
464 info.wrapperModule = (dest ? dest.getValue() :
"");
470 extractionWorklist.push_back({inst,
info});
480 unsigned nlaLen = nla.getNamepath().size();
482 auto parentName = cast<FModuleOp>(inst->getParentOp()).getModuleNameAttr();
483 for (
unsigned nlaIdx = 0; nlaIdx < nlaLen; ++nlaIdx) {
484 auto refPart = nla.refPart(nlaIdx);
485 if (nla.modPart(nlaIdx) == parentName && (!refPart || refPart == instName))
494void ExtractInstancesPass::extractInstances() {
497 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
499 DenseMap<StringRef, unsigned> prefixUniqueIDs;
501 SmallPtrSet<Operation *, 4> nlasToRemove;
503 auto &nlaTable = getAnalysis<NLATable>();
506 for (
auto &[inst, info] : extractionWorklist)
507 originalInstanceParents[inst] =
508 inst->getParentOfType<FModuleLike>().getModuleNameAttr();
510 while (!extractionWorklist.empty()) {
513 std::tie(inst, info) = extractionWorklist.pop_back_val();
515 auto parent = inst->getParentOfType<FModuleOp>();
524 auto &instPrefixEntry = instPrefixNamesPair[inst];
525 instPrefixEntry.second = inst.getInstanceNameAttr();
526 if (!
info.prefix.empty()) {
527 auto &prefixSlot = instPrefixEntry.first;
528 if (prefixSlot.empty()) {
529 auto idx = prefixUniqueIDs[
info.prefix]++;
530 (Twine(
info.prefix) +
"_" + Twine(idx)).
toVector(prefixSlot);
537 bool stopAtDUT = !moveDut && !
info.wrapperModule.empty();
543 if (inst->getParentOfType<LayerBlockOp>() ||
544 !instanceInfo->anyInstanceInDesign(parent) ||
545 instanceGraph->lookup(parent)->noUses() ||
546 (stopAtDUT && instanceInfo->isDut(parent))) {
547 LLVM_DEBUG(llvm::dbgs() <<
"\nNo need to further move " << inst <<
"\n");
548 extractedInstances.push_back({inst,
info});
552 llvm::dbgs() <<
"\nMoving ";
554 llvm::dbgs() <<
"`" << prefix <<
"` ";
555 llvm::dbgs() << inst <<
"\n";
560 unsigned numParentPorts = parent.getNumPorts();
561 unsigned numInstPorts = inst.getNumResults();
563 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
566 auto name = inst.getPortNameStr(portIdx);
567 auto nameAttr = StringAttr::get(
569 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
572 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
574 newPort.
loc = inst.getResult(portIdx).getLoc();
575 newPorts.push_back({numParentPorts, newPort});
576 LLVM_DEBUG(llvm::dbgs()
577 <<
"- Adding port " << newPort.direction <<
" "
578 << newPort.name.getValue() <<
": " << newPort.type <<
"\n");
580 parent.insertPorts(newPorts);
581 anythingChanged =
true;
585 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
586 inst.getResult(portIdx).replaceAllUsesWith(
587 parent.getArgument(numParentPorts + portIdx));
589 assert(inst.use_empty() &&
"instance ports should have been detached");
590 DenseSet<hw::HierPathOp> instanceNLAs;
593 nlaTable.getInstanceNLAs(inst, instanceNLAs);
596 DenseMap<hw::HierPathOp, SmallVector<Annotation>> instNonlocalAnnos;
599 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
603 if (hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr())) {
604 instNonlocalAnnos[nla].push_back(anno);
605 instanceNLAs.insert(nla);
612 SmallVector<hw::HierPathOp> sortedInstanceNLAs(instanceNLAs.begin(),
614 llvm::sort(sortedInstanceNLAs,
615 [](
auto a,
auto b) {
return a.getSymName() < b.getSymName(); });
620 auto *instParentNode =
621 instanceGraph->lookup(cast<igraph::ModuleOpInterface>(*parent));
622 for (
auto *instRecord : instParentNode->uses()) {
623 auto oldParentInst = cast<InstanceOp>(*instRecord->getInstance());
624 auto newParent = oldParentInst->getParentOfType<FModuleLike>();
625 LLVM_DEBUG(llvm::dbgs() <<
"- Updating " << oldParentInst <<
"\n");
626 auto newParentInst = oldParentInst.cloneAndInsertPorts(newPorts);
627 if (newParentInst.getInnerSymAttr())
628 innerRefToInstances[
getInnerRefTo(newParentInst)] = newParentInst;
631 for (
unsigned portIdx = 0; portIdx < numParentPorts; ++portIdx)
632 oldParentInst.getResult(portIdx).replaceAllUsesWith(
633 newParentInst.getResult(portIdx));
637 auto newInst = inst.cloneAndInsertPorts({});
644 getModuleNamespace(newParent).newName(instSym.getValue());
645 if (newName != instSym.getValue())
646 newInst.setInnerSymAttr(
647 hw::InnerSymAttr::get(StringAttr::get(&getContext(), newName)));
651 ImplicitLocOpBuilder builder(inst.getLoc(), newParentInst);
652 builder.setInsertionPointAfter(newParentInst);
653 builder.insert(newInst);
654 if (newParentInst.getInnerSymAttr())
656 for (
unsigned portIdx = 0; portIdx < numInstPorts; ++portIdx) {
657 auto dst = newInst.getResult(portIdx);
658 auto src = newParentInst.getResult(numParentPorts + portIdx);
659 if (newPorts[portIdx].second.direction == Direction::In)
661 builder.create<MatchingConnectOp>(dst, src);
670 auto oldPrefix = instPrefixNamesPair.find(inst);
671 if (oldPrefix != instPrefixNamesPair.end()) {
672 LLVM_DEBUG(llvm::dbgs() <<
" - Reusing prefix `"
673 << oldPrefix->second.first <<
"`\n");
674 auto newPrefix = std::move(oldPrefix->second);
675 instPrefixNamesPair.erase(oldPrefix);
676 instPrefixNamesPair.insert({newInst, newPrefix});
680 extractionPaths.try_emplace(newInst);
681 auto &extractionPath = (extractionPaths[newInst] = extractionPaths[inst]);
683 innerRefToInstances[instInnerRef] = newParentInst;
684 extractionPath.push_back(instInnerRef);
685 originalInstanceParents.try_emplace(newInst);
686 originalInstanceParents[newInst] = originalInstanceParents[inst];
689 SmallVector<Annotation> newInstNonlocalAnnos;
692 for (
auto nla : sortedInstanceNLAs) {
693 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
697 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
698 nla.getNamepath().end());
707 if (nlaIdx >= nlaPath.size()) {
708 LLVM_DEBUG(llvm::dbgs() <<
" - Instance no longer in path\n");
711 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
718 auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx - 1]);
720 !(innerRef.getModule() == newParent.getModuleNameAttr() &&
722 LLVM_DEBUG(llvm::dbgs()
723 <<
" - Ignored since NLA parent " << innerRef
724 <<
" does not pass through extraction parent\n");
742 LLVM_DEBUG(llvm::dbgs() <<
" - Re-rooting " << nlaPath[0] <<
"\n");
743 assert(isa<InnerRefAttr>(nlaPath[0]) &&
744 "head of hierpath must be an InnerRefAttr");
745 nlaPath[0] = InnerRefAttr::get(newParent.getModuleNameAttr(),
748 if (instParentNode->hasOneUse()) {
753 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
754 for (
auto anno : instNonlocalAnnos.lookup(nla))
755 newInstNonlocalAnnos.push_back(anno);
756 nlaTable.addNLA(nla);
757 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
761 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
762 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
764 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
765 newInstNonlocalAnnos.push_back(anno);
768 nlaTable.addNLA(newNla);
769 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
779 inst.emitWarning(
"extraction of instance `")
780 << inst.getInstanceName()
781 <<
"` could break non-local annotations rooted at `"
782 << parent.getModuleName() <<
"`";
793 if (nlaPath.size() == 2) {
794 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
796 newInstNonlocalAnnos.push_back(anno);
797 LLVM_DEBUG(llvm::dbgs() <<
" - Converted to local "
801 nlasToRemove.insert(nla);
809 StringAttr parentName =
810 cast<InnerRefAttr>(nlaPath[nlaIdx - 1]).getModule();
812 if (isa<InnerRefAttr>(nlaPath[nlaIdx]))
815 newRef = FlatSymbolRefAttr::get(parentName);
816 LLVM_DEBUG(llvm::dbgs()
817 <<
" - Replacing " << nlaPath[nlaIdx - 1] <<
" and "
818 << nlaPath[nlaIdx] <<
" with " << newRef <<
"\n");
819 nlaPath[nlaIdx] = newRef;
820 nlaPath.erase(nlaPath.begin() + nlaIdx - 1);
822 if (isa<FlatSymbolRefAttr>(newRef)) {
827 auto newNla = cloneWithNewNameAndPath(nla, nlaPath);
828 nlaTable.addNLA(newNla);
829 LLVM_DEBUG(llvm::dbgs() <<
" - Created " << newNla <<
"\n");
830 for (
auto anno : instNonlocalAnnos.lookup(nla)) {
832 FlatSymbolRefAttr::get(newNla.getSymNameAttr()));
833 newInstNonlocalAnnos.push_back(anno);
836 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
837 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
838 for (
auto anno : instNonlocalAnnos.lookup(nla))
839 newInstNonlocalAnnos.push_back(anno);
847 newInstAnnos.addAnnotations(newInstNonlocalAnnos);
848 newInstAnnos.applyToOperation(newInst);
852 extractionWorklist.push_back({newInst,
info});
853 LLVM_DEBUG(llvm::dbgs() <<
" - Updated to " << newInst <<
"\n");
856 instanceGraph->replaceInstance(oldParentInst, newParentInst);
857 oldParentInst.erase();
862 nlaTable.removeNLAsfromModule(instanceNLAs, parent.getNameAttr());
870 for (Operation *op : nlasToRemove) {
871 LLVM_DEBUG(llvm::dbgs() <<
"Removing obsolete " << *op <<
"\n");
879void ExtractInstancesPass::groupInstances() {
884 llvm::MapVector<std::pair<Operation *, StringRef>, SmallVector<InstanceOp>>
886 for (
auto &[inst, info] : extractedInstances) {
887 if (!
info.wrapperModule.empty())
888 instsByWrapper[{inst->getParentOfType<FModuleOp>(),
info.wrapperModule}]
891 if (instsByWrapper.empty())
893 LLVM_DEBUG(llvm::dbgs() <<
"\nGrouping instances into wrappers\n");
896 SmallVector<PortInfo> ports;
897 auto &nlaTable = getAnalysis<NLATable>();
899 for (
auto &[parentAndWrapperName, insts] : instsByWrapper) {
900 auto [parentOp, wrapperName] = parentAndWrapperName;
901 auto parent = cast<FModuleOp>(parentOp);
902 LLVM_DEBUG(llvm::dbgs() <<
"- Wrapper `" << wrapperName <<
"` in `"
903 << parent.getModuleName() <<
"` with "
904 << insts.size() <<
" instances\n");
905 OpBuilder builder(parentOp);
908 auto wrapperModuleName =
909 builder.getStringAttr(circuitNamespace.newName(wrapperName));
910 auto wrapperInstName =
911 builder.getStringAttr(getModuleNamespace(parent).newName(wrapperName));
918 for (
auto inst : insts) {
920 StringRef prefix(instPrefixNamesPair[inst].first);
921 unsigned portNum = inst.getNumResults();
922 for (
unsigned portIdx = 0; portIdx < portNum; ++portIdx) {
923 auto name = inst.getPortNameStr(portIdx);
924 auto nameAttr = builder.getStringAttr(
925 prefix.empty() ? Twine(name) : Twine(prefix) +
"_" + name);
927 type_cast<FIRRTLType>(inst.getResult(portIdx).getType()),
928 inst.getPortDirection(portIdx)};
929 port.
loc = inst.getResult(portIdx).getLoc();
930 ports.push_back(port);
934 DenseSet<hw::HierPathOp> instNlas;
936 nlaTable.getInstanceNLAs(inst, instNlas);
940 for (
auto anno : instAnnos) {
941 auto nlaName = anno.
getMember<FlatSymbolRefAttr>(
"circt.nonlocal");
944 hw::HierPathOp nla = nlaTable.getNLA(nlaName.getAttr());
946 instNlas.insert(nla);
948 for (
auto nla : instNlas) {
949 LLVM_DEBUG(llvm::dbgs() <<
" - Updating " << nla <<
"\n");
953 SmallVector<Attribute> nlaPath(nla.getNamepath().begin(),
954 nla.getNamepath().end());
956 assert(nlaIdx < nlaPath.size() &&
"instance not found in its own NLA");
957 LLVM_DEBUG(llvm::dbgs() <<
" - Position " << nlaIdx <<
"\n");
962 InnerRefAttr::get(parent.getModuleNameAttr(), wrapperInstName);
964 if (
auto innerRef = dyn_cast<InnerRefAttr>(nlaPath[nlaIdx]))
965 ref2 = InnerRefAttr::get(wrapperModuleName, innerRef.getName());
967 ref2 = FlatSymbolRefAttr::get(wrapperModuleName);
968 LLVM_DEBUG(llvm::dbgs() <<
" - Expanding " << nlaPath[nlaIdx]
969 <<
" to (" << ref1 <<
", " << ref2 <<
")\n");
970 nlaPath[nlaIdx] = ref1;
971 nlaPath.insert(nlaPath.begin() + nlaIdx + 1, ref2);
975 nla.setNamepathAttr(builder.getArrayAttr(nlaPath));
976 LLVM_DEBUG(llvm::dbgs() <<
" - Modified to " << nla <<
"\n");
978 nlaTable.addNLAtoModule(nla, wrapperModuleName);
983 auto wrapper = builder.create<FModuleOp>(
984 builder.getUnknownLoc(), wrapperModuleName,
985 ConventionAttr::get(builder.getContext(), Convention::Internal), ports);
986 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
991 builder.setInsertionPointToStart(parent.getBodyBlock());
992 auto wrapperInst = builder.create<InstanceOp>(
993 wrapper.getLoc(), wrapper, wrapperName, NameKindEnum::DroppableName,
994 ArrayRef<Attribute>{},
995 ArrayRef<Attribute>{},
false,
996 false, hw::InnerSymAttr::get(wrapperInstName));
997 unsigned portIdx = 0;
998 for (
auto inst : insts)
999 for (auto result : inst.getResults())
1000 result.replaceAllUsesWith(wrapperInst.getResult(portIdx++));
1005 builder.setInsertionPointToStart(wrapper.getBodyBlock());
1006 for (
auto inst : insts) {
1008 builder.insert(inst);
1009 for (
auto result : inst.getResults()) {
1011 Value src = wrapper.getArgument(portIdx);
1012 if (ports[portIdx].direction == Direction::Out)
1013 std::swap(dst, src);
1014 builder.create<MatchingConnectOp>(result.getLoc(), dst, src);
1024void ExtractInstancesPass::createTraceFiles(ClassOp &sifiveMetadataClass) {
1025 LLVM_DEBUG(llvm::dbgs() <<
"\nGenerating trace files\n");
1028 llvm::MapVector<StringRef, SmallVector<InstanceOp>> instsByTraceFile;
1029 for (
auto &[inst, info] : extractedInstances)
1031 instsByTraceFile[
info.traceFilename].push_back(inst);
1034 SmallVector<Attribute> symbols;
1036 if (sifiveMetadataClass && !extractMetadataClass)
1039 auto addPortsToClass = [&](ArrayRef<std::pair<Value, Twine>> objFields,
1041 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1042 classOp.getLoc(), classOp.getBodyBlock());
1043 auto portIndex = classOp.getNumPorts();
1044 SmallVector<std::pair<unsigned, PortInfo>> newPorts;
1045 for (
auto [index, port] : enumerate(objFields)) {
1047 auto obj = port.first;
1048 newPorts.emplace_back(
1050 PortInfo(builderOM.getStringAttr(port.second + Twine(portIndex)),
1051 obj.getType(), Direction::Out));
1053 classOp.getBodyBlock()->addArgument(obj.getType(), obj.getLoc());
1054 builderOM.create<PropAssignOp>(blockarg, obj);
1056 classOp.insertPorts(newPorts);
1060 SmallVector<std::pair<Value, Twine>> classFields;
1061 for (
auto &[fileName, insts] : instsByTraceFile) {
1062 LLVM_DEBUG(llvm::dbgs() <<
"- " << fileName <<
"\n");
1064 llvm::raw_string_ostream os(buffer);
1066 symbolIndices.clear();
1068 auto addSymbol = [&](Attribute symbol) {
1070 auto it = symbolIndices.find(symbol);
1071 if (it != symbolIndices.end()) {
1074 id = symbols.size();
1075 symbols.push_back(symbol);
1076 symbolIndices.insert({symbol,
id});
1078 os <<
"{{" <<
id <<
"}}";
1081 auto file = getOrCreateFile(fileName);
1082 auto builder = OpBuilder::atBlockEnd(file.getBody());
1083 for (
auto inst : insts) {
1084 StringRef prefix(instPrefixNamesPair[inst].first);
1085 StringAttr origInstName(instPrefixNamesPair[inst].second);
1086 if (prefix.empty()) {
1087 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1088 <<
"` since it has no extraction prefix\n");
1091 ArrayRef<InnerRefAttr> path(extractionPaths[inst]);
1093 LLVM_DEBUG(llvm::dbgs() <<
" - Skipping `" << inst.getName()
1094 <<
"` since it has not been moved\n");
1097 LLVM_DEBUG(llvm::dbgs()
1098 <<
" - " << prefix <<
": " << inst.getName() <<
"\n");
1099 os << prefix <<
" -> ";
1101 if (sifiveMetadataClass) {
1103 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1104 inst.getLoc(), extractMetadataClass.getBodyBlock());
1105 auto prefixName = builderOM.create<StringConstantOp>(prefix);
1106 auto object = builderOM.create<ObjectOp>(schemaClass, prefix);
1108 builderOM.create<ObjectSubfieldOp>(object, prefixNameFieldId);
1109 builderOM.create<PropAssignOp>(fPrefix, prefixName);
1111 auto targetInstance = innerRefToInstances[path.front()];
1112 SmallVector<Attribute> pathOpAttr(llvm::reverse(path));
1113 auto nla = pathCache.getOpFor(
1114 ArrayAttr::get(circuitOp->getContext(), pathOpAttr));
1116 auto pathOp =
createPathRef(targetInstance, nla, builderOM);
1117 auto fPath = builderOM.create<ObjectSubfieldOp>(object, pathFieldId);
1118 builderOM.create<PropAssignOp>(fPath, pathOp);
1120 builderOM.create<ObjectSubfieldOp>(object, fileNameFieldId);
1122 builderOM.create<StringConstantOp>(builder.getStringAttr(fileName));
1123 builderOM.create<PropAssignOp>(fFile, fileNameOp);
1126 builderOM.create<ObjectSubfieldOp>(object, instNameFieldId);
1127 auto instNameOp = builderOM.create<StringConstantOp>(origInstName);
1128 builderOM.create<PropAssignOp>(finstName, instNameOp);
1131 classFields.emplace_back(
object, prefix +
"_field");
1136 while (!path.empty() &&
1137 !instanceInfo->anyInstanceInDesign(cast<igraph::ModuleOpInterface>(
1138 symbolTable->lookup(path.back().getModule())))) {
1139 LLVM_DEBUG(llvm::dbgs()
1140 <<
" - Dropping non-DUT segment " << path.back() <<
"\n");
1141 path = path.drop_back();
1146 addSymbol(FlatSymbolRefAttr::get(path.empty()
1147 ? originalInstanceParents[inst]
1148 : path.back().getModule()));
1149 for (
auto sym :
llvm::reverse(path)) {
1153 os <<
"." << origInstName.getValue();
1161 builder.create<sv::VerbatimOp>(builder.getUnknownLoc(), buffer,
1162 ValueRange{}, builder.getArrayAttr(symbols));
1164 if (!classFields.empty()) {
1165 addPortsToClass(classFields, extractMetadataClass);
1169 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1170 sifiveMetadataClass->getLoc(), sifiveMetadataClass.getBodyBlock());
1171 SmallVector<std::pair<Value, Twine>> classFields = {
1172 {builderOM.create<ObjectOp>(
1173 extractMetadataClass,
1174 builderOM.getStringAttr(
"extract_instances_metadata")),
1175 "extractedInstances_field"}};
1177 addPortsToClass(classFields, sifiveMetadataClass);
1178 auto *node = instanceGraph->lookup(sifiveMetadataClass);
1179 assert(node && node->hasOneUse());
1180 ObjectOp metadataObj = (*node->usesBegin())->getInstance<ObjectOp>();
1182 "expected the class to be instantiated by an object op");
1183 builderOM.setInsertionPoint(metadataObj);
1185 builderOM.create<ObjectOp>(sifiveMetadataClass, metadataObj.getName());
1186 metadataObj->replaceAllUsesWith(newObj);
1187 metadataObj->remove();
1191void ExtractInstancesPass::createSchema() {
1193 auto *context = circuitOp->getContext();
1194 auto unknownLoc = mlir::UnknownLoc::get(context);
1195 auto builderOM = mlir::ImplicitLocOpBuilder::atBlockEnd(
1196 unknownLoc, circuitOp.getBodyBlock());
1197 mlir::Type portsType[] = {
1203 StringRef portFields[] = {
"name",
"path",
"filename",
"inst_name"};
1205 schemaClass = builderOM.create<ClassOp>(
"ExtractInstancesSchema", portFields,
1209 SmallVector<PortInfo> mports;
1210 extractMetadataClass = builderOM.create<ClassOp>(
1211 builderOM.getStringAttr(
"ExtractInstancesMetadata"), mports);
1218 return std::make_unique<ExtractInstancesPass>();
assert(baseType &&"element must be base type")
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static InstancePath empty
static Location getLoc(DefSlot slot)
static Block * getBodyBlock(FModuleLike mod)
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.
This class provides a read-only projection of an annotation.
DictionaryAttr getDict() const
Get the data dictionary of this attribute.
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.
void removeMember(StringAttr name)
Remove a member of the annotation.
StringRef getClass() const
Return the 'class' that this annotation is representing.
bool isClass(Args... names) const
Return true if this annotation matches any of the specified class names.
This graph tracks modules and where they are instantiated.
Direction flip(Direction direction)
Flip a port direction.
constexpr const char * injectDUTHierarchyAnnoClass
constexpr const char * extractBlackBoxAnnoClass
PathOp createPathRef(Operation *op, hw::HierPathOp nla, mlir::ImplicitLocOpBuilder &builderOM)
Add the tracker annotation to the op and get a PathOp to the op.
constexpr const char * extractSeqMemsAnnoClass
hw::InnerRefAttr getInnerRefTo(const hw::InnerSymTarget &target, GetNamespaceCallback getNamespace)
Obtain an inner reference to the target (operation or port), adding an inner symbol as necessary.
std::unique_ptr< mlir::Pass > createExtractInstancesPass()
constexpr const char * extractClockGatesAnnoClass
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.
The namespace of a CircuitOp, generally inhabited by modules.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.
This holds the name and type that describes the module's ports.