23 #include "llvm/ADT/PostOrderIterator.h"
24 #include "llvm/ADT/STLFunctionalExtras.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/Parallel.h"
27 #include "llvm/Support/Path.h"
29 using namespace circt;
30 using namespace firrtl;
33 struct AddSeqMemPortsPass :
public AddSeqMemPortsBase<AddSeqMemPortsPass> {
34 void runOnOperation()
override;
35 LogicalResult processAddPortAnno(Location loc,
Annotation anno);
36 LogicalResult processFileAnno(Location loc, StringRef metadataDir,
38 LogicalResult processAnnos(CircuitOp circuit);
41 void processMemModule(FMemModuleOp mem);
42 LogicalResult processModule(FModuleOp module,
bool isDUT);
45 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
46 return moduleNamespaces.try_emplace(module, module).first->second;
53 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
54 return getModuleNamespace(mod);
62 std::vector<std::pair<unsigned, PortInfo>> extraPorts;
67 std::vector<SmallVector<Attribute>> instancePaths;
70 DenseMap<Operation *, MemoryInfo> memInfoMap;
75 StringAttr outputFile;
78 SmallVector<PortInfo> userPorts;
80 ArrayAttr extraPortsAttr;
83 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
89 LogicalResult AddSeqMemPortsPass::processAddPortAnno(Location loc,
91 auto name = anno.
getMember<StringAttr>(
"name");
94 loc,
"AddSeqMemPortAnnotation requires field 'name' of string type");
96 auto input = anno.
getMember<BoolAttr>(
"input");
99 loc,
"AddSeqMemPortAnnotation requires field 'input' of boolean type");
105 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
107 userPorts.push_back({name, type, direction});
111 LogicalResult AddSeqMemPortsPass::processFileAnno(Location loc,
112 StringRef metadataDir,
116 loc,
"circuit has two AddSeqMemPortsFileAnnotation annotations");
118 auto filename = anno.
getMember<StringAttr>(
"filename");
120 return emitError(loc,
121 "AddSeqMemPortsFileAnnotation requires field 'filename' "
124 SmallString<128> outputFilePath(metadataDir);
130 LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
131 auto loc = circuit.getLoc();
136 StringRef metadataDir =
"metadata";
138 auto dir = dirAnno.
getMember<StringAttr>(
"dirname");
140 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
142 metadataDir = dir.getValue();
151 error = failed(processAddPortAnno(loc, anno));
155 error = failed(processFileAnno(loc, metadataDir, anno));
160 return failure(error);
165 for (
auto *node : *instanceGraph) {
169 return instanceGraph->getTopLevelNode();
172 void AddSeqMemPortsPass::processMemModule(FMemModuleOp mem) {
174 size_t portIndex = mem.getNumPorts();
175 auto &memInfo = memInfoMap[mem];
176 auto &extraPorts = memInfo.extraPorts;
177 for (
auto &p : userPorts)
178 extraPorts.emplace_back(portIndex, p);
179 mem.insertPorts(extraPorts);
181 mem.setExtraPortsAttr(extraPortsAttr);
184 LogicalResult AddSeqMemPortsPass::processModule(FModuleOp module,
bool isDUT) {
185 auto *context = &getContext();
187 auto builder = OpBuilder::atBlockEnd(module.getBodyBlock());
188 auto &memInfo = memInfoMap[module];
189 auto &extraPorts = memInfo.extraPorts;
192 SmallVector<Value> values;
195 unsigned firstPortIndex = module.getNumPorts();
197 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
198 if (
auto inst = dyn_cast<InstanceOp>(op)) {
199 auto submodule = inst.getReferencedModule(*instanceGraph);
201 auto subMemInfoIt = memInfoMap.find(submodule);
203 if (subMemInfoIt == memInfoMap.end() ||
204 subMemInfoIt->second.extraPorts.empty())
206 auto &subMemInfo = subMemInfoIt->second;
208 auto &subExtraPorts = subMemInfo.extraPorts;
211 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
212 inst.replaceAllUsesWith(
213 clone.getResults().drop_back(subExtraPorts.size()));
214 instanceGraph->replaceInstance(inst, clone);
219 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
220 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
222 auto userIndex = i % userPorts.size();
223 auto &sramPort = userPorts[userIndex];
225 auto sramIndex = extraPorts.size() / userPorts.size();
228 sramPort.name.getValue());
229 auto portDirection = sramPort.direction;
230 auto portType = sramPort.type;
232 extraPorts.push_back(
234 {portName, type_cast<FIRRTLType>(portType), portDirection}});
238 extraPorts.back().second.annotations.addDontTouch();
241 values.push_back(inst.getResult(firstSubIndex + i));
249 auto &instancePaths = memInfo.instancePaths;
252 if (isa<FMemModuleOp>(submodule))
253 instancePaths.push_back({ref});
256 for (
auto subPath : subMemInfo.instancePaths) {
257 instancePaths.push_back(subPath);
258 instancePaths.back().push_back(ref);
265 module.insertPorts(extraPorts);
268 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
269 auto &[firstArg, port] = extraPorts[i];
270 Value modulePort = module.getArgument(firstArg + i);
271 Value instPort = values[i];
273 std::swap(modulePort, instPort);
274 builder.create<StrictConnectOp>(port.loc, modulePort, instPort);
281 auto circuit = getOperation();
282 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
286 llvm::raw_string_ostream os(buffer);
289 unsigned paramIndex = 0;
291 SmallVector<Attribute> params;
293 DenseMap<Attribute, unsigned> usedParams;
296 auto addSymbol = [&](Attribute ref) {
297 auto it = usedParams.find(ref);
299 if (it != usedParams.end()) {
303 usedParams[ref] = paramIndex++;
304 params.push_back(ref);
306 os <<
"{{" << index <<
"}}";
310 unsigned sramIndex = 0;
312 auto &instancePaths = memInfoMap[module].instancePaths;
313 for (
auto instancePath : instancePaths) {
314 os << sramIndex++ <<
" -> ";
315 addSymbol(dutSymbol);
317 for (
auto ref : llvm::reverse(instancePath)) {
325 auto loc =
builder.getUnknownLoc();
326 builder.create<emit::FileOp>(loc, outputFile, [&] {
327 builder.create<sv::VerbatimOp>(loc, buffer, ValueRange{},
330 anythingChanged =
true;
333 void AddSeqMemPortsPass::runOnOperation() {
334 auto *context = &getContext();
335 auto circuit = getOperation();
336 instanceGraph = &getAnalysis<InstanceGraph>();
341 anythingChanged =
false;
344 if (failed(processAnnos(circuit)))
345 return signalPassFailure();
349 std::reverse(userPorts.begin(), userPorts.end());
351 auto *dutNode = findDUT();
354 SmallVector<Attribute> extraPorts;
356 for (
auto &userPort : userPorts) {
357 SmallVector<NamedAttribute, 3> attrs;
362 context, userPort.direction ==
Direction::In ?
"input" :
"output"));
367 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
373 if (userPorts.size() > 0) {
375 numAddedPorts += userPorts.size();
378 for (
auto *node : llvm::post_order(dutNode)) {
379 auto op = node->getModule();
380 if (
auto module = dyn_cast<FModuleOp>(*op)) {
381 if (failed(processModule(module, node == dutNode)))
382 return signalPassFailure();
383 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
384 processMemModule(mem);
389 if (
auto dut = dyn_cast<FModuleOp>(*dutNode->getModule())) {
392 for (
auto *instRec : dutNode->uses()) {
393 auto inst = cast<InstanceOp>(*instRec->getInstance());
394 auto &dutMemInfo = memInfoMap[dut];
396 auto &subExtraPorts = dutMemInfo.extraPorts;
398 if (subExtraPorts.size() == 0)
402 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
403 inst.replaceAllUsesWith(
404 clone.getResults().drop_back(subExtraPorts.size()));
405 instanceGraph->replaceInstance(inst, clone);
411 builder.setInsertionPointAfter(inst);
412 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
413 auto &[firstResult, portInfo] = subExtraPorts[i];
416 auto value = inst.getResult(firstResult + i);
417 auto type = value.getType();
419 auto zero =
builder.create<ConstantOp>(portInfo.loc, type, attr);
420 builder.create<StrictConnectOp>(portInfo.loc, value, zero);
431 markAnalysesPreserved<InstanceGraph>();
433 markAllAnalysesPreserved();
437 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.