13 #include "../PassDetails.h"
25 #include "mlir/Transforms/DialectConversion.h"
27 #include "llvm/ADT/StringExtras.h"
28 #include "llvm/ADT/TypeSwitch.h"
29 #include "llvm/Support/JSON.h"
31 using namespace circt;
45 using OpConversionPattern::OpConversionPattern;
48 matchAndRewrite(PipelineStageOp stage, OpAdaptor adaptor,
49 ConversionPatternRewriter &rewriter)
const final;
56 LogicalResult PipelineStageLowering::matchAndRewrite(
57 PipelineStageOp stage, OpAdaptor adaptor,
58 ConversionPatternRewriter &rewriter)
const {
59 auto loc = stage.getLoc();
60 auto chPort = dyn_cast<ChannelType>(stage.getInput().getType());
62 return rewriter.notifyMatchFailure(stage,
"stage had wrong type");
63 Operation *symTable = stage->getParentWithTrait<OpTrait::SymbolTable>();
64 auto stageModule =
builder.declareStage(symTable, stage);
68 ArrayAttr stageParams =
69 builder.getStageParameterList(rewriter.getUI32IntegerAttr(
width));
77 rewriter.create<UnwrapValidReadyOp>(loc, stage.getInput(), wrapReady);
79 StringRef pipeStageName =
"pipelineStage";
80 if (
auto name = stage->getAttrOfType<StringAttr>(
"name"))
81 pipeStageName = name.getValue();
85 llvm::SmallVector<Value> operands = {stage.getClk(), stage.getRst()};
86 operands.push_back(
unwrap.getRawOutput());
87 operands.push_back(
unwrap.getValid());
88 operands.push_back(stageReady);
89 auto stageInst = rewriter.create<hw::InstanceOp>(
90 loc, stageModule, pipeStageName, operands, stageParams);
91 auto stageInstResults = stageInst.getResults();
95 wrapReady.
setValue(stageInstResults[0]);
97 x = stageInstResults[1];
98 xValid = stageInstResults[2];
101 auto wrap = rewriter.create<WrapValidReadyOp>(
102 loc, chPort, rewriter.getI1Type(), x, xValid);
106 rewriter.replaceOp(stage,
wrap.getChanOutput());
114 using OpConversionPattern::OpConversionPattern;
117 matchAndRewrite(NullSourceOp nullop, OpAdaptor adaptor,
118 ConversionPatternRewriter &rewriter)
const final;
122 LogicalResult NullSourceOpLowering::matchAndRewrite(
123 NullSourceOp nullop, OpAdaptor adaptor,
124 ConversionPatternRewriter &rewriter)
const {
125 auto innerType = cast<ChannelType>(nullop.getOut().getType()).getInner();
126 Location loc = nullop.getLoc();
129 return rewriter.notifyMatchFailure(
130 nullop,
"NullOp lowering only supports hw types");
132 rewriter.create<
hw::ConstantOp>(nullop.getLoc(), rewriter.getI1Type(), 0);
136 auto wrap = rewriter.create<WrapValidReadyOp>(loc, typedZero, valid);
137 wrap->setAttr(
"name", rewriter.getStringAttr(
"nullsource"));
138 rewriter.replaceOp(nullop, {
wrap.getChanOutput()});
144 struct RemoveWrapUnwrap :
public ConversionPattern {
146 RemoveWrapUnwrap(MLIRContext *context)
147 : ConversionPattern(MatchAnyOpTypeTag(), 1, context) {}
150 matchAndRewrite(Operation *op, ArrayRef<Value> operands,
151 ConversionPatternRewriter &rewriter)
const override {
152 Value valid, ready,
data;
153 WrapValidReadyOp
wrap = dyn_cast<WrapValidReadyOp>(op);
154 UnwrapValidReadyOp
unwrap = dyn_cast<UnwrapValidReadyOp>(op);
156 if (
wrap.getChanOutput().getUsers().empty()) {
158 rewriter.getI1Type(), 1);
159 rewriter.replaceOp(
wrap, {
nullptr, c1});
163 if (!
wrap.getChanOutput().hasOneUse() ||
164 !(
unwrap = dyn_cast<UnwrapValidReadyOp>(
165 wrap.getChanOutput().use_begin()->getOwner())))
166 return rewriter.notifyMatchFailure(
167 wrap,
"This conversion only supports wrap-unwrap back-to-back. "
168 "Could not find 'unwrap'.");
172 ready =
unwrap.getReady();
174 wrap = dyn_cast<WrapValidReadyOp>(operands[0].getDefiningOp());
176 return rewriter.notifyMatchFailure(
177 operands[0].getDefiningOp(),
178 "This conversion only supports wrap-unwrap back-to-back. "
179 "Could not find 'wrap'.");
180 valid =
wrap.getValid();
187 if (!
wrap.getChanOutput().hasOneUse())
188 return rewriter.notifyMatchFailure(
wrap, [](Diagnostic &d) {
189 d <<
"This conversion only supports wrap-unwrap back-to-back. "
190 "Wrap didn't have exactly one use.";
192 rewriter.replaceOp(
wrap, {
nullptr, ready});
202 template <
typename Op>
208 matchAndRewrite(Op op,
typename Op::Adaptor adaptor,
209 ConversionPatternRewriter &rewriter)
const final {
210 if (failed(Op::canonicalize(op, rewriter)))
211 return rewriter.notifyMatchFailure(op->getLoc(),
"canonicalizer failed");
218 struct ESItoHWPass :
public LowerESItoHWBase<ESItoHWPass> {
219 void runOnOperation()
override;
228 using OpConversionPattern::OpConversionPattern;
231 matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
232 ConversionPatternRewriter &rewriter)
const final;
237 WrapInterfaceLower::matchAndRewrite(WrapSVInterfaceOp
wrap, OpAdaptor adaptor,
238 ConversionPatternRewriter &rewriter)
const {
239 auto operands = adaptor.getOperands();
240 if (operands.size() != 1)
241 return rewriter.notifyMatchFailure(
wrap, [&operands](Diagnostic &d) {
242 d <<
"wrap.iface has 1 argument. Got " << operands.size() <<
"operands";
244 auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
248 dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
252 auto loc =
wrap.getLoc();
253 auto validSignal = rewriter.create<ReadInterfaceSignalOp>(
256 dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
258 auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
259 rewriter.create<AssignInterfaceSignalOp>(
261 rewriter.replaceOp(
wrap, {wrapVR.getChanOutput()});
271 using OpConversionPattern::OpConversionPattern;
274 matchAndRewrite(UnwrapSVInterfaceOp
wrap, OpAdaptor adaptor,
275 ConversionPatternRewriter &rewriter)
const final;
279 LogicalResult UnwrapInterfaceLower::matchAndRewrite(
280 UnwrapSVInterfaceOp
unwrap, OpAdaptor adaptor,
281 ConversionPatternRewriter &rewriter)
const {
282 auto operands = adaptor.getOperands();
283 if (operands.size() != 2)
284 return rewriter.notifyMatchFailure(
unwrap, [&operands](Diagnostic &d) {
285 d <<
"Unwrap.iface has 2 arguments. Got " << operands.size()
289 auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
293 dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
297 auto loc =
unwrap.getLoc();
298 auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
301 rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
302 rewriter.create<AssignInterfaceSignalOp>(
305 rewriter.create<AssignInterfaceSignalOp>(
319 using OpConversionPattern::OpConversionPattern;
322 matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
323 ConversionPatternRewriter &rewriter)
const final;
330 LogicalResult CosimToHostLowering::matchAndRewrite(
331 CosimToHostEndpointOp ep, OpAdaptor adaptor,
332 ConversionPatternRewriter &rewriter)
const {
333 auto loc = ep.getLoc();
334 auto *
ctxt = rewriter.getContext();
337 Value toHost = adaptor.getToHost();
338 Type type = toHost.getType();
342 SmallVector<Attribute, 8> params;
346 "TO_HOST_SIZE_BITS", rewriter.getI32IntegerAttr(
width > 0 ?
width : 1)));
349 auto sendReady = bb.get(rewriter.getI1Type());
350 UnwrapValidReadyOp unwrapSend =
351 rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
352 Value castedSendData;
355 loc, rewriter.getIntegerType(
width), unwrapSend.getRawOutput());
358 loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(
false));
361 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
362 HWModuleExternOp endpoint =
363 builder.declareCosimEndpointToHostModule(symTable);
369 unwrapSend.getValid(),
372 auto cosimEpModule = rewriter.
create<hw::InstanceOp>(
374 sendReady.setValue(cosimEpModule.getResult(0));
377 rewriter.eraseOp(ep);
385 struct CosimFromHostLowering
391 using OpConversionPattern::OpConversionPattern;
394 matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
395 ConversionPatternRewriter &rewriter)
const final;
402 LogicalResult CosimFromHostLowering::matchAndRewrite(
403 CosimFromHostEndpointOp ep, OpAdaptor adaptor,
404 ConversionPatternRewriter &rewriter)
const {
405 auto loc = ep.getLoc();
406 auto *
ctxt = rewriter.getContext();
409 ChannelType type = ep.getFromHost().getType();
413 SmallVector<Attribute, 8> params;
418 rewriter.getI32IntegerAttr(
width > 0 ?
width : 1)));
421 auto recvReady = bb.get(rewriter.getI1Type());
424 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
425 HWModuleExternOp endpoint =
426 builder.declareCosimEndpointFromHostModule(symTable);
429 Value operands[] = {adaptor.getClk(), adaptor.getRst(), recvReady};
430 auto cosimEpModule = rewriter.create<hw::InstanceOp>(
434 Value recvDataFromCosim = cosimEpModule.getResult(1);
435 Value recvValidFromCosim = cosimEpModule.getResult(0);
436 Value castedRecvData;
439 rewriter.create<
hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
442 loc, rewriter.getIntegerType(0),
443 rewriter.getIntegerAttr(rewriter.getIntegerType(0), 0));
444 WrapValidReadyOp wrapRecv = rewriter.create<WrapValidReadyOp>(
445 loc, castedRecvData, recvValidFromCosim);
446 recvReady.setValue(wrapRecv.getReady());
449 rewriter.replaceOp(ep, wrapRecv.getChanOutput());
460 using OpConversionPattern::OpConversionPattern;
461 constexpr
static StringRef manifestRomName =
"__ESI_Manifest_ROM";
464 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
465 ConversionPatternRewriter &rewriter)
const override;
468 LogicalResult createRomModule(CompressedManifestOp op,
469 ConversionPatternRewriter &rewriter)
const;
473 LogicalResult ManifestRomLowering::createRomModule(
474 CompressedManifestOp op, ConversionPatternRewriter &rewriter)
const {
475 Location loc = op.getLoc();
476 auto mlirModBody = op->getParentOfType<mlir::ModuleOp>();
477 rewriter.setInsertionPointToStart(mlirModBody.getBody());
481 if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
482 if (!isa<hw::HWModuleExternOp>(existingExtern))
483 return rewriter.notifyMatchFailure(
485 "Found " + manifestRomName +
" but it wasn't an HWModuleExternOp");
486 rewriter.eraseOp(existingExtern);
491 {{rewriter.getStringAttr(
"clk"), rewriter.getType<seq::ClockType>(),
493 {{rewriter.getStringAttr(
"address"), rewriter.getIntegerType(30),
495 {{rewriter.getStringAttr(
"data"), rewriter.getI32Type(),
498 auto rom = rewriter.create<HWModuleOp>(
499 loc, rewriter.getStringAttr(manifestRomName), ports);
500 Block *romBody = rom.getBodyBlock();
501 rewriter.setInsertionPointToStart(romBody);
502 Value
clk = romBody->getArgument(0);
503 Value inputAddress = romBody->getArgument(1);
506 ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
507 SmallVector<uint32_t> words;
508 words.push_back(maniBytes.size());
510 for (
size_t i = 0; i < maniBytes.size() - 3; i += 4) {
511 uint32_t word = maniBytes[i] | (maniBytes[i + 1] << 8) |
512 (maniBytes[i + 2] << 16) | (maniBytes[i + 3] << 24);
513 words.push_back(word);
515 size_t overHang = maniBytes.size() % 4;
518 for (
size_t i = 0; i < overHang; ++i)
519 word |= maniBytes[maniBytes.size() - overHang + i] << (i * 8);
520 words.push_back(word);
525 SmallVector<Attribute> wordAttrs;
526 for (uint32_t word : words)
527 wordAttrs.push_back(rewriter.getI32IntegerAttr(word));
528 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
530 rewriter.getArrayAttr(wordAttrs));
532 rewriter.create<
sv::RegOp>(loc, manifestConstant.getType());
533 rewriter.create<
sv::AssignOp>(loc, manifestReg, manifestConstant);
536 size_t addrBits = llvm::Log2_64_Ceil(words.size());
541 rewriter.
create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
544 if (
auto *term = romBody->getTerminator())
545 rewriter.eraseOp(term);
546 rewriter.
create<hw::OutputOp>(loc, ValueRange{readDataReg});
550 LogicalResult ManifestRomLowering::matchAndRewrite(
551 CompressedManifestOp op, OpAdaptor adaptor,
552 ConversionPatternRewriter &rewriter)
const {
553 LogicalResult ret = createRomModule(op, rewriter);
554 rewriter.eraseOp(op);
561 struct CosimManifestLowering :
public ManifestRomLowering {
563 using ManifestRomLowering::ManifestRomLowering;
566 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
567 ConversionPatternRewriter &rewriter)
const final;
571 LogicalResult CosimManifestLowering::matchAndRewrite(
572 CompressedManifestOp op, OpAdaptor adaptor,
573 ConversionPatternRewriter &rewriter)
const {
574 MLIRContext *
ctxt = rewriter.getContext();
575 Location loc = op.getLoc();
579 LogicalResult ret = createRomModule(op, rewriter);
584 Attribute params[] = {
587 {{rewriter.getStringAttr(
"compressed_manifest"),
588 rewriter.getType<hw::ArrayType>(
589 rewriter.getI8Type(),
591 rewriter.getStringAttr(
"COMPRESSED_MANIFEST_SIZE"),
592 rewriter.getI32Type())),
596 rewriter.setInsertionPointToEnd(
597 op->getParentOfType<mlir::ModuleOp>().getBody());
598 auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
599 loc, rewriter.getStringAttr(
"Cosim_Manifest"), ports,
"Cosim_Manifest",
602 hw::ModulePortInfo portInfo({});
604 loc, rewriter.getStringAttr(
"__ESIManifest"), portInfo,
605 [&](OpBuilder &rewriter,
const hw::HWModulePortAccessor &) {
607 SmallVector<Attribute> bytes;
608 for (uint8_t b : op.getCompressedManifest().getData())
609 bytes.push_back(rewriter.getI8IntegerAttr(b));
610 auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
612 rewriter.getArrayAttr(bytes));
614 rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
615 rewriter.create<
sv::AssignOp>(loc, manifestLogic, manifestConstant);
619 rewriter.
create<hw::InstanceOp>(
620 loc, cosimManifestExternModule,
"__manifest",
621 ArrayRef<Value>({manifest}),
622 rewriter.getArrayAttr({ParamDeclAttr::get(
623 "COMPRESSED_MANIFEST_SIZE",
624 rewriter.getI32IntegerAttr(bytes.size()))}));
627 rewriter.setInsertionPoint(op);
628 rewriter.create<hw::InstanceOp>(loc, manifestMod,
"__manifest",
629 ArrayRef<Value>({}));
631 rewriter.eraseOp(op);
634 void ESItoHWPass::runOnOperation() {
635 auto top = getOperation();
636 auto *
ctxt = &getContext();
638 ConversionTarget noBundlesTarget(*
ctxt);
639 noBundlesTarget.markUnknownOpDynamicallyLegal(
640 [](Operation *) {
return true; });
641 noBundlesTarget.addIllegalOp<PackBundleOp>();
642 noBundlesTarget.addIllegalOp<UnpackBundleOp>();
643 RewritePatternSet bundlePatterns(&getContext());
644 bundlePatterns.add<CanonicalizerOpLowering<PackBundleOp>>(&getContext());
645 bundlePatterns.add<CanonicalizerOpLowering<UnpackBundleOp>>(&getContext());
646 if (failed(applyPartialConversion(getOperation(), noBundlesTarget,
647 std::move(bundlePatterns))))
651 ConversionTarget pass1Target(*
ctxt);
652 pass1Target.addLegalDialect<comb::CombDialect>();
653 pass1Target.addLegalDialect<HWDialect>();
654 pass1Target.addLegalDialect<SVDialect>();
655 pass1Target.addLegalDialect<seq::SeqDialect>();
656 pass1Target.addLegalOp<WrapValidReadyOp, UnwrapValidReadyOp>();
658 pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
659 pass1Target.addIllegalOp<PipelineStageOp>();
660 pass1Target.addIllegalOp<CompressedManifestOp>();
664 RewritePatternSet pass1Patterns(
ctxt);
665 pass1Patterns.insert<PipelineStageLowering>(esiBuilder,
ctxt);
666 pass1Patterns.insert<WrapInterfaceLower>(
ctxt);
667 pass1Patterns.insert<UnwrapInterfaceLower>(
ctxt);
668 pass1Patterns.insert<CosimToHostLowering>(esiBuilder);
669 pass1Patterns.insert<CosimFromHostLowering>(esiBuilder);
670 pass1Patterns.insert<NullSourceOpLowering>(
ctxt);
673 pass1Patterns.insert<CosimManifestLowering>(
ctxt);
675 pass1Patterns.insert<ManifestRomLowering>(
ctxt);
681 applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
684 ConversionTarget pass2Target(*
ctxt);
685 pass2Target.addLegalDialect<comb::CombDialect>();
686 pass2Target.addLegalDialect<HWDialect>();
687 pass2Target.addLegalDialect<SVDialect>();
688 pass2Target.addIllegalDialect<ESIDialect>();
690 RewritePatternSet pass2Patterns(
ctxt);
691 pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(
ctxt);
692 pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(
ctxt);
693 pass2Patterns.insert<RemoveWrapUnwrap>(
ctxt);
695 applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
700 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)
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.