24 #include "mlir/Pass/Pass.h"
25 #include "llvm/ADT/PostOrderIterator.h"
26 #include "llvm/ADT/STLFunctionalExtras.h"
27 #include "llvm/Support/Path.h"
31 #define GEN_PASS_DEF_ADDSEQMEMPORTS
32 #include "circt/Dialect/FIRRTL/Passes.h.inc"
36 using namespace circt;
37 using namespace firrtl;
40 struct AddSeqMemPortsPass
41 :
public circt::firrtl::impl::AddSeqMemPortsBase<AddSeqMemPortsPass> {
42 void runOnOperation()
override;
43 LogicalResult processAddPortAnno(Location loc,
Annotation anno);
44 LogicalResult processFileAnno(Location loc, StringRef metadataDir,
46 LogicalResult processAnnos(CircuitOp circuit);
48 LogicalResult processMemModule(FMemModuleOp mem);
49 LogicalResult processModule(FModuleOp moduleOp);
52 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike moduleOp) {
53 return moduleNamespaces.try_emplace(moduleOp, moduleOp).first->second;
60 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
61 return getModuleNamespace(mod);
69 std::vector<std::pair<unsigned, PortInfo>> extraPorts;
74 std::vector<SmallVector<Attribute>> instancePaths;
79 DenseMap<Operation *, MemoryInfo> memInfoMap;
80 DenseMap<Attribute, Operation *> innerRefToInstanceMap;
86 StringAttr outputFile;
89 SmallVector<PortInfo> userPorts;
91 ArrayAttr extraPortsAttr;
94 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
100 LogicalResult AddSeqMemPortsPass::processAddPortAnno(Location loc,
102 auto name = anno.
getMember<StringAttr>(
"name");
105 loc,
"AddSeqMemPortAnnotation requires field 'name' of string type");
107 auto input = anno.
getMember<BoolAttr>(
"input");
110 loc,
"AddSeqMemPortAnnotation requires field 'input' of boolean type");
113 auto width = anno.
getMember<IntegerAttr>(
"width");
116 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
118 userPorts.push_back({name, type, direction});
122 LogicalResult AddSeqMemPortsPass::processFileAnno(Location loc,
123 StringRef metadataDir,
127 loc,
"circuit has two AddSeqMemPortsFileAnnotation annotations");
129 auto filename = anno.
getMember<StringAttr>(
"filename");
131 return emitError(loc,
132 "AddSeqMemPortsFileAnnotation requires field 'filename' "
135 SmallString<128> outputFilePath(metadataDir);
141 LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
142 auto loc = circuit.getLoc();
147 StringRef metadataDir =
"metadata";
149 auto dir = dirAnno.
getMember<StringAttr>(
"dirname");
151 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
153 metadataDir = dir.getValue();
162 error = failed(processAddPortAnno(loc, anno));
166 error = failed(processFileAnno(loc, metadataDir, anno));
171 return failure(error);
174 LogicalResult AddSeqMemPortsPass::processMemModule(FMemModuleOp mem) {
176 if (!instanceInfo->allInstancesInEffectiveDesign(mem)) {
177 auto diag = mem->emitOpError()
178 <<
"cannot have ports added to it because it is instantiated "
179 "both under and not under the design-under-test (DUT)";
180 for (
auto *instNode : instanceGraph->lookup(mem)->uses()) {
181 auto instanceOp = instNode->getInstance();
182 if (
auto layerBlockOp = instanceOp->getParentOfType<LayerBlockOp>()) {
183 diag.attachNote(instanceOp.getLoc())
184 <<
"this instance is under a layer block";
185 diag.attachNote(layerBlockOp.getLoc())
186 <<
"the innermost layer block is here";
189 if (!instanceInfo->allInstancesInEffectiveDesign(
190 instNode->getParent()->getModule())) {
191 diag.attachNote(instanceOp.getLoc())
192 <<
"this instance is inside a module that is instantiated outside "
200 size_t portIndex = mem.getNumPorts();
201 auto &memInfo = memInfoMap[mem];
202 auto &extraPorts = memInfo.extraPorts;
203 for (
auto const &p : userPorts)
204 extraPorts.emplace_back(portIndex, p);
205 mem.insertPorts(extraPorts);
207 mem.setExtraPortsAttr(extraPortsAttr);
211 LogicalResult AddSeqMemPortsPass::processModule(FModuleOp moduleOp) {
212 auto *context = &getContext();
213 auto builder = OpBuilder(moduleOp.getContext());
214 auto &memInfo = memInfoMap[moduleOp];
215 auto &extraPorts = memInfo.extraPorts;
218 SmallVector<Value> values;
221 unsigned firstPortIndex = moduleOp.getNumPorts();
223 auto result = moduleOp.walk([&](Operation *op) {
224 if (
auto inst = dyn_cast<InstanceOp>(op)) {
225 auto submodule = inst.getReferencedModule(*instanceGraph);
227 auto subMemInfoIt = memInfoMap.find(submodule);
229 if (subMemInfoIt == memInfoMap.end() ||
230 subMemInfoIt->second.extraPorts.empty())
231 return WalkResult::advance();
232 auto &subMemInfo = subMemInfoIt->second;
234 auto &subExtraPorts = subMemInfo.extraPorts;
237 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
238 inst.replaceAllUsesWith(
239 clone.getResults().drop_back(subExtraPorts.size()));
240 instanceGraph->replaceInstance(inst, clone);
245 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
246 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
248 auto userIndex = i % userPorts.size();
249 auto const &sramPort = userPorts[userIndex];
251 auto sramIndex = extraPorts.size() / userPorts.size();
253 StringAttr::get(context,
"sram_" + Twine(sramIndex) +
"_" +
254 sramPort.name.getValue());
255 auto portDirection = sramPort.direction;
256 auto portType = sramPort.type;
258 extraPorts.push_back(
260 {portName, type_cast<FIRRTLType>(portType), portDirection}});
263 if (instanceInfo->isEffectiveDut(moduleOp))
264 extraPorts.back().second.annotations.addDontTouch();
267 values.push_back(inst.getResult(firstSubIndex + i));
275 auto &instancePaths = memInfo.instancePaths;
277 innerRefToInstanceMap[ref] = inst;
279 if (isa<FMemModuleOp>(submodule))
280 instancePaths.push_back({ref});
283 for (
const auto &subPath : subMemInfo.instancePaths) {
284 instancePaths.push_back(subPath);
285 instancePaths.back().push_back(ref);
289 return WalkResult::advance();
292 return WalkResult::advance();
295 if (result.wasInterrupted())
299 moduleOp.insertPorts(extraPorts);
302 DenseMap<Type, InvalidValueOp> invalids;
303 auto getOrCreateInvalid = [&](Type type) -> InvalidValueOp {
304 auto it = invalids.find(type);
305 if (it != invalids.end())
306 return it->getSecond();
309 builder.create<InvalidValueOp>(builder.getUnknownLoc(), type)})
314 DenseMap<Operation *, OpBuilder::InsertPoint> instToInsertionPoint;
315 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
316 auto &[firstArg, port] = extraPorts[i];
317 Value modulePort = moduleOp.getArgument(firstArg + i);
318 Value instPort = values[i];
319 Operation *instOp = instPort.getDefiningOp();
320 auto insertPoint = instToInsertionPoint.find(instOp);
321 if (insertPoint == instToInsertionPoint.end())
322 builder.setInsertionPointAfter(instOp);
324 builder.restoreInsertionPoint(insertPoint->getSecond());
326 std::swap(modulePort, instPort);
328 builder.create<MatchingConnectOp>(port.loc, modulePort, instPort);
329 instToInsertionPoint[instOp] = builder.saveInsertionPoint();
333 connectOp->getParentOfType<WhenOp>()) {
334 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
335 builder.create<MatchingConnectOp>(port.loc, modulePort,
336 getOrCreateInvalid(port.type));
344 auto circuit = getOperation();
345 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
349 llvm::raw_string_ostream os(buffer);
351 SymbolTable &symTable = getAnalysis<SymbolTable>();
355 unsigned paramIndex = 0;
357 SmallVector<Attribute> params;
359 DenseMap<Attribute, unsigned> usedParams;
362 auto addSymbol = [&](Attribute ref) {
363 auto it = usedParams.find(ref);
365 if (it != usedParams.end()) {
369 usedParams[ref] = paramIndex++;
370 params.push_back(ref);
372 os <<
"{{" << index <<
"}}";
376 unsigned sramIndex = 0;
377 auto &instancePaths = memInfoMap[moduleOp].instancePaths;
380 auto loc = builder.getUnknownLoc();
382 builder.create<emit::FileOp>(loc, outputFile, [&] {
383 for (
auto instancePath : instancePaths) {
385 SmallVector<Attribute> path(llvm::reverse(instancePath));
386 os << sramIndex++ <<
" -> ";
387 addSymbol(dutSymbol);
390 auto nlaSymbol = cache.getRefFor(builder.getArrayAttr(path));
391 addSymbol(nlaSymbol);
392 NamedAttrList fields;
395 auto id = DistinctAttr::create(
UnitAttr::get(builder.getContext()));
396 fields.append(
"id",
id);
397 fields.append(
"class", builder.getStringAttr(
"circt.tracker"));
398 fields.append(
"circt.nonlocal", nlaSymbol);
400 auto *leafInstance = innerRefToInstanceMap[instancePath.front()];
403 annos.addAnnotations(builder.getDictionaryAttr(fields));
404 annos.applyToOperation(leafInstance);
408 builder.create<sv::VerbatimOp>(loc, buffer, ValueRange{},
409 builder.getArrayAttr(params));
411 anythingChanged =
true;
414 void AddSeqMemPortsPass::runOnOperation() {
415 auto *context = &getContext();
416 auto circuit = getOperation();
417 instanceGraph = &getAnalysis<InstanceGraph>();
418 instanceInfo = &getAnalysis<InstanceInfo>();
424 anythingChanged =
false;
427 if (failed(processAnnos(circuit)))
428 return signalPassFailure();
432 std::reverse(userPorts.begin(), userPorts.end());
435 SmallVector<Attribute> extraPorts;
437 for (
auto &userPort : userPorts) {
438 SmallVector<NamedAttribute, 3> attrs;
443 context, userPort.direction == Direction::In ?
"input" :
"output"));
448 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
454 if (!userPorts.empty()) {
456 numAddedPorts += userPorts.size();
461 for (
auto *node : llvm::post_order(
462 instanceGraph->lookup(instanceInfo->getEffectiveDut()))) {
463 auto op = node->getModule();
466 if (!instanceInfo->anyInstanceInEffectiveDesign(op))
470 if (
auto moduleOp = dyn_cast<FModuleOp>(*op)) {
471 if (failed(processModule(moduleOp)))
472 return signalPassFailure();
473 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
474 if (failed(processMemModule(mem)))
475 return signalPassFailure();
480 auto effectiveDut = instanceInfo->getEffectiveDut();
481 if (
auto *dut = dyn_cast<FModuleOp>(&effectiveDut)) {
484 for (
auto *instRec : instanceGraph->lookup(effectiveDut)->uses()) {
485 auto inst = cast<InstanceOp>(*instRec->getInstance());
486 auto &dutMemInfo = memInfoMap[*dut];
488 auto &subExtraPorts = dutMemInfo.extraPorts;
490 if (subExtraPorts.empty())
494 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
495 inst.replaceAllUsesWith(
496 clone.getResults().drop_back(subExtraPorts.size()));
497 instanceGraph->replaceInstance(inst, clone);
502 OpBuilder builder(context);
503 builder.setInsertionPointAfter(inst);
504 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
505 auto &[firstResult, portInfo] = subExtraPorts[i];
506 if (portInfo.direction == Direction::Out)
508 auto value = inst.getResult(firstResult + i);
509 auto type = value.getType();
511 auto zero = builder.create<ConstantOp>(portInfo.loc, type, attr);
512 builder.create<MatchingConnectOp>(portInfo.loc, value, zero);
523 markAnalysesPreserved<InstanceGraph>();
525 markAllAnalysesPreserved();
529 return std::make_unique<AddSeqMemPortsPass>();
static std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
static StringAttr append(StringAttr base, const Twine &suffix)
Return a attribute with the specified suffix appended.
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.
Annotation getAnnotation(StringRef className) const
If this annotation set has an annotation with the specified class name, return it.
This class provides a read-only projection of an annotation.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
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 get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * metadataDirectoryAttrName
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.
constexpr const char * addSeqMemPortAnnoClass
constexpr const char * addSeqMemPortsFileAnnoClass
std::unique_ptr< mlir::Pass > createAddSeqMemPortsPass()
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
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.