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 auto stages = buffer.getStagesAttr();
82 uint64_t numStages = 1;
85 numStages = stages.getValue().getLimitedValue();
86 StringAttr bufferName = buffer.getNameAttr();
88 for (uint64_t i = 0; i < numStages; ++i) {
90 auto stage = PipelineStageOp::create(rewriter, loc, buffer.getClk(),
91 buffer.getRst(), stageInput);
93 SmallString<64> stageName(
94 {bufferName.getValue(),
"_stage", std::to_string(i)});
95 stage->setAttr(
"name", StringAttr::get(rewriter.getContext(), stageName));
100 ChannelType outputType = buffer.getOutput().getType();
101 Value output = stageInput;
103 if (outputType.getSignaling() == ChannelSignaling::FIFO) {
105 Backedge ready = bb.get(rewriter.getI1Type());
108 auto unwrap = UnwrapValidReadyOp::create(rewriter, loc, stageInput, ready);
109 auto wrap = WrapFIFOOp::create(rewriter, loc,
110 TypeRange{outputType, rewriter.getI1Type()},
114 empty.setValue(comb::XorOp::create(
115 rewriter, loc,
unwrap.getValid(),
117 output =
wrap.getChanOutput();
121 rewriter.replaceOp(buffer, output);
131 using OpConversionPattern::OpConversionPattern;
134 matchAndRewrite(FIFOOp, OpAdaptor adaptor,
135 ConversionPatternRewriter &rewriter)
const final;
140FIFOLowering::matchAndRewrite(FIFOOp op, OpAdaptor adaptor,
141 ConversionPatternRewriter &rewriter)
const {
142 auto loc = op.getLoc();
143 auto outputType = op.getType();
145 auto i1 = rewriter.getI1Type();
147 rewriter.getBoolAttr(
true));
148 mlir::TypedValue<ChannelType> chanInput = op.getInput();
149 if (chanInput.getType().getDataDelay() != 0)
150 return op.emitOpError(
151 "currently only supports input channels with zero data delay");
155 Value dataNotAvailable;
156 if (chanInput.getType().getSignaling() == ChannelSignaling::ValidReady) {
157 auto unwrapValidReady =
158 UnwrapValidReadyOp::create(rewriter, loc, chanInput, inputEn);
159 rawData = unwrapValidReady.getRawOutput();
160 dataNotAvailable = comb::createOrFoldNot(loc, unwrapValidReady.getValid(),
162 dataNotAvailable.getDefiningOp()->setAttr(
163 "sv.namehint", rewriter.getStringAttr(
"dataNotAvailable"));
164 }
else if (chanInput.getType().getSignaling() == ChannelSignaling::FIFO) {
165 auto unwrapPull = UnwrapFIFOOp::create(rewriter, loc, chanInput, inputEn);
166 rawData = unwrapPull.getData();
167 dataNotAvailable = unwrapPull.getEmpty();
169 return rewriter.notifyMatchFailure(
170 op,
"only supports ValidReady and FIFO signaling");
174 auto seqFifo = seq::FIFOOp::create(
175 rewriter, loc, outputType.getInner(), i1, i1, Type(), Type(), rawData,
176 outputRdEn, inputEn, op.getClk(), op.getRst(), op.getDepthAttr(),
177 rewriter.getI64IntegerAttr(outputType.getDataDelay()), IntegerAttr(),
179 auto inputNotEmpty = comb::XorOp::create(rewriter, loc, dataNotAvailable, c1);
180 inputNotEmpty->setAttr(
"sv.namehint",
181 rewriter.getStringAttr(
"inputNotEmpty"));
182 auto seqFifoNotFull =
183 comb::XorOp::create(rewriter, loc, seqFifo.getFull(), c1);
184 seqFifoNotFull->setAttr(
"sv.namehint",
185 rewriter.getStringAttr(
"seqFifoNotFull"));
187 comb::AndOp::create(rewriter, loc, inputNotEmpty, seqFifoNotFull));
188 static_cast<Value
>(inputEn).getDefiningOp()->setAttr(
189 "sv.namehint", rewriter.getStringAttr(
"inputEn"));
192 if (outputType.getSignaling() == ChannelSignaling::ValidReady) {
193 auto wrap = WrapValidReadyOp::create(
194 rewriter, loc, mlir::TypeRange{outputType, i1}, seqFifo.getOutput(),
195 comb::createOrFoldNot(loc, seqFifo.getEmpty(), rewriter,
197 output =
wrap.getChanOutput();
199 comb::AndOp::create(rewriter, loc,
wrap.getValid(),
wrap.getReady()));
200 static_cast<Value
>(outputRdEn)
202 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"outputRdEn"));
203 }
else if (outputType.getSignaling() == ChannelSignaling::FIFO) {
205 WrapFIFOOp::create(rewriter, loc, mlir::TypeRange{outputType, i1},
206 seqFifo.getOutput(), seqFifo.getEmpty());
207 output =
wrap.getChanOutput();
210 return rewriter.notifyMatchFailure(op,
"only supports ValidReady and FIFO");
213 rewriter.replaceOp(op, output);
221 using OpConversionPattern::OpConversionPattern;
224 matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
225 ConversionPatternRewriter &rewriter)
const final;
230PureModuleLowering::matchAndRewrite(ESIPureModuleOp pureMod, OpAdaptor adaptor,
231 ConversionPatternRewriter &rewriter)
const {
232 auto loc = pureMod.getLoc();
233 Block *body = &pureMod.getBody().front();
237 DenseMap<StringAttr, ESIPureModuleInputOp> inputPortNames;
239 SmallVector<hw::PortInfo> ports;
241 SmallVector<ESIPureModuleInputOp> inputs;
242 SmallVector<ESIPureModuleOutputOp> outputs;
243 SmallVector<Attribute> params;
245 for (Operation &op :
llvm::make_early_inc_range(body->getOperations())) {
246 if (
auto port = dyn_cast<ESIPureModuleInputOp>(op)) {
250 auto existingPort = inputPortNames.find(port.getNameAttr());
251 if (existingPort != inputPortNames.end()) {
252 rewriter.replaceAllUsesWith(port.getResult(),
253 existingPort->getSecond().getResult());
254 rewriter.eraseOp(port);
259 hw::PortInfo{{port.getNameAttr(), port.getResult().getType(),
260 hw::ModulePort::Direction::Input},
264 inputs.push_back(port);
265 }
else if (
auto port = dyn_cast<ESIPureModuleOutputOp>(op)) {
267 hw::PortInfo{{port.getNameAttr(), port.getValue().getType(),
268 hw::ModulePort::Direction::Output},
272 outputs.push_back(port);
273 }
else if (
auto param = dyn_cast<ESIPureModuleParamOp>(op)) {
275 ParamDeclAttr::get(param.getNameAttr(), param.getType()));
276 rewriter.eraseOp(param);
282 hw::HWModuleOp::create(rewriter, loc, pureMod.getNameAttr(), ports,
283 ArrayAttr::get(getContext(), params));
284 hwMod->setDialectAttrs(pureMod->getDialectAttrs());
285 rewriter.eraseBlock(hwMod.getBodyBlock());
286 rewriter.inlineRegionBefore(*body->getParent(), hwMod.getBodyRegion(),
287 hwMod.getBodyRegion().end());
288 body = hwMod.getBodyBlock();
291 for (
auto input : inputs) {
292 BlockArgument newArg;
293 rewriter.modifyOpInPlace(hwMod, [&]() {
294 newArg = body->addArgument(input.getResult().getType(), input.getLoc());
296 rewriter.replaceAllUsesWith(input.getResult(), newArg);
297 rewriter.eraseOp(input);
301 SmallVector<Value> hwOutputOperands;
302 for (
auto output : outputs) {
303 hwOutputOperands.push_back(output.getValue());
304 rewriter.eraseOp(output);
306 rewriter.setInsertionPointToEnd(body);
307 hw::OutputOp::create(rewriter, pureMod.getLoc(), hwOutputOperands);
310 rewriter.eraseOp(pureMod);
316struct ESIToPhysicalPass
317 :
public circt::esi::impl::LowerESIToPhysicalBase<ESIToPhysicalPass> {
318 void runOnOperation()
override;
322void ESIToPhysicalPass::runOnOperation() {
324 ConversionTarget target(getContext());
325 target.markUnknownOpDynamicallyLegal([](Operation *) {
return true; });
326 target.addIllegalOp<ChannelBufferOp, ESIPureModuleOp, FIFOOp>();
329 RewritePatternSet
patterns(&getContext());
330 patterns.insert<ChannelBufferLowering, PureModuleLowering, FIFOLowering>(
335 applyPartialConversion(getOperation(), target, std::move(
patterns))))
339std::unique_ptr<OperationPass<ModuleOp>>
341 return std::make_unique<ESIToPhysicalPass>();
AIGLongestPathObject wrap(llvm::PointerUnion< Object *, DataflowPath::OutputPort * > object)
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.