25 #include "mlir/Pass/Pass.h"
26 #include "llvm/ADT/PostOrderIterator.h"
27 #include "llvm/ADT/STLFunctionalExtras.h"
28 #include "llvm/Support/FileSystem.h"
29 #include "llvm/Support/Parallel.h"
30 #include "llvm/Support/Path.h"
34 #define GEN_PASS_DEF_ADDSEQMEMPORTS
35 #include "circt/Dialect/FIRRTL/Passes.h.inc"
39 using namespace circt;
40 using namespace firrtl;
43 struct AddSeqMemPortsPass
44 :
public circt::firrtl::impl::AddSeqMemPortsBase<AddSeqMemPortsPass> {
45 void runOnOperation()
override;
46 LogicalResult processAddPortAnno(Location loc,
Annotation anno);
47 LogicalResult processFileAnno(Location loc, StringRef metadataDir,
49 LogicalResult processAnnos(CircuitOp circuit);
51 LogicalResult processMemModule(FMemModuleOp mem);
52 LogicalResult processModule(FModuleOp moduleOp);
55 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike moduleOp) {
56 return moduleNamespaces.try_emplace(moduleOp, moduleOp).first->second;
63 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
64 return getModuleNamespace(mod);
72 std::vector<std::pair<unsigned, PortInfo>> extraPorts;
77 std::vector<SmallVector<Attribute>> instancePaths;
82 DenseMap<Operation *, MemoryInfo> memInfoMap;
83 DenseMap<Attribute, Operation *> innerRefToInstanceMap;
89 StringAttr outputFile;
92 SmallVector<PortInfo> userPorts;
94 ArrayAttr extraPortsAttr;
97 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
103 LogicalResult AddSeqMemPortsPass::processAddPortAnno(Location loc,
105 auto name = anno.
getMember<StringAttr>(
"name");
108 loc,
"AddSeqMemPortAnnotation requires field 'name' of string type");
110 auto input = anno.
getMember<BoolAttr>(
"input");
113 loc,
"AddSeqMemPortAnnotation requires field 'input' of boolean type");
119 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
121 userPorts.push_back({name, type, direction});
125 LogicalResult AddSeqMemPortsPass::processFileAnno(Location loc,
126 StringRef metadataDir,
130 loc,
"circuit has two AddSeqMemPortsFileAnnotation annotations");
132 auto filename = anno.
getMember<StringAttr>(
"filename");
134 return emitError(loc,
135 "AddSeqMemPortsFileAnnotation requires field 'filename' "
138 SmallString<128> outputFilePath(metadataDir);
144 LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
145 auto loc = circuit.getLoc();
150 StringRef metadataDir =
"metadata";
152 auto dir = dirAnno.
getMember<StringAttr>(
"dirname");
154 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
156 metadataDir = dir.getValue();
165 error = failed(processAddPortAnno(loc, anno));
169 error = failed(processFileAnno(loc, metadataDir, anno));
174 return failure(error);
177 LogicalResult AddSeqMemPortsPass::processMemModule(FMemModuleOp mem) {
179 if (!instanceInfo->allInstancesInEffectiveDesign(mem)) {
180 auto diag = mem->emitOpError()
181 <<
"cannot have ports added to it because it is instantiated "
182 "both under and not under the design-under-test (DUT)";
183 for (
auto *instNode : instanceGraph->lookup(mem)->uses()) {
184 auto instanceOp = instNode->getInstance();
185 if (
auto layerBlockOp = instanceOp->getParentOfType<LayerBlockOp>()) {
186 diag.attachNote(instanceOp.getLoc())
187 <<
"this instance is under a layer block";
188 diag.attachNote(layerBlockOp.getLoc())
189 <<
"the innermost layer block is here";
192 if (!instanceInfo->allInstancesInEffectiveDesign(
193 instNode->getParent()->getModule())) {
194 diag.attachNote(instanceOp.getLoc())
195 <<
"this instance is inside a module that is instantiated outside "
203 size_t portIndex = mem.getNumPorts();
204 auto &memInfo = memInfoMap[mem];
205 auto &extraPorts = memInfo.extraPorts;
206 for (
auto const &p : userPorts)
207 extraPorts.emplace_back(portIndex, p);
208 mem.insertPorts(extraPorts);
210 mem.setExtraPortsAttr(extraPortsAttr);
214 LogicalResult AddSeqMemPortsPass::processModule(FModuleOp moduleOp) {
215 auto *context = &getContext();
216 auto builder = OpBuilder(moduleOp.getContext());
217 auto &memInfo = memInfoMap[moduleOp];
218 auto &extraPorts = memInfo.extraPorts;
221 SmallVector<Value> values;
224 unsigned firstPortIndex = moduleOp.getNumPorts();
226 auto result = moduleOp.walk([&](Operation *op) {
227 if (
auto inst = dyn_cast<InstanceOp>(op)) {
228 auto submodule = inst.getReferencedModule(*instanceGraph);
230 auto subMemInfoIt = memInfoMap.find(submodule);
232 if (subMemInfoIt == memInfoMap.end() ||
233 subMemInfoIt->second.extraPorts.empty())
234 return WalkResult::advance();
235 auto &subMemInfo = subMemInfoIt->second;
237 auto &subExtraPorts = subMemInfo.extraPorts;
240 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
241 inst.replaceAllUsesWith(
242 clone.getResults().drop_back(subExtraPorts.size()));
243 instanceGraph->replaceInstance(inst, clone);
248 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
249 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
251 auto userIndex = i % userPorts.size();
252 auto const &sramPort = userPorts[userIndex];
254 auto sramIndex = extraPorts.size() / userPorts.size();
256 StringAttr::get(context,
"sram_" + Twine(sramIndex) +
"_" +
257 sramPort.name.getValue());
258 auto portDirection = sramPort.direction;
259 auto portType = sramPort.type;
261 extraPorts.push_back(
263 {portName, type_cast<FIRRTLType>(portType), portDirection}});
266 if (instanceInfo->isEffectiveDut(moduleOp))
267 extraPorts.back().second.annotations.addDontTouch();
270 values.push_back(inst.getResult(firstSubIndex + i));
278 auto &instancePaths = memInfo.instancePaths;
280 innerRefToInstanceMap[ref] = inst;
282 if (isa<FMemModuleOp>(submodule))
283 instancePaths.push_back({ref});
286 for (
const auto &subPath : subMemInfo.instancePaths) {
287 instancePaths.push_back(subPath);
288 instancePaths.back().push_back(ref);
292 return WalkResult::advance();
295 return WalkResult::advance();
298 if (result.wasInterrupted())
302 moduleOp.insertPorts(extraPorts);
305 DenseMap<Type, InvalidValueOp> invalids;
306 auto getOrCreateInvalid = [&](Type type) -> InvalidValueOp {
307 auto it = invalids.find(type);
308 if (it != invalids.end())
309 return it->getSecond();
312 builder.create<InvalidValueOp>(builder.getUnknownLoc(), type)})
317 DenseMap<Operation *, OpBuilder::InsertPoint> instToInsertionPoint;
318 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
319 auto &[firstArg, port] = extraPorts[i];
320 Value modulePort = moduleOp.getArgument(firstArg + i);
321 Value instPort = values[i];
322 Operation *instOp = instPort.getDefiningOp();
323 auto insertPoint = instToInsertionPoint.find(instOp);
324 if (insertPoint == instToInsertionPoint.end())
325 builder.setInsertionPointAfter(instOp);
327 builder.restoreInsertionPoint(insertPoint->getSecond());
329 std::swap(modulePort, instPort);
331 builder.create<MatchingConnectOp>(port.loc, modulePort, instPort);
332 instToInsertionPoint[instOp] = builder.saveInsertionPoint();
336 connectOp->getParentOfType<WhenOp>()) {
337 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
338 builder.create<MatchingConnectOp>(port.loc, modulePort,
339 getOrCreateInvalid(port.type));
347 auto circuit = getOperation();
348 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
352 llvm::raw_string_ostream os(buffer);
354 SymbolTable &symTable = getAnalysis<SymbolTable>();
358 unsigned paramIndex = 0;
360 SmallVector<Attribute> params;
362 DenseMap<Attribute, unsigned> usedParams;
365 auto addSymbol = [&](Attribute ref) {
366 auto it = usedParams.find(ref);
368 if (it != usedParams.end()) {
372 usedParams[ref] = paramIndex++;
373 params.push_back(ref);
375 os <<
"{{" << index <<
"}}";
379 unsigned sramIndex = 0;
380 auto &instancePaths = memInfoMap[moduleOp].instancePaths;
383 auto loc = builder.getUnknownLoc();
385 builder.create<emit::FileOp>(loc, outputFile, [&] {
386 for (
auto instancePath : instancePaths) {
388 SmallVector<Attribute> path(llvm::reverse(instancePath));
389 os << sramIndex++ <<
" -> ";
390 addSymbol(dutSymbol);
393 auto nlaSymbol = cache.getRefFor(builder.getArrayAttr(path));
394 addSymbol(nlaSymbol);
395 NamedAttrList fields;
398 auto id = DistinctAttr::create(
UnitAttr::get(builder.getContext()));
399 fields.append(
"id",
id);
400 fields.append(
"class", builder.getStringAttr(
"circt.tracker"));
401 fields.append(
"circt.nonlocal", nlaSymbol);
403 auto *leafInstance = innerRefToInstanceMap[instancePath.front()];
406 annos.addAnnotations(builder.getDictionaryAttr(fields));
407 annos.applyToOperation(leafInstance);
411 builder.create<sv::VerbatimOp>(loc, buffer, ValueRange{},
412 builder.getArrayAttr(params));
414 anythingChanged =
true;
417 void AddSeqMemPortsPass::runOnOperation() {
418 auto *context = &getContext();
419 auto circuit = getOperation();
420 instanceGraph = &getAnalysis<InstanceGraph>();
421 instanceInfo = &getAnalysis<InstanceInfo>();
427 anythingChanged =
false;
430 if (failed(processAnnos(circuit)))
431 return signalPassFailure();
435 std::reverse(userPorts.begin(), userPorts.end());
438 SmallVector<Attribute> extraPorts;
440 for (
auto &userPort : userPorts) {
441 SmallVector<NamedAttribute, 3> attrs;
446 context, userPort.direction == Direction::In ?
"input" :
"output"));
451 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
457 if (!userPorts.empty()) {
459 numAddedPorts += userPorts.size();
464 for (
auto *node : llvm::post_order(
465 instanceGraph->lookup(instanceInfo->getEffectiveDut()))) {
466 auto op = node->getModule();
469 if (!instanceInfo->anyInstanceInEffectiveDesign(op))
473 if (
auto moduleOp = dyn_cast<FModuleOp>(*op)) {
474 if (failed(processModule(moduleOp)))
475 return signalPassFailure();
476 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
477 if (failed(processMemModule(mem)))
478 return signalPassFailure();
483 auto effectiveDut = instanceInfo->getEffectiveDut();
484 if (
auto *dut = dyn_cast<FModuleOp>(&effectiveDut)) {
487 for (
auto *instRec : instanceGraph->lookup(effectiveDut)->uses()) {
488 auto inst = cast<InstanceOp>(*instRec->getInstance());
489 auto &dutMemInfo = memInfoMap[*dut];
491 auto &subExtraPorts = dutMemInfo.extraPorts;
493 if (subExtraPorts.empty())
497 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
498 inst.replaceAllUsesWith(
499 clone.getResults().drop_back(subExtraPorts.size()));
500 instanceGraph->replaceInstance(inst, clone);
505 OpBuilder builder(context);
506 builder.setInsertionPointAfter(inst);
507 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
508 auto &[firstResult, portInfo] = subExtraPorts[i];
509 if (portInfo.direction == Direction::Out)
511 auto value = inst.getResult(firstResult + i);
512 auto type = value.getType();
514 auto zero = builder.create<ConstantOp>(portInfo.loc, type, attr);
515 builder.create<MatchingConnectOp>(portInfo.loc, value, zero);
526 markAnalysesPreserved<InstanceGraph>();
528 markAllAnalysesPreserved();
532 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.