CIRCT  19.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)
51  using OpConversionPattern::OpConversionPattern;
52 
53  LogicalResult
54  matchAndRewrite(PipelineStageOp stage, OpAdaptor adaptor,
55  ConversionPatternRewriter &rewriter) const final;
56 
57 private:
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:
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:
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(30),
501  {{rewriter.getStringAttr("data"), rewriter.getI32Type(),
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 32-bit words.
512  ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
513  SmallVector<uint32_t> words;
514  words.push_back(maniBytes.size());
515 
516  for (size_t i = 0; i < maniBytes.size() - 3; i += 4) {
517  uint32_t word = maniBytes[i] | (maniBytes[i + 1] << 8) |
518  (maniBytes[i + 2] << 16) | (maniBytes[i + 3] << 24);
519  words.push_back(word);
520  }
521  size_t overHang = maniBytes.size() % 4;
522  if (overHang != 0) {
523  uint32_t word = 0;
524  for (size_t i = 0; i < overHang; ++i)
525  word |= maniBytes[maniBytes.size() - overHang + i] << (i * 8);
526  words.push_back(word);
527  }
528 
529  // From the words, create an the register which will hold the manifest (and
530  // hopefully synthized to a ROM).
531  SmallVector<Attribute> wordAttrs;
532  for (uint32_t word : words)
533  wordAttrs.push_back(rewriter.getI32IntegerAttr(word));
534  auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
535  loc, hw::UnpackedArrayType::get(rewriter.getI32Type(), words.size()),
536  rewriter.getArrayAttr(wordAttrs));
537  auto manifestReg =
538  rewriter.create<sv::RegOp>(loc, manifestConstant.getType());
539  rewriter.create<sv::AssignOp>(loc, manifestReg, manifestConstant);
540 
541  // Slim down the address, register it, do the lookup, and register the output.
542  size_t addrBits = llvm::Log2_64_Ceil(words.size());
543  auto slimmedIdx =
544  rewriter.create<comb::ExtractOp>(loc, inputAddress, 0, addrBits);
545  Value inputAddresReg = rewriter.create<seq::CompRegOp>(loc, slimmedIdx, clk);
546  auto readIdx =
547  rewriter.create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
548  auto readData = rewriter.create<sv::ReadInOutOp>(loc, readIdx);
549  Value readDataReg = rewriter.create<seq::CompRegOp>(loc, readData, clk);
550  if (auto *term = romBody->getTerminator())
551  rewriter.eraseOp(term);
552  rewriter.create<hw::OutputOp>(loc, ValueRange{readDataReg});
553  return success();
554 }
555 
556 LogicalResult ManifestRomLowering::matchAndRewrite(
557  CompressedManifestOp op, OpAdaptor adaptor,
558  ConversionPatternRewriter &rewriter) const {
559  LogicalResult ret = createRomModule(op, rewriter);
560  rewriter.eraseOp(op);
561  return ret;
562 }
563 
564 namespace {
565 /// Lower `CompressedManifestOps` ops to a SystemVerilog module which sets the
566 /// Cosim manifest using a DPI support module.
567 struct CosimManifestLowering : public ManifestRomLowering {
568 public:
569  using ManifestRomLowering::ManifestRomLowering;
570 
571  LogicalResult
572  matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
573  ConversionPatternRewriter &rewriter) const final;
574 };
575 } // anonymous namespace
576 
577 LogicalResult CosimManifestLowering::matchAndRewrite(
578  CompressedManifestOp op, OpAdaptor adaptor,
579  ConversionPatternRewriter &rewriter) const {
580  MLIRContext *ctxt = rewriter.getContext();
581  Location loc = op.getLoc();
582 
583  // Cosim can optionally include a manifest simulation, so produce it in case
584  // the Cosim BSP wants it.
585  LogicalResult ret = createRomModule(op, rewriter);
586  if (failed(ret))
587  return ret;
588 
589  // Declare external module.
590  Attribute params[] = {
591  ParamDeclAttr::get("COMPRESSED_MANIFEST_SIZE", rewriter.getI32Type())};
592  PortInfo ports[] = {
593  {{rewriter.getStringAttr("compressed_manifest"),
594  rewriter.getType<hw::ArrayType>(
595  rewriter.getI8Type(),
597  rewriter.getStringAttr("COMPRESSED_MANIFEST_SIZE"),
598  rewriter.getI32Type())),
600  0},
601  };
602  rewriter.setInsertionPointToEnd(
603  op->getParentOfType<mlir::ModuleOp>().getBody());
604  auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
605  loc, rewriter.getStringAttr("Cosim_Manifest"), ports, "Cosim_Manifest",
606  ArrayAttr::get(ctxt, params));
607 
608  hw::ModulePortInfo portInfo({});
609  auto manifestMod = rewriter.create<hw::HWModuleOp>(
610  loc, rewriter.getStringAttr("__ESIManifest"), portInfo,
611  [&](OpBuilder &rewriter, const hw::HWModulePortAccessor &) {
612  // Assemble the manifest data into a constant.
613  SmallVector<Attribute> bytes;
614  for (uint8_t b : op.getCompressedManifest().getData())
615  bytes.push_back(rewriter.getI8IntegerAttr(b));
616  auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
617  loc, hw::ArrayType::get(rewriter.getI8Type(), bytes.size()),
618  rewriter.getArrayAttr(bytes));
619  auto manifestLogic =
620  rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
621  rewriter.create<sv::AssignOp>(loc, manifestLogic, manifestConstant);
622  auto manifest = rewriter.create<sv::ReadInOutOp>(loc, manifestLogic);
623 
624  // Then instantiate the external module.
625  rewriter.create<hw::InstanceOp>(
626  loc, cosimManifestExternModule, "__manifest",
627  ArrayRef<Value>({manifest}),
628  rewriter.getArrayAttr({ParamDeclAttr::get(
629  "COMPRESSED_MANIFEST_SIZE",
630  rewriter.getI32IntegerAttr(bytes.size()))}));
631  });
632 
633  rewriter.setInsertionPoint(op);
634  rewriter.create<hw::InstanceOp>(loc, manifestMod, "__manifest",
635  ArrayRef<Value>({}));
636 
637  rewriter.eraseOp(op);
638  return success();
639 }
640 void ESItoHWPass::runOnOperation() {
641  auto top = getOperation();
642  auto *ctxt = &getContext();
643 
644  ConversionTarget noBundlesTarget(*ctxt);
645  noBundlesTarget.markUnknownOpDynamicallyLegal(
646  [](Operation *) { return true; });
647  noBundlesTarget.addIllegalOp<PackBundleOp>();
648  noBundlesTarget.addIllegalOp<UnpackBundleOp>();
649  RewritePatternSet bundlePatterns(&getContext());
650  bundlePatterns.add<CanonicalizerOpLowering<PackBundleOp>>(&getContext());
651  bundlePatterns.add<CanonicalizerOpLowering<UnpackBundleOp>>(&getContext());
652  if (failed(applyPartialConversion(getOperation(), noBundlesTarget,
653  std::move(bundlePatterns))))
654  signalPassFailure();
655 
656  // Set up a conversion and give it a set of laws.
657  ConversionTarget pass1Target(*ctxt);
658  pass1Target.addLegalDialect<comb::CombDialect>();
659  pass1Target.addLegalDialect<HWDialect>();
660  pass1Target.addLegalDialect<SVDialect>();
661  pass1Target.addLegalDialect<seq::SeqDialect>();
662  pass1Target.addLegalOp<WrapValidReadyOp, UnwrapValidReadyOp>();
663 
664  pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
665  pass1Target.addIllegalOp<PipelineStageOp>();
666  pass1Target.addIllegalOp<CompressedManifestOp>();
667 
668  // Add all the conversion patterns.
669  ESIHWBuilder esiBuilder(top);
670  RewritePatternSet pass1Patterns(ctxt);
671  pass1Patterns.insert<PipelineStageLowering>(esiBuilder, ctxt);
672  pass1Patterns.insert<WrapInterfaceLower>(ctxt);
673  pass1Patterns.insert<UnwrapInterfaceLower>(ctxt);
674  pass1Patterns.insert<CosimToHostLowering>(esiBuilder);
675  pass1Patterns.insert<CosimFromHostLowering>(esiBuilder);
676  pass1Patterns.insert<NullSourceOpLowering>(ctxt);
677 
678  if (platform == Platform::cosim)
679  pass1Patterns.insert<CosimManifestLowering>(ctxt);
680  else if (platform == Platform::fpga)
681  pass1Patterns.insert<ManifestRomLowering>(ctxt);
682  else
683  pass1Patterns.insert<RemoveOpLowering<CompressedManifestOp>>(ctxt);
684 
685  // Run the conversion.
686  if (failed(
687  applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
688  signalPassFailure();
689 
690  ConversionTarget pass2Target(*ctxt);
691  pass2Target.addLegalDialect<comb::CombDialect>();
692  pass2Target.addLegalDialect<HWDialect>();
693  pass2Target.addLegalDialect<SVDialect>();
694  pass2Target.addIllegalDialect<ESIDialect>();
695 
696  RewritePatternSet pass2Patterns(ctxt);
697  pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(ctxt);
698  pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(ctxt);
699  pass2Patterns.insert<RemoveWrapUnwrap>(ctxt);
700  if (failed(
701  applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
702  signalPassFailure();
703 }
704 
705 std::unique_ptr<OperationPass<ModuleOp>> circt::esi::createESItoHWPass() {
706  return std::make_unique<ESItoHWPass>();
707 }
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:96
Builder builder
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:137
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:64
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:54
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:109
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.