62 using ModuleMemories = SmallVector<std::pair<FirMemConfig, FirMemOp>, 0>;
63 SmallVector<ModuleMemories> memories(modules.size());
65 mlir::parallelFor(
context, 0, modules.size(), [&](
auto idx) {
68 HWModuleOp(modules[idx]).walk([&](seq::FirMemOp op) {
69 memories[idx].push_back({collectMemory(op), op});
75 for (
auto [module, moduleMemories] :
llvm::zip(modules, memories))
76 for (auto [summary, memOp] : moduleMemories)
77 grouped[summary].push_back(memOp);
99 cfg.
depth = op.getType().getDepth();
102 cfg.
maskBits = op.getType().getMaskWidth().value_or(1);
105 if (
auto init = op.getInitAttr()) {
111 if (
auto prefix = op.getPrefixAttr())
112 cfg.
prefix = prefix.getValue();
119 for (
auto *user : op->getUsers()) {
120 if (isa<FirMemReadOp>(user))
122 else if (isa<FirMemWriteOp>(user))
124 else if (isa<FirMemReadWriteOp>(user))
129 if (isa<FirMemWriteOp, FirMemReadWriteOp>(user)) {
132 clockValues.insert({clock, clockValues.size()}).first->second);
169 ArrayRef<seq::FirMemOp> memOps) {
175 for (
auto memOp : memOps) {
176 auto parent = memOp->getParentOfType<
HWModuleOp>();
182 builder.setInsertionPoint(insertPt);
190 StringRef baseName =
"";
191 bool firstFound =
false;
192 for (
auto memOp : memOps) {
193 if (
auto memName = memOp.getName()) {
200 for (; idx < memName->size() && idx < baseName.size(); ++idx)
201 if ((*memName)[idx] != baseName[idx])
203 baseName = baseName.take_front(idx);
206 baseName = baseName.rtrim(
'_');
208 SmallString<32> nameBuffer;
210 if (!baseName.empty()) {
211 nameBuffer += baseName;
219 LLVM_DEBUG(llvm::dbgs() <<
"Creating " << name <<
" for " << mem.
depth
220 <<
" x " << mem.
dataWidth <<
" memory\n");
223 SmallVector<hw::PortInfo> ports;
226 Type clkType = ClockType::get(
context);
227 Type bitType = IntegerType::get(
context, 1);
228 Type dataType = IntegerType::get(
context, std::max((
size_t)1, mem.
dataWidth));
231 IntegerType::get(
context, std::max(1U, llvm::Log2_64_Ceil(mem.
depth)));
235 auto addInput = [&](StringRef prefix,
size_t idx, StringRef suffix,
237 ports.push_back({{builder.getStringAttr(prefix + Twine(idx) + suffix), type,
238 ModulePort::Direction::Input},
243 size_t outputIdx = 0;
244 auto addOutput = [&](StringRef prefix,
size_t idx, StringRef suffix,
246 ports.push_back({{builder.getStringAttr(prefix + Twine(idx) + suffix), type,
247 ModulePort::Direction::Output},
252 auto addCommonPorts = [&](StringRef prefix,
size_t idx) {
253 addInput(prefix, idx,
"_addr", addrType);
254 addInput(prefix, idx,
"_en", bitType);
255 addInput(prefix, idx,
"_clk", clkType);
260 addCommonPorts(
"R", i);
261 addOutput(
"R", i,
"_data", dataType);
266 addCommonPorts(
"RW", i);
267 addInput(
"RW", i,
"_wmode", bitType);
268 addInput(
"RW", i,
"_wdata", dataType);
269 addOutput(
"RW", i,
"_rdata", dataType);
271 addInput(
"RW", i,
"_wmask", maskType);
276 addCommonPorts(
"W", i);
277 addInput(
"W", i,
"_data", dataType);
279 addInput(
"W", i,
"_mask", maskType);
284 auto genAttr = [&](StringRef name, Attribute attr) {
285 return builder.getNamedAttr(name, attr);
287 auto genAttrUI32 = [&](StringRef name, uint32_t value) {
288 return genAttr(name, builder.getUI32IntegerAttr(value));
290 NamedAttribute genAttrs[] = {
291 genAttr(
"depth", builder.getI64IntegerAttr(mem.
depth)),
299 genAttr(
"readUnderWrite",
301 genAttr(
"writeUnderWrite",
303 genAttr(
"writeClockIDs", builder.getI32ArrayAttr(mem.
writeClockIDs)),
304 genAttr(
"initFilename", builder.getStringAttr(mem.
initFilename)),
305 genAttr(
"initIsBinary", builder.getBoolAttr(mem.
initIsBinary)),
306 genAttr(
"initIsInline", builder.getBoolAttr(mem.
initIsInline))};
310 Location loc = FirMemOp(memOps.front()).getLoc();
311 if (memOps.size() > 1) {
312 SmallVector<Location> locs;
313 for (
auto memOp : memOps)
314 locs.push_back(memOp.getLoc());
315 loc = FusedLoc::get(
context, locs);
320 hw::HWModuleGeneratedOp::create(builder, loc, schemaSymRef, name, ports,
321 StringRef{}, ArrayAttr{}, genAttrs);
325 genOp->setAttr(
"output_file", outputFile);
334 ArrayRef<std::tuple<FirMemConfig *, HWModuleGeneratedOp, FirMemOp>> mems) {
335 LLVM_DEBUG(llvm::dbgs() <<
"Lowering " << mems.size() <<
" memories in "
336 << module.getName() <<
"\n");
338 DenseMap<unsigned, Value> constOneOps;
339 auto constOne = [&](
unsigned width = 1) {
340 auto it = constOneOps.try_emplace(width, Value{});
342 auto builder = OpBuilder::atBlockBegin(module.getBodyBlock());
344 builder, module.getLoc(), builder.getIntegerType(width), 1);
346 return it.first->second;
348 auto valueOrOne = [&](Value value,
unsigned width = 1) {
349 return value ? value : constOne(width);
352 for (
auto [config, genOp, memOp] : mems) {
353 LLVM_DEBUG(llvm::dbgs() <<
"- Lowering " << memOp.getName() <<
"\n");
354 SmallVector<Value> inputs;
355 SmallVector<Value> outputs;
357 auto addInput = [&](Value value) { inputs.push_back(value); };
358 auto addOutput = [&](Value value) { outputs.push_back(value); };
361 for (
auto *op : memOp->getUsers()) {
362 auto port = dyn_cast<FirMemReadOp>(op);
365 addInput(port.getAddress());
366 addInput(valueOrOne(port.getEnable()));
367 addInput(port.getClk());
368 addOutput(port.getData());
372 for (
auto *op : memOp->getUsers()) {
373 auto port = dyn_cast<FirMemReadWriteOp>(op);
376 addInput(port.getAddress());
377 addInput(valueOrOne(port.getEnable()));
378 addInput(port.getClk());
379 addInput(port.getMode());
380 addInput(port.getWriteData());
381 addOutput(port.getReadData());
382 if (config->maskBits > 1)
383 addInput(valueOrOne(port.getMask(), config->maskBits));
387 for (
auto *op : memOp->getUsers()) {
388 auto port = dyn_cast<FirMemWriteOp>(op);
391 addInput(port.getAddress());
392 addInput(valueOrOne(port.getEnable()));
393 addInput(port.getClk());
394 addInput(port.getData());
395 if (config->maskBits > 1)
396 addInput(valueOrOne(port.getMask(), config->maskBits));
400 StringRef memName =
"mem";
401 if (
auto name = memOp.getName(); name && !name->empty())
403 ImplicitLocOpBuilder builder(memOp.getLoc(), memOp);
404 auto instOp = hw::InstanceOp::create(
405 builder, genOp, builder.getStringAttr(memName +
"_ext"), inputs,
406 ArrayAttr{}, memOp.getInnerSymAttr());
407 for (
auto [oldOutput, newOutput] : llvm::zip(outputs, instOp.getResults()))
408 oldOutput.replaceAllUsesWith(newOutput);
411 auto defaultAttrNames = memOp.getAttributeNames();
412 for (
auto namedAttr : memOp->getAttrs())
413 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
414 instOp->setAttr(namedAttr.getName(), namedAttr.getValue());
417 for (
auto *user : llvm::make_early_inc_range(memOp->getUsers()))
static std::unique_ptr< Context > context