20#include "mlir/IR/Dominance.h"
21#include "mlir/Pass/Pass.h"
22#include "llvm/ADT/DepthFirstIterator.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/STLFunctionalExtras.h"
25#include "llvm/ADT/SmallPtrSet.h"
26#include "llvm/Support/Parallel.h"
32#define GEN_PASS_DEF_LOWERMEMORY
33#include "circt/Dialect/FIRRTL/Passes.h.inc"
42 size_t numReadPorts = 0;
43 size_t numWritePorts = 0;
44 size_t numReadWritePorts = 0;
46 SmallVector<int32_t> writeClockIDs;
48 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
49 auto portKind = op.getPortKind(i);
50 if (portKind == MemOp::PortKind::Read)
52 else if (portKind == MemOp::PortKind::Write) {
53 for (
auto *a : op.getResult(i).getUsers()) {
54 auto subfield = dyn_cast<SubfieldOp>(a);
55 if (!subfield || subfield.getFieldIndex() != 2)
57 auto clockPort = a->getResult(0);
58 for (
auto *b : clockPort.getUsers()) {
59 if (
auto connect = dyn_cast<FConnectLike>(b)) {
60 if (connect.getDest() == clockPort) {
62 clockToLeader.insert({connect.getSrc(), numWritePorts});
64 writeClockIDs.push_back(numWritePorts);
66 writeClockIDs.push_back(result.first->second);
78 auto width = op.getDataType().getBitWidthOrSentinel();
80 op.emitError(
"'firrtl.mem' should have simple type and known width");
91 *seq::symbolizeRUW(
unsigned(op.getRuw())),
102struct LowerMemoryPass
103 :
public circt::firrtl::impl::LowerMemoryBase<LowerMemoryPass> {
106 hw::InnerSymbolNamespace &getModuleNamespace(FModuleLike moduleOp) {
107 return moduleNamespaces.try_emplace(moduleOp, moduleOp).first->second;
110 SmallVector<PortInfo> getMemoryModulePorts(
const FirMemory &mem);
111 FMemModuleOp emitMemoryModule(MemOp op,
const FirMemory &summary,
112 const SmallVectorImpl<PortInfo> &ports);
113 FMemModuleOp getOrCreateMemModule(MemOp op,
const FirMemory &summary,
114 const SmallVectorImpl<PortInfo> &ports);
115 FModuleOp createWrapperModule(MemOp op,
const FirMemory &summary);
116 InstanceOp emitMemoryInstance(MemOp op, FModuleOp moduleOp,
117 const FirMemory &summary);
118 void lowerMemory(MemOp mem,
const FirMemory &summary);
119 LogicalResult runOnModule(FModuleOp moduleOp);
120 void runOnOperation()
override;
123 DenseMap<Operation *, hw::InnerSymbolNamespace> moduleNamespaces;
124 CircuitNamespace circuitNamespace;
125 SymbolTable *symbolTable;
129 std::map<FirMemory, FMemModuleOp> memories;
132 SetVector<Operation *> operationsToErase;
137LowerMemoryPass::getMemoryModulePorts(
const FirMemory &mem) {
142 FIRRTLType u1Type = UIntType::get(
context, 1);
145 FIRRTLType addrType =
146 UIntType::get(
context, std::max(1U, llvm::Log2_64_Ceil(mem.
depth)));
147 FIRRTLType clockType = ClockType::get(
context);
148 Location loc = UnknownLoc::get(
context);
149 AnnotationSet annotations = AnnotationSet(
context);
151 SmallVector<PortInfo> ports;
152 auto addPort = [&](
const Twine &name, FIRRTLType type,
Direction direction) {
153 auto nameAttr = StringAttr::get(
context, name);
155 {nameAttr, type, direction, hw::InnerSymAttr{}, loc, annotations, {}});
158 auto makePortCommon = [&](StringRef prefix,
size_t idx, FIRRTLType addrType) {
159 addPort(prefix + Twine(idx) +
"_addr", addrType, Direction::In);
160 addPort(prefix + Twine(idx) +
"_en", u1Type, Direction::In);
161 addPort(prefix + Twine(idx) +
"_clk", clockType, Direction::In);
165 makePortCommon(
"R", i, addrType);
166 addPort(
"R" + Twine(i) +
"_data", dataType, Direction::Out);
169 makePortCommon(
"RW", i, addrType);
170 addPort(
"RW" + Twine(i) +
"_wmode", u1Type, Direction::In);
171 addPort(
"RW" + Twine(i) +
"_wdata", dataType, Direction::In);
172 addPort(
"RW" + Twine(i) +
"_rdata", dataType, Direction::Out);
175 addPort(
"RW" + Twine(i) +
"_wmask", maskType, Direction::In);
179 makePortCommon(
"W", i, addrType);
180 addPort(
"W" + Twine(i) +
"_data", dataType, Direction::In);
183 addPort(
"W" + Twine(i) +
"_mask", maskType, Direction::In);
190LowerMemoryPass::emitMemoryModule(MemOp op,
const FirMemory &mem,
191 const SmallVectorImpl<PortInfo> &ports) {
193 StringRef prefix =
"";
195 prefix = mem.
prefix.getValue();
198 auto moduleName = StringAttr::get(&getContext(), newName);
201 OpBuilder b(op->getParentOfType<FModuleOp>());
202 ++numCreatedMemModules;
203 auto moduleOp = FMemModuleOp::create(
207 *symbolizeRUWBehavior(
static_cast<uint32_t
>(mem.
readUnderWrite)));
208 SymbolTable::setSymbolVisibility(moduleOp, SymbolTable::Visibility::Private);
213LowerMemoryPass::getOrCreateMemModule(MemOp op,
const FirMemory &summary,
214 const SmallVectorImpl<PortInfo> &ports) {
216 auto it = memories.find(summary);
217 if (it != memories.end())
222 auto moduleOp = emitMemoryModule(op, summary, ports);
225 memories[summary] = moduleOp;
230void LowerMemoryPass::lowerMemory(MemOp mem,
const FirMemory &summary) {
232 auto ports = getMemoryModulePorts(summary);
235 StringRef prefix =
"";
237 prefix = summary.
prefix.getValue();
238 auto newName = circuitNamespace.
newName(prefix + mem.getName());
240 auto wrapperName = StringAttr::get(&getContext(), newName);
243 OpBuilder b(mem->getParentOfType<FModuleOp>());
244 auto wrapper = FModuleOp::create(
245 b, mem->getLoc(), wrapperName,
246 ConventionAttr::get(
context, Convention::Internal), ports);
247 SymbolTable::setSymbolVisibility(wrapper, SymbolTable::Visibility::Private);
251 auto memModule = getOrCreateMemModule(mem, summary, ports);
252 b.setInsertionPointToStart(wrapper.getBodyBlock());
253 auto memInst = InstanceOp::create(
254 b, mem->getLoc(), memModule, (mem.getName() +
"_ext").str(),
255 mem.getNameKind(), mem.getAnnotations().getValue());
258 for (
auto [dst, src] : llvm::zip(wrapper.getBodyBlock()->getArguments(),
259 memInst.getResults())) {
260 if (wrapper.getPortDirection(dst.getArgNumber()) == Direction::Out)
261 MatchingConnectOp::create(b, mem->getLoc(), dst, src);
263 MatchingConnectOp::create(b, mem->getLoc(), src, dst);
268 auto inst = emitMemoryInstance(mem, wrapper, summary);
274 auto leafSym = memModule.getModuleNameAttr();
275 auto leafAttr = FlatSymbolRefAttr::get(wrapper.getModuleNameAttr());
278 llvm::SmallDenseMap<StringAttr, StringAttr> processedNLAs;
279 auto nonlocalAttr = StringAttr::get(
context,
"circt.nonlocal");
280 bool nlaUpdated =
false;
281 SmallVector<Annotation> newMemModAnnos;
286 auto nlaSym = anno.
getMember<FlatSymbolRefAttr>(nonlocalAttr);
290 auto newNLAIter = processedNLAs.find(nlaSym.getAttr());
291 StringAttr newNLAName;
292 if (newNLAIter == processedNLAs.end()) {
296 dyn_cast<hw::HierPathOp>(symbolTable->lookup(nlaSym.getAttr()));
297 auto namepath = nla.getNamepath().getValue();
298 SmallVector<Attribute> newNamepath(namepath.begin(), namepath.end());
299 if (!nla.isComponent())
301 getInnerRefTo(inst, [&](
auto mod) -> hw::InnerSymbolNamespace & {
302 return getModuleNamespace(mod);
304 newNamepath.push_back(leafAttr);
306 nlaBuilder.setInsertionPointAfter(nla);
307 auto newNLA = cast<hw::HierPathOp>(nlaBuilder.clone(*nla));
308 newNLA.setSymNameAttr(StringAttr::get(
309 context, circuitNamespace.
newName(nla.getNameAttr().getValue())));
310 newNLA.setNamepathAttr(ArrayAttr::get(
context, newNamepath));
311 newNLAName = newNLA.getNameAttr();
312 processedNLAs[nlaSym.getAttr()] = newNLAName;
314 newNLAName = newNLAIter->getSecond();
315 anno.
setMember(
"circt.nonlocal", FlatSymbolRefAttr::get(newNLAName));
317 newMemModAnnos.push_back(anno);
321 memInst.setInnerSymAttr(hw::InnerSymAttr::get(leafSym));
322 AnnotationSet newAnnos(memInst);
323 newAnnos.addAnnotations(newMemModAnnos);
324 newAnnos.applyToOperation(memInst);
326 operationsToErase.insert(mem);
332 SmallVector<SubfieldOp> accesses;
333 for (
auto *op : structValue.getUsers()) {
334 assert(isa<SubfieldOp>(op));
335 auto fieldAccess = cast<SubfieldOp>(op);
337 fieldAccess.getInput().getType().base().getElementIndex(field);
338 if (elemIndex && *elemIndex == fieldAccess.getFieldIndex())
339 accesses.push_back(fieldAccess);
344InstanceOp LowerMemoryPass::emitMemoryInstance(MemOp op, FModuleOp module,
345 const FirMemory &summary) {
346 OpBuilder builder(op);
348 auto memName = op.getName();
353 SmallVector<Type, 8> portTypes;
354 SmallVector<Direction> portDirections;
355 SmallVector<Attribute> portNames;
356 SmallVector<Attribute> domainInfo;
357 DenseMap<Operation *, size_t> returnHolder;
358 mlir::DominanceInfo domInfo(op->getParentOfType<FModuleOp>());
363 for (
unsigned memportKindIdx = 0; memportKindIdx != 3; ++memportKindIdx) {
364 MemOp::PortKind memportKind = MemOp::PortKind::Read;
365 auto *portLabel =
"R";
366 switch (memportKindIdx) {
370 memportKind = MemOp::PortKind::ReadWrite;
374 memportKind = MemOp::PortKind::Write;
381 unsigned portNumber = 0;
384 auto getType = [&](
size_t width) {
return UIntType::get(
context, width); };
385 auto ui1Type = getType(1);
386 auto addressType = getType(std::max(1U, llvm::Log2_64_Ceil(summary.
depth)));
388 auto clockType = ClockType::get(
context);
392 for (
size_t i = 0, e = op.getNumResults(); i != e; ++i) {
394 if (memportKind != op.getPortKind(i))
397 auto addPort = [&](
Direction direction, StringRef field, Type portType) {
400 for (
auto a : accesses)
401 returnHolder[a] = portTypes.size();
403 portTypes.push_back(portType);
404 portDirections.push_back(direction);
406 builder.getStringAttr(portLabel + Twine(portNumber) +
"_" + field));
407 domainInfo.push_back(builder.getArrayAttr({}));
410 auto getDriver = [&](StringRef field) -> Operation * {
412 for (
auto a : accesses) {
413 for (
auto *user : a->getUsers()) {
415 if (
auto connect = dyn_cast<FConnectLike>(user);
426 auto removeMask = [&](StringRef enable, StringRef
mask) {
428 auto *maskConnect = getDriver(
mask);
432 auto *enConnect = getDriver(enable);
437 OpBuilder b(maskConnect);
438 if (domInfo.dominates(maskConnect, enConnect))
439 b.setInsertionPoint(enConnect);
442 AndPrimOp::create(b, op->getLoc(), maskConnect->getOperand(1),
443 enConnect->getOperand(1));
444 enConnect->setOperand(1, andOp);
445 enConnect->moveAfter(andOp);
447 auto *maskField = maskConnect->getOperand(0).getDefiningOp();
448 operationsToErase.insert(maskConnect);
449 operationsToErase.insert(maskField);
452 if (memportKind == MemOp::PortKind::Read) {
453 addPort(Direction::In,
"addr", addressType);
454 addPort(Direction::In,
"en", ui1Type);
455 addPort(Direction::In,
"clk", clockType);
456 addPort(Direction::Out,
"data", dataType);
457 }
else if (memportKind == MemOp::PortKind::ReadWrite) {
458 addPort(Direction::In,
"addr", addressType);
459 addPort(Direction::In,
"en", ui1Type);
460 addPort(Direction::In,
"clk", clockType);
461 addPort(Direction::In,
"wmode", ui1Type);
462 addPort(Direction::In,
"wdata", dataType);
463 addPort(Direction::Out,
"rdata", dataType);
466 addPort(Direction::In,
"wmask", getType(summary.
maskBits));
468 removeMask(
"wmode",
"wmask");
470 addPort(Direction::In,
"addr", addressType);
471 addPort(Direction::In,
"en", ui1Type);
472 addPort(Direction::In,
"clk", clockType);
473 addPort(Direction::In,
"data", dataType);
476 addPort(Direction::In,
"mask", getType(summary.
maskBits));
478 removeMask(
"en",
"mask");
488 auto inst = InstanceOp::create(
489 builder, op.getLoc(), portTypes, module.getNameAttr(),
492 ArrayRef<Attribute>(),
493 ArrayRef<Attribute>(),
494 ArrayRef<Attribute>(),
false,
495 false, op.getInnerSymAttr());
498 for (
auto [subfield, result] : returnHolder) {
499 subfield->getResult(0).replaceAllUsesWith(inst.getResult(result));
500 operationsToErase.insert(subfield);
506LogicalResult LowerMemoryPass::runOnModule(FModuleOp moduleOp) {
507 assert(operationsToErase.empty() &&
"operationsToErase must be empty");
509 auto result = moduleOp.walk([&](MemOp op) {
512 op->emitError(
"memories should be flattened before running LowerMemory");
513 return WalkResult::interrupt();
518 lowerMemory(op, summary);
520 return WalkResult::advance();
523 if (result.wasInterrupted())
526 for (Operation *op : operationsToErase)
529 operationsToErase.clear();
534void LowerMemoryPass::runOnOperation() {
535 auto circuit = getOperation();
536 symbolTable = &getAnalysis<SymbolTable>();
537 circuitNamespace.
add(circuit);
544 for (
auto moduleOp : circuit.getBodyBlock()->getOps<FModuleOp>()) {
545 if (failed(runOnModule(moduleOp)))
546 return signalPassFailure();
549 circuitNamespace.
clear();
550 symbolTable =
nullptr;
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
FirMemory getSummary(MemOp op)
static SmallVector< SubfieldOp > getAllFieldAccesses(Value structValue, StringRef field)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
void clear()
Empty the namespace.
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
bool removeAnnotations(llvm::function_ref< bool(Annotation)> predicate)
Remove all annotations from this annotation set for which predicate returns true.
AttrClass getMember(StringAttr name) const
Return a member of the annotation.
void setMember(StringAttr name, Attribute value)
Add or set a member of the annotation to a value.
connect(destination, source)
Direction
This represents the direction of a single port.
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.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void add(CircuitOp circuit)
Populate the namespace from a circuit operation.
bool isSeqMem() const
Check whether the memory is a seq mem.
StringAttr getFirMemoryName() const