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);
163 for (
auto user :
wrap.getChanOutput().getUsers())
164 if (auto snoop = dyn_cast<SnoopValidReadyOp>(user))
166 snoop, {
wrap.getValid(),
wrap.getReady(),
wrap.getRawInput()});
168 if (ChannelType::hasNoConsumers(
wrap.getChanOutput())) {
170 rewriter.getI1Type(), 1);
171 rewriter.replaceOp(
wrap, {
nullptr, c1});
175 if (!ChannelType::hasOneConsumer(
wrap.getChanOutput()))
176 return rewriter.notifyMatchFailure(
177 wrap,
"This conversion only supports wrap-unwrap back-to-back. "
178 "Wrap didn't have exactly one use.");
179 if (!(
unwrap = dyn_cast<UnwrapValidReadyOp>(
180 ChannelType::getSingleConsumer(
wrap.getChanOutput())
182 return rewriter.notifyMatchFailure(
183 wrap,
"This conversion only supports wrap-unwrap back-to-back. "
184 "Could not find 'unwrap'.");
188 ready =
unwrap.getReady();
190 wrap = dyn_cast<WrapValidReadyOp>(operands[0].getDefiningOp());
192 return rewriter.notifyMatchFailure(
193 operands[0].getDefiningOp(),
194 "This conversion only supports wrap-unwrap back-to-back. "
195 "Could not find 'wrap'.");
196 valid =
wrap.getValid();
201 for (
auto user : operands[0].getUsers())
202 if (auto snoop = dyn_cast<SnoopValidReadyOp>(user))
203 rewriter.replaceOp(snoop, {valid, ready,
data});
208 if (!ChannelType::hasOneConsumer(
wrap.getChanOutput()))
209 return rewriter.notifyMatchFailure(
wrap, [](Diagnostic &d) {
210 d <<
"This conversion only supports wrap-unwrap back-to-back. "
211 "Wrap didn't have exactly one use.";
213 rewriter.replaceOp(
wrap, {
nullptr, ready});
223template <
typename Op>
229 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
230 ConversionPatternRewriter &rewriter)
const final {
231 if (failed(Op::canonicalize(op, rewriter)))
232 return rewriter.notifyMatchFailure(op->getLoc(),
"canonicalizer failed");
239struct ESItoHWPass :
public circt::esi::impl::LowerESItoHWBase<ESItoHWPass> {
240 void runOnOperation()
override;
249 using OpConversionPattern::OpConversionPattern;
252 matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
253 ConversionPatternRewriter &rewriter)
const final;
258WrapInterfaceLower::matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
259 ConversionPatternRewriter &rewriter)
const {
260 auto operands = adaptor.getOperands();
261 if (operands.size() != 1)
262 return rewriter.notifyMatchFailure(
wrap, [&operands](Diagnostic &d) {
263 d <<
"wrap.iface has 1 argument. Got " << operands.size() <<
"operands";
265 auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
269 dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
273 auto loc =
wrap.getLoc();
274 auto validSignal = rewriter.
create<ReadInterfaceSignalOp>(
277 dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
279 auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
280 rewriter.create<AssignInterfaceSignalOp>(
282 rewriter.replaceOp(
wrap, {wrapVR.getChanOutput()});
292 using OpConversionPattern::OpConversionPattern;
295 matchAndRewrite(UnwrapSVInterfaceOp
wrap, OpAdaptor adaptor,
296 ConversionPatternRewriter &rewriter)
const final;
300LogicalResult UnwrapInterfaceLower::matchAndRewrite(
301 UnwrapSVInterfaceOp
unwrap, OpAdaptor adaptor,
302 ConversionPatternRewriter &rewriter)
const {
303 auto operands = adaptor.getOperands();
304 if (operands.size() != 2)
305 return rewriter.notifyMatchFailure(
unwrap, [&operands](Diagnostic &d) {
306 d <<
"Unwrap.iface has 2 arguments. Got " << operands.size()
310 auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
314 dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
318 auto loc =
unwrap.getLoc();
319 auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
322 rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
323 rewriter.create<AssignInterfaceSignalOp>(
326 rewriter.create<AssignInterfaceSignalOp>(
340 using OpConversionPattern::OpConversionPattern;
343 matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
344 ConversionPatternRewriter &rewriter)
const final;
351LogicalResult CosimToHostLowering::matchAndRewrite(
352 CosimToHostEndpointOp ep, OpAdaptor adaptor,
353 ConversionPatternRewriter &rewriter)
const {
354 auto loc = ep.getLoc();
355 auto *
ctxt = rewriter.getContext();
358 Value toHost = adaptor.getToHost();
359 Type type = toHost.getType();
363 SmallVector<Attribute, 8> params;
364 params.push_back(ParamDeclAttr::get(
"ENDPOINT_ID", ep.getIdAttr()));
365 params.push_back(ParamDeclAttr::get(
"TO_HOST_TYPE_ID",
getTypeID(type)));
366 params.push_back(ParamDeclAttr::get(
367 "TO_HOST_SIZE_BITS", rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
370 auto sendReady = bb.get(rewriter.getI1Type());
371 UnwrapValidReadyOp unwrapSend =
372 rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
373 Value castedSendData;
376 loc, rewriter.getIntegerType(width), unwrapSend.getRawOutput());
379 loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(
false));
382 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
383 HWModuleExternOp endpoint =
384 builder.declareCosimEndpointToHostModule(symTable);
390 unwrapSend.getValid(),
393 auto cosimEpModule = rewriter.
create<hw::InstanceOp>(
394 loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
395 sendReady.setValue(cosimEpModule.getResult(0));
398 rewriter.eraseOp(ep);
406struct CosimFromHostLowering
412 using OpConversionPattern::OpConversionPattern;
415 matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
416 ConversionPatternRewriter &rewriter)
const final;
423LogicalResult CosimFromHostLowering::matchAndRewrite(
424 CosimFromHostEndpointOp ep, OpAdaptor adaptor,
425 ConversionPatternRewriter &rewriter)
const {
426 auto loc = ep.getLoc();
427 auto *
ctxt = rewriter.getContext();
430 ChannelType type = ep.getFromHost().getType();
434 SmallVector<Attribute, 8> params;
435 params.push_back(ParamDeclAttr::get(
"ENDPOINT_ID", ep.getIdAttr()));
436 params.push_back(ParamDeclAttr::get(
"FROM_HOST_TYPE_ID",
getTypeID(type)));
438 ParamDeclAttr::get(
"FROM_HOST_SIZE_BITS",
439 rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
442 auto recvReady = bb.get(rewriter.getI1Type());
445 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
446 HWModuleExternOp endpoint =
447 builder.declareCosimEndpointFromHostModule(symTable);
450 Value operands[] = {adaptor.getClk(), adaptor.getRst(), recvReady};
451 auto cosimEpModule = rewriter.create<hw::InstanceOp>(
452 loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
455 Value recvDataFromCosim = cosimEpModule.getResult(1);
456 Value recvValidFromCosim = cosimEpModule.getResult(0);
457 Value castedRecvData;
460 rewriter.create<
hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
463 loc, rewriter.getIntegerType(0),
464 rewriter.getIntegerAttr(rewriter.getIntegerType(0), 0));
465 WrapValidReadyOp wrapRecv = rewriter.create<WrapValidReadyOp>(
466 loc, castedRecvData, recvValidFromCosim);
467 recvReady.setValue(wrapRecv.getReady());
470 rewriter.replaceOp(ep, wrapRecv.getChanOutput());
481 using OpConversionPattern::OpConversionPattern;
482 constexpr static StringRef manifestRomName =
"__ESI_Manifest_ROM";
485 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
486 ConversionPatternRewriter &rewriter)
const override;
489 LogicalResult createRomModule(CompressedManifestOp op,
490 ConversionPatternRewriter &rewriter)
const;
494LogicalResult ManifestRomLowering::createRomModule(
495 CompressedManifestOp op, ConversionPatternRewriter &rewriter)
const {
496 Location loc = op.getLoc();
497 auto mlirModBody = op->getParentOfType<mlir::ModuleOp>();
498 rewriter.setInsertionPointToStart(mlirModBody.getBody());
502 if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
503 if (!isa<hw::HWModuleExternOp>(existingExtern))
504 return rewriter.notifyMatchFailure(
506 "Found " + manifestRomName +
" but it wasn't an HWModuleExternOp");
507 rewriter.eraseOp(existingExtern);
512 {{rewriter.getStringAttr(
"clk"), rewriter.getType<seq::ClockType>(),
513 ModulePort::Direction::Input}},
514 {{rewriter.getStringAttr(
"address"), rewriter.getIntegerType(29),
515 ModulePort::Direction::Input}},
516 {{rewriter.getStringAttr(
"data"), rewriter.getI64Type(),
517 ModulePort::Direction::Output}},
519 auto rom = rewriter.create<HWModuleOp>(
520 loc, rewriter.getStringAttr(manifestRomName), ports);
521 Block *romBody = rom.getBodyBlock();
522 rewriter.setInsertionPointToStart(romBody);
523 Value
clk = romBody->getArgument(0);
524 Value inputAddress = romBody->getArgument(1);
527 ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
528 SmallVector<uint64_t> words;
529 words.push_back(maniBytes.size());
531 for (
size_t i = 0; i < maniBytes.size() - 7; i += 8) {
533 for (
size_t b = 0; b < 8; ++b)
534 word |=
static_cast<uint64_t
>(maniBytes[i + b]) << (8 * b);
535 words.push_back(word);
537 size_t overHang = maniBytes.size() % 8;
540 for (
size_t i = 0; i < overHang; ++i)
541 word |=
static_cast<uint64_t
>(maniBytes[maniBytes.size() - overHang + i])
543 words.push_back(word);
548 SmallVector<Attribute> wordAttrs;
549 for (uint64_t word : words)
550 wordAttrs.push_back(rewriter.getI64IntegerAttr(word));
551 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
552 loc, hw::UnpackedArrayType::get(rewriter.getI64Type(), words.size()),
553 rewriter.getArrayAttr(wordAttrs));
555 rewriter.create<
sv::RegOp>(loc, manifestConstant.getType());
556 rewriter.create<
sv::AssignOp>(loc, manifestReg, manifestConstant);
559 size_t addrBits = llvm::Log2_64_Ceil(words.size());
564 rewriter.
create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
567 if (
auto *term = romBody->getTerminator())
568 rewriter.eraseOp(term);
569 rewriter.
create<hw::OutputOp>(loc, ValueRange{readDataReg});
573LogicalResult ManifestRomLowering::matchAndRewrite(
574 CompressedManifestOp op, OpAdaptor adaptor,
575 ConversionPatternRewriter &rewriter)
const {
576 LogicalResult ret = createRomModule(op, rewriter);
577 rewriter.eraseOp(op);
584struct CosimManifestLowering :
public ManifestRomLowering {
586 using ManifestRomLowering::ManifestRomLowering;
589 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
590 ConversionPatternRewriter &rewriter)
const final;
594LogicalResult CosimManifestLowering::matchAndRewrite(
595 CompressedManifestOp op, OpAdaptor adaptor,
596 ConversionPatternRewriter &rewriter)
const {
597 MLIRContext *
ctxt = rewriter.getContext();
598 Location loc = op.getLoc();
602 LogicalResult ret = createRomModule(op, rewriter);
607 Attribute params[] = {
608 ParamDeclAttr::get(
"COMPRESSED_MANIFEST_SIZE", rewriter.getI32Type())};
610 {{rewriter.getStringAttr(
"compressed_manifest"),
611 rewriter.getType<hw::ArrayType>(
612 rewriter.getI8Type(),
613 ParamDeclRefAttr::get(
614 rewriter.getStringAttr(
"COMPRESSED_MANIFEST_SIZE"),
615 rewriter.getI32Type())),
616 ModulePort::Direction::Input},
619 rewriter.setInsertionPointToEnd(
620 op->getParentOfType<mlir::ModuleOp>().getBody());
621 auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
622 loc, rewriter.getStringAttr(
"Cosim_Manifest"), ports,
"Cosim_Manifest",
623 ArrayAttr::get(ctxt, params));
627 loc, rewriter.getStringAttr(
"__ESIManifest"), portInfo,
630 SmallVector<Attribute> bytes;
631 for (uint8_t b : op.getCompressedManifest().getData())
632 bytes.push_back(rewriter.getI8IntegerAttr(b));
633 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
634 loc, hw::ArrayType::get(rewriter.getI8Type(), bytes.size()),
635 rewriter.getArrayAttr(bytes));
637 rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
638 rewriter.create<
sv::AssignOp>(loc, manifestLogic, manifestConstant);
642 rewriter.
create<hw::InstanceOp>(
643 loc, cosimManifestExternModule,
"__manifest",
644 ArrayRef<Value>({manifest}),
645 rewriter.getArrayAttr({ParamDeclAttr::get(
646 "COMPRESSED_MANIFEST_SIZE",
647 rewriter.getI32IntegerAttr(bytes.size()))}));
650 rewriter.setInsertionPoint(op);
651 rewriter.create<hw::InstanceOp>(loc, manifestMod,
"__manifest",
652 ArrayRef<Value>({}));
654 rewriter.eraseOp(op);
657void ESItoHWPass::runOnOperation() {
658 auto top = getOperation();
659 auto *
ctxt = &getContext();
661 ConversionTarget noBundlesTarget(*ctxt);
662 noBundlesTarget.markUnknownOpDynamicallyLegal(
663 [](Operation *) {
return true; });
664 noBundlesTarget.addIllegalOp<PackBundleOp>();
665 noBundlesTarget.addIllegalOp<UnpackBundleOp>();
666 RewritePatternSet bundlePatterns(&getContext());
667 bundlePatterns.add<CanonicalizerOpLowering<PackBundleOp>>(&getContext());
668 bundlePatterns.add<CanonicalizerOpLowering<UnpackBundleOp>>(&getContext());
669 if (failed(applyPartialConversion(getOperation(), noBundlesTarget,
670 std::move(bundlePatterns))))
674 ConversionTarget pass1Target(*ctxt);
675 pass1Target.addLegalDialect<comb::CombDialect>();
676 pass1Target.addLegalDialect<HWDialect>();
677 pass1Target.addLegalDialect<SVDialect>();
678 pass1Target.addLegalDialect<seq::SeqDialect>();
679 pass1Target.addLegalOp<WrapValidReadyOp, UnwrapValidReadyOp>();
681 pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
682 pass1Target.addIllegalOp<PipelineStageOp>();
683 pass1Target.addIllegalOp<CompressedManifestOp>();
687 RewritePatternSet pass1Patterns(ctxt);
688 pass1Patterns.insert<PipelineStageLowering>(esiBuilder,
ctxt);
689 pass1Patterns.insert<WrapInterfaceLower>(
ctxt);
690 pass1Patterns.insert<UnwrapInterfaceLower>(
ctxt);
691 pass1Patterns.insert<CosimToHostLowering>(esiBuilder);
692 pass1Patterns.insert<CosimFromHostLowering>(esiBuilder);
693 pass1Patterns.insert<NullSourceOpLowering>(
ctxt);
696 pass1Patterns.insert<CosimManifestLowering>(
ctxt);
698 pass1Patterns.insert<ManifestRomLowering>(
ctxt);
704 applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
707 ConversionTarget pass2Target(*ctxt);
708 pass2Target.addLegalDialect<comb::CombDialect>();
709 pass2Target.addLegalDialect<HWDialect>();
710 pass2Target.addLegalDialect<SVDialect>();
711 pass2Target.addIllegalDialect<ESIDialect>();
713 RewritePatternSet pass2Patterns(ctxt);
714 pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(
ctxt);
715 pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(
ctxt);
716 pass2Patterns.insert<RemoveWrapUnwrap>(
ctxt);
718 applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
723 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.