22#include "mlir/Transforms/DialectConversion.h"
26#define GEN_PASS_DEF_LOWERESITOPHYSICAL
27#include "circt/Dialect/ESI/ESIPasses.h.inc"
42 using OpConversionPattern::OpConversionPattern;
45 matchAndRewrite(ChannelBufferOp buffer, OpAdaptor adaptor,
46 ConversionPatternRewriter &rewriter)
const final;
54LogicalResult ChannelBufferLowering::matchAndRewrite(
55 ChannelBufferOp buffer, OpAdaptor adaptor,
56 ConversionPatternRewriter &rewriter)
const {
57 auto loc = buffer.getLoc();
59 ChannelType inputType = buffer.getInput().getType();
60 Value stageInput = buffer.getInput();
63 ChannelType::get(rewriter.getContext(), inputType.getInner(),
64 ChannelSignaling::ValidReady, inputType.getDataDelay());
67 if (inputType.getSignaling() == ChannelSignaling::FIFO) {
68 BackedgeBuilder bb(rewriter, loc);
69 Backedge rdEn = bb.get(rewriter.getI1Type());
70 Backedge valid = bb.get(rewriter.getI1Type());
72 auto unwrap = UnwrapFIFOOp::create(rewriter, loc, stageInput, rdEn);
74 WrapValidReadyOp::create(rewriter, loc, stageType, rewriter.getI1Type(),
76 stageInput =
wrap.getChanOutput();
79 rdEn.
setValue(comb::AndOp::create(rewriter, loc,
wrap.getReady(), valid));
82 rewriter, loc,
unwrap.getEmpty(),
87 if (inputType.getSignaling() == ChannelSignaling::ValidOnly) {
88 auto unwrap = UnwrapValidOnlyOp::create(rewriter, loc, stageInput);
90 WrapValidReadyOp::create(rewriter, loc, stageType, rewriter.getI1Type(),
92 stageInput =
wrap.getChanOutput();
96 auto stages = buffer.getStagesAttr();
97 uint64_t numStages = 1;
100 numStages = stages.getValue().getLimitedValue();
101 StringAttr bufferName = buffer.getNameAttr();
103 for (uint64_t i = 0; i < numStages; ++i) {
105 auto stage = PipelineStageOp::create(rewriter, loc, buffer.getClk(),
106 buffer.getRst(), stageInput);
108 SmallString<64> stageName(
109 {bufferName.getValue(),
"_stage", std::to_string(i)});
110 stage->setAttr(
"name", StringAttr::get(rewriter.getContext(), stageName));
115 ChannelType outputType = buffer.getOutput().getType();
116 Value output = stageInput;
117 uint64_t inDelay = inputType.getDataDelay();
118 uint64_t outDelay = outputType.getDataDelay();
119 auto i1 = rewriter.getI1Type();
120 auto regName = [&](StringRef suffix, uint64_t i) -> std::string {
121 std::string name = (suffix + Twine(i)).str();
123 name = (bufferName.getValue() +
"_" + name).str();
127 if (outDelay > inDelay) {
129 BackedgeBuilder bb(rewriter, loc);
130 Backedge ready = bb.get(i1);
131 auto unwrap = UnwrapValidReadyOp::create(rewriter, loc, output, ready);
135 for (uint64_t i = 0; i < outDelay - inDelay; ++i)
137 regName(
"data_dly", i), InnerSymAttr());
138 auto wrap = WrapValidReadyOp::create(rewriter, loc, data,
unwrap.getValid(),
141 output =
wrap.getChanOutput();
142 }
else if (outDelay < inDelay &&
143 outputType.getSignaling() == ChannelSignaling::ValidOnly) {
148 UnwrapValidReadyOp::create(rewriter, loc, output, alwaysReady);
149 Value valid =
unwrap.getValid();
151 for (uint64_t i = 0; i < inDelay - outDelay; ++i)
153 buffer.getRst(), false_,
154 regName(
"valid_dly", i), InnerSymAttr());
155 output = WrapValidOnlyOp::create(rewriter, loc,
unwrap.getRawOutput(),
158 }
else if (outDelay < inDelay) {
162 uint64_t depth = 2 * inDelay + std::max<uint64_t>(outDelay, 1) + 1;
163 output = FIFOOp::create(rewriter, loc, outputType, buffer.getClk(),
164 buffer.getRst(), output,
165 rewriter.getI64IntegerAttr(depth));
169 if (output.getType() != outputType &&
170 outputType.getSignaling() == ChannelSignaling::FIFO) {
171 BackedgeBuilder bb(rewriter, loc);
172 Backedge ready = bb.get(rewriter.getI1Type());
173 Backedge
empty = bb.get(rewriter.getI1Type());
175 auto unwrap = UnwrapValidReadyOp::create(rewriter, loc, output, ready);
176 auto wrap = WrapFIFOOp::create(rewriter, loc,
177 TypeRange{outputType, rewriter.getI1Type()},
181 empty.setValue(comb::XorOp::create(
182 rewriter, loc,
unwrap.getValid(),
184 output =
wrap.getChanOutput();
188 if (output.getType() != outputType &&
189 outputType.getSignaling() == ChannelSignaling::ValidOnly) {
194 UnwrapValidReadyOp::create(rewriter, loc, output, alwaysReady);
196 WrapValidOnlyOp::create(rewriter, loc,
unwrap.getRawOutput(),
197 unwrap.getValid(), outputType.getDataDelay());
198 output =
wrap.getChanOutput();
202 rewriter.replaceOp(buffer, output);
210struct FIFOLowering :
public OpConversionPattern<FIFOOp> {
212 using OpConversionPattern::OpConversionPattern;
215 matchAndRewrite(FIFOOp, OpAdaptor adaptor,
216 ConversionPatternRewriter &rewriter)
const final;
221FIFOLowering::matchAndRewrite(FIFOOp op, OpAdaptor adaptor,
222 ConversionPatternRewriter &rewriter)
const {
223 auto loc = op.getLoc();
224 auto outputType = op.getType();
225 BackedgeBuilder bb(rewriter, loc);
226 auto i1 = rewriter.getI1Type();
228 rewriter.getBoolAttr(
true));
229 mlir::TypedValue<ChannelType> chanInput = op.getInput();
230 uint64_t inputDelay = chanInput.getType().getDataDelay();
232 Backedge inputEn = bb.get(i1);
234 Value dataNotAvailable;
235 if (chanInput.getType().getSignaling() == ChannelSignaling::ValidReady) {
236 auto unwrapValidReady =
237 UnwrapValidReadyOp::create(rewriter, loc, chanInput, inputEn);
238 rawData = unwrapValidReady.getRawOutput();
240 comb::createOrFoldNot(rewriter, loc, unwrapValidReady.getValid(),
242 dataNotAvailable.getDefiningOp()->setAttr(
243 "sv.namehint", rewriter.getStringAttr(
"dataNotAvailable"));
244 }
else if (chanInput.getType().getSignaling() == ChannelSignaling::FIFO) {
245 auto unwrapPull = UnwrapFIFOOp::create(rewriter, loc, chanInput, inputEn);
246 rawData = unwrapPull.getData();
247 dataNotAvailable = unwrapPull.getEmpty();
248 }
else if (chanInput.getType().getSignaling() ==
249 ChannelSignaling::ValidOnly) {
250 auto unwrapVO = UnwrapValidOnlyOp::create(rewriter, loc, chanInput);
251 rawData = unwrapVO.getRawOutput();
252 dataNotAvailable = comb::createOrFoldNot(rewriter, loc, unwrapVO.getValid(),
254 dataNotAvailable.getDefiningOp()->setAttr(
255 "sv.namehint", rewriter.getStringAttr(
"dataNotAvailable"));
257 return rewriter.notifyMatchFailure(
258 op,
"only supports ValidReady, FIFO, and ValidOnly signaling");
267 bool validOnlyInput =
268 chanInput.getType().getSignaling() == ChannelSignaling::ValidOnly;
269 bool reserveSlots = inputDelay > 0 && !validOnlyInput;
270 Backedge wrEn = bb.get(i1);
272 IntegerAttr almostFullThreshold;
275 almostFullThreshold =
276 rewriter.getI64IntegerAttr(op.getDepth() - inputDelay);
279 Backedge outputRdEn = bb.get(i1);
280 auto seqFifo = seq::FIFOOp::create(
281 rewriter, loc, outputType.getInner(), i1, i1, almostFullType, Type(),
282 rawData, outputRdEn, wrEn, op.getClk(), op.getRst(), op.getDepthAttr(),
283 rewriter.getI64IntegerAttr(outputType.getDataDelay()),
284 almostFullThreshold, IntegerAttr());
285 auto inputNotEmpty = comb::XorOp::create(rewriter, loc, dataNotAvailable, c1);
286 inputNotEmpty->setAttr(
"sv.namehint",
287 rewriter.getStringAttr(
"inputNotEmpty"));
288 auto seqFifoNotFull = comb::XorOp::create(
289 rewriter, loc, reserveSlots ? seqFifo.getAlmostFull() : seqFifo.getFull(),
291 seqFifoNotFull->setAttr(
"sv.namehint",
292 rewriter.getStringAttr(
"seqFifoNotFull"));
294 comb::AndOp::create(rewriter, loc, inputNotEmpty, seqFifoNotFull));
295 static_cast<Value
>(inputEn).getDefiningOp()->setAttr(
296 "sv.namehint", rewriter.getStringAttr(
"inputEn"));
298 Value
write = validOnlyInput ? Value(inputNotEmpty) : Value(inputEn);
299 if (inputDelay > 0) {
301 for (uint64_t i = 0; i < inputDelay; ++i)
303 rewriter, loc, write, op.getClk(), op.getRst(), false_,
304 (validOnlyInput ?
"valid_dly" :
"inputEn_dly") + Twine(i),
310 if (outputType.getSignaling() == ChannelSignaling::ValidReady) {
311 auto wrap = WrapValidReadyOp::create(
312 rewriter, loc, mlir::TypeRange{outputType, i1}, seqFifo.getOutput(),
313 comb::createOrFoldNot(rewriter, loc, seqFifo.getEmpty(),
315 output =
wrap.getChanOutput();
317 comb::AndOp::create(rewriter, loc,
wrap.getValid(),
wrap.getReady()));
318 static_cast<Value
>(outputRdEn)
320 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"outputRdEn"));
321 }
else if (outputType.getSignaling() == ChannelSignaling::FIFO) {
323 WrapFIFOOp::create(rewriter, loc, mlir::TypeRange{outputType, i1},
324 seqFifo.getOutput(), seqFifo.getEmpty());
325 output =
wrap.getChanOutput();
327 }
else if (outputType.getSignaling() == ChannelSignaling::ValidOnly) {
330 auto notEmpty = comb::createOrFoldNot(rewriter, loc, seqFifo.getEmpty(),
332 auto wrap = WrapValidOnlyOp::create(rewriter, loc, seqFifo.getOutput(),
333 notEmpty, outputType.getDataDelay());
334 output =
wrap.getChanOutput();
338 return rewriter.notifyMatchFailure(
339 op,
"only supports ValidReady, FIFO, and ValidOnly");
342 rewriter.replaceOp(op, output);
348struct PureModuleLowering :
public OpConversionPattern<ESIPureModuleOp> {
350 using OpConversionPattern::OpConversionPattern;
353 matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
354 ConversionPatternRewriter &rewriter)
const final;
359PureModuleLowering::matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
360 ConversionPatternRewriter &rewriter)
const {
361 auto loc = pureMod.getLoc();
362 Block *body = &pureMod.getBody().front();
366 DenseMap<StringAttr, ESIPureModuleInputOp> inputPortNames;
368 SmallVector<hw::PortInfo> ports;
370 SmallVector<ESIPureModuleInputOp> inputs;
371 SmallVector<ESIPureModuleOutputOp> outputs;
372 SmallVector<Attribute> params;
374 for (Operation &op : llvm::make_early_inc_range(body->getOperations())) {
375 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
379 auto existingPort = inputPortNames.find(port.getNameAttr());
380 if (existingPort != inputPortNames.end()) {
381 rewriter.replaceOp(port, existingPort->getSecond().getResult());
386 hw::PortInfo{{port.getNameAttr(), port.getResult().getType(),
387 hw::ModulePort::Direction::Input},
391 inputs.push_back(port);
392 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
394 hw::PortInfo{{port.getNameAttr(), port.getValue().getType(),
395 hw::ModulePort::Direction::Output},
399 outputs.push_back(port);
400 }
else if (
auto param = dyn_cast<ESIPureModuleParamOp>(op)) {
402 ParamDeclAttr::get(param.getNameAttr(), param.getType()));
403 rewriter.eraseOp(param);
409 hw::HWModuleOp::create(rewriter, loc, pureMod.getNameAttr(), ports,
410 ArrayAttr::get(getContext(), params));
411 hwMod->setDialectAttrs(pureMod->getDialectAttrs());
412 rewriter.eraseBlock(hwMod.getBodyBlock());
413 rewriter.inlineRegionBefore(*body->getParent(), hwMod.getBodyRegion(),
414 hwMod.getBodyRegion().end());
415 body = hwMod.getBodyBlock();
418 for (
auto input : inputs) {
419 BlockArgument newArg;
420 rewriter.modifyOpInPlace(hwMod, [&]() {
421 newArg = body->addArgument(input.getResult().getType(), input.getLoc());
423 rewriter.replaceOp(input, newArg);
427 SmallVector<Value> hwOutputOperands;
428 for (
auto output : outputs) {
429 hwOutputOperands.push_back(output.getValue());
430 rewriter.eraseOp(output);
432 rewriter.setInsertionPointToEnd(body);
433 hw::OutputOp::create(rewriter, pureMod.getLoc(), hwOutputOperands);
436 rewriter.eraseOp(pureMod);
442struct ESIToPhysicalPass
443 :
public circt::esi::impl::LowerESIToPhysicalBase<ESIToPhysicalPass> {
444 void runOnOperation()
override;
448void ESIToPhysicalPass::runOnOperation() {
450 ConversionTarget target(getContext());
451 target.markUnknownOpDynamicallyLegal([](Operation *) {
return true; });
452 target.addIllegalOp<ChannelBufferOp, ESIPureModuleOp, FIFOOp>();
455 RewritePatternSet patterns(&getContext());
456 patterns.insert<ChannelBufferLowering, PureModuleLowering, FIFOLowering>(
461 applyPartialConversion(getOperation(), target, std::move(patterns))))
465std::unique_ptr<OperationPass<ModuleOp>>
467 return std::make_unique<ESIToPhysicalPass>();
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
static InstancePath empty
void setValue(mlir::Value)
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
std::unique_ptr< OperationPass< ModuleOp > > createESIPhysicalLoweringPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.