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"
37using namespace firrtl;
40struct 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);
53 return moduleNamespaces.try_emplace(moduleOp, moduleOp).first->second;
59 return ::getInnerRefTo(op,
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;
100LogicalResult 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");
111 auto direction = input.getValue() ? Direction::In : Direction::Out;
113 auto width = anno.
getMember<IntegerAttr>(
"width");
116 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
117 auto type = UIntType::get(&getContext(), width.getInt());
118 userPorts.push_back({name, type, direction});
122LogicalResult 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);
136 llvm::sys::path::append(outputFilePath, filename.getValue());
137 outputFile = StringAttr::get(&getContext(), outputFilePath);
141LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
142 auto loc = circuit.getLoc();
146 StringRef metadataDir =
"metadata";
148 auto dir = dirAnno.
getMember<StringAttr>(
"dirname");
150 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
152 metadataDir = dir.getValue();
160 if (anno.
isClass(addSeqMemPortAnnoClass)) {
161 error = failed(processAddPortAnno(loc, anno));
164 if (anno.
isClass(addSeqMemPortsFileAnnoClass)) {
165 error = failed(processFileAnno(loc, metadataDir, anno));
170 return failure(error);
173LogicalResult AddSeqMemPortsPass::processMemModule(FMemModuleOp mem) {
175 if (!instanceInfo->allInstancesInEffectiveDesign(mem)) {
176 auto diag = mem->emitOpError()
177 <<
"cannot have ports added to it because it is instantiated "
178 "both under and not under the design-under-test (DUT)";
179 for (
auto *instNode : instanceGraph->lookup(mem)->uses()) {
180 auto instanceOp = instNode->getInstance();
181 if (
auto layerBlockOp = instanceOp->getParentOfType<LayerBlockOp>()) {
182 diag.attachNote(instanceOp.getLoc())
183 <<
"this instance is under a layer block";
184 diag.attachNote(layerBlockOp.getLoc())
185 <<
"the innermost layer block is here";
188 if (!instanceInfo->allInstancesInEffectiveDesign(
189 instNode->getParent()->getModule())) {
190 diag.attachNote(instanceOp.getLoc())
191 <<
"this instance is inside a module that is instantiated outside "
199 size_t portIndex = mem.getNumPorts();
200 auto &memInfo = memInfoMap[mem];
201 auto &extraPorts = memInfo.extraPorts;
202 for (
auto const &p : userPorts)
203 extraPorts.emplace_back(portIndex, p);
204 mem.insertPorts(extraPorts);
206 mem.setExtraPortsAttr(extraPortsAttr);
210LogicalResult AddSeqMemPortsPass::processModule(FModuleOp moduleOp) {
212 auto builder = OpBuilder(moduleOp.getContext());
213 auto &memInfo = memInfoMap[moduleOp];
214 auto &extraPorts = memInfo.extraPorts;
217 SmallVector<Value> values;
220 unsigned firstPortIndex = moduleOp.getNumPorts();
222 auto result = moduleOp.walk([&](Operation *op) {
223 if (
auto inst = dyn_cast<InstanceOp>(op)) {
224 auto submodule = inst.getReferencedModule(*instanceGraph);
226 auto subMemInfoIt = memInfoMap.find(submodule);
228 if (subMemInfoIt == memInfoMap.end() ||
229 subMemInfoIt->second.extraPorts.empty())
230 return WalkResult::advance();
231 auto &subMemInfo = subMemInfoIt->second;
233 auto &subExtraPorts = subMemInfo.extraPorts;
236 auto clone = inst.cloneWithInsertedPortsAndReplaceUses(subExtraPorts);
237 instanceGraph->replaceInstance(inst, clone);
242 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
243 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
245 auto userIndex = i % userPorts.size();
246 auto const &sramPort = userPorts[userIndex];
248 auto sramIndex = extraPorts.size() / userPorts.size();
250 StringAttr::get(
context,
"sram_" + Twine(sramIndex) +
"_" +
251 sramPort.name.getValue());
252 auto portDirection = sramPort.direction;
253 auto portType = sramPort.type;
255 extraPorts.push_back(
257 {portName, type_cast<FIRRTLType>(portType), portDirection}});
260 if (instanceInfo->isEffectiveDut(moduleOp))
261 extraPorts.back().second.annotations.addDontTouch();
264 values.push_back(inst.getResult(firstSubIndex + i));
272 auto &instancePaths = memInfo.instancePaths;
274 innerRefToInstanceMap[ref] = inst;
276 if (isa<FMemModuleOp>(submodule))
277 instancePaths.push_back({ref});
280 for (
const auto &subPath : subMemInfo.instancePaths) {
281 instancePaths.push_back(subPath);
282 instancePaths.back().push_back(ref);
286 return WalkResult::advance();
289 return WalkResult::advance();
292 if (result.wasInterrupted())
296 moduleOp.insertPorts(extraPorts);
299 DenseMap<Type, InvalidValueOp> invalids;
300 auto getOrCreateInvalid = [&](Type type) -> InvalidValueOp {
301 auto it = invalids.find(type);
302 if (it != invalids.end())
303 return it->getSecond();
305 .insert({type, InvalidValueOp::create(builder, builder.getUnknownLoc(),
311 DenseMap<Operation *, OpBuilder::InsertPoint> instToInsertionPoint;
312 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
313 auto &[firstArg, port] = extraPorts[i];
314 Value modulePort = moduleOp.getArgument(firstArg + i);
315 Value instPort = values[i];
316 Operation *instOp = instPort.getDefiningOp();
317 auto insertPoint = instToInsertionPoint.find(instOp);
318 if (insertPoint == instToInsertionPoint.end())
319 builder.setInsertionPointAfter(instOp);
321 builder.restoreInsertionPoint(insertPoint->getSecond());
322 if (port.direction == Direction::In)
323 std::swap(modulePort, instPort);
325 MatchingConnectOp::create(builder, port.loc, modulePort, instPort);
326 instToInsertionPoint[instOp] = builder.saveInsertionPoint();
329 if (port.direction == Direction::Out &&
330 connectOp->getParentOfType<WhenOp>()) {
331 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
332 MatchingConnectOp::create(builder, port.loc, modulePort,
333 getOrCreateInvalid(port.type));
339void AddSeqMemPortsPass::createOutputFile(igraph::ModuleOpInterface moduleOp) {
341 auto circuit = getOperation();
342 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
346 llvm::raw_string_ostream os(buffer);
348 SymbolTable &symTable = getAnalysis<SymbolTable>();
352 unsigned paramIndex = 0;
354 SmallVector<Attribute> params;
356 DenseMap<Attribute, unsigned> usedParams;
359 auto addSymbol = [&](Attribute ref) {
360 auto it = usedParams.find(ref);
362 if (it != usedParams.end()) {
366 usedParams[ref] = paramIndex++;
367 params.push_back(ref);
369 os <<
"{{" << index <<
"}}";
373 unsigned sramIndex = 0;
374 auto &instancePaths = memInfoMap[moduleOp].instancePaths;
375 auto dutSymbol = FlatSymbolRefAttr::get(moduleOp.getModuleNameAttr());
377 auto loc = builder.getUnknownLoc();
379 emit::FileOp::create(builder, loc, outputFile, [&] {
380 for (
auto instancePath : instancePaths) {
382 SmallVector<Attribute> path(llvm::reverse(instancePath));
383 os << sramIndex++ <<
" -> ";
384 addSymbol(dutSymbol);
387 auto nlaSymbol = cache.getRefFor(builder.getArrayAttr(path));
388 addSymbol(nlaSymbol);
389 NamedAttrList fields;
392 auto id = DistinctAttr::create(UnitAttr::get(builder.getContext()));
393 fields.append(
"id",
id);
394 fields.append(
"class", builder.getStringAttr(
"circt.tracker"));
395 fields.append(
"circt.nonlocal", nlaSymbol);
397 auto *leafInstance = innerRefToInstanceMap[instancePath.front()];
400 annos.addAnnotations(builder.getDictionaryAttr(fields));
401 annos.applyToOperation(leafInstance);
405 sv::VerbatimOp::create(builder, loc, buffer, ValueRange{},
406 builder.getArrayAttr(params));
408 anythingChanged =
true;
411void AddSeqMemPortsPass::runOnOperation() {
413 auto circuit = getOperation();
414 instanceGraph = &getAnalysis<InstanceGraph>();
415 instanceInfo = &getAnalysis<InstanceInfo>();
421 anythingChanged =
false;
424 if (failed(processAnnos(circuit)))
425 return signalPassFailure();
429 std::reverse(userPorts.begin(), userPorts.end());
432 SmallVector<Attribute> extraPorts;
433 auto ui32Type = IntegerType::get(
context, 32, IntegerType::Unsigned);
434 for (
auto &userPort : userPorts) {
435 SmallVector<NamedAttribute, 3> attrs;
436 attrs.emplace_back(StringAttr::get(
context,
"name"), userPort.name);
438 StringAttr::get(
context,
"direction"),
440 context, userPort.direction == Direction::In ?
"input" :
"output"));
442 StringAttr::get(
context,
"width"),
445 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
446 extraPorts.push_back(DictionaryAttr::get(
context, attrs));
448 extraPortsAttr = ArrayAttr::get(
context, extraPorts);
451 if (!userPorts.empty()) {
453 numAddedPorts += userPorts.size();
458 for (
auto *node :
llvm::post_order(
459 instanceGraph->lookup(instanceInfo->getEffectiveDut()))) {
460 auto op = node->getModule();
463 if (!instanceInfo->anyInstanceInEffectiveDesign(op))
467 if (
auto moduleOp = dyn_cast<FModuleOp>(*op)) {
469 return signalPassFailure();
470 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
471 if (failed(processMemModule(mem)))
472 return signalPassFailure();
477 auto effectiveDut = instanceInfo->getEffectiveDut();
478 if (
auto *dut = dyn_cast<FModuleOp>(&effectiveDut)) {
481 for (
auto *instRec : instanceGraph->lookup(effectiveDut)->uses()) {
482 auto inst = cast<InstanceOp>(*instRec->getInstance());
483 auto &dutMemInfo = memInfoMap[*dut];
485 auto &subExtraPorts = dutMemInfo.extraPorts;
487 if (subExtraPorts.empty())
491 auto clone = inst.cloneWithInsertedPortsAndReplaceUses(subExtraPorts);
492 instanceGraph->replaceInstance(inst, clone);
498 builder.setInsertionPointAfter(inst);
499 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
500 auto &[firstResult, portInfo] = subExtraPorts[i];
501 if (portInfo.direction == Direction::Out)
503 auto value = inst.getResult(firstResult + i);
504 auto type = value.getType();
506 auto zero = ConstantOp::create(builder, portInfo.loc, type, attr);
507 MatchingConnectOp::create(builder, portInfo.loc, value, zero);
518 markAnalysesPreserved<InstanceGraph>();
520 markAllAnalysesPreserved();
static std::unique_ptr< Context > context
static LogicalResult processModule(const DomainInfo &info, TermAllocator &allocator, DomainTable &table, const ModuleUpdateTable &updateTable, FModuleOp moduleOp)
Populate the domain table by processing the moduleOp.
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.
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.
IntegerAttr getIntZerosAttr(Type type)
Utility for generating a constant zero attribute.
void error(Twine message)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef filename, StringRef dirname, function_ref< InFlightDiagnostic()> emitError)
Creates an output file with the given filename in the specified directory.
The namespace of a CircuitOp, generally inhabited by modules.
A cache of existing HierPathOps, mostly used to facilitate HierPathOp reuse.