CIRCT  20.0.0git
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 
17 #include "circt/Dialect/HW/HWOps.h"
18 #include "circt/Dialect/SV/SVOps.h"
21 #include "circt/Support/LLVM.h"
22 #include "circt/Support/SymCache.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 
30 namespace circt {
31 namespace esi {
32 #define GEN_PASS_DEF_LOWERESITOHW
33 #include "circt/Dialect/ESI/ESIPasses.h.inc"
34 } // namespace esi
35 } // namespace circt
36 
37 using namespace circt;
38 using namespace circt::esi;
39 using namespace circt::esi::detail;
40 using namespace circt::hw;
41 using namespace circt::sv;
42 
43 namespace {
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.
47 struct PipelineStageLowering : public OpConversionPattern<PipelineStageOp> {
48 public:
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 
57 private:
58  ESIHWBuilder &builder;
59 };
60 } // anonymous namespace
61 
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());
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 
116 namespace {
117 struct NullSourceOpLowering : public OpConversionPattern<NullSourceOp> {
118 public:
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 
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();
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 
148 namespace {
149 /// Eliminate back-to-back wrap-unwraps to reduce the number of ESI channels.
150 struct RemoveWrapUnwrap : public ConversionPattern {
151 public:
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  if (wrap.getChanOutput().getUsers().empty()) {
163  auto c1 = rewriter.create<hw::ConstantOp>(wrap.getLoc(),
164  rewriter.getI1Type(), 1);
165  rewriter.replaceOp(wrap, {nullptr, c1});
166  return success();
167  }
168 
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'.");
175 
176  data = operands[0];
177  valid = operands[1];
178  ready = unwrap.getReady();
179  } else if (unwrap) {
180  wrap = dyn_cast<WrapValidReadyOp>(operands[0].getDefiningOp());
181  if (!wrap)
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();
187  data = wrap.getRawInput();
188  ready = operands[1];
189  } else {
190  return failure();
191  }
192 
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.";
197  });
198  rewriter.replaceOp(wrap, {nullptr, ready});
199  rewriter.replaceOp(unwrap, {data, valid});
200  return success();
201  }
202 };
203 } // anonymous namespace
204 
205 namespace {
206 /// Use the op canonicalizer to lower away the op. Assumes the canonicalizer
207 /// deletes the op.
208 template <typename Op>
209 struct CanonicalizerOpLowering : public OpConversionPattern<Op> {
210 public:
211  CanonicalizerOpLowering(MLIRContext *ctxt) : OpConversionPattern<Op>(ctxt) {}
212 
213  LogicalResult
214  matchAndRewrite(Op op, typename Op::Adaptor adaptor,
215  ConversionPatternRewriter &rewriter) const final {
216  if (failed(Op::canonicalize(op, rewriter)))
217  return rewriter.notifyMatchFailure(op->getLoc(), "canonicalizer failed");
218  return success();
219  }
220 };
221 } // anonymous namespace
222 
223 namespace {
224 struct ESItoHWPass : public circt::esi::impl::LowerESItoHWBase<ESItoHWPass> {
225  void runOnOperation() override;
226 };
227 } // anonymous namespace
228 
229 namespace {
230 /// Lower a `wrap.iface` to `wrap.vr` by extracting the wires then feeding the
231 /// new `wrap.vr`.
232 struct WrapInterfaceLower : public OpConversionPattern<WrapSVInterfaceOp> {
233 public:
234  using OpConversionPattern::OpConversionPattern;
235 
236  LogicalResult
237  matchAndRewrite(WrapSVInterfaceOp wrap, OpAdaptor adaptor,
238  ConversionPatternRewriter &rewriter) const final;
239 };
240 } // anonymous namespace
241 
242 LogicalResult
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";
249  });
250  auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
251  if (!sinkModport)
252  return failure();
253  auto ifaceInstance =
254  dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
255  if (!ifaceInstance)
256  return failure();
257 
258  auto loc = wrap.getLoc();
259  auto validSignal = rewriter.create<ReadInterfaceSignalOp>(
260  loc, ifaceInstance, ESIHWBuilder::validStr);
261  Value dataSignal;
262  dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
264  auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
265  rewriter.create<AssignInterfaceSignalOp>(
266  loc, ifaceInstance, ESIHWBuilder::readyStr, wrapVR.getReady());
267  rewriter.replaceOp(wrap, {wrapVR.getChanOutput()});
268  return success();
269 }
270 
271 namespace {
272 /// Lower an unwrap interface to just extract the wires and feed them into an
273 /// `unwrap.vr`.
274 struct UnwrapInterfaceLower : public OpConversionPattern<UnwrapSVInterfaceOp> {
275 public:
276  UnwrapInterfaceLower(MLIRContext *ctxt) : OpConversionPattern(ctxt) {}
277  using OpConversionPattern::OpConversionPattern;
278 
279  LogicalResult
280  matchAndRewrite(UnwrapSVInterfaceOp wrap, OpAdaptor adaptor,
281  ConversionPatternRewriter &rewriter) const final;
282 };
283 } // anonymous namespace
284 
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()
292  << "operands";
293  });
294 
295  auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
296  if (!sourceModport)
297  return failure();
298  auto ifaceInstance =
299  dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
300  if (!ifaceInstance)
301  return failure();
302 
303  auto loc = unwrap.getLoc();
304  auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
305  loc, ifaceInstance, ESIHWBuilder::readyStr);
306  auto unwrapVR =
307  rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
308  rewriter.create<AssignInterfaceSignalOp>(
309  loc, ifaceInstance, ESIHWBuilder::validStr, unwrapVR.getValid());
310 
311  rewriter.create<AssignInterfaceSignalOp>(
312  loc, ifaceInstance, ESIHWBuilder::dataStr, unwrapVR.getRawOutput());
313  rewriter.eraseOp(unwrap);
314  return success();
315 }
316 
317 namespace {
318 /// Lower `CosimEndpointOp` ops to a SystemVerilog extern module and a Capnp
319 /// gasket op.
320 struct CosimToHostLowering : public OpConversionPattern<CosimToHostEndpointOp> {
321 public:
322  CosimToHostLowering(ESIHWBuilder &b)
323  : OpConversionPattern(b.getContext(), 1), builder(b) {}
324 
325  using OpConversionPattern::OpConversionPattern;
326 
327  LogicalResult
328  matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
329  ConversionPatternRewriter &rewriter) const final;
330 
331 private:
332  ESIHWBuilder &builder;
333 };
334 } // anonymous namespace
335 
336 LogicalResult CosimToHostLowering::matchAndRewrite(
337  CosimToHostEndpointOp ep, OpAdaptor adaptor,
338  ConversionPatternRewriter &rewriter) const {
339  auto loc = ep.getLoc();
340  auto *ctxt = rewriter.getContext();
341  circt::BackedgeBuilder bb(rewriter, loc);
342 
343  Value toHost = adaptor.getToHost();
344  Type type = toHost.getType();
345  uint64_t width = getWidth(type);
346 
347  // Set all the parameters.
348  SmallVector<Attribute, 8> params;
349  params.push_back(ParamDeclAttr::get("ENDPOINT_ID", ep.getIdAttr()));
350  params.push_back(ParamDeclAttr::get("TO_HOST_TYPE_ID", getTypeID(type)));
351  params.push_back(ParamDeclAttr::get(
352  "TO_HOST_SIZE_BITS", rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
353 
354  // Set up the egest route to drive the EP's toHost ports.
355  auto sendReady = bb.get(rewriter.getI1Type());
356  UnwrapValidReadyOp unwrapSend =
357  rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
358  Value castedSendData;
359  if (width > 0)
360  castedSendData = rewriter.create<hw::BitcastOp>(
361  loc, rewriter.getIntegerType(width), unwrapSend.getRawOutput());
362  else
363  castedSendData = rewriter.create<hw::ConstantOp>(
364  loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(false));
365 
366  // Build or get the cached Cosim Endpoint module parameterization.
367  Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
368  HWModuleExternOp endpoint =
369  builder.declareCosimEndpointToHostModule(symTable);
370 
371  // Create replacement Cosim_Endpoint instance.
372  Value operands[] = {
373  adaptor.getClk(),
374  adaptor.getRst(),
375  unwrapSend.getValid(),
376  castedSendData,
377  };
378  auto cosimEpModule = rewriter.create<hw::InstanceOp>(
379  loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
380  sendReady.setValue(cosimEpModule.getResult(0));
381 
382  // Replace the CosimEndpointOp op.
383  rewriter.eraseOp(ep);
384 
385  return success();
386 }
387 
388 namespace {
389 /// Lower `CosimEndpointOp` ops to a SystemVerilog extern module and a Capnp
390 /// gasket op.
391 struct CosimFromHostLowering
392  : public OpConversionPattern<CosimFromHostEndpointOp> {
393 public:
394  CosimFromHostLowering(ESIHWBuilder &b)
395  : OpConversionPattern(b.getContext(), 1), builder(b) {}
396 
397  using OpConversionPattern::OpConversionPattern;
398 
399  LogicalResult
400  matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
401  ConversionPatternRewriter &rewriter) const final;
402 
403 private:
404  ESIHWBuilder &builder;
405 };
406 } // anonymous namespace
407 
408 LogicalResult CosimFromHostLowering::matchAndRewrite(
409  CosimFromHostEndpointOp ep, OpAdaptor adaptor,
410  ConversionPatternRewriter &rewriter) const {
411  auto loc = ep.getLoc();
412  auto *ctxt = rewriter.getContext();
413  circt::BackedgeBuilder bb(rewriter, loc);
414 
415  ChannelType type = ep.getFromHost().getType();
416  uint64_t width = getWidth(type);
417 
418  // Set all the parameters.
419  SmallVector<Attribute, 8> params;
420  params.push_back(ParamDeclAttr::get("ENDPOINT_ID", ep.getIdAttr()));
421  params.push_back(ParamDeclAttr::get("FROM_HOST_TYPE_ID", getTypeID(type)));
422  params.push_back(
423  ParamDeclAttr::get("FROM_HOST_SIZE_BITS",
424  rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
425 
426  // Get information necessary for injest path.
427  auto recvReady = bb.get(rewriter.getI1Type());
428 
429  // Build or get the cached Cosim Endpoint module parameterization.
430  Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
431  HWModuleExternOp endpoint =
432  builder.declareCosimEndpointFromHostModule(symTable);
433 
434  // Create replacement Cosim_Endpoint instance.
435  Value operands[] = {adaptor.getClk(), adaptor.getRst(), recvReady};
436  auto cosimEpModule = rewriter.create<hw::InstanceOp>(
437  loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
438 
439  // Set up the injest path.
440  Value recvDataFromCosim = cosimEpModule.getResult(1);
441  Value recvValidFromCosim = cosimEpModule.getResult(0);
442  Value castedRecvData;
443  if (width > 0)
444  castedRecvData =
445  rewriter.create<hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
446  else
447  castedRecvData = rewriter.create<hw::ConstantOp>(
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());
453 
454  // Replace the CosimEndpointOp op.
455  rewriter.replaceOp(ep, wrapRecv.getChanOutput());
456 
457  return success();
458 }
459 
460 namespace {
461 /// Lower `CompressedManifestOps` ops to a module containing an on-chip ROM.
462 /// Said module has registered input and outputs, so it has two cycles latency
463 /// between changing the address and the data being reflected on the output.
464 struct ManifestRomLowering : public OpConversionPattern<CompressedManifestOp> {
465 public:
466  using OpConversionPattern::OpConversionPattern;
467  constexpr static StringRef manifestRomName = "__ESI_Manifest_ROM";
468 
469  LogicalResult
470  matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
471  ConversionPatternRewriter &rewriter) const override;
472 
473 protected:
474  LogicalResult createRomModule(CompressedManifestOp op,
475  ConversionPatternRewriter &rewriter) const;
476 };
477 } // anonymous namespace
478 
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());
484 
485  // Find possible existing module (which may have been created as a dummy
486  // module) and erase it.
487  if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
488  if (!isa<hw::HWModuleExternOp>(existingExtern))
489  return rewriter.notifyMatchFailure(
490  op,
491  "Found " + manifestRomName + " but it wasn't an HWModuleExternOp");
492  rewriter.eraseOp(existingExtern);
493  }
494 
495  // Create the real module.
496  PortInfo ports[] = {
497  {{rewriter.getStringAttr("clk"), rewriter.getType<seq::ClockType>(),
499  {{rewriter.getStringAttr("address"), rewriter.getIntegerType(29),
501  {{rewriter.getStringAttr("data"), rewriter.getI64Type(),
503  };
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);
510 
511  // Manifest the compressed manifest into 64-bit words.
512  ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
513  SmallVector<uint64_t> words;
514  words.push_back(maniBytes.size());
515 
516  for (size_t i = 0; i < maniBytes.size() - 7; i += 8) {
517  uint64_t word = 0;
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);
521  }
522  size_t overHang = maniBytes.size() % 8;
523  if (overHang != 0) {
524  uint64_t word = 0;
525  for (size_t i = 0; i < overHang; ++i)
526  word |= static_cast<uint64_t>(maniBytes[maniBytes.size() - overHang + i])
527  << (i * 8);
528  words.push_back(word);
529  }
530 
531  // From the words, create an the register which will hold the manifest (and
532  // hopefully synthized to a ROM).
533  SmallVector<Attribute> wordAttrs;
534  for (uint64_t word : words)
535  wordAttrs.push_back(rewriter.getI64IntegerAttr(word));
536  auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
537  loc, hw::UnpackedArrayType::get(rewriter.getI64Type(), words.size()),
538  rewriter.getArrayAttr(wordAttrs));
539  auto manifestReg =
540  rewriter.create<sv::RegOp>(loc, manifestConstant.getType());
541  rewriter.create<sv::AssignOp>(loc, manifestReg, manifestConstant);
542 
543  // Slim down the address, register it, do the lookup, and register the output.
544  size_t addrBits = llvm::Log2_64_Ceil(words.size());
545  auto slimmedIdx =
546  rewriter.create<comb::ExtractOp>(loc, inputAddress, 0, addrBits);
547  Value inputAddresReg = rewriter.create<seq::CompRegOp>(loc, slimmedIdx, clk);
548  auto readIdx =
549  rewriter.create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
550  auto readData = rewriter.create<sv::ReadInOutOp>(loc, readIdx);
551  Value readDataReg = rewriter.create<seq::CompRegOp>(loc, readData, clk);
552  if (auto *term = romBody->getTerminator())
553  rewriter.eraseOp(term);
554  rewriter.create<hw::OutputOp>(loc, ValueRange{readDataReg});
555  return success();
556 }
557 
558 LogicalResult ManifestRomLowering::matchAndRewrite(
559  CompressedManifestOp op, OpAdaptor adaptor,
560  ConversionPatternRewriter &rewriter) const {
561  LogicalResult ret = createRomModule(op, rewriter);
562  rewriter.eraseOp(op);
563  return ret;
564 }
565 
566 namespace {
567 /// Lower `CompressedManifestOps` ops to a SystemVerilog module which sets the
568 /// Cosim manifest using a DPI support module.
569 struct CosimManifestLowering : public ManifestRomLowering {
570 public:
571  using ManifestRomLowering::ManifestRomLowering;
572 
573  LogicalResult
574  matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
575  ConversionPatternRewriter &rewriter) const final;
576 };
577 } // anonymous namespace
578 
579 LogicalResult CosimManifestLowering::matchAndRewrite(
580  CompressedManifestOp op, OpAdaptor adaptor,
581  ConversionPatternRewriter &rewriter) const {
582  MLIRContext *ctxt = rewriter.getContext();
583  Location loc = op.getLoc();
584 
585  // Cosim can optionally include a manifest simulation, so produce it in case
586  // the Cosim BSP wants it.
587  LogicalResult ret = createRomModule(op, rewriter);
588  if (failed(ret))
589  return ret;
590 
591  // Declare external module.
592  Attribute params[] = {
593  ParamDeclAttr::get("COMPRESSED_MANIFEST_SIZE", rewriter.getI32Type())};
594  PortInfo ports[] = {
595  {{rewriter.getStringAttr("compressed_manifest"),
596  rewriter.getType<hw::ArrayType>(
597  rewriter.getI8Type(),
599  rewriter.getStringAttr("COMPRESSED_MANIFEST_SIZE"),
600  rewriter.getI32Type())),
602  0},
603  };
604  rewriter.setInsertionPointToEnd(
605  op->getParentOfType<mlir::ModuleOp>().getBody());
606  auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
607  loc, rewriter.getStringAttr("Cosim_Manifest"), ports, "Cosim_Manifest",
608  ArrayAttr::get(ctxt, params));
609 
610  hw::ModulePortInfo portInfo({});
611  auto manifestMod = rewriter.create<hw::HWModuleOp>(
612  loc, rewriter.getStringAttr("__ESIManifest"), portInfo,
613  [&](OpBuilder &rewriter, const hw::HWModulePortAccessor &) {
614  // Assemble the manifest data into a constant.
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>(
619  loc, hw::ArrayType::get(rewriter.getI8Type(), bytes.size()),
620  rewriter.getArrayAttr(bytes));
621  auto manifestLogic =
622  rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
623  rewriter.create<sv::AssignOp>(loc, manifestLogic, manifestConstant);
624  auto manifest = rewriter.create<sv::ReadInOutOp>(loc, manifestLogic);
625 
626  // Then instantiate the external module.
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()))}));
633  });
634 
635  rewriter.setInsertionPoint(op);
636  rewriter.create<hw::InstanceOp>(loc, manifestMod, "__manifest",
637  ArrayRef<Value>({}));
638 
639  rewriter.eraseOp(op);
640  return success();
641 }
642 void ESItoHWPass::runOnOperation() {
643  auto top = getOperation();
644  auto *ctxt = &getContext();
645 
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))))
656  signalPassFailure();
657 
658  // Set up a conversion and give it a set of laws.
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>();
665 
666  pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
667  pass1Target.addIllegalOp<PipelineStageOp>();
668  pass1Target.addIllegalOp<CompressedManifestOp>();
669 
670  // Add all the conversion patterns.
671  ESIHWBuilder esiBuilder(top);
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);
679 
680  if (platform == Platform::cosim)
681  pass1Patterns.insert<CosimManifestLowering>(ctxt);
682  else if (platform == Platform::fpga)
683  pass1Patterns.insert<ManifestRomLowering>(ctxt);
684  else
685  pass1Patterns.insert<RemoveOpLowering<CompressedManifestOp>>(ctxt);
686 
687  // Run the conversion.
688  if (failed(
689  applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
690  signalPassFailure();
691 
692  ConversionTarget pass2Target(*ctxt);
693  pass2Target.addLegalDialect<comb::CombDialect>();
694  pass2Target.addLegalDialect<HWDialect>();
695  pass2Target.addLegalDialect<SVDialect>();
696  pass2Target.addIllegalDialect<ESIDialect>();
697 
698  RewritePatternSet pass2Patterns(ctxt);
699  pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(ctxt);
700  pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(ctxt);
701  pass2Patterns.insert<RemoveWrapUnwrap>(ctxt);
702  if (failed(
703  applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
704  signalPassFailure();
705 }
706 
707 std::unique_ptr<OperationPass<ModuleOp>> circt::esi::createESItoHWPass() {
708  return std::make_unique<ESItoHWPass>();
709 }
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
int32_t width
Definition: FIRRTL.cpp:36
@ Input
Definition: HW.h:35
@ Output
Definition: HW.h:35
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
def create(low_bit, result_type, input=None)
Definition: comb.py:187
def create(data_type, value)
Definition: hw.py:401
def create(data_type, value)
Definition: hw.py:393
def create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
Definition: seq.py:153
def create(dest, src)
Definition: sv.py:98
def create(value)
Definition: sv.py:106
Definition: sv.py:68
static LogicalResult canonicalize(Op op, PatternRewriter &rewriter)
Definition: VerifOps.cpp:66
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
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:184
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: DebugAnalysis.h:21
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 the name, type, direction of a module's ports.