24 #include "mlir/Pass/Pass.h"
25 #include "llvm/ADT/PostOrderIterator.h"
26 #include "llvm/ADT/STLFunctionalExtras.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/Parallel.h"
29 #include "llvm/Support/Path.h"
33 #define GEN_PASS_DEF_ADDSEQMEMPORTS
34 #include "circt/Dialect/FIRRTL/Passes.h.inc"
38 using namespace circt;
39 using namespace firrtl;
42 struct AddSeqMemPortsPass
43 :
public circt::firrtl::impl::AddSeqMemPortsBase<AddSeqMemPortsPass> {
44 void runOnOperation()
override;
45 LogicalResult processAddPortAnno(Location loc,
Annotation anno);
46 LogicalResult processFileAnno(Location loc, StringRef metadataDir,
48 LogicalResult processAnnos(CircuitOp circuit);
51 void processMemModule(FMemModuleOp mem);
52 LogicalResult processModule(FModuleOp module,
bool isDUT);
55 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
56 return moduleNamespaces.try_emplace(module, module).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;
88 StringAttr outputFile;
91 SmallVector<PortInfo> userPorts;
93 ArrayAttr extraPortsAttr;
96 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
102 LogicalResult AddSeqMemPortsPass::processAddPortAnno(Location loc,
104 auto name = anno.
getMember<StringAttr>(
"name");
107 loc,
"AddSeqMemPortAnnotation requires field 'name' of string type");
109 auto input = anno.
getMember<BoolAttr>(
"input");
112 loc,
"AddSeqMemPortAnnotation requires field 'input' of boolean type");
118 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
120 userPorts.push_back({name, type, direction});
124 LogicalResult AddSeqMemPortsPass::processFileAnno(Location loc,
125 StringRef metadataDir,
129 loc,
"circuit has two AddSeqMemPortsFileAnnotation annotations");
131 auto filename = anno.
getMember<StringAttr>(
"filename");
133 return emitError(loc,
134 "AddSeqMemPortsFileAnnotation requires field 'filename' "
137 SmallString<128> outputFilePath(metadataDir);
143 LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
144 auto loc = circuit.getLoc();
149 StringRef metadataDir =
"metadata";
151 auto dir = dirAnno.
getMember<StringAttr>(
"dirname");
153 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
155 metadataDir = dir.getValue();
164 error = failed(processAddPortAnno(loc, anno));
168 error = failed(processFileAnno(loc, metadataDir, anno));
173 return failure(error);
178 for (
auto *node : *instanceGraph) {
182 return instanceGraph->getTopLevelNode();
185 void AddSeqMemPortsPass::processMemModule(FMemModuleOp mem) {
187 size_t portIndex = mem.getNumPorts();
188 auto &memInfo = memInfoMap[mem];
189 auto &extraPorts = memInfo.extraPorts;
190 for (
auto &p : userPorts)
191 extraPorts.emplace_back(portIndex, p);
192 mem.insertPorts(extraPorts);
194 mem.setExtraPortsAttr(extraPortsAttr);
197 LogicalResult AddSeqMemPortsPass::processModule(FModuleOp module,
bool isDUT) {
198 auto *context = &getContext();
200 auto builder = OpBuilder::atBlockEnd(module.getBodyBlock());
201 auto &memInfo = memInfoMap[module];
202 auto &extraPorts = memInfo.extraPorts;
205 SmallVector<Value> values;
208 unsigned firstPortIndex = module.getNumPorts();
210 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
211 if (
auto inst = dyn_cast<InstanceOp>(op)) {
212 auto submodule = inst.getReferencedModule(*instanceGraph);
214 auto subMemInfoIt = memInfoMap.find(submodule);
216 if (subMemInfoIt == memInfoMap.end() ||
217 subMemInfoIt->second.extraPorts.empty())
219 auto &subMemInfo = subMemInfoIt->second;
221 auto &subExtraPorts = subMemInfo.extraPorts;
224 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
225 inst.replaceAllUsesWith(
226 clone.getResults().drop_back(subExtraPorts.size()));
227 instanceGraph->replaceInstance(inst, clone);
232 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
233 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
235 auto userIndex = i % userPorts.size();
236 auto &sramPort = userPorts[userIndex];
238 auto sramIndex = extraPorts.size() / userPorts.size();
241 sramPort.name.getValue());
242 auto portDirection = sramPort.direction;
243 auto portType = sramPort.type;
245 extraPorts.push_back(
247 {portName, type_cast<FIRRTLType>(portType), portDirection}});
251 extraPorts.back().second.annotations.addDontTouch();
254 values.push_back(inst.getResult(firstSubIndex + i));
262 auto &instancePaths = memInfo.instancePaths;
264 innerRefToInstanceMap[ref] = inst;
266 if (isa<FMemModuleOp>(submodule))
267 instancePaths.push_back({ref});
270 for (
const auto &subPath : subMemInfo.instancePaths) {
271 instancePaths.push_back(subPath);
272 instancePaths.back().push_back(ref);
279 module.insertPorts(extraPorts);
282 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
283 auto &[firstArg, port] = extraPorts[i];
284 Value modulePort = module.getArgument(firstArg + i);
285 Value instPort = values[i];
287 std::swap(modulePort, instPort);
288 builder.create<MatchingConnectOp>(port.loc, modulePort, instPort);
295 auto circuit = getOperation();
296 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
300 llvm::raw_string_ostream os(buffer);
302 SymbolTable &symTable = getAnalysis<SymbolTable>();
306 unsigned paramIndex = 0;
308 SmallVector<Attribute> params;
310 DenseMap<Attribute, unsigned> usedParams;
313 auto addSymbol = [&](Attribute ref) {
314 auto it = usedParams.find(ref);
316 if (it != usedParams.end()) {
320 usedParams[ref] = paramIndex++;
321 params.push_back(ref);
323 os <<
"{{" << index <<
"}}";
327 unsigned sramIndex = 0;
328 auto &instancePaths = memInfoMap[module].instancePaths;
331 auto loc = builder.getUnknownLoc();
333 builder.create<emit::FileOp>(loc, outputFile, [&] {
334 for (
auto instancePath : instancePaths) {
336 SmallVector<Attribute> path(llvm::reverse(instancePath));
337 os << sramIndex++ <<
" -> ";
338 addSymbol(dutSymbol);
341 auto nlaSymbol = cache.getRefFor(builder.getArrayAttr(path));
342 addSymbol(nlaSymbol);
343 NamedAttrList fields;
346 auto id = DistinctAttr::create(
UnitAttr::get(builder.getContext()));
347 fields.append(
"id",
id);
348 fields.append(
"class", builder.getStringAttr(
"circt.tracker"));
349 fields.append(
"circt.nonlocal", nlaSymbol);
351 auto *leafInstance = innerRefToInstanceMap[instancePath.front()];
354 annos.addAnnotations(builder.getDictionaryAttr(fields));
355 annos.applyToOperation(leafInstance);
359 builder.create<sv::VerbatimOp>(loc, buffer, ValueRange{},
360 builder.getArrayAttr(params));
362 anythingChanged =
true;
365 void AddSeqMemPortsPass::runOnOperation() {
366 auto *context = &getContext();
367 auto circuit = getOperation();
368 instanceGraph = &getAnalysis<InstanceGraph>();
374 anythingChanged =
false;
377 if (failed(processAnnos(circuit)))
378 return signalPassFailure();
382 std::reverse(userPorts.begin(), userPorts.end());
384 auto *dutNode = findDUT();
387 SmallVector<Attribute> extraPorts;
389 for (
auto &userPort : userPorts) {
390 SmallVector<NamedAttribute, 3> attrs;
395 context, userPort.direction ==
Direction::In ?
"input" :
"output"));
400 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
406 if (userPorts.size() > 0) {
408 numAddedPorts += userPorts.size();
411 for (
auto *node : llvm::post_order(dutNode)) {
412 auto op = node->getModule();
413 if (
auto module = dyn_cast<FModuleOp>(*op)) {
414 if (failed(processModule(module, node == dutNode)))
415 return signalPassFailure();
416 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
417 processMemModule(mem);
422 if (
auto dut = dyn_cast<FModuleOp>(*dutNode->getModule())) {
425 for (
auto *instRec : dutNode->uses()) {
426 auto inst = cast<InstanceOp>(*instRec->getInstance());
427 auto &dutMemInfo = memInfoMap[dut];
429 auto &subExtraPorts = dutMemInfo.extraPorts;
431 if (subExtraPorts.size() == 0)
435 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
436 inst.replaceAllUsesWith(
437 clone.getResults().drop_back(subExtraPorts.size()));
438 instanceGraph->replaceInstance(inst, clone);
443 OpBuilder builder(context);
444 builder.setInsertionPointAfter(inst);
445 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
446 auto &[firstResult, portInfo] = subExtraPorts[i];
449 auto value = inst.getResult(firstResult + i);
450 auto type = value.getType();
452 auto zero = builder.create<ConstantOp>(portInfo.loc, type, attr);
453 builder.create<MatchingConnectOp>(portInfo.loc, value, zero);
464 markAnalysesPreserved<InstanceGraph>();
466 markAllAnalysesPreserved();
470 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.
bool hasAnnotation(StringRef className) const
Return true if we have an annotation with the specified class name.
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.
This is a Node in the InstanceGraph.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
constexpr const char * metadataDirectoryAttrName
constexpr const char * dutAnnoClass
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.