13#include "../PassDetails.h"
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;
53LogicalResult ChannelBufferLowering::matchAndRewrite(
54 ChannelBufferOp buffer, OpAdaptor adaptor,
55 ConversionPatternRewriter &rewriter)
const {
56 auto loc = buffer.getLoc();
58 ChannelType inputType = buffer.getInput().getType();
59 Value stageInput = buffer.getInput();
62 if (inputType.getSignaling() == ChannelSignaling::FIFO) {
64 Backedge rdEn = bb.get(rewriter.getI1Type());
65 Backedge valid = bb.get(rewriter.getI1Type());
67 auto unwrap = UnwrapFIFOOp::create(rewriter, loc, stageInput, rdEn);
69 WrapValidReadyOp::create(rewriter, loc,
unwrap.getData(), valid);
70 stageInput =
wrap.getChanOutput();
73 rdEn.
setValue(comb::AndOp::create(rewriter, loc,
wrap.getReady(), valid));
76 rewriter, loc,
unwrap.getEmpty(),
81 if (inputType.getSignaling() == ChannelSignaling::ValidOnly) {
82 auto unwrap = UnwrapValidOnlyOp::create(rewriter, loc, stageInput);
83 auto wrap = WrapValidReadyOp::create(rewriter, loc,
unwrap.getRawOutput(),
85 stageInput =
wrap.getChanOutput();
89 auto stages = buffer.getStagesAttr();
90 uint64_t numStages = 1;
93 numStages = stages.getValue().getLimitedValue();
94 StringAttr bufferName = buffer.getNameAttr();
96 for (uint64_t i = 0; i < numStages; ++i) {
98 auto stage = PipelineStageOp::create(rewriter, loc, buffer.getClk(),
99 buffer.getRst(), stageInput);
101 SmallString<64> stageName(
102 {bufferName.getValue(),
"_stage", std::to_string(i)});
103 stage->setAttr(
"name", StringAttr::get(rewriter.getContext(), stageName));
108 ChannelType outputType = buffer.getOutput().getType();
109 Value output = stageInput;
111 if (outputType.getSignaling() == ChannelSignaling::FIFO) {
113 Backedge ready = bb.get(rewriter.getI1Type());
116 auto unwrap = UnwrapValidReadyOp::create(rewriter, loc, stageInput, ready);
117 auto wrap = WrapFIFOOp::create(rewriter, loc,
118 TypeRange{outputType, rewriter.getI1Type()},
122 empty.setValue(comb::XorOp::create(
123 rewriter, loc,
unwrap.getValid(),
125 output =
wrap.getChanOutput();
129 if (outputType.getSignaling() == ChannelSignaling::ValidOnly) {
134 UnwrapValidReadyOp::create(rewriter, loc, output, alwaysReady);
135 auto wrap = WrapValidOnlyOp::create(rewriter, loc,
unwrap.getRawOutput(),
137 output =
wrap.getChanOutput();
141 rewriter.replaceOp(buffer, output);
151 using OpConversionPattern::OpConversionPattern;
154 matchAndRewrite(FIFOOp, OpAdaptor adaptor,
155 ConversionPatternRewriter &rewriter)
const final;
160FIFOLowering::matchAndRewrite(FIFOOp op, OpAdaptor adaptor,
161 ConversionPatternRewriter &rewriter)
const {
162 auto loc = op.getLoc();
163 auto outputType = op.getType();
165 auto i1 = rewriter.getI1Type();
167 rewriter.getBoolAttr(
true));
168 mlir::TypedValue<ChannelType> chanInput = op.getInput();
169 if (chanInput.getType().getDataDelay() != 0)
170 return op.emitOpError(
171 "currently only supports input channels with zero data delay");
175 Value dataNotAvailable;
176 if (chanInput.getType().getSignaling() == ChannelSignaling::ValidReady) {
177 auto unwrapValidReady =
178 UnwrapValidReadyOp::create(rewriter, loc, chanInput, inputEn);
179 rawData = unwrapValidReady.getRawOutput();
181 comb::createOrFoldNot(rewriter, loc, unwrapValidReady.getValid(),
183 dataNotAvailable.getDefiningOp()->setAttr(
184 "sv.namehint", rewriter.getStringAttr(
"dataNotAvailable"));
185 }
else if (chanInput.getType().getSignaling() == ChannelSignaling::FIFO) {
186 auto unwrapPull = UnwrapFIFOOp::create(rewriter, loc, chanInput, inputEn);
187 rawData = unwrapPull.getData();
188 dataNotAvailable = unwrapPull.getEmpty();
189 }
else if (chanInput.getType().getSignaling() ==
190 ChannelSignaling::ValidOnly) {
191 auto unwrapVO = UnwrapValidOnlyOp::create(rewriter, loc, chanInput);
192 rawData = unwrapVO.getRawOutput();
193 dataNotAvailable = comb::createOrFoldNot(rewriter, loc, unwrapVO.getValid(),
195 dataNotAvailable.getDefiningOp()->setAttr(
196 "sv.namehint", rewriter.getStringAttr(
"dataNotAvailable"));
198 return rewriter.notifyMatchFailure(
199 op,
"only supports ValidReady, FIFO, and ValidOnly signaling");
203 auto seqFifo = seq::FIFOOp::create(
204 rewriter, loc, outputType.getInner(), i1, i1, Type(), Type(), rawData,
205 outputRdEn, inputEn, op.getClk(), op.getRst(), op.getDepthAttr(),
206 rewriter.getI64IntegerAttr(outputType.getDataDelay()), IntegerAttr(),
208 auto inputNotEmpty = comb::XorOp::create(rewriter, loc, dataNotAvailable, c1);
209 inputNotEmpty->setAttr(
"sv.namehint",
210 rewriter.getStringAttr(
"inputNotEmpty"));
211 auto seqFifoNotFull =
212 comb::XorOp::create(rewriter, loc, seqFifo.getFull(), c1);
213 seqFifoNotFull->setAttr(
"sv.namehint",
214 rewriter.getStringAttr(
"seqFifoNotFull"));
216 comb::AndOp::create(rewriter, loc, inputNotEmpty, seqFifoNotFull));
217 static_cast<Value
>(inputEn).getDefiningOp()->setAttr(
218 "sv.namehint", rewriter.getStringAttr(
"inputEn"));
221 if (outputType.getSignaling() == ChannelSignaling::ValidReady) {
222 auto wrap = WrapValidReadyOp::create(
223 rewriter, loc, mlir::TypeRange{outputType, i1}, seqFifo.getOutput(),
224 comb::createOrFoldNot(rewriter, loc, seqFifo.getEmpty(),
226 output =
wrap.getChanOutput();
228 comb::AndOp::create(rewriter, loc,
wrap.getValid(),
wrap.getReady()));
229 static_cast<Value
>(outputRdEn)
231 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"outputRdEn"));
232 }
else if (outputType.getSignaling() == ChannelSignaling::FIFO) {
234 WrapFIFOOp::create(rewriter, loc, mlir::TypeRange{outputType, i1},
235 seqFifo.getOutput(), seqFifo.getEmpty());
236 output =
wrap.getChanOutput();
238 }
else if (outputType.getSignaling() == ChannelSignaling::ValidOnly) {
241 auto notEmpty = comb::createOrFoldNot(rewriter, loc, seqFifo.getEmpty(),
244 WrapValidOnlyOp::create(rewriter, loc, seqFifo.getOutput(), notEmpty);
245 output =
wrap.getChanOutput();
249 return rewriter.notifyMatchFailure(
250 op,
"only supports ValidReady, FIFO, and ValidOnly");
253 rewriter.replaceOp(op, output);
261 using OpConversionPattern::OpConversionPattern;
264 matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
265 ConversionPatternRewriter &rewriter)
const final;
270PureModuleLowering::matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
271 ConversionPatternRewriter &rewriter)
const {
272 auto loc = pureMod.getLoc();
273 Block *body = &pureMod.getBody().front();
277 DenseMap<StringAttr, ESIPureModuleInputOp> inputPortNames;
279 SmallVector<hw::PortInfo> ports;
281 SmallVector<ESIPureModuleInputOp> inputs;
282 SmallVector<ESIPureModuleOutputOp> outputs;
283 SmallVector<Attribute> params;
285 for (Operation &op :
llvm::make_early_inc_range(body->getOperations())) {
286 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
290 auto existingPort = inputPortNames.find(port.getNameAttr());
291 if (existingPort != inputPortNames.end()) {
292 rewriter.replaceOp(port, existingPort->getSecond().getResult());
297 hw::PortInfo{{port.getNameAttr(), port.getResult().getType(),
298 hw::ModulePort::Direction::Input},
302 inputs.push_back(port);
303 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
305 hw::PortInfo{{port.getNameAttr(), port.getValue().getType(),
306 hw::ModulePort::Direction::Output},
310 outputs.push_back(port);
311 }
else if (
auto param = dyn_cast<ESIPureModuleParamOp>(op)) {
313 ParamDeclAttr::get(param.getNameAttr(), param.getType()));
314 rewriter.eraseOp(param);
320 hw::HWModuleOp::create(rewriter, loc, pureMod.getNameAttr(), ports,
321 ArrayAttr::get(getContext(), params));
322 hwMod->setDialectAttrs(pureMod->getDialectAttrs());
323 rewriter.eraseBlock(hwMod.getBodyBlock());
324 rewriter.inlineRegionBefore(*body->getParent(), hwMod.getBodyRegion(),
325 hwMod.getBodyRegion().end());
326 body = hwMod.getBodyBlock();
329 for (
auto input : inputs) {
330 BlockArgument newArg;
331 rewriter.modifyOpInPlace(hwMod, [&]() {
332 newArg = body->addArgument(input.getResult().getType(), input.getLoc());
334 rewriter.replaceOp(input, newArg);
338 SmallVector<Value> hwOutputOperands;
339 for (
auto output : outputs) {
340 hwOutputOperands.push_back(output.getValue());
341 rewriter.eraseOp(output);
343 rewriter.setInsertionPointToEnd(body);
344 hw::OutputOp::create(rewriter, pureMod.getLoc(), hwOutputOperands);
347 rewriter.eraseOp(pureMod);
353struct ESIToPhysicalPass
354 :
public circt::esi::impl::LowerESIToPhysicalBase<ESIToPhysicalPass> {
355 void runOnOperation()
override;
359void ESIToPhysicalPass::runOnOperation() {
361 ConversionTarget target(getContext());
362 target.markUnknownOpDynamicallyLegal([](Operation *) {
return true; });
363 target.addIllegalOp<ChannelBufferOp, ESIPureModuleOp, FIFOOp>();
366 RewritePatternSet
patterns(&getContext());
367 patterns.insert<ChannelBufferLowering, PureModuleLowering, FIFOLowering>(
372 applyPartialConversion(getOperation(), target, std::move(
patterns))))
376std::unique_ptr<OperationPass<ModuleOp>>
378 return std::make_unique<ESIToPhysicalPass>();
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
static InstancePath empty
Instantiate one of these and use it to build typed backedges.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
std::unique_ptr< OperationPass< ModuleOp > > createESIPhysicalLoweringPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
This holds the name, type, direction of a module's ports.