22 #include "llvm/ADT/PostOrderIterator.h"
23 #include "llvm/ADT/STLFunctionalExtras.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/Parallel.h"
27 using namespace circt;
28 using namespace firrtl;
31 struct AddSeqMemPortsPass :
public AddSeqMemPortsBase<AddSeqMemPortsPass> {
32 void runOnOperation()
override;
33 LogicalResult processAddPortAnno(Location loc,
Annotation anno);
34 LogicalResult processFileAnno(Location loc, StringRef metadataDir,
36 LogicalResult processAnnos(CircuitOp circuit);
39 void processMemModule(FMemModuleOp mem);
40 LogicalResult processModule(FModuleOp module,
bool isDUT);
43 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike module) {
44 return moduleNamespaces.try_emplace(module, module).first->second;
51 [&](FModuleLike mod) -> hw::InnerSymbolNamespace & {
52 return getModuleNamespace(mod);
60 std::vector<std::pair<unsigned, PortInfo>> extraPorts;
65 std::vector<SmallVector<Attribute>> instancePaths;
68 DenseMap<Operation *, MemoryInfo> memInfoMap;
73 hw::OutputFileAttr outputFile;
76 SmallVector<PortInfo> userPorts;
78 ArrayAttr extraPortsAttr;
81 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
87 LogicalResult AddSeqMemPortsPass::processAddPortAnno(Location loc,
89 auto name = anno.
getMember<StringAttr>(
"name");
92 loc,
"AddSeqMemPortAnnotation requires field 'name' of string type");
94 auto input = anno.
getMember<BoolAttr>(
"input");
97 loc,
"AddSeqMemPortAnnotation requires field 'input' of boolean type");
103 loc,
"AddSeqMemPortAnnotation requires field 'width' of integer type");
105 userPorts.push_back({name, type, direction});
109 LogicalResult AddSeqMemPortsPass::processFileAnno(Location loc,
110 StringRef metadataDir,
114 loc,
"circuit has two AddSeqMemPortsFileAnnotation annotations");
116 auto filename = anno.
getMember<StringAttr>(
"filename");
118 return emitError(loc,
119 "AddSeqMemPortsFileAnnotation requires field 'filename' "
122 outputFile = hw::OutputFileAttr::getFromDirectoryAndFilename(
123 &getContext(), metadataDir, filename.getValue(),
128 LogicalResult AddSeqMemPortsPass::processAnnos(CircuitOp circuit) {
129 auto loc = circuit.getLoc();
134 StringRef metadataDir =
"metadata";
136 auto dir = dirAnno.
getMember<StringAttr>(
"dirname");
138 return emitError(loc,
"MetadataDirAnnotation requires field 'dirname' of "
140 metadataDir = dir.getValue();
149 error = failed(processAddPortAnno(loc, anno));
153 error = failed(processFileAnno(loc, metadataDir, anno));
158 return failure(error);
163 for (
auto *node : *instanceGraph) {
167 return instanceGraph->getTopLevelNode();
170 void AddSeqMemPortsPass::processMemModule(FMemModuleOp mem) {
172 size_t portIndex = mem.getNumPorts();
173 auto &memInfo = memInfoMap[mem];
174 auto &extraPorts = memInfo.extraPorts;
175 for (
auto &p : userPorts)
176 extraPorts.emplace_back(portIndex, p);
177 mem.insertPorts(extraPorts);
179 mem.setExtraPortsAttr(extraPortsAttr);
182 LogicalResult AddSeqMemPortsPass::processModule(FModuleOp module,
bool isDUT) {
183 auto *context = &getContext();
185 auto builder = OpBuilder::atBlockEnd(module.getBodyBlock());
186 auto &memInfo = memInfoMap[module];
187 auto &extraPorts = memInfo.extraPorts;
190 SmallVector<Value> values;
193 unsigned firstPortIndex = module.getNumPorts();
195 for (
auto &op : llvm::make_early_inc_range(*module.getBodyBlock())) {
196 if (
auto inst = dyn_cast<InstanceOp>(op)) {
197 auto submodule = instanceGraph->getReferencedModule(inst);
199 auto subMemInfoIt = memInfoMap.find(submodule);
201 if (subMemInfoIt == memInfoMap.end() ||
202 subMemInfoIt->second.extraPorts.empty())
204 auto &subMemInfo = subMemInfoIt->second;
206 auto &subExtraPorts = subMemInfo.extraPorts;
209 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
210 inst.replaceAllUsesWith(
211 clone.getResults().drop_back(subExtraPorts.size()));
212 instanceGraph->replaceInstance(inst, clone);
217 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
218 auto &[firstSubIndex, portInfo] = subExtraPorts[i];
220 auto userIndex = i % userPorts.size();
221 auto &sramPort = userPorts[userIndex];
223 auto sramIndex = extraPorts.size() / userPorts.size();
226 sramPort.name.getValue());
227 auto portDirection = sramPort.direction;
228 auto portType = sramPort.type;
230 extraPorts.push_back(
232 {portName, type_cast<FIRRTLType>(portType), portDirection}});
236 extraPorts.back().second.annotations.addDontTouch();
239 values.push_back(inst.getResult(firstSubIndex + i));
247 auto &instancePaths = memInfo.instancePaths;
250 if (isa<FMemModuleOp>(submodule))
251 instancePaths.push_back({ref});
254 for (
auto subPath : subMemInfo.instancePaths) {
255 instancePaths.push_back(subPath);
256 instancePaths.back().push_back(ref);
263 module.insertPorts(extraPorts);
266 for (
unsigned i = 0, e = values.size(); i < e; ++i) {
267 auto &[firstArg, port] = extraPorts[i];
268 Value modulePort = module.getArgument(firstArg + i);
269 Value instPort = values[i];
271 std::swap(modulePort, instPort);
272 builder.create<StrictConnectOp>(port.loc, modulePort, instPort);
279 auto circuit = getOperation();
280 auto builder = OpBuilder::atBlockEnd(circuit.getBodyBlock());
284 llvm::raw_string_ostream os(buffer);
287 unsigned paramIndex = 0;
289 SmallVector<Attribute> params;
291 DenseMap<Attribute, unsigned> usedParams;
294 auto addSymbol = [&](Attribute ref) {
295 auto it = usedParams.find(ref);
297 if (it != usedParams.end()) {
301 usedParams[ref] = paramIndex++;
302 params.push_back(ref);
304 os <<
"{{" << index <<
"}}";
308 unsigned sramIndex = 0;
310 auto &instancePaths = memInfoMap[module].instancePaths;
311 for (
auto instancePath : instancePaths) {
312 os << sramIndex++ <<
" -> ";
313 addSymbol(dutSymbol);
315 for (
auto ref : llvm::reverse(instancePath)) {
323 auto verbatimOp =
builder.create<sv::VerbatimOp>(
324 builder.getUnknownLoc(), buffer, ValueRange{},
326 verbatimOp->setAttr(
"output_file", outputFile);
327 anythingChanged =
true;
330 void AddSeqMemPortsPass::runOnOperation() {
331 auto *context = &getContext();
332 auto circuit = getOperation();
333 instanceGraph = &getAnalysis<InstanceGraph>();
338 anythingChanged =
false;
341 if (failed(processAnnos(circuit)))
342 return signalPassFailure();
346 std::reverse(userPorts.begin(), userPorts.end());
348 auto *dutNode = findDUT();
351 SmallVector<Attribute> extraPorts;
353 for (
auto &userPort : userPorts) {
354 SmallVector<NamedAttribute, 3> attrs;
359 context, userPort.direction ==
Direction::In ?
"input" :
"output"));
364 type_cast<FIRRTLBaseType>(userPort.type).getBitWidthOrSentinel()));
370 if (userPorts.size() > 0) {
372 numAddedPorts += userPorts.size();
375 for (
auto *node : llvm::post_order(dutNode)) {
376 auto op = node->getModule();
377 if (
auto module = dyn_cast<FModuleOp>(*op)) {
378 if (failed(processModule(module, node == dutNode)))
379 return signalPassFailure();
380 }
else if (
auto mem = dyn_cast<FMemModuleOp>(*op)) {
381 processMemModule(mem);
386 if (
auto dut = dyn_cast<FModuleOp>(*dutNode->getModule())) {
389 for (
auto *instRec : dutNode->uses()) {
390 auto inst = cast<InstanceOp>(*instRec->getInstance());
391 auto &dutMemInfo = memInfoMap[dut];
393 auto &subExtraPorts = dutMemInfo.extraPorts;
395 if (subExtraPorts.size() == 0)
399 auto clone = inst.cloneAndInsertPorts(subExtraPorts);
400 inst.replaceAllUsesWith(
401 clone.getResults().drop_back(subExtraPorts.size()));
402 instanceGraph->replaceInstance(inst, clone);
408 builder.setInsertionPointAfter(inst);
409 for (
unsigned i = 0, e = subExtraPorts.size(); i < e; ++i) {
410 auto &[firstResult, portInfo] = subExtraPorts[i];
413 auto value = inst.getResult(firstResult + i);
414 auto type =
value.getType();
416 auto zero =
builder.create<ConstantOp>(portInfo.loc, type, attr);
417 builder.create<StrictConnectOp>(portInfo.loc,
value, zero);
428 markAnalysesPreserved<InstanceGraph>();
430 markAllAnalysesPreserved();
434 return std::make_unique<AddSeqMemPortsPass>();
static std::unique_ptr< llvm::ToolOutputFile > createOutputFile(StringRef fileName, StringRef dirname, SharedEmitterState &emitter)
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.
This file defines an intermediate representation for circuits acting as an abstraction for constraint...