13#include "../PassDetails.h"
24#include "mlir/Transforms/DialectConversion.h"
26#include "llvm/ADT/StringExtras.h"
27#include "llvm/ADT/TypeSwitch.h"
28#include "llvm/Support/JSON.h"
32#define GEN_PASS_DEF_LOWERESITOHW
33#include "circt/Dialect/ESI/ESIPasses.h.inc"
51 using OpConversionPattern::OpConversionPattern;
54 matchAndRewrite(PipelineStageOp stage, OpAdaptor adaptor,
55 ConversionPatternRewriter &rewriter)
const final;
62LogicalResult PipelineStageLowering::matchAndRewrite(
63 PipelineStageOp stage, OpAdaptor adaptor,
64 ConversionPatternRewriter &rewriter)
const {
65 auto loc = stage.getLoc();
66 auto chPort = dyn_cast<ChannelType>(stage.getInput().getType());
68 return rewriter.notifyMatchFailure(stage,
"stage had wrong type");
69 Operation *symTable = stage->getParentWithTrait<OpTrait::SymbolTable>();
70 auto stageModule = builder.declareStage(symTable, stage);
74 ArrayAttr stageParams =
75 builder.getStageParameterList(rewriter.getUI32IntegerAttr(width));
83 rewriter.create<UnwrapValidReadyOp>(loc, stage.getInput(), wrapReady);
85 StringRef pipeStageName =
"pipelineStage";
86 if (
auto name = stage->getAttrOfType<StringAttr>(
"name"))
87 pipeStageName = name.getValue();
91 llvm::SmallVector<Value> operands = {stage.getClk(), stage.getRst()};
92 operands.push_back(
unwrap.getRawOutput());
93 operands.push_back(
unwrap.getValid());
94 operands.push_back(stageReady);
95 auto stageInst = rewriter.create<hw::InstanceOp>(
96 loc, stageModule, pipeStageName, operands, stageParams);
97 auto stageInstResults = stageInst.getResults();
101 wrapReady.
setValue(stageInstResults[0]);
103 x = stageInstResults[1];
104 xValid = stageInstResults[2];
107 auto wrap = rewriter.create<WrapValidReadyOp>(
108 loc, chPort, rewriter.getI1Type(), x, xValid);
112 rewriter.replaceOp(stage,
wrap.getChanOutput());
120 using OpConversionPattern::OpConversionPattern;
123 matchAndRewrite(NullSourceOp nullop, OpAdaptor adaptor,
124 ConversionPatternRewriter &rewriter)
const final;
128LogicalResult NullSourceOpLowering::matchAndRewrite(
129 NullSourceOp nullop, OpAdaptor adaptor,
130 ConversionPatternRewriter &rewriter)
const {
131 auto innerType = cast<ChannelType>(nullop.getOut().getType()).getInner();
132 Location loc = nullop.getLoc();
133 int64_t width = hw::getBitWidth(
innerType);
135 return rewriter.notifyMatchFailure(
136 nullop,
"NullOp lowering only supports hw types");
138 rewriter.create<
hw::ConstantOp>(nullop.getLoc(), rewriter.getI1Type(), 0);
142 auto wrap = rewriter.
create<WrapValidReadyOp>(loc, typedZero, valid);
143 wrap->setAttr(
"name", rewriter.getStringAttr(
"nullsource"));
144 rewriter.replaceOp(nullop, {
wrap.getChanOutput()});
150struct RemoveWrapUnwrap :
public ConversionPattern {
152 RemoveWrapUnwrap(MLIRContext *context)
153 : ConversionPattern(MatchAnyOpTypeTag(), 1, context) {}
156 matchAndRewrite(Operation *op, ArrayRef<Value> operands,
157 ConversionPatternRewriter &rewriter)
const override {
158 Value valid, ready,
data;
159 WrapValidReadyOp
wrap = dyn_cast<WrapValidReadyOp>(op);
160 UnwrapValidReadyOp
unwrap = dyn_cast<UnwrapValidReadyOp>(op);
162 if (ChannelType::hasNoConsumers(
wrap.getChanOutput())) {
164 rewriter.getI1Type(), 1);
165 rewriter.replaceOp(
wrap, {
nullptr, c1});
169 if (!ChannelType::hasOneConsumer(
wrap.getChanOutput()))
170 return rewriter.notifyMatchFailure(
171 wrap,
"This conversion only supports wrap-unwrap back-to-back. "
172 "Wrap didn't have exactly one use.");
173 if (!(
unwrap = dyn_cast<UnwrapValidReadyOp>(
174 ChannelType::getSingleConsumer(
wrap.getChanOutput())
176 return rewriter.notifyMatchFailure(
177 wrap,
"This conversion only supports wrap-unwrap back-to-back. "
178 "Could not find 'unwrap'.");
182 ready =
unwrap.getReady();
184 Operation *defOp = operands[0].getDefiningOp();
186 return rewriter.notifyMatchFailure(
187 unwrap,
"unwrap input is not defined by an op");
188 wrap = dyn_cast<WrapValidReadyOp>(defOp);
190 return rewriter.notifyMatchFailure(
191 operands[0].getDefiningOp(),
192 "This conversion only supports wrap-unwrap back-to-back. "
193 "Could not find 'wrap'.");
194 valid =
wrap.getValid();
201 if (!ChannelType::hasOneConsumer(
wrap.getChanOutput()))
202 return rewriter.notifyMatchFailure(
wrap, [](Diagnostic &d) {
203 d <<
"This conversion only supports wrap-unwrap back-to-back. "
204 "Wrap didn't have exactly one use.";
206 rewriter.replaceOp(
wrap, {
nullptr, ready});
217 using OpConversionPattern::OpConversionPattern;
220 matchAndRewrite(SnoopValidReadyOp op, SnoopValidReadyOpAdaptor operands,
221 ConversionPatternRewriter &rewriter)
const override {
222 Operation *defOp = op.getInput().getDefiningOp();
224 return rewriter.notifyMatchFailure(op,
225 "snoop input is not defined by an op");
226 auto wrap = dyn_cast<WrapValidReadyOp>(defOp);
228 return rewriter.notifyMatchFailure(
229 defOp,
"This conversion only supports wrap-unwrap back-to-back. "
230 "Could not find 'wrap'.");
231 auto *unwrapOpOperand =
232 ChannelType::getSingleConsumer(
wrap.getChanOutput());
233 if (!unwrapOpOperand)
234 return rewriter.notifyMatchFailure(
235 defOp,
"This conversion only supports wrap-unwrap back-to-back. "
236 "Could sole consumer.");
237 auto unwrap = dyn_cast<UnwrapValidReadyOp>(unwrapOpOperand->getOwner());
239 return rewriter.notifyMatchFailure(
240 defOp,
"This conversion only supports wrap-unwrap back-to-back. "
241 "Could not find 'unwrap'.");
252template <
typename Op>
258 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
259 ConversionPatternRewriter &rewriter)
const final {
260 if (failed(Op::canonicalize(op, rewriter)))
261 return rewriter.notifyMatchFailure(op->getLoc(),
"canonicalizer failed");
268struct ESItoHWPass :
public circt::esi::impl::LowerESItoHWBase<ESItoHWPass> {
269 void runOnOperation()
override;
278 using OpConversionPattern::OpConversionPattern;
281 matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
282 ConversionPatternRewriter &rewriter)
const final;
287WrapInterfaceLower::matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
288 ConversionPatternRewriter &rewriter)
const {
289 auto operands = adaptor.getOperands();
290 if (operands.size() != 1)
291 return rewriter.notifyMatchFailure(
wrap, [&operands](Diagnostic &d) {
292 d <<
"wrap.iface has 1 argument. Got " << operands.size() <<
"operands";
294 auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
298 dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
302 auto loc =
wrap.getLoc();
303 auto validSignal = rewriter.
create<ReadInterfaceSignalOp>(
306 dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
308 auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
309 rewriter.create<AssignInterfaceSignalOp>(
311 rewriter.replaceOp(
wrap, {wrapVR.getChanOutput()});
321 using OpConversionPattern::OpConversionPattern;
324 matchAndRewrite(UnwrapSVInterfaceOp
wrap, OpAdaptor adaptor,
325 ConversionPatternRewriter &rewriter)
const final;
329LogicalResult UnwrapInterfaceLower::matchAndRewrite(
330 UnwrapSVInterfaceOp
unwrap, OpAdaptor adaptor,
331 ConversionPatternRewriter &rewriter)
const {
332 auto operands = adaptor.getOperands();
333 if (operands.size() != 2)
334 return rewriter.notifyMatchFailure(
unwrap, [&operands](Diagnostic &d) {
335 d <<
"Unwrap.iface has 2 arguments. Got " << operands.size()
339 auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
343 dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
347 auto loc =
unwrap.getLoc();
348 auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
351 rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
352 rewriter.create<AssignInterfaceSignalOp>(
355 rewriter.create<AssignInterfaceSignalOp>(
369 using OpConversionPattern::OpConversionPattern;
372 matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
373 ConversionPatternRewriter &rewriter)
const final;
380LogicalResult CosimToHostLowering::matchAndRewrite(
381 CosimToHostEndpointOp ep, OpAdaptor adaptor,
382 ConversionPatternRewriter &rewriter)
const {
383 auto loc = ep.getLoc();
384 auto *
ctxt = rewriter.getContext();
387 Value toHost = adaptor.getToHost();
388 Type type = toHost.getType();
392 SmallVector<Attribute, 8> params;
393 params.push_back(ParamDeclAttr::get(
"ENDPOINT_ID", ep.getIdAttr()));
394 params.push_back(ParamDeclAttr::get(
"TO_HOST_TYPE_ID",
getTypeID(type)));
395 params.push_back(ParamDeclAttr::get(
396 "TO_HOST_SIZE_BITS", rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
399 auto sendReady = bb.get(rewriter.getI1Type());
400 UnwrapValidReadyOp unwrapSend =
401 rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
402 Value castedSendData;
405 loc, rewriter.getIntegerType(width), unwrapSend.getRawOutput());
408 loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(
false));
411 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
412 HWModuleExternOp endpoint =
413 builder.declareCosimEndpointToHostModule(symTable);
419 unwrapSend.getValid(),
422 auto cosimEpModule = rewriter.
create<hw::InstanceOp>(
423 loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
424 sendReady.setValue(cosimEpModule.getResult(0));
427 rewriter.eraseOp(ep);
435struct CosimFromHostLowering
441 using OpConversionPattern::OpConversionPattern;
444 matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
445 ConversionPatternRewriter &rewriter)
const final;
452LogicalResult CosimFromHostLowering::matchAndRewrite(
453 CosimFromHostEndpointOp ep, OpAdaptor adaptor,
454 ConversionPatternRewriter &rewriter)
const {
455 auto loc = ep.getLoc();
456 auto *
ctxt = rewriter.getContext();
459 ChannelType type = ep.getFromHost().getType();
463 SmallVector<Attribute, 8> params;
464 params.push_back(ParamDeclAttr::get(
"ENDPOINT_ID", ep.getIdAttr()));
465 params.push_back(ParamDeclAttr::get(
"FROM_HOST_TYPE_ID",
getTypeID(type)));
467 ParamDeclAttr::get(
"FROM_HOST_SIZE_BITS",
468 rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
471 auto recvReady = bb.get(rewriter.getI1Type());
474 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
475 HWModuleExternOp endpoint =
476 builder.declareCosimEndpointFromHostModule(symTable);
479 Value operands[] = {adaptor.getClk(), adaptor.getRst(), recvReady};
480 auto cosimEpModule = rewriter.create<hw::InstanceOp>(
481 loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
484 Value recvDataFromCosim = cosimEpModule.getResult(1);
485 Value recvValidFromCosim = cosimEpModule.getResult(0);
486 Value castedRecvData;
489 rewriter.create<
hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
492 loc, rewriter.getIntegerType(0),
493 rewriter.getIntegerAttr(rewriter.getIntegerType(0), 0));
494 WrapValidReadyOp wrapRecv = rewriter.create<WrapValidReadyOp>(
495 loc, castedRecvData, recvValidFromCosim);
496 recvReady.setValue(wrapRecv.getReady());
499 rewriter.replaceOp(ep, wrapRecv.getChanOutput());
510 using OpConversionPattern::OpConversionPattern;
511 constexpr static StringRef manifestRomName =
"__ESI_Manifest_ROM";
514 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
515 ConversionPatternRewriter &rewriter)
const override;
518 LogicalResult createRomModule(CompressedManifestOp op,
519 ConversionPatternRewriter &rewriter)
const;
523LogicalResult ManifestRomLowering::createRomModule(
524 CompressedManifestOp op, ConversionPatternRewriter &rewriter)
const {
525 Location loc = op.getLoc();
526 auto mlirModBody = op->getParentOfType<mlir::ModuleOp>();
527 rewriter.setInsertionPointToStart(mlirModBody.getBody());
531 if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
532 if (!isa<hw::HWModuleExternOp>(existingExtern))
533 return rewriter.notifyMatchFailure(
535 "Found " + manifestRomName +
" but it wasn't an HWModuleExternOp");
536 rewriter.eraseOp(existingExtern);
541 {{rewriter.getStringAttr(
"clk"), rewriter.getType<seq::ClockType>(),
542 ModulePort::Direction::Input}},
543 {{rewriter.getStringAttr(
"address"), rewriter.getIntegerType(29),
544 ModulePort::Direction::Input}},
545 {{rewriter.getStringAttr(
"data"), rewriter.getI64Type(),
546 ModulePort::Direction::Output}},
548 auto rom = rewriter.create<HWModuleOp>(
549 loc, rewriter.getStringAttr(manifestRomName), ports);
550 Block *romBody = rom.getBodyBlock();
551 rewriter.setInsertionPointToStart(romBody);
552 Value
clk = romBody->getArgument(0);
553 Value inputAddress = romBody->getArgument(1);
556 ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
557 SmallVector<uint64_t> words;
558 words.push_back(maniBytes.size());
560 for (
size_t i = 0; i < maniBytes.size() - 7; i += 8) {
562 for (
size_t b = 0; b < 8; ++b)
563 word |=
static_cast<uint64_t
>(maniBytes[i + b]) << (8 * b);
564 words.push_back(word);
566 size_t overHang = maniBytes.size() % 8;
569 for (
size_t i = 0; i < overHang; ++i)
570 word |=
static_cast<uint64_t
>(maniBytes[maniBytes.size() - overHang + i])
572 words.push_back(word);
577 SmallVector<Attribute> wordAttrs;
578 for (uint64_t word : words)
579 wordAttrs.push_back(rewriter.getI64IntegerAttr(word));
580 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
581 loc, hw::UnpackedArrayType::get(rewriter.getI64Type(), words.size()),
582 rewriter.getArrayAttr(wordAttrs));
584 rewriter.create<
sv::RegOp>(loc, manifestConstant.getType());
585 rewriter.create<
sv::AssignOp>(loc, manifestReg, manifestConstant);
588 size_t addrBits = llvm::Log2_64_Ceil(words.size());
593 rewriter.
create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
596 if (
auto *term = romBody->getTerminator())
597 rewriter.eraseOp(term);
598 rewriter.
create<hw::OutputOp>(loc, ValueRange{readDataReg});
602LogicalResult ManifestRomLowering::matchAndRewrite(
603 CompressedManifestOp op, OpAdaptor adaptor,
604 ConversionPatternRewriter &rewriter)
const {
605 LogicalResult ret = createRomModule(op, rewriter);
606 rewriter.eraseOp(op);
613struct CosimManifestLowering :
public ManifestRomLowering {
615 using ManifestRomLowering::ManifestRomLowering;
618 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
619 ConversionPatternRewriter &rewriter)
const final;
623LogicalResult CosimManifestLowering::matchAndRewrite(
624 CompressedManifestOp op, OpAdaptor adaptor,
625 ConversionPatternRewriter &rewriter)
const {
626 MLIRContext *
ctxt = rewriter.getContext();
627 Location loc = op.getLoc();
631 LogicalResult ret = createRomModule(op, rewriter);
636 Attribute params[] = {
637 ParamDeclAttr::get(
"COMPRESSED_MANIFEST_SIZE", rewriter.getI32Type())};
639 {{rewriter.getStringAttr(
"compressed_manifest"),
640 rewriter.getType<hw::ArrayType>(
641 rewriter.getI8Type(),
642 ParamDeclRefAttr::get(
643 rewriter.getStringAttr(
"COMPRESSED_MANIFEST_SIZE"),
644 rewriter.getI32Type())),
645 ModulePort::Direction::Input},
648 rewriter.setInsertionPointToEnd(
649 op->getParentOfType<mlir::ModuleOp>().getBody());
650 auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
651 loc, rewriter.getStringAttr(
"Cosim_Manifest"), ports,
"Cosim_Manifest",
652 ArrayAttr::get(ctxt, params));
656 loc, rewriter.getStringAttr(
"__ESIManifest"), portInfo,
659 SmallVector<Attribute> bytes;
660 for (uint8_t b : op.getCompressedManifest().getData())
661 bytes.push_back(rewriter.getI8IntegerAttr(b));
662 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
663 loc, hw::ArrayType::get(rewriter.getI8Type(), bytes.size()),
664 rewriter.getArrayAttr(bytes));
666 rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
667 rewriter.create<
sv::AssignOp>(loc, manifestLogic, manifestConstant);
671 rewriter.
create<hw::InstanceOp>(
672 loc, cosimManifestExternModule,
"__manifest",
673 ArrayRef<Value>({manifest}),
674 rewriter.getArrayAttr({ParamDeclAttr::get(
675 "COMPRESSED_MANIFEST_SIZE",
676 rewriter.getI32IntegerAttr(bytes.size()))}));
679 rewriter.setInsertionPoint(op);
680 rewriter.create<hw::InstanceOp>(loc, manifestMod,
"__manifest",
681 ArrayRef<Value>({}));
683 rewriter.eraseOp(op);
686void ESItoHWPass::runOnOperation() {
687 auto top = getOperation();
688 auto *
ctxt = &getContext();
690 ConversionTarget noBundlesTarget(*ctxt);
691 noBundlesTarget.markUnknownOpDynamicallyLegal(
692 [](Operation *) {
return true; });
693 noBundlesTarget.addIllegalOp<PackBundleOp>();
694 noBundlesTarget.addIllegalOp<UnpackBundleOp>();
695 RewritePatternSet bundlePatterns(&getContext());
696 bundlePatterns.add<CanonicalizerOpLowering<PackBundleOp>>(&getContext());
697 bundlePatterns.add<CanonicalizerOpLowering<UnpackBundleOp>>(&getContext());
698 if (failed(applyPartialConversion(getOperation(), noBundlesTarget,
699 std::move(bundlePatterns))))
703 ConversionTarget pass1Target(*ctxt);
704 pass1Target.addLegalDialect<comb::CombDialect>();
705 pass1Target.addLegalDialect<HWDialect>();
706 pass1Target.addLegalDialect<SVDialect>();
707 pass1Target.addLegalDialect<seq::SeqDialect>();
708 pass1Target.addLegalOp<WrapValidReadyOp, UnwrapValidReadyOp>();
710 pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
711 pass1Target.addIllegalOp<PipelineStageOp>();
712 pass1Target.addIllegalOp<CompressedManifestOp>();
716 RewritePatternSet pass1Patterns(ctxt);
717 pass1Patterns.insert<PipelineStageLowering>(esiBuilder,
ctxt);
718 pass1Patterns.insert<WrapInterfaceLower>(
ctxt);
719 pass1Patterns.insert<UnwrapInterfaceLower>(
ctxt);
720 pass1Patterns.insert<CosimToHostLowering>(esiBuilder);
721 pass1Patterns.insert<CosimFromHostLowering>(esiBuilder);
722 pass1Patterns.insert<NullSourceOpLowering>(
ctxt);
725 pass1Patterns.insert<CosimManifestLowering>(
ctxt);
727 pass1Patterns.insert<ManifestRomLowering>(
ctxt);
733 applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
736 ConversionTarget pass2Target(*ctxt);
737 pass2Target.addLegalDialect<comb::CombDialect>();
738 pass2Target.addLegalDialect<HWDialect>();
739 pass2Target.addLegalDialect<SVDialect>();
740 pass2Target.addIllegalDialect<ESIDialect>();
742 RewritePatternSet pass2Patterns(ctxt);
743 pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(
ctxt);
744 pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(
ctxt);
745 pass2Patterns.insert<RemoveWrapUnwrap>(
ctxt);
746 pass2Patterns.insert<RemoveSnoopOp>(
ctxt);
748 applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
753 return std::make_unique<ESItoHWPass>();
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
Instantiate one of these and use it to build typed backedges.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
Assist the lowering steps for conversions which need to create auxiliary IR.
static constexpr char validStr[]
static constexpr char readyStr[]
static constexpr char dataStr[]
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
uint64_t getWidth(Type t)
StringAttr getTypeID(Type t)
std::unique_ptr< OperationPass< ModuleOp > > createESItoHWPass()
mlir::Type innerType(mlir::Type type)
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Generic pattern for removing an op during pattern conversion.
This holds a decoded list of input/inout and output ports for a module or instance.
This holds the name, type, direction of a module's ports.