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"
37 using namespace circt;
51 using OpConversionPattern::OpConversionPattern;
54 matchAndRewrite(PipelineStageOp stage, OpAdaptor adaptor,
55 ConversionPatternRewriter &rewriter)
const final;
62 LogicalResult 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;
128 LogicalResult 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();
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()});
150 struct 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 (
wrap.getChanOutput().getUsers().empty()) {
164 rewriter.getI1Type(), 1);
165 rewriter.replaceOp(
wrap, {
nullptr, c1});
169 if (!
wrap.getChanOutput().hasOneUse() ||
170 !(
unwrap = dyn_cast<UnwrapValidReadyOp>(
171 wrap.getChanOutput().use_begin()->getOwner())))
172 return rewriter.notifyMatchFailure(
173 wrap,
"This conversion only supports wrap-unwrap back-to-back. "
174 "Could not find 'unwrap'.");
178 ready =
unwrap.getReady();
180 wrap = dyn_cast<WrapValidReadyOp>(operands[0].getDefiningOp());
182 return rewriter.notifyMatchFailure(
183 operands[0].getDefiningOp(),
184 "This conversion only supports wrap-unwrap back-to-back. "
185 "Could not find 'wrap'.");
186 valid =
wrap.getValid();
193 if (!
wrap.getChanOutput().hasOneUse())
194 return rewriter.notifyMatchFailure(
wrap, [](Diagnostic &d) {
195 d <<
"This conversion only supports wrap-unwrap back-to-back. "
196 "Wrap didn't have exactly one use.";
198 rewriter.replaceOp(
wrap, {
nullptr, ready});
208 template <
typename Op>
214 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
215 ConversionPatternRewriter &rewriter)
const final {
217 return rewriter.notifyMatchFailure(op->getLoc(),
"canonicalizer failed");
224 struct ESItoHWPass :
public circt::esi::impl::LowerESItoHWBase<ESItoHWPass> {
225 void runOnOperation()
override;
234 using OpConversionPattern::OpConversionPattern;
237 matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
238 ConversionPatternRewriter &rewriter)
const final;
243 WrapInterfaceLower::matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
244 ConversionPatternRewriter &rewriter)
const {
245 auto operands = adaptor.getOperands();
246 if (operands.size() != 1)
247 return rewriter.notifyMatchFailure(
wrap, [&operands](Diagnostic &d) {
248 d <<
"wrap.iface has 1 argument. Got " << operands.size() <<
"operands";
250 auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
254 dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
258 auto loc =
wrap.getLoc();
259 auto validSignal = rewriter.create<ReadInterfaceSignalOp>(
262 dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
264 auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
265 rewriter.create<AssignInterfaceSignalOp>(
267 rewriter.replaceOp(
wrap, {wrapVR.getChanOutput()});
277 using OpConversionPattern::OpConversionPattern;
280 matchAndRewrite(UnwrapSVInterfaceOp
wrap, OpAdaptor adaptor,
281 ConversionPatternRewriter &rewriter)
const final;
285 LogicalResult UnwrapInterfaceLower::matchAndRewrite(
286 UnwrapSVInterfaceOp
unwrap, OpAdaptor adaptor,
287 ConversionPatternRewriter &rewriter)
const {
288 auto operands = adaptor.getOperands();
289 if (operands.size() != 2)
290 return rewriter.notifyMatchFailure(
unwrap, [&operands](Diagnostic &d) {
291 d <<
"Unwrap.iface has 2 arguments. Got " << operands.size()
295 auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
299 dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
303 auto loc =
unwrap.getLoc();
304 auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
307 rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
308 rewriter.create<AssignInterfaceSignalOp>(
311 rewriter.create<AssignInterfaceSignalOp>(
325 using OpConversionPattern::OpConversionPattern;
328 matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
329 ConversionPatternRewriter &rewriter)
const final;
336 LogicalResult CosimToHostLowering::matchAndRewrite(
337 CosimToHostEndpointOp ep, OpAdaptor adaptor,
338 ConversionPatternRewriter &rewriter)
const {
339 auto loc = ep.getLoc();
340 auto *
ctxt = rewriter.getContext();
343 Value toHost = adaptor.getToHost();
344 Type type = toHost.getType();
348 SmallVector<Attribute, 8> params;
352 "TO_HOST_SIZE_BITS", rewriter.getI32IntegerAttr(
width > 0 ?
width : 1)));
355 auto sendReady = bb.get(rewriter.getI1Type());
356 UnwrapValidReadyOp unwrapSend =
357 rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
358 Value castedSendData;
361 loc, rewriter.getIntegerType(
width), unwrapSend.getRawOutput());
364 loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(
false));
367 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
368 HWModuleExternOp endpoint =
369 builder.declareCosimEndpointToHostModule(symTable);
375 unwrapSend.getValid(),
378 auto cosimEpModule = rewriter.
create<hw::InstanceOp>(
380 sendReady.setValue(cosimEpModule.getResult(0));
383 rewriter.eraseOp(ep);
391 struct CosimFromHostLowering
397 using OpConversionPattern::OpConversionPattern;
400 matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
401 ConversionPatternRewriter &rewriter)
const final;
408 LogicalResult CosimFromHostLowering::matchAndRewrite(
409 CosimFromHostEndpointOp ep, OpAdaptor adaptor,
410 ConversionPatternRewriter &rewriter)
const {
411 auto loc = ep.getLoc();
412 auto *
ctxt = rewriter.getContext();
415 ChannelType type = ep.getFromHost().getType();
419 SmallVector<Attribute, 8> params;
424 rewriter.getI32IntegerAttr(
width > 0 ?
width : 1)));
427 auto recvReady = bb.get(rewriter.getI1Type());
430 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
431 HWModuleExternOp endpoint =
432 builder.declareCosimEndpointFromHostModule(symTable);
435 Value operands[] = {adaptor.getClk(), adaptor.getRst(), recvReady};
436 auto cosimEpModule = rewriter.create<hw::InstanceOp>(
440 Value recvDataFromCosim = cosimEpModule.getResult(1);
441 Value recvValidFromCosim = cosimEpModule.getResult(0);
442 Value castedRecvData;
445 rewriter.create<
hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
448 loc, rewriter.getIntegerType(0),
449 rewriter.getIntegerAttr(rewriter.getIntegerType(0), 0));
450 WrapValidReadyOp wrapRecv = rewriter.create<WrapValidReadyOp>(
451 loc, castedRecvData, recvValidFromCosim);
452 recvReady.setValue(wrapRecv.getReady());
455 rewriter.replaceOp(ep, wrapRecv.getChanOutput());
466 using OpConversionPattern::OpConversionPattern;
467 constexpr
static StringRef manifestRomName =
"__ESI_Manifest_ROM";
470 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
471 ConversionPatternRewriter &rewriter)
const override;
474 LogicalResult createRomModule(CompressedManifestOp op,
475 ConversionPatternRewriter &rewriter)
const;
479 LogicalResult ManifestRomLowering::createRomModule(
480 CompressedManifestOp op, ConversionPatternRewriter &rewriter)
const {
481 Location loc = op.getLoc();
482 auto mlirModBody = op->getParentOfType<mlir::ModuleOp>();
483 rewriter.setInsertionPointToStart(mlirModBody.getBody());
487 if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
488 if (!isa<hw::HWModuleExternOp>(existingExtern))
489 return rewriter.notifyMatchFailure(
491 "Found " + manifestRomName +
" but it wasn't an HWModuleExternOp");
492 rewriter.eraseOp(existingExtern);
497 {{rewriter.getStringAttr(
"clk"), rewriter.getType<seq::ClockType>(),
499 {{rewriter.getStringAttr(
"address"), rewriter.getIntegerType(29),
501 {{rewriter.getStringAttr(
"data"), rewriter.getI64Type(),
504 auto rom = rewriter.create<HWModuleOp>(
505 loc, rewriter.getStringAttr(manifestRomName), ports);
506 Block *romBody = rom.getBodyBlock();
507 rewriter.setInsertionPointToStart(romBody);
508 Value
clk = romBody->getArgument(0);
509 Value inputAddress = romBody->getArgument(1);
512 ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
513 SmallVector<uint64_t> words;
514 words.push_back(maniBytes.size());
516 for (
size_t i = 0; i < maniBytes.size() - 7; i += 8) {
518 for (
size_t b = 0; b < 8; ++b)
519 word |=
static_cast<uint64_t
>(maniBytes[i + b]) << (8 * b);
520 words.push_back(word);
522 size_t overHang = maniBytes.size() % 8;
525 for (
size_t i = 0; i < overHang; ++i)
526 word |=
static_cast<uint64_t
>(maniBytes[maniBytes.size() - overHang + i])
528 words.push_back(word);
533 SmallVector<Attribute> wordAttrs;
534 for (uint64_t word : words)
535 wordAttrs.push_back(rewriter.getI64IntegerAttr(word));
536 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
538 rewriter.getArrayAttr(wordAttrs));
540 rewriter.create<
sv::RegOp>(loc, manifestConstant.getType());
541 rewriter.create<
sv::AssignOp>(loc, manifestReg, manifestConstant);
544 size_t addrBits = llvm::Log2_64_Ceil(words.size());
549 rewriter.
create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
552 if (
auto *term = romBody->getTerminator())
553 rewriter.eraseOp(term);
554 rewriter.
create<hw::OutputOp>(loc, ValueRange{readDataReg});
558 LogicalResult ManifestRomLowering::matchAndRewrite(
559 CompressedManifestOp op, OpAdaptor adaptor,
560 ConversionPatternRewriter &rewriter)
const {
561 LogicalResult ret = createRomModule(op, rewriter);
562 rewriter.eraseOp(op);
569 struct CosimManifestLowering :
public ManifestRomLowering {
571 using ManifestRomLowering::ManifestRomLowering;
574 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
575 ConversionPatternRewriter &rewriter)
const final;
579 LogicalResult CosimManifestLowering::matchAndRewrite(
580 CompressedManifestOp op, OpAdaptor adaptor,
581 ConversionPatternRewriter &rewriter)
const {
582 MLIRContext *
ctxt = rewriter.getContext();
583 Location loc = op.getLoc();
587 LogicalResult ret = createRomModule(op, rewriter);
592 Attribute params[] = {
595 {{rewriter.getStringAttr(
"compressed_manifest"),
596 rewriter.getType<hw::ArrayType>(
597 rewriter.getI8Type(),
599 rewriter.getStringAttr(
"COMPRESSED_MANIFEST_SIZE"),
600 rewriter.getI32Type())),
604 rewriter.setInsertionPointToEnd(
605 op->getParentOfType<mlir::ModuleOp>().getBody());
606 auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
607 loc, rewriter.getStringAttr(
"Cosim_Manifest"), ports,
"Cosim_Manifest",
610 hw::ModulePortInfo portInfo({});
612 loc, rewriter.getStringAttr(
"__ESIManifest"), portInfo,
613 [&](OpBuilder &rewriter,
const hw::HWModulePortAccessor &) {
615 SmallVector<Attribute> bytes;
616 for (uint8_t b : op.getCompressedManifest().getData())
617 bytes.push_back(rewriter.getI8IntegerAttr(b));
618 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
620 rewriter.getArrayAttr(bytes));
622 rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
623 rewriter.create<
sv::AssignOp>(loc, manifestLogic, manifestConstant);
627 rewriter.
create<hw::InstanceOp>(
628 loc, cosimManifestExternModule,
"__manifest",
629 ArrayRef<Value>({manifest}),
630 rewriter.getArrayAttr({ParamDeclAttr::get(
631 "COMPRESSED_MANIFEST_SIZE",
632 rewriter.getI32IntegerAttr(bytes.size()))}));
635 rewriter.setInsertionPoint(op);
636 rewriter.create<hw::InstanceOp>(loc, manifestMod,
"__manifest",
637 ArrayRef<Value>({}));
639 rewriter.eraseOp(op);
642 void ESItoHWPass::runOnOperation() {
643 auto top = getOperation();
644 auto *
ctxt = &getContext();
646 ConversionTarget noBundlesTarget(*
ctxt);
647 noBundlesTarget.markUnknownOpDynamicallyLegal(
648 [](Operation *) {
return true; });
649 noBundlesTarget.addIllegalOp<PackBundleOp>();
650 noBundlesTarget.addIllegalOp<UnpackBundleOp>();
651 RewritePatternSet bundlePatterns(&getContext());
652 bundlePatterns.add<CanonicalizerOpLowering<PackBundleOp>>(&getContext());
653 bundlePatterns.add<CanonicalizerOpLowering<UnpackBundleOp>>(&getContext());
654 if (failed(applyPartialConversion(getOperation(), noBundlesTarget,
655 std::move(bundlePatterns))))
659 ConversionTarget pass1Target(*
ctxt);
660 pass1Target.addLegalDialect<comb::CombDialect>();
661 pass1Target.addLegalDialect<HWDialect>();
662 pass1Target.addLegalDialect<SVDialect>();
663 pass1Target.addLegalDialect<seq::SeqDialect>();
664 pass1Target.addLegalOp<WrapValidReadyOp, UnwrapValidReadyOp>();
666 pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
667 pass1Target.addIllegalOp<PipelineStageOp>();
668 pass1Target.addIllegalOp<CompressedManifestOp>();
672 RewritePatternSet pass1Patterns(
ctxt);
673 pass1Patterns.insert<PipelineStageLowering>(esiBuilder,
ctxt);
674 pass1Patterns.insert<WrapInterfaceLower>(
ctxt);
675 pass1Patterns.insert<UnwrapInterfaceLower>(
ctxt);
676 pass1Patterns.insert<CosimToHostLowering>(esiBuilder);
677 pass1Patterns.insert<CosimFromHostLowering>(esiBuilder);
678 pass1Patterns.insert<NullSourceOpLowering>(
ctxt);
681 pass1Patterns.insert<CosimManifestLowering>(
ctxt);
683 pass1Patterns.insert<ManifestRomLowering>(
ctxt);
689 applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
692 ConversionTarget pass2Target(*
ctxt);
693 pass2Target.addLegalDialect<comb::CombDialect>();
694 pass2Target.addLegalDialect<HWDialect>();
695 pass2Target.addLegalDialect<SVDialect>();
696 pass2Target.addIllegalDialect<ESIDialect>();
698 RewritePatternSet pass2Patterns(
ctxt);
699 pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(
ctxt);
700 pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(
ctxt);
701 pass2Patterns.insert<RemoveWrapUnwrap>(
ctxt);
703 applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
708 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[]
def create(data_type, value)
def create(data_type, value)
def create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
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 the name, type, direction of a module's ports.