25#include "mlir/Pass/Pass.h"
26#include "llvm/ADT/PostOrderIterator.h"
27#include "llvm/ADT/STLFunctionalExtras.h"
28#include "llvm/Support/Path.h"
32#define GEN_PASS_DEF_ADDSEQMEMPORTS
33#include "circt/Dialect/FIRRTL/Passes.h.inc"
41struct AddSeqMemPortsPass
42 :
public circt::firrtl::impl::AddSeqMemPortsBase<AddSeqMemPortsPass> {
43 void runOnOperation()
override;
44 LogicalResult processAddPortAnno(Location loc, Annotation anno);
45 LogicalResult processFileAnno(Location loc, StringRef metadataDir,
47 LogicalResult processAnnos(CircuitOp circuit);
49 LogicalResult processMemModule(FMemModuleOp mem);
50 LogicalResult processModule(FModuleOp moduleOp);
53 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike moduleOp) {
54 return moduleNamespaces.try_emplace(moduleOp, moduleOp).first->second;
60 return ::getInnerRefTo(op,
61 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
62 return getModuleNamespace(mod);
70 std::vector<std::pair<unsigned, PortInfo>> extraPorts;
75 std::vector<SmallVector<Attribute>> instancePaths;
78 CircuitNamespace circtNamespace;
80 DenseMap<Operation *, MemoryInfo> memInfoMap;
81 DenseMap<Attribute, Operation *> innerRefToInstanceMap;
84 InstanceInfo *instanceInfo;
87 StringAttr outputFile;
90 SmallVector<PortInfo> userPorts;
92 ArrayAttr extraPortsAttr;
95 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
101LogicalResult AddSeqMemPortsPass::processAddPortAnno(Location loc,
103 auto name = anno.
getMember<StringAttr>(
"name");
106 loc,
"AddSeqMemPortAnnotation requires field 'name' of string type");
108 auto input = anno.
getMember<BoolAttr>(
"input");
111 loc,
"AddSeqMemPortAnnotation requires field 'input' of boolean type");
112 auto direction = input.getValue() ? Direction::In : Direction::Out;
114 auto width = anno.
getMember<IntegerAttr>(
"width");
117 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
118 auto type = UIntType::get(&getContext(), width.getInt());
119 userPorts.push_back({name, type, direction});
123LogicalResult AddSeqMemPortsPass::processFileAnno(Location loc,
124 StringRef metadataDir,
128 loc,
"circuit has two AddSeqMemPortsFileAnnotation annotations");
130 auto filename = anno.
getMember<StringAttr>(
"filename");
132 return emitError(loc,
133 "AddSeqMemPortsFileAnnotation requires field 'filename' "
136 SmallString<128> outputFilePath(metadataDir);
137 llvm::sys::path::append(outputFilePath, filename.getValue());
138 outputFile = StringAttr::get(&getContext(), outputFilePath);
142LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
143 auto loc = circuit.getLoc();
146 auto dirAnno = AnnotationSet(circuit).getAnnotation(metadataDirAnnoClass);
147 StringRef metadataDir =
"metadata";
149 auto dir = dirAnno.getMember<StringAttr>(
"dirname");
151 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
153 metadataDir = dir.getValue();
161 if (anno.
isClass(addSeqMemPortAnnoClass)) {
162 error = failed(processAddPortAnno(loc, anno));
165 if (anno.
isClass(addSeqMemPortsFileAnnoClass)) {
166 error = failed(processFileAnno(loc, metadataDir, anno));
174LogicalResult AddSeqMemPortsPass::processMemModule(FMemModuleOp 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";
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) {
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);
240 inst = cast<InstanceOp>(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(
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>();
350 HierPathCache cache(circuit, symTable);
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()];
400 AnnotationSet annos(leafInstance);
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() {
414 auto circuit = getOperation();
415 instanceGraph = &getAnalysis<InstanceGraph>();
416 instanceInfo = &getAnalysis<InstanceInfo>();
417 circtNamespace = CircuitNamespace(circuit);
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"),
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(
461 auto op = node->getModule();
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();
479 if (
auto *dut = dyn_cast<FModuleOp>(&effectiveDut)) {
482 for (
auto *instRec : instanceGraph->
lookup(effectiveDut)->
uses()) {
483 auto inst = cast<FInstanceLike>(*instRec->getInstance());
484 auto &dutMemInfo = memInfoMap[*dut];
486 auto &subExtraPorts = dutMemInfo.extraPorts;
488 if (subExtraPorts.empty())
492 auto clone = inst.cloneWithInsertedPortsAndReplaceUses(subExtraPorts);
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();
static std::unique_ptr< Context > context
false return failure()
Parse an optional keyword or string and set instance into 'result'.
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
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.
bool isEffectiveDut(igraph::ModuleOpInterface op)
Return true if this module is the design-under-test and the circuit has a design-under-test.
bool allInstancesInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if all instances of this module are within (or transitively within) the effective design.
igraph::ModuleOpInterface getEffectiveDut()
Return the "effective" design-under-test.
bool anyInstanceInEffectiveDesign(igraph::ModuleOpInterface op)
Return true if any instance of this module is within (or transitively within) the effective design.
llvm::iterator_range< UseIterator > uses()
virtual void replaceInstance(InstanceOpInterface inst, InstanceOpInterface newInst)
Replaces an instance of a module with another instance.
InstanceGraphNode * lookup(ModuleOpInterface op)
Look up an InstanceGraphNode for a module.
BaseTy type_cast(Type type)
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.
std::map< std::string, std::set< std::string > > InstanceGraph
Iterates over the handshake::FuncOp's in the program to build an instance graph.
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.