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->allInstancesUnderEffectiveDut(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 (instanceInfo->anyInstanceUnderDut(
186 instNode->getParent()->getModule())) {
187 diag.attachNote(instanceOp.getLoc())
188 <<
"this instance is under the DUT";
191 diag.attachNote(instanceOp.getLoc())
192 <<
"this instance is not under the DUT";
204 if (instanceInfo->anyInstanceUnderLayer(mem)) {
205 auto diag = mem->emitOpError()
206 <<
"cannot have ports added to it because it is "
207 "instantiated under "
208 "both the design-under-test and layer blocks";
209 for (
auto *instNode : instanceGraph->lookup(mem)->uses()) {
210 auto instanceOp = instNode->getInstance();
211 if (
auto layerBlockOp = instanceOp->getParentOfType<LayerBlockOp>()) {
212 diag.attachNote(instanceOp.getLoc())
213 <<
"this instance is under a layer block";
214 diag.attachNote(layerBlockOp.getLoc())
215 <<
"the innermost layer block is here";
218 if (instanceInfo->anyInstanceUnderLayer(
219 instNode->getParent()->getModule())) {
220 diag.attachNote(instanceOp.getLoc())
221 <<
"this instance is inside a module that is instantiated "
222 "under a layer block";
229 size_t portIndex = mem.getNumPorts();
230 auto &memInfo = memInfoMap[mem];
231 auto &extraPorts = memInfo.extraPorts;
232 for (
auto const &p : userPorts)
233 extraPorts.emplace_back(portIndex, p);
234 mem.insertPorts(extraPorts);
236 mem.setExtraPortsAttr(extraPortsAttr);
240 LogicalResult AddSeqMemPortsPass::processModule(FModuleOp moduleOp) {
241 auto *context = &getContext();
242 auto builder = OpBuilder(moduleOp.getContext());
243 auto &memInfo = memInfoMap[moduleOp];
244 auto &extraPorts = memInfo.extraPorts;
247 SmallVector<Value> values;
250 unsigned firstPortIndex = moduleOp.getNumPorts();
252 auto result = moduleOp.walk([&](Operation *op) {
253 if (
auto inst = dyn_cast<InstanceOp>(op)) {
254 auto submodule = inst.getReferencedModule(*instanceGraph);
256 auto subMemInfoIt = memInfoMap.find(submodule);
258 if (subMemInfoIt == memInfoMap.end() ||
259 subMemInfoIt->second.extraPorts.empty())
260 return WalkResult::advance();
261 auto &subMemInfo = subMemInfoIt->second;
263 auto &subExtraPorts = subMemInfo.extraPorts;
266 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
267 inst.replaceAllUsesWith(
268 clone.getResults().drop_back(subExtraPorts.size()));
269 instanceGraph->replaceInstance(inst, clone);
274 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
275 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
277 auto userIndex = i % userPorts.size();
278 auto const &sramPort = userPorts[userIndex];
280 auto sramIndex = extraPorts.size() / userPorts.size();
282 StringAttr::get(context,
"sram_" + Twine(sramIndex) +
"_" +
283 sramPort.name.getValue());
284 auto portDirection = sramPort.direction;
285 auto portType = sramPort.type;
287 extraPorts.push_back(
289 {portName, type_cast<FIRRTLType>(portType), portDirection}});
292 if (instanceInfo->isEffectiveDut(moduleOp))
293 extraPorts.back().second.annotations.addDontTouch();
296 values.push_back(inst.getResult(firstSubIndex + i));
304 auto &instancePaths = memInfo.instancePaths;
306 innerRefToInstanceMap[ref] = inst;
308 if (isa<FMemModuleOp>(submodule))
309 instancePaths.push_back({ref});
312 for (
const auto &subPath : subMemInfo.instancePaths) {
313 instancePaths.push_back(subPath);
314 instancePaths.back().push_back(ref);
318 return WalkResult::advance();
321 return WalkResult::advance();
324 if (result.wasInterrupted())
328 moduleOp.insertPorts(extraPorts);
331 DenseMap<Type, InvalidValueOp> invalids;
332 auto getOrCreateInvalid = [&](Type type) -> InvalidValueOp {
333 auto it = invalids.find(type);
334 if (it != invalids.end())
335 return it->getSecond();
338 builder.create<InvalidValueOp>(builder.getUnknownLoc(), type)})
343 DenseMap<Operation *, OpBuilder::InsertPoint> instToInsertionPoint;
344 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
345 auto &[firstArg, port] = extraPorts[i];
346 Value modulePort = moduleOp.getArgument(firstArg + i);
347 Value instPort = values[i];
348 Operation *instOp = instPort.getDefiningOp();
349 auto insertPoint = instToInsertionPoint.find(instOp);
350 if (insertPoint == instToInsertionPoint.end())
351 builder.setInsertionPointAfter(instOp);
353 builder.restoreInsertionPoint(insertPoint->getSecond());
355 std::swap(modulePort, instPort);
357 builder.create<MatchingConnectOp>(port.loc, modulePort, instPort);
358 instToInsertionPoint[instOp] = builder.saveInsertionPoint();
362 connectOp->getParentOfType<WhenOp>()) {
363 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
364 builder.create<MatchingConnectOp>(port.loc, modulePort,
365 getOrCreateInvalid(port.type));
373 auto circuit = getOperation();
374 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
378 llvm::raw_string_ostream os(buffer);
380 SymbolTable &symTable = getAnalysis<SymbolTable>();
384 unsigned paramIndex = 0;
386 SmallVector<Attribute> params;
388 DenseMap<Attribute, unsigned> usedParams;
391 auto addSymbol = [&](Attribute ref) {
392 auto it = usedParams.find(ref);
394 if (it != usedParams.end()) {
398 usedParams[ref] = paramIndex++;
399 params.push_back(ref);
401 os <<
"{{" << index <<
"}}";
405 unsigned sramIndex = 0;
406 auto &instancePaths = memInfoMap[moduleOp].instancePaths;
409 auto loc = builder.getUnknownLoc();
411 builder.create<emit::FileOp>(loc, outputFile, [&] {
412 for (
auto instancePath : instancePaths) {
414 SmallVector<Attribute> path(llvm::reverse(instancePath));
415 os << sramIndex++ <<
" -> ";
416 addSymbol(dutSymbol);
419 auto nlaSymbol = cache.getRefFor(builder.getArrayAttr(path));
420 addSymbol(nlaSymbol);
421 NamedAttrList fields;
424 auto id = DistinctAttr::create(
UnitAttr::get(builder.getContext()));
425 fields.append(
"id",
id);
426 fields.append(
"class", builder.getStringAttr(
"circt.tracker"));
427 fields.append(
"circt.nonlocal", nlaSymbol);
429 auto *leafInstance = innerRefToInstanceMap[instancePath.front()];
432 annos.addAnnotations(builder.getDictionaryAttr(fields));
433 annos.applyToOperation(leafInstance);
437 builder.create<sv::VerbatimOp>(loc, buffer, ValueRange{},
438 builder.getArrayAttr(params));
440 anythingChanged =
true;
443 void AddSeqMemPortsPass::runOnOperation() {
444 auto *context = &getContext();
445 auto circuit = getOperation();
446 instanceGraph = &getAnalysis<InstanceGraph>();
447 instanceInfo = &getAnalysis<InstanceInfo>();
453 anythingChanged =
false;
456 if (failed(processAnnos(circuit)))
457 return signalPassFailure();
461 std::reverse(userPorts.begin(), userPorts.end());
464 SmallVector<Attribute> extraPorts;
466 for (
auto &userPort : userPorts) {
467 SmallVector<NamedAttribute, 3> attrs;
472 context, userPort.direction == Direction::In ?
"input" :
"output"));
477 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
483 if (!userPorts.empty()) {
485 numAddedPorts += userPorts.size();
491 for (
auto *node : llvm::post_order(
492 instanceGraph->lookup(instanceInfo->getEffectiveDut()))) {
493 auto op = node->getModule();
496 if (instanceInfo->allInstancesUnderLayer(op))
500 if (
auto moduleOp = dyn_cast<FModuleOp>(*op)) {
501 if (failed(processModule(moduleOp)))
502 return signalPassFailure();
503 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
504 if (failed(processMemModule(mem)))
505 return signalPassFailure();
510 auto effectiveDut = instanceInfo->getEffectiveDut();
511 if (
auto *dut = dyn_cast<FModuleOp>(&effectiveDut)) {
514 for (
auto *instRec : instanceGraph->lookup(effectiveDut)->uses()) {
515 auto inst = cast<InstanceOp>(*instRec->getInstance());
516 auto &dutMemInfo = memInfoMap[*dut];
518 auto &subExtraPorts = dutMemInfo.extraPorts;
520 if (subExtraPorts.empty())
524 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
525 inst.replaceAllUsesWith(
526 clone.getResults().drop_back(subExtraPorts.size()));
527 instanceGraph->replaceInstance(inst, clone);
532 OpBuilder builder(context);
533 builder.setInsertionPointAfter(inst);
534 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
535 auto &[firstResult, portInfo] = subExtraPorts[i];
536 if (portInfo.direction == Direction::Out)
538 auto value = inst.getResult(firstResult + i);
539 auto type = value.getType();
541 auto zero = builder.create<ConstantOp>(portInfo.loc, type, attr);
542 builder.create<MatchingConnectOp>(portInfo.loc, value, zero);
553 markAnalysesPreserved<InstanceGraph>();
555 markAllAnalysesPreserved();
559 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.