CIRCT 20.0.0git
Loading...
Searching...
No Matches
ESILowerToHW.cpp
Go to the documentation of this file.
1//===- ESILowerToHW.cpp - Lower ESI to HW -----------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Lower to HW/SV conversions and pass.
10//
11//===----------------------------------------------------------------------===//
12
13#include "../PassDetails.h"
14
21#include "circt/Support/LLVM.h"
23
24#include "mlir/Transforms/DialectConversion.h"
25
26#include "llvm/ADT/StringExtras.h"
27#include "llvm/ADT/TypeSwitch.h"
28#include "llvm/Support/JSON.h"
29
30namespace circt {
31namespace esi {
32#define GEN_PASS_DEF_LOWERESITOHW
33#include "circt/Dialect/ESI/ESIPasses.h.inc"
34} // namespace esi
35} // namespace circt
36
37using namespace circt;
38using namespace circt::esi;
39using namespace circt::esi::detail;
40using namespace circt::hw;
41using namespace circt::sv;
42
43namespace {
44/// Lower PipelineStageOp ops to an HW implementation. Unwrap and re-wrap
45/// appropriately. Another conversion will take care merging the resulting
46/// adjacent wrap/unwrap ops.
47struct PipelineStageLowering : public OpConversionPattern<PipelineStageOp> {
48public:
49 PipelineStageLowering(ESIHWBuilder &builder, MLIRContext *ctxt)
50 : OpConversionPattern(ctxt), builder(builder) {}
51 using OpConversionPattern::OpConversionPattern;
52
53 LogicalResult
54 matchAndRewrite(PipelineStageOp stage, OpAdaptor adaptor,
55 ConversionPatternRewriter &rewriter) const final;
56
57private:
58 ESIHWBuilder &builder;
59};
60} // anonymous namespace
61
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());
67 if (!chPort)
68 return rewriter.notifyMatchFailure(stage, "stage had wrong type");
69 Operation *symTable = stage->getParentWithTrait<OpTrait::SymbolTable>();
70 auto stageModule = builder.declareStage(symTable, stage);
71
72 size_t width = circt::hw::getBitWidth(chPort.getInner());
73
74 ArrayAttr stageParams =
75 builder.getStageParameterList(rewriter.getUI32IntegerAttr(width));
76
77 // Unwrap the channel. The ready signal is a Value we haven't created yet,
78 // so create a temp value and replace it later. Give this constant an
79 // odd-looking type to make debugging easier.
80 circt::BackedgeBuilder back(rewriter, loc);
81 circt::Backedge wrapReady = back.get(rewriter.getI1Type());
82 auto unwrap =
83 rewriter.create<UnwrapValidReadyOp>(loc, stage.getInput(), wrapReady);
84
85 StringRef pipeStageName = "pipelineStage";
86 if (auto name = stage->getAttrOfType<StringAttr>("name"))
87 pipeStageName = name.getValue();
88
89 // Instantiate the "ESI_PipelineStage" external module.
90 circt::Backedge stageReady = back.get(rewriter.getI1Type());
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();
98
99 // Set a_ready (from the unwrap) back edge correctly to its output from
100 // stage.
101 wrapReady.setValue(stageInstResults[0]);
102 Value x, xValid;
103 x = stageInstResults[1];
104 xValid = stageInstResults[2];
105
106 // Wrap up the output of the HW stage module.
107 auto wrap = rewriter.create<WrapValidReadyOp>(
108 loc, chPort, rewriter.getI1Type(), x, xValid);
109 // Set the stages x_ready backedge correctly.
110 stageReady.setValue(wrap.getReady());
111
112 rewriter.replaceOp(stage, wrap.getChanOutput());
113 return success();
114}
115
116namespace {
117struct NullSourceOpLowering : public OpConversionPattern<NullSourceOp> {
118public:
119 NullSourceOpLowering(MLIRContext *ctxt) : OpConversionPattern(ctxt) {}
120 using OpConversionPattern::OpConversionPattern;
121
122 LogicalResult
123 matchAndRewrite(NullSourceOp nullop, OpAdaptor adaptor,
124 ConversionPatternRewriter &rewriter) const final;
125};
126} // anonymous namespace
127
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);
134 if (width == -1)
135 return rewriter.notifyMatchFailure(
136 nullop, "NullOp lowering only supports hw types");
137 auto valid =
138 rewriter.create<hw::ConstantOp>(nullop.getLoc(), rewriter.getI1Type(), 0);
139 auto zero =
140 rewriter.create<hw::ConstantOp>(loc, rewriter.getIntegerType(width), 0);
141 auto typedZero = rewriter.create<hw::BitcastOp>(loc, innerType, zero);
142 auto wrap = rewriter.create<WrapValidReadyOp>(loc, typedZero, valid);
143 wrap->setAttr("name", rewriter.getStringAttr("nullsource"));
144 rewriter.replaceOp(nullop, {wrap.getChanOutput()});
145 return success();
146}
147
148namespace {
149/// Eliminate back-to-back wrap-unwraps to reduce the number of ESI channels.
150struct RemoveWrapUnwrap : public ConversionPattern {
151public:
152 RemoveWrapUnwrap(MLIRContext *context)
153 : ConversionPattern(MatchAnyOpTypeTag(), /*benefit=*/1, context) {}
154
155 LogicalResult
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);
161 if (wrap) {
162 // Lower away snoop ops.
163 for (auto user : wrap.getChanOutput().getUsers())
164 if (auto snoop = dyn_cast<SnoopValidReadyOp>(user))
165 rewriter.replaceOp(
166 snoop, {wrap.getValid(), wrap.getReady(), wrap.getRawInput()});
167
168 if (ChannelType::hasNoConsumers(wrap.getChanOutput())) {
169 auto c1 = rewriter.create<hw::ConstantOp>(wrap.getLoc(),
170 rewriter.getI1Type(), 1);
171 rewriter.replaceOp(wrap, {nullptr, c1});
172 return success();
173 }
174
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())
181 ->getOwner())))
182 return rewriter.notifyMatchFailure(
183 wrap, "This conversion only supports wrap-unwrap back-to-back. "
184 "Could not find 'unwrap'.");
185
186 data = operands[0];
187 valid = operands[1];
188 ready = unwrap.getReady();
189 } else if (unwrap) {
190 wrap = dyn_cast<WrapValidReadyOp>(operands[0].getDefiningOp());
191 if (!wrap)
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();
197 data = wrap.getRawInput();
198 ready = operands[1];
199
200 // Lower away snoop ops.
201 for (auto user : operands[0].getUsers())
202 if (auto snoop = dyn_cast<SnoopValidReadyOp>(user))
203 rewriter.replaceOp(snoop, {valid, ready, data});
204 } else {
205 return failure();
206 }
207
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.";
212 });
213 rewriter.replaceOp(wrap, {nullptr, ready});
214 rewriter.replaceOp(unwrap, {data, valid});
215 return success();
216 }
217};
218} // anonymous namespace
219
220namespace {
221/// Use the op canonicalizer to lower away the op. Assumes the canonicalizer
222/// deletes the op.
223template <typename Op>
224struct CanonicalizerOpLowering : public OpConversionPattern<Op> {
225public:
226 CanonicalizerOpLowering(MLIRContext *ctxt) : OpConversionPattern<Op>(ctxt) {}
227
228 LogicalResult
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");
233 return success();
234 }
235};
236} // anonymous namespace
237
238namespace {
239struct ESItoHWPass : public circt::esi::impl::LowerESItoHWBase<ESItoHWPass> {
240 void runOnOperation() override;
241};
242} // anonymous namespace
243
244namespace {
245/// Lower a `wrap.iface` to `wrap.vr` by extracting the wires then feeding the
246/// new `wrap.vr`.
247struct WrapInterfaceLower : public OpConversionPattern<WrapSVInterfaceOp> {
248public:
249 using OpConversionPattern::OpConversionPattern;
250
251 LogicalResult
252 matchAndRewrite(WrapSVInterfaceOp wrap, OpAdaptor adaptor,
253 ConversionPatternRewriter &rewriter) const final;
254};
255} // anonymous namespace
256
257LogicalResult
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";
264 });
265 auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
266 if (!sinkModport)
267 return failure();
268 auto ifaceInstance =
269 dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
270 if (!ifaceInstance)
271 return failure();
272
273 auto loc = wrap.getLoc();
274 auto validSignal = rewriter.create<ReadInterfaceSignalOp>(
275 loc, ifaceInstance, ESIHWBuilder::validStr);
276 Value dataSignal;
277 dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
279 auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
280 rewriter.create<AssignInterfaceSignalOp>(
281 loc, ifaceInstance, ESIHWBuilder::readyStr, wrapVR.getReady());
282 rewriter.replaceOp(wrap, {wrapVR.getChanOutput()});
283 return success();
284}
285
286namespace {
287/// Lower an unwrap interface to just extract the wires and feed them into an
288/// `unwrap.vr`.
289struct UnwrapInterfaceLower : public OpConversionPattern<UnwrapSVInterfaceOp> {
290public:
291 UnwrapInterfaceLower(MLIRContext *ctxt) : OpConversionPattern(ctxt) {}
292 using OpConversionPattern::OpConversionPattern;
293
294 LogicalResult
295 matchAndRewrite(UnwrapSVInterfaceOp wrap, OpAdaptor adaptor,
296 ConversionPatternRewriter &rewriter) const final;
297};
298} // anonymous namespace
299
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()
307 << "operands";
308 });
309
310 auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
311 if (!sourceModport)
312 return failure();
313 auto ifaceInstance =
314 dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
315 if (!ifaceInstance)
316 return failure();
317
318 auto loc = unwrap.getLoc();
319 auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
320 loc, ifaceInstance, ESIHWBuilder::readyStr);
321 auto unwrapVR =
322 rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
323 rewriter.create<AssignInterfaceSignalOp>(
324 loc, ifaceInstance, ESIHWBuilder::validStr, unwrapVR.getValid());
325
326 rewriter.create<AssignInterfaceSignalOp>(
327 loc, ifaceInstance, ESIHWBuilder::dataStr, unwrapVR.getRawOutput());
328 rewriter.eraseOp(unwrap);
329 return success();
330}
331
332namespace {
333/// Lower `CosimEndpointOp` ops to a SystemVerilog extern module and a Capnp
334/// gasket op.
335struct CosimToHostLowering : public OpConversionPattern<CosimToHostEndpointOp> {
336public:
337 CosimToHostLowering(ESIHWBuilder &b)
338 : OpConversionPattern(b.getContext(), 1), builder(b) {}
339
340 using OpConversionPattern::OpConversionPattern;
341
342 LogicalResult
343 matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
344 ConversionPatternRewriter &rewriter) const final;
345
346private:
347 ESIHWBuilder &builder;
348};
349} // anonymous namespace
350
351LogicalResult CosimToHostLowering::matchAndRewrite(
352 CosimToHostEndpointOp ep, OpAdaptor adaptor,
353 ConversionPatternRewriter &rewriter) const {
354 auto loc = ep.getLoc();
355 auto *ctxt = rewriter.getContext();
356 circt::BackedgeBuilder bb(rewriter, loc);
357
358 Value toHost = adaptor.getToHost();
359 Type type = toHost.getType();
360 uint64_t width = getWidth(type);
361
362 // Set all the parameters.
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)));
368
369 // Set up the egest route to drive the EP's toHost ports.
370 auto sendReady = bb.get(rewriter.getI1Type());
371 UnwrapValidReadyOp unwrapSend =
372 rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
373 Value castedSendData;
374 if (width > 0)
375 castedSendData = rewriter.create<hw::BitcastOp>(
376 loc, rewriter.getIntegerType(width), unwrapSend.getRawOutput());
377 else
378 castedSendData = rewriter.create<hw::ConstantOp>(
379 loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(false));
380
381 // Build or get the cached Cosim Endpoint module parameterization.
382 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
383 HWModuleExternOp endpoint =
384 builder.declareCosimEndpointToHostModule(symTable);
385
386 // Create replacement Cosim_Endpoint instance.
387 Value operands[] = {
388 adaptor.getClk(),
389 adaptor.getRst(),
390 unwrapSend.getValid(),
391 castedSendData,
392 };
393 auto cosimEpModule = rewriter.create<hw::InstanceOp>(
394 loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
395 sendReady.setValue(cosimEpModule.getResult(0));
396
397 // Replace the CosimEndpointOp op.
398 rewriter.eraseOp(ep);
399
400 return success();
401}
402
403namespace {
404/// Lower `CosimEndpointOp` ops to a SystemVerilog extern module and a Capnp
405/// gasket op.
406struct CosimFromHostLowering
407 : public OpConversionPattern<CosimFromHostEndpointOp> {
408public:
409 CosimFromHostLowering(ESIHWBuilder &b)
410 : OpConversionPattern(b.getContext(), 1), builder(b) {}
411
412 using OpConversionPattern::OpConversionPattern;
413
414 LogicalResult
415 matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
416 ConversionPatternRewriter &rewriter) const final;
417
418private:
419 ESIHWBuilder &builder;
420};
421} // anonymous namespace
422
423LogicalResult CosimFromHostLowering::matchAndRewrite(
424 CosimFromHostEndpointOp ep, OpAdaptor adaptor,
425 ConversionPatternRewriter &rewriter) const {
426 auto loc = ep.getLoc();
427 auto *ctxt = rewriter.getContext();
428 circt::BackedgeBuilder bb(rewriter, loc);
429
430 ChannelType type = ep.getFromHost().getType();
431 uint64_t width = getWidth(type);
432
433 // Set all the parameters.
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)));
437 params.push_back(
438 ParamDeclAttr::get("FROM_HOST_SIZE_BITS",
439 rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
440
441 // Get information necessary for injest path.
442 auto recvReady = bb.get(rewriter.getI1Type());
443
444 // Build or get the cached Cosim Endpoint module parameterization.
445 Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
446 HWModuleExternOp endpoint =
447 builder.declareCosimEndpointFromHostModule(symTable);
448
449 // Create replacement Cosim_Endpoint instance.
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));
453
454 // Set up the injest path.
455 Value recvDataFromCosim = cosimEpModule.getResult(1);
456 Value recvValidFromCosim = cosimEpModule.getResult(0);
457 Value castedRecvData;
458 if (width > 0)
459 castedRecvData =
460 rewriter.create<hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
461 else
462 castedRecvData = rewriter.create<hw::ConstantOp>(
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());
468
469 // Replace the CosimEndpointOp op.
470 rewriter.replaceOp(ep, wrapRecv.getChanOutput());
471
472 return success();
473}
474
475namespace {
476/// Lower `CompressedManifestOps` ops to a module containing an on-chip ROM.
477/// Said module has registered input and outputs, so it has two cycles latency
478/// between changing the address and the data being reflected on the output.
479struct ManifestRomLowering : public OpConversionPattern<CompressedManifestOp> {
480public:
481 using OpConversionPattern::OpConversionPattern;
482 constexpr static StringRef manifestRomName = "__ESI_Manifest_ROM";
483
484 LogicalResult
485 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
486 ConversionPatternRewriter &rewriter) const override;
487
488protected:
489 LogicalResult createRomModule(CompressedManifestOp op,
490 ConversionPatternRewriter &rewriter) const;
491};
492} // anonymous namespace
493
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());
499
500 // Find possible existing module (which may have been created as a dummy
501 // module) and erase it.
502 if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
503 if (!isa<hw::HWModuleExternOp>(existingExtern))
504 return rewriter.notifyMatchFailure(
505 op,
506 "Found " + manifestRomName + " but it wasn't an HWModuleExternOp");
507 rewriter.eraseOp(existingExtern);
508 }
509
510 // Create the real module.
511 PortInfo ports[] = {
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}},
518 };
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);
525
526 // Manifest the compressed manifest into 64-bit words.
527 ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
528 SmallVector<uint64_t> words;
529 words.push_back(maniBytes.size());
530
531 for (size_t i = 0; i < maniBytes.size() - 7; i += 8) {
532 uint64_t word = 0;
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);
536 }
537 size_t overHang = maniBytes.size() % 8;
538 if (overHang != 0) {
539 uint64_t word = 0;
540 for (size_t i = 0; i < overHang; ++i)
541 word |= static_cast<uint64_t>(maniBytes[maniBytes.size() - overHang + i])
542 << (i * 8);
543 words.push_back(word);
544 }
545
546 // From the words, create an the register which will hold the manifest (and
547 // hopefully synthized to a ROM).
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));
554 auto manifestReg =
555 rewriter.create<sv::RegOp>(loc, manifestConstant.getType());
556 rewriter.create<sv::AssignOp>(loc, manifestReg, manifestConstant);
557
558 // Slim down the address, register it, do the lookup, and register the output.
559 size_t addrBits = llvm::Log2_64_Ceil(words.size());
560 auto slimmedIdx =
561 rewriter.create<comb::ExtractOp>(loc, inputAddress, 0, addrBits);
562 Value inputAddresReg = rewriter.create<seq::CompRegOp>(loc, slimmedIdx, clk);
563 auto readIdx =
564 rewriter.create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
565 auto readData = rewriter.create<sv::ReadInOutOp>(loc, readIdx);
566 Value readDataReg = rewriter.create<seq::CompRegOp>(loc, readData, clk);
567 if (auto *term = romBody->getTerminator())
568 rewriter.eraseOp(term);
569 rewriter.create<hw::OutputOp>(loc, ValueRange{readDataReg});
570 return success();
571}
572
573LogicalResult ManifestRomLowering::matchAndRewrite(
574 CompressedManifestOp op, OpAdaptor adaptor,
575 ConversionPatternRewriter &rewriter) const {
576 LogicalResult ret = createRomModule(op, rewriter);
577 rewriter.eraseOp(op);
578 return ret;
579}
580
581namespace {
582/// Lower `CompressedManifestOps` ops to a SystemVerilog module which sets the
583/// Cosim manifest using a DPI support module.
584struct CosimManifestLowering : public ManifestRomLowering {
585public:
586 using ManifestRomLowering::ManifestRomLowering;
587
588 LogicalResult
589 matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
590 ConversionPatternRewriter &rewriter) const final;
591};
592} // anonymous namespace
593
594LogicalResult CosimManifestLowering::matchAndRewrite(
595 CompressedManifestOp op, OpAdaptor adaptor,
596 ConversionPatternRewriter &rewriter) const {
597 MLIRContext *ctxt = rewriter.getContext();
598 Location loc = op.getLoc();
599
600 // Cosim can optionally include a manifest simulation, so produce it in case
601 // the Cosim BSP wants it.
602 LogicalResult ret = createRomModule(op, rewriter);
603 if (failed(ret))
604 return ret;
605
606 // Declare external module.
607 Attribute params[] = {
608 ParamDeclAttr::get("COMPRESSED_MANIFEST_SIZE", rewriter.getI32Type())};
609 PortInfo ports[] = {
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},
617 0},
618 };
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));
624
625 hw::ModulePortInfo portInfo({});
626 auto manifestMod = rewriter.create<hw::HWModuleOp>(
627 loc, rewriter.getStringAttr("__ESIManifest"), portInfo,
628 [&](OpBuilder &rewriter, const hw::HWModulePortAccessor &) {
629 // Assemble the manifest data into a constant.
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));
636 auto manifestLogic =
637 rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
638 rewriter.create<sv::AssignOp>(loc, manifestLogic, manifestConstant);
639 auto manifest = rewriter.create<sv::ReadInOutOp>(loc, manifestLogic);
640
641 // Then instantiate the external module.
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()))}));
648 });
649
650 rewriter.setInsertionPoint(op);
651 rewriter.create<hw::InstanceOp>(loc, manifestMod, "__manifest",
652 ArrayRef<Value>({}));
653
654 rewriter.eraseOp(op);
655 return success();
656}
657void ESItoHWPass::runOnOperation() {
658 auto top = getOperation();
659 auto *ctxt = &getContext();
660
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))))
671 signalPassFailure();
672
673 // Set up a conversion and give it a set of laws.
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>();
680
681 pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
682 pass1Target.addIllegalOp<PipelineStageOp>();
683 pass1Target.addIllegalOp<CompressedManifestOp>();
684
685 // Add all the conversion patterns.
686 ESIHWBuilder esiBuilder(top);
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);
694
695 if (platform == Platform::cosim)
696 pass1Patterns.insert<CosimManifestLowering>(ctxt);
697 else if (platform == Platform::fpga)
698 pass1Patterns.insert<ManifestRomLowering>(ctxt);
699 else
700 pass1Patterns.insert<RemoveOpLowering<CompressedManifestOp>>(ctxt);
701
702 // Run the conversion.
703 if (failed(
704 applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
705 signalPassFailure();
706
707 ConversionTarget pass2Target(*ctxt);
708 pass2Target.addLegalDialect<comb::CombDialect>();
709 pass2Target.addLegalDialect<HWDialect>();
710 pass2Target.addLegalDialect<SVDialect>();
711 pass2Target.addIllegalDialect<ESIDialect>();
712
713 RewritePatternSet pass2Patterns(ctxt);
714 pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(ctxt);
715 pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(ctxt);
716 pass2Patterns.insert<RemoveWrapUnwrap>(ctxt);
717 if (failed(
718 applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
719 signalPassFailure();
720}
721
722std::unique_ptr<OperationPass<ModuleOp>> circt::esi::createESItoHWPass() {
723 return std::make_unique<ESItoHWPass>();
724}
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
static EvaluatorValuePtr unwrap(OMEvaluatorValue c)
Definition OM.cpp:113
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.
Definition PassDetails.h:56
static constexpr char validStr[]
Definition PassDetails.h:76
static constexpr char readyStr[]
Definition PassDetails.h:77
static constexpr char dataStr[]
Definition PassDetails.h:76
create(low_bit, result_type, input=None)
Definition comb.py:187
create(data_type, value)
Definition hw.py:441
create(data_type, value)
Definition hw.py:433
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
Definition seq.py:157
create(dest, src)
Definition sv.py:98
create(value)
Definition sv.py:106
Definition sv.py:68
uint64_t getWidth(Type t)
Definition ESIPasses.cpp:32
StringAttr getTypeID(Type t)
Definition ESIPasses.cpp:26
std::unique_ptr< OperationPass< ModuleOp > > createESItoHWPass()
mlir::Type innerType(mlir::Type type)
Definition ESITypes.cpp:227
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
Definition HWTypes.cpp:110
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition esi.py:1
static constexpr char fpga[]
Definition ESIPasses.h:29
static constexpr char cosim[]
Definition ESIPasses.h:28
Generic pattern for removing an op during pattern conversion.
Definition PassDetails.h:40
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.