10 #include "mlir/IR/Threading.h"
11 #include "llvm/Support/Debug.h"
13 using namespace circt;
16 using llvm::MapVector;
18 #define DEBUG_TYPE "lower-seq-firmem"
21 : context(circuit.getContext()), circuit(circuit) {
37 using ModuleMemories = SmallVector<std::pair<FirMemConfig, FirMemOp>, 0>;
38 SmallVector<ModuleMemories> memories(modules.size());
40 mlir::parallelFor(
context, 0, modules.size(), [&](
auto idx) {
43 HWModuleOp(modules[idx]).walk([&](seq::FirMemOp op) {
44 memories[idx].push_back({collectMemory(op), op});
49 MapVector<FirMemConfig, SmallVector<FirMemOp, 1>> grouped;
50 for (
auto [module, moduleMemories] : llvm::zip(modules, memories))
51 for (
auto [summary, memOp] : moduleMemories)
52 grouped[summary].push_back(memOp);
60 if (
auto wireOp = value.getDefiningOp<WireOp>()) {
61 value = wireOp.getInput();
74 cfg.
depth = op.getType().getDepth();
77 cfg.
maskBits = op.getType().getMaskWidth().value_or(1);
80 if (
auto init = op.getInitAttr()) {
86 if (
auto prefix = op.getPrefixAttr())
87 cfg.
prefix = prefix.getValue();
94 for (
auto *user : op->getUsers()) {
95 if (isa<FirMemReadOp>(user))
97 else if (isa<FirMemWriteOp>(user))
99 else if (isa<FirMemReadWriteOp>(user))
104 if (isa<FirMemWriteOp, FirMemReadWriteOp>(user)) {
107 clockValues.insert({clock, clockValues.size()}).first->second);
117 for (
auto op :
circuit.getOps<hw::HWGeneratorSchemaOp>()) {
118 if (op.getDescriptor() ==
"FIRRTL_Memory") {
125 std::array<StringRef, 14> schemaFields = {
126 "depth",
"numReadPorts",
127 "numWritePorts",
"numReadWritePorts",
128 "readLatency",
"writeLatency",
130 "readUnderWrite",
"writeUnderWrite",
131 "writeClockIDs",
"initFilename",
132 "initIsBinary",
"initIsInline"};
134 circuit.getLoc(),
"FIRRTLMem",
"FIRRTL_Memory",
135 builder.getStrArrayAttr(schemaFields));
144 ArrayRef<seq::FirMemOp> memOps) {
150 for (
auto memOp : memOps) {
151 auto parent = memOp->getParentOfType<
HWModuleOp>();
157 builder.setInsertionPoint(insertPt);
165 StringRef baseName =
"";
166 bool firstFound =
false;
167 for (
auto memOp : memOps) {
168 if (
auto memName = memOp.getName()) {
175 for (; idx < memName->size() && idx < baseName.size(); ++idx)
176 if ((*memName)[idx] != baseName[idx])
178 baseName = baseName.take_front(idx);
181 baseName = baseName.rtrim(
'_');
183 SmallString<32> nameBuffer;
185 if (!baseName.empty()) {
186 nameBuffer += baseName;
194 LLVM_DEBUG(llvm::dbgs() <<
"Creating " << name <<
" for " << mem.
depth
195 <<
" x " << mem.
dataWidth <<
" memory\n");
198 SmallVector<hw::PortInfo> ports;
210 auto addInput = [&](StringRef prefix,
size_t idx, StringRef suffix,
212 ports.push_back({{
builder.getStringAttr(prefix + Twine(idx) + suffix), type,
218 size_t outputIdx = 0;
219 auto addOutput = [&](StringRef prefix,
size_t idx, StringRef suffix,
221 ports.push_back({{
builder.getStringAttr(prefix + Twine(idx) + suffix), type,
227 auto addCommonPorts = [&](StringRef prefix,
size_t idx) {
228 addInput(prefix, idx,
"_addr", addrType);
229 addInput(prefix, idx,
"_en", bitType);
230 addInput(prefix, idx,
"_clk", clkType);
235 addCommonPorts(
"R", i);
236 addOutput(
"R", i,
"_data", dataType);
241 addCommonPorts(
"RW", i);
242 addInput(
"RW", i,
"_wmode", bitType);
243 addInput(
"RW", i,
"_wdata", dataType);
244 addOutput(
"RW", i,
"_rdata", dataType);
246 addInput(
"RW", i,
"_wmask", maskType);
251 addCommonPorts(
"W", i);
252 addInput(
"W", i,
"_data", dataType);
254 addInput(
"W", i,
"_mask", maskType);
259 auto genAttr = [&](StringRef name, Attribute attr) {
260 return builder.getNamedAttr(name, attr);
262 auto genAttrUI32 = [&](StringRef name, uint32_t value) {
263 return genAttr(name,
builder.getUI32IntegerAttr(value));
265 NamedAttribute genAttrs[] = {
274 genAttr(
"readUnderWrite",
276 genAttr(
"writeUnderWrite",
285 Location loc = FirMemOp(memOps.front()).getLoc();
286 if (memOps.size() > 1) {
287 SmallVector<Location> locs;
288 for (
auto memOp : memOps)
289 locs.push_back(memOp.getLoc());
294 auto genOp =
builder.create<hw::HWModuleGeneratedOp>(
295 loc, schemaSymRef, name, ports, StringRef{}, ArrayAttr{}, genAttrs);
297 genOp->setAttr(
"output_file", mem.
outputFile);
306 ArrayRef<std::tuple<FirMemConfig *, HWModuleGeneratedOp, FirMemOp>> mems) {
307 LLVM_DEBUG(llvm::dbgs() <<
"Lowering " << mems.size() <<
" memories in "
308 << module.getName() <<
"\n");
310 DenseMap<unsigned, Value> constOneOps;
311 auto constOne = [&](
unsigned width = 1) {
312 auto it = constOneOps.try_emplace(
width, Value{});
314 auto builder = OpBuilder::atBlockBegin(module.getBodyBlock());
318 return it.first->second;
320 auto valueOrOne = [&](Value value,
unsigned width = 1) {
321 return value ? value : constOne(
width);
324 for (
auto [config, genOp, memOp] : mems) {
325 LLVM_DEBUG(llvm::dbgs() <<
"- Lowering " << memOp.getName() <<
"\n");
326 SmallVector<Value>
inputs;
329 auto addInput = [&](Value value) {
inputs.push_back(value); };
330 auto addOutput = [&](Value value) {
outputs.push_back(value); };
333 for (
auto *op : memOp->getUsers()) {
334 auto port = dyn_cast<FirMemReadOp>(op);
337 addInput(port.getAddress());
338 addInput(valueOrOne(port.getEnable()));
339 addInput(port.getClk());
340 addOutput(port.getData());
344 for (
auto *op : memOp->getUsers()) {
345 auto port = dyn_cast<FirMemReadWriteOp>(op);
348 addInput(port.getAddress());
349 addInput(valueOrOne(port.getEnable()));
350 addInput(port.getClk());
351 addInput(port.getMode());
352 addInput(port.getWriteData());
353 addOutput(port.getReadData());
354 if (config->maskBits > 1)
355 addInput(valueOrOne(port.getMask(), config->maskBits));
359 for (
auto *op : memOp->getUsers()) {
360 auto port = dyn_cast<FirMemWriteOp>(op);
363 addInput(port.getAddress());
364 addInput(valueOrOne(port.getEnable()));
365 addInput(port.getClk());
366 addInput(port.getData());
367 if (config->maskBits > 1)
368 addInput(valueOrOne(port.getMask(), config->maskBits));
372 StringRef memName =
"mem";
373 if (
auto name = memOp.getName(); name && !name->empty())
375 ImplicitLocOpBuilder
builder(memOp.getLoc(), memOp);
376 auto instOp =
builder.create<hw::InstanceOp>(
377 genOp,
builder.getStringAttr(memName +
"_ext"),
inputs, ArrayAttr{},
378 memOp.getInnerSymAttr());
379 for (
auto [oldOutput, newOutput] : llvm::zip(
outputs, instOp.getResults()))
380 oldOutput.replaceAllUsesWith(newOutput);
383 auto defaultAttrNames = memOp.getAttributeNames();
384 for (
auto namedAttr : memOp->getAttrs())
385 if (!llvm::is_contained(defaultAttrNames, namedAttr.getName()))
386 instOp->setAttr(namedAttr.getName(), namedAttr.getValue());
389 for (
auto *user : llvm::make_early_inc_range(memOp->getUsers()))
static Value lookThroughWires(Value value)
Trace a value through wires to its original definition.
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
llvm::SmallVector< StringAttr > inputs
llvm::SmallVector< StringAttr > outputs
UniqueConfigs collectMemories(ArrayRef< hw::HWModuleOp > modules)
Groups memories by their kind from the whole design.
void lowerMemoriesInModule(hw::HWModuleOp module, ArrayRef< MemoryConfig > mems)
Lowers a group of memories from the same module.
hw::HWGeneratorSchemaOp schemaOp
FirMemLowering(ModuleOp circuit)
hw::HWModuleGeneratedOp createMemoryModule(FirMemConfig &mem, ArrayRef< seq::FirMemOp > memOps)
Creates the generated module for a given configuration.
FlatSymbolRefAttr getOrCreateSchema()
Find the schema or create it if it does not exist.
DenseMap< hw::HWModuleOp, size_t > moduleIndex
llvm::MapVector< FirMemConfig, SmallVector< seq::FirMemOp, 1 > > UniqueConfigs
A vector of unique FirMemConfigs and all the FirMemOps that use it.
Namespace globalNamespace
void add(SymbolCache &symCache)
SymbolCache initializer; initialize from every key that is convertible to a StringAttr in the SymbolC...
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
The configuration of a FIR memory.
SmallVector< int32_t, 1 > writeClockIDs