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);
49 LogicalResult processModule(FModuleOp moduleOp);
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();
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);
174LogicalResult 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);
211LogicalResult 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.cloneWithInsertedPortsAndReplaceUses(subExtraPorts);
238 instanceGraph->replaceInstance(inst, clone);
243 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
244 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
246 auto userIndex = i % userPorts.size();
247 auto const &sramPort = userPorts[userIndex];
249 auto sramIndex = extraPorts.size() / userPorts.size();
251 StringAttr::get(context,
"sram_" + Twine(sramIndex) +
"_" +
252 sramPort.name.getValue());
253 auto portDirection = sramPort.direction;
254 auto portType = sramPort.type;
256 extraPorts.push_back(
258 {portName, type_cast<FIRRTLType>(portType), portDirection}});
261 if (instanceInfo->isEffectiveDut(moduleOp))
262 extraPorts.back().second.annotations.addDontTouch();
265 values.push_back(inst.getResult(firstSubIndex + i));
273 auto &instancePaths = memInfo.instancePaths;
275 innerRefToInstanceMap[ref] = inst;
277 if (isa<FMemModuleOp>(submodule))
278 instancePaths.push_back({ref});
281 for (
const auto &subPath : subMemInfo.instancePaths) {
282 instancePaths.push_back(subPath);
283 instancePaths.back().push_back(ref);
287 return WalkResult::advance();
290 return WalkResult::advance();
293 if (result.wasInterrupted())
297 moduleOp.insertPorts(extraPorts);
300 DenseMap<Type, InvalidValueOp> invalids;
301 auto getOrCreateInvalid = [&](Type type) -> InvalidValueOp {
302 auto it = invalids.find(type);
303 if (it != invalids.end())
304 return it->getSecond();
306 .insert({type, InvalidValueOp::create(builder, builder.getUnknownLoc(),
312 DenseMap<Operation *, OpBuilder::InsertPoint> instToInsertionPoint;
313 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
314 auto &[firstArg, port] = extraPorts[i];
315 Value modulePort = moduleOp.getArgument(firstArg + i);
316 Value instPort = values[i];
317 Operation *instOp = instPort.getDefiningOp();
318 auto insertPoint = instToInsertionPoint.find(instOp);
319 if (insertPoint == instToInsertionPoint.end())
320 builder.setInsertionPointAfter(instOp);
322 builder.restoreInsertionPoint(insertPoint->getSecond());
323 if (port.direction == Direction::In)
324 std::swap(modulePort, instPort);
326 MatchingConnectOp::create(builder, port.loc, modulePort, instPort);
327 instToInsertionPoint[instOp] = builder.saveInsertionPoint();
330 if (port.direction == Direction::Out &&
331 connectOp->getParentOfType<WhenOp>()) {
332 builder.setInsertionPointToStart(moduleOp.getBodyBlock());
333 MatchingConnectOp::create(builder, port.loc, modulePort,
334 getOrCreateInvalid(port.type));
340void AddSeqMemPortsPass::createOutputFile(igraph::ModuleOpInterface moduleOp) {
342 auto circuit = getOperation();
343 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
347 llvm::raw_string_ostream os(buffer);
349 SymbolTable &symTable = getAnalysis<SymbolTable>();
353 unsigned paramIndex = 0;
355 SmallVector<Attribute> params;
357 DenseMap<Attribute, unsigned> usedParams;
360 auto addSymbol = [&](Attribute ref) {
361 auto it = usedParams.find(ref);
363 if (it != usedParams.end()) {
367 usedParams[ref] = paramIndex++;
368 params.push_back(ref);
370 os <<
"{{" << index <<
"}}";
374 unsigned sramIndex = 0;
375 auto &instancePaths = memInfoMap[moduleOp].instancePaths;
376 auto dutSymbol = FlatSymbolRefAttr::get(moduleOp.getModuleNameAttr());
378 auto loc = builder.getUnknownLoc();
380 emit::FileOp::create(builder, loc, outputFile, [&] {
381 for (
auto instancePath : instancePaths) {
383 SmallVector<Attribute> path(llvm::reverse(instancePath));
384 os << sramIndex++ <<
" -> ";
385 addSymbol(dutSymbol);
388 auto nlaSymbol = cache.getRefFor(builder.getArrayAttr(path));
389 addSymbol(nlaSymbol);
390 NamedAttrList fields;
393 auto id = DistinctAttr::create(UnitAttr::get(builder.getContext()));
394 fields.append(
"id",
id);
395 fields.append(
"class", builder.getStringAttr(
"circt.tracker"));
396 fields.append(
"circt.nonlocal", nlaSymbol);
398 auto *leafInstance = innerRefToInstanceMap[instancePath.front()];
401 annos.addAnnotations(builder.getDictionaryAttr(fields));
402 annos.applyToOperation(leafInstance);
406 sv::VerbatimOp::create(builder, loc, buffer, ValueRange{},
407 builder.getArrayAttr(params));
409 anythingChanged =
true;
412void AddSeqMemPortsPass::runOnOperation() {
413 auto *context = &getContext();
414 auto circuit = getOperation();
415 instanceGraph = &getAnalysis<InstanceGraph>();
416 instanceInfo = &getAnalysis<InstanceInfo>();
422 anythingChanged =
false;
425 if (failed(processAnnos(circuit)))
426 return signalPassFailure();
430 std::reverse(userPorts.begin(), userPorts.end());
433 SmallVector<Attribute> extraPorts;
434 auto ui32Type = IntegerType::get(context, 32, IntegerType::Unsigned);
435 for (
auto &userPort : userPorts) {
436 SmallVector<NamedAttribute, 3> attrs;
437 attrs.emplace_back(StringAttr::get(context,
"name"), userPort.name);
439 StringAttr::get(context,
"direction"),
441 context, userPort.direction == Direction::In ?
"input" :
"output"));
443 StringAttr::get(context,
"width"),
446 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
447 extraPorts.push_back(DictionaryAttr::get(context, attrs));
449 extraPortsAttr = ArrayAttr::get(context, extraPorts);
452 if (!userPorts.empty()) {
454 numAddedPorts += userPorts.size();
459 for (
auto *node :
llvm::post_order(
460 instanceGraph->lookup(instanceInfo->getEffectiveDut()))) {
461 auto op = node->getModule();
464 if (!instanceInfo->anyInstanceInEffectiveDesign(op))
468 if (
auto moduleOp = dyn_cast<FModuleOp>(*op)) {
469 if (failed(processModule(moduleOp)))
470 return signalPassFailure();
471 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
472 if (failed(processMemModule(mem)))
473 return signalPassFailure();
478 auto effectiveDut = instanceInfo->getEffectiveDut();
479 if (
auto *dut = dyn_cast<FModuleOp>(&effectiveDut)) {
482 for (
auto *instRec : instanceGraph->lookup(effectiveDut)->uses()) {
483 auto inst = cast<InstanceOp>(*instRec->getInstance());
484 auto &dutMemInfo = memInfoMap[*dut];
486 auto &subExtraPorts = dutMemInfo.extraPorts;
488 if (subExtraPorts.empty())
492 auto clone = inst.cloneWithInsertedPortsAndReplaceUses(subExtraPorts);
493 instanceGraph->replaceInstance(inst, clone);
498 OpBuilder builder(context);
499 builder.setInsertionPointAfter(inst);
500 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
501 auto &[firstResult, portInfo] = subExtraPorts[i];
502 if (portInfo.direction == Direction::Out)
504 auto value = inst.getResult(firstResult + i);
505 auto type = value.getType();
507 auto zero = ConstantOp::create(builder, portInfo.loc, type, attr);
508 MatchingConnectOp::create(builder, portInfo.loc, value, zero);
519 markAnalysesPreserved<InstanceGraph>();
521 markAllAnalysesPreserved();
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.
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
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.