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 
18 #include "circt/Dialect/HW/HWOps.h"
19 #include "circt/Dialect/SV/SVOps.h"
22 #include "circt/Support/LLVM.h"
23 #include "circt/Support/SymCache.h"
24 
25 #include "mlir/Transforms/DialectConversion.h"
26 
27 #include "llvm/ADT/StringExtras.h"
28 #include "llvm/ADT/TypeSwitch.h"
29 #include "llvm/Support/JSON.h"
30 
31 using namespace circt;
32 using namespace circt::esi;
33 using namespace circt::esi::detail;
34 using namespace circt::hw;
35 using namespace circt::sv;
36 
37 namespace {
38 /// Lower PipelineStageOp ops to an HW implementation. Unwrap and re-wrap
39 /// appropriately. Another conversion will take care merging the resulting
40 /// adjacent wrap/unwrap ops.
41 struct PipelineStageLowering : public OpConversionPattern<PipelineStageOp> {
42 public:
43  PipelineStageLowering(ESIHWBuilder &builder, MLIRContext *ctxt)
45  using OpConversionPattern::OpConversionPattern;
46 
47  LogicalResult
48  matchAndRewrite(PipelineStageOp stage, OpAdaptor adaptor,
49  ConversionPatternRewriter &rewriter) const final;
50 
51 private:
53 };
54 } // anonymous namespace
55 
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());
61  if (!chPort)
62  return rewriter.notifyMatchFailure(stage, "stage had wrong type");
63  Operation *symTable = stage->getParentWithTrait<OpTrait::SymbolTable>();
64  auto stageModule = builder.declareStage(symTable, stage);
65 
66  size_t width = circt::hw::getBitWidth(chPort.getInner());
67 
68  ArrayAttr stageParams =
69  builder.getStageParameterList(rewriter.getUI32IntegerAttr(width));
70 
71  // Unwrap the channel. The ready signal is a Value we haven't created yet,
72  // so create a temp value and replace it later. Give this constant an
73  // odd-looking type to make debugging easier.
74  circt::BackedgeBuilder back(rewriter, loc);
75  circt::Backedge wrapReady = back.get(rewriter.getI1Type());
76  auto unwrap =
77  rewriter.create<UnwrapValidReadyOp>(loc, stage.getInput(), wrapReady);
78 
79  StringRef pipeStageName = "pipelineStage";
80  if (auto name = stage->getAttrOfType<StringAttr>("name"))
81  pipeStageName = name.getValue();
82 
83  // Instantiate the "ESI_PipelineStage" external module.
84  circt::Backedge stageReady = back.get(rewriter.getI1Type());
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();
92 
93  // Set a_ready (from the unwrap) back edge correctly to its output from
94  // stage.
95  wrapReady.setValue(stageInstResults[0]);
96  Value x, xValid;
97  x = stageInstResults[1];
98  xValid = stageInstResults[2];
99 
100  // Wrap up the output of the HW stage module.
101  auto wrap = rewriter.create<WrapValidReadyOp>(
102  loc, chPort, rewriter.getI1Type(), x, xValid);
103  // Set the stages x_ready backedge correctly.
104  stageReady.setValue(wrap.getReady());
105 
106  rewriter.replaceOp(stage, wrap.getChanOutput());
107  return success();
108 }
109 
110 namespace {
111 struct NullSourceOpLowering : public OpConversionPattern<NullSourceOp> {
112 public:
113  NullSourceOpLowering(MLIRContext *ctxt) : OpConversionPattern(ctxt) {}
114  using OpConversionPattern::OpConversionPattern;
115 
116  LogicalResult
117  matchAndRewrite(NullSourceOp nullop, OpAdaptor adaptor,
118  ConversionPatternRewriter &rewriter) const final;
119 };
120 } // anonymous namespace
121 
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();
127  int64_t width = hw::getBitWidth(innerType);
128  if (width == -1)
129  return rewriter.notifyMatchFailure(
130  nullop, "NullOp lowering only supports hw types");
131  auto valid =
132  rewriter.create<hw::ConstantOp>(nullop.getLoc(), rewriter.getI1Type(), 0);
133  auto zero =
134  rewriter.create<hw::ConstantOp>(loc, rewriter.getIntegerType(width), 0);
135  auto typedZero = rewriter.create<hw::BitcastOp>(loc, innerType, zero);
136  auto wrap = rewriter.create<WrapValidReadyOp>(loc, typedZero, valid);
137  wrap->setAttr("name", rewriter.getStringAttr("nullsource"));
138  rewriter.replaceOp(nullop, {wrap.getChanOutput()});
139  return success();
140 }
141 
142 namespace {
143 /// Eliminate back-to-back wrap-unwraps to reduce the number of ESI channels.
144 struct RemoveWrapUnwrap : public ConversionPattern {
145 public:
146  RemoveWrapUnwrap(MLIRContext *context)
147  : ConversionPattern(MatchAnyOpTypeTag(), /*benefit=*/1, context) {}
148 
149  LogicalResult
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);
155  if (wrap) {
156  if (wrap.getChanOutput().getUsers().empty()) {
157  auto c1 = rewriter.create<hw::ConstantOp>(wrap.getLoc(),
158  rewriter.getI1Type(), 1);
159  rewriter.replaceOp(wrap, {nullptr, c1});
160  return success();
161  }
162 
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'.");
169 
170  data = operands[0];
171  valid = operands[1];
172  ready = unwrap.getReady();
173  } else if (unwrap) {
174  wrap = dyn_cast<WrapValidReadyOp>(operands[0].getDefiningOp());
175  if (!wrap)
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();
181  data = wrap.getRawInput();
182  ready = operands[1];
183  } else {
184  return failure();
185  }
186 
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.";
191  });
192  rewriter.replaceOp(wrap, {nullptr, ready});
193  rewriter.replaceOp(unwrap, {data, valid});
194  return success();
195  }
196 };
197 } // anonymous namespace
198 
199 namespace {
200 /// Use the op canonicalizer to lower away the op. Assumes the canonicalizer
201 /// deletes the op.
202 template <typename Op>
203 struct CanonicalizerOpLowering : public OpConversionPattern<Op> {
204 public:
205  CanonicalizerOpLowering(MLIRContext *ctxt) : OpConversionPattern<Op>(ctxt) {}
206 
207  LogicalResult
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");
212  return success();
213  }
214 };
215 } // anonymous namespace
216 
217 namespace {
218 struct ESItoHWPass : public LowerESItoHWBase<ESItoHWPass> {
219  void runOnOperation() override;
220 };
221 } // anonymous namespace
222 
223 namespace {
224 /// Lower a `wrap.iface` to `wrap.vr` by extracting the wires then feeding the
225 /// new `wrap.vr`.
226 struct WrapInterfaceLower : public OpConversionPattern<WrapSVInterfaceOp> {
227 public:
228  using OpConversionPattern::OpConversionPattern;
229 
230  LogicalResult
231  matchAndRewrite(WrapSVInterfaceOp wrap, OpAdaptor adaptor,
232  ConversionPatternRewriter &rewriter) const final;
233 };
234 } // anonymous namespace
235 
236 LogicalResult
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";
243  });
244  auto sinkModport = dyn_cast<GetModportOp>(operands[0].getDefiningOp());
245  if (!sinkModport)
246  return failure();
247  auto ifaceInstance =
248  dyn_cast<InterfaceInstanceOp>(sinkModport.getIface().getDefiningOp());
249  if (!ifaceInstance)
250  return failure();
251 
252  auto loc = wrap.getLoc();
253  auto validSignal = rewriter.create<ReadInterfaceSignalOp>(
254  loc, ifaceInstance, ESIHWBuilder::validStr);
255  Value dataSignal;
256  dataSignal = rewriter.create<ReadInterfaceSignalOp>(loc, ifaceInstance,
258  auto wrapVR = rewriter.create<WrapValidReadyOp>(loc, dataSignal, validSignal);
259  rewriter.create<AssignInterfaceSignalOp>(
260  loc, ifaceInstance, ESIHWBuilder::readyStr, wrapVR.getReady());
261  rewriter.replaceOp(wrap, {wrapVR.getChanOutput()});
262  return success();
263 }
264 
265 namespace {
266 /// Lower an unwrap interface to just extract the wires and feed them into an
267 /// `unwrap.vr`.
268 struct UnwrapInterfaceLower : public OpConversionPattern<UnwrapSVInterfaceOp> {
269 public:
270  UnwrapInterfaceLower(MLIRContext *ctxt) : OpConversionPattern(ctxt) {}
271  using OpConversionPattern::OpConversionPattern;
272 
273  LogicalResult
274  matchAndRewrite(UnwrapSVInterfaceOp wrap, OpAdaptor adaptor,
275  ConversionPatternRewriter &rewriter) const final;
276 };
277 } // anonymous namespace
278 
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()
286  << "operands";
287  });
288 
289  auto sourceModport = dyn_cast<GetModportOp>(operands[1].getDefiningOp());
290  if (!sourceModport)
291  return failure();
292  auto ifaceInstance =
293  dyn_cast<InterfaceInstanceOp>(sourceModport.getIface().getDefiningOp());
294  if (!ifaceInstance)
295  return failure();
296 
297  auto loc = unwrap.getLoc();
298  auto readySignal = rewriter.create<ReadInterfaceSignalOp>(
299  loc, ifaceInstance, ESIHWBuilder::readyStr);
300  auto unwrapVR =
301  rewriter.create<UnwrapValidReadyOp>(loc, operands[0], readySignal);
302  rewriter.create<AssignInterfaceSignalOp>(
303  loc, ifaceInstance, ESIHWBuilder::validStr, unwrapVR.getValid());
304 
305  rewriter.create<AssignInterfaceSignalOp>(
306  loc, ifaceInstance, ESIHWBuilder::dataStr, unwrapVR.getRawOutput());
307  rewriter.eraseOp(unwrap);
308  return success();
309 }
310 
311 namespace {
312 /// Lower `CosimEndpointOp` ops to a SystemVerilog extern module and a Capnp
313 /// gasket op.
314 struct CosimToHostLowering : public OpConversionPattern<CosimToHostEndpointOp> {
315 public:
316  CosimToHostLowering(ESIHWBuilder &b)
317  : OpConversionPattern(b.getContext(), 1), builder(b) {}
318 
319  using OpConversionPattern::OpConversionPattern;
320 
321  LogicalResult
322  matchAndRewrite(CosimToHostEndpointOp, OpAdaptor adaptor,
323  ConversionPatternRewriter &rewriter) const final;
324 
325 private:
327 };
328 } // anonymous namespace
329 
330 LogicalResult CosimToHostLowering::matchAndRewrite(
331  CosimToHostEndpointOp ep, OpAdaptor adaptor,
332  ConversionPatternRewriter &rewriter) const {
333  auto loc = ep.getLoc();
334  auto *ctxt = rewriter.getContext();
335  circt::BackedgeBuilder bb(rewriter, loc);
336 
337  Value toHost = adaptor.getToHost();
338  Type type = toHost.getType();
339  uint64_t width = getWidth(type);
340 
341  // Set all the parameters.
342  SmallVector<Attribute, 8> params;
343  params.push_back(ParamDeclAttr::get("ENDPOINT_ID", ep.getIdAttr()));
344  params.push_back(ParamDeclAttr::get("TO_HOST_TYPE_ID", getTypeID(type)));
345  params.push_back(ParamDeclAttr::get(
346  "TO_HOST_SIZE_BITS", rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
347 
348  // Set up the egest route to drive the EP's toHost ports.
349  auto sendReady = bb.get(rewriter.getI1Type());
350  UnwrapValidReadyOp unwrapSend =
351  rewriter.create<UnwrapValidReadyOp>(loc, toHost, sendReady);
352  Value castedSendData;
353  if (width > 0)
354  castedSendData = rewriter.create<hw::BitcastOp>(
355  loc, rewriter.getIntegerType(width), unwrapSend.getRawOutput());
356  else
357  castedSendData = rewriter.create<hw::ConstantOp>(
358  loc, rewriter.getIntegerType(1), rewriter.getBoolAttr(false));
359 
360  // Build or get the cached Cosim Endpoint module parameterization.
361  Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
362  HWModuleExternOp endpoint =
363  builder.declareCosimEndpointToHostModule(symTable);
364 
365  // Create replacement Cosim_Endpoint instance.
366  Value operands[] = {
367  adaptor.getClk(),
368  adaptor.getRst(),
369  unwrapSend.getValid(),
370  castedSendData,
371  };
372  auto cosimEpModule = rewriter.create<hw::InstanceOp>(
373  loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
374  sendReady.setValue(cosimEpModule.getResult(0));
375 
376  // Replace the CosimEndpointOp op.
377  rewriter.eraseOp(ep);
378 
379  return success();
380 }
381 
382 namespace {
383 /// Lower `CosimEndpointOp` ops to a SystemVerilog extern module and a Capnp
384 /// gasket op.
385 struct CosimFromHostLowering
386  : public OpConversionPattern<CosimFromHostEndpointOp> {
387 public:
388  CosimFromHostLowering(ESIHWBuilder &b)
389  : OpConversionPattern(b.getContext(), 1), builder(b) {}
390 
391  using OpConversionPattern::OpConversionPattern;
392 
393  LogicalResult
394  matchAndRewrite(CosimFromHostEndpointOp, OpAdaptor adaptor,
395  ConversionPatternRewriter &rewriter) const final;
396 
397 private:
399 };
400 } // anonymous namespace
401 
402 LogicalResult CosimFromHostLowering::matchAndRewrite(
403  CosimFromHostEndpointOp ep, OpAdaptor adaptor,
404  ConversionPatternRewriter &rewriter) const {
405  auto loc = ep.getLoc();
406  auto *ctxt = rewriter.getContext();
407  circt::BackedgeBuilder bb(rewriter, loc);
408 
409  ChannelType type = ep.getFromHost().getType();
410  uint64_t width = getWidth(type);
411 
412  // Set all the parameters.
413  SmallVector<Attribute, 8> params;
414  params.push_back(ParamDeclAttr::get("ENDPOINT_ID", ep.getIdAttr()));
415  params.push_back(ParamDeclAttr::get("FROM_HOST_TYPE_ID", getTypeID(type)));
416  params.push_back(
417  ParamDeclAttr::get("FROM_HOST_SIZE_BITS",
418  rewriter.getI32IntegerAttr(width > 0 ? width : 1)));
419 
420  // Get information necessary for injest path.
421  auto recvReady = bb.get(rewriter.getI1Type());
422 
423  // Build or get the cached Cosim Endpoint module parameterization.
424  Operation *symTable = ep->getParentWithTrait<OpTrait::SymbolTable>();
425  HWModuleExternOp endpoint =
426  builder.declareCosimEndpointFromHostModule(symTable);
427 
428  // Create replacement Cosim_Endpoint instance.
429  Value operands[] = {adaptor.getClk(), adaptor.getRst(), recvReady};
430  auto cosimEpModule = rewriter.create<hw::InstanceOp>(
431  loc, endpoint, ep.getIdAttr(), operands, ArrayAttr::get(ctxt, params));
432 
433  // Set up the injest path.
434  Value recvDataFromCosim = cosimEpModule.getResult(1);
435  Value recvValidFromCosim = cosimEpModule.getResult(0);
436  Value castedRecvData;
437  if (width > 0)
438  castedRecvData =
439  rewriter.create<hw::BitcastOp>(loc, type.getInner(), recvDataFromCosim);
440  else
441  castedRecvData = rewriter.create<hw::ConstantOp>(
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());
447 
448  // Replace the CosimEndpointOp op.
449  rewriter.replaceOp(ep, wrapRecv.getChanOutput());
450 
451  return success();
452 }
453 
454 namespace {
455 /// Lower `CompressedManifestOps` ops to a module containing an on-chip ROM.
456 /// Said module has registered input and outputs, so it has two cycles latency
457 /// between changing the address and the data being reflected on the output.
458 struct ManifestRomLowering : public OpConversionPattern<CompressedManifestOp> {
459 public:
460  using OpConversionPattern::OpConversionPattern;
461  constexpr static StringRef manifestRomName = "__ESI_Manifest_ROM";
462 
463  LogicalResult
464  matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
465  ConversionPatternRewriter &rewriter) const override;
466 
467 protected:
468  LogicalResult createRomModule(CompressedManifestOp op,
469  ConversionPatternRewriter &rewriter) const;
470 };
471 } // anonymous namespace
472 
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());
478 
479  // Find possible existing module (which may have been created as a dummy
480  // module) and erase it.
481  if (Operation *existingExtern = mlirModBody.lookupSymbol(manifestRomName)) {
482  if (!isa<hw::HWModuleExternOp>(existingExtern))
483  return rewriter.notifyMatchFailure(
484  op,
485  "Found " + manifestRomName + " but it wasn't an HWModuleExternOp");
486  rewriter.eraseOp(existingExtern);
487  }
488 
489  // Create the real module.
490  PortInfo ports[] = {
491  {{rewriter.getStringAttr("clk"), rewriter.getType<seq::ClockType>(),
493  {{rewriter.getStringAttr("address"), rewriter.getIntegerType(30),
495  {{rewriter.getStringAttr("data"), rewriter.getI32Type(),
497  };
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);
504 
505  // Manifest the compressed manifest into 32-bit words.
506  ArrayRef<uint8_t> maniBytes = op.getCompressedManifest().getData();
507  SmallVector<uint32_t> words;
508  words.push_back(maniBytes.size());
509 
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);
514  }
515  size_t overHang = maniBytes.size() % 4;
516  if (overHang != 0) {
517  uint32_t word = 0;
518  for (size_t i = 0; i < overHang; ++i)
519  word |= maniBytes[maniBytes.size() - overHang + i] << (i * 8);
520  words.push_back(word);
521  }
522 
523  // From the words, create an the register which will hold the manifest (and
524  // hopefully synthized to a ROM).
525  SmallVector<Attribute> wordAttrs;
526  for (uint32_t word : words)
527  wordAttrs.push_back(rewriter.getI32IntegerAttr(word));
528  auto manifestConstant = rewriter.create<hw::AggregateConstantOp>(
529  loc, hw::UnpackedArrayType::get(rewriter.getI32Type(), words.size()),
530  rewriter.getArrayAttr(wordAttrs));
531  auto manifestReg =
532  rewriter.create<sv::RegOp>(loc, manifestConstant.getType());
533  rewriter.create<sv::AssignOp>(loc, manifestReg, manifestConstant);
534 
535  // Slim down the address, register it, do the lookup, and register the output.
536  size_t addrBits = llvm::Log2_64_Ceil(words.size());
537  auto slimmedIdx =
538  rewriter.create<comb::ExtractOp>(loc, inputAddress, 0, addrBits);
539  Value inputAddresReg = rewriter.create<seq::CompRegOp>(loc, slimmedIdx, clk);
540  auto readIdx =
541  rewriter.create<sv::ArrayIndexInOutOp>(loc, manifestReg, inputAddresReg);
542  auto readData = rewriter.create<sv::ReadInOutOp>(loc, readIdx);
543  Value readDataReg = rewriter.create<seq::CompRegOp>(loc, readData, clk);
544  if (auto *term = romBody->getTerminator())
545  rewriter.eraseOp(term);
546  rewriter.create<hw::OutputOp>(loc, ValueRange{readDataReg});
547  return success();
548 }
549 
550 LogicalResult ManifestRomLowering::matchAndRewrite(
551  CompressedManifestOp op, OpAdaptor adaptor,
552  ConversionPatternRewriter &rewriter) const {
553  LogicalResult ret = createRomModule(op, rewriter);
554  rewriter.eraseOp(op);
555  return ret;
556 }
557 
558 namespace {
559 /// Lower `CompressedManifestOps` ops to a SystemVerilog module which sets the
560 /// Cosim manifest using a DPI support module.
561 struct CosimManifestLowering : public ManifestRomLowering {
562 public:
563  using ManifestRomLowering::ManifestRomLowering;
564 
565  LogicalResult
566  matchAndRewrite(CompressedManifestOp, OpAdaptor adaptor,
567  ConversionPatternRewriter &rewriter) const final;
568 };
569 } // anonymous namespace
570 
571 LogicalResult CosimManifestLowering::matchAndRewrite(
572  CompressedManifestOp op, OpAdaptor adaptor,
573  ConversionPatternRewriter &rewriter) const {
574  MLIRContext *ctxt = rewriter.getContext();
575  Location loc = op.getLoc();
576 
577  // Cosim can optionally include a manifest simulation, so produce it in case
578  // the Cosim BSP wants it.
579  LogicalResult ret = createRomModule(op, rewriter);
580  if (failed(ret))
581  return ret;
582 
583  // Declare external module.
584  Attribute params[] = {
585  ParamDeclAttr::get("COMPRESSED_MANIFEST_SIZE", rewriter.getI32Type())};
586  PortInfo ports[] = {
587  {{rewriter.getStringAttr("compressed_manifest"),
588  rewriter.getType<hw::ArrayType>(
589  rewriter.getI8Type(),
591  rewriter.getStringAttr("COMPRESSED_MANIFEST_SIZE"),
592  rewriter.getI32Type())),
594  0},
595  };
596  rewriter.setInsertionPointToEnd(
597  op->getParentOfType<mlir::ModuleOp>().getBody());
598  auto cosimManifestExternModule = rewriter.create<HWModuleExternOp>(
599  loc, rewriter.getStringAttr("Cosim_Manifest"), ports, "Cosim_Manifest",
600  ArrayAttr::get(ctxt, params));
601 
602  hw::ModulePortInfo portInfo({});
603  auto manifestMod = rewriter.create<hw::HWModuleOp>(
604  loc, rewriter.getStringAttr("__ESIManifest"), portInfo,
605  [&](OpBuilder &rewriter, const hw::HWModulePortAccessor &) {
606  // Assemble the manifest data into a constant.
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>(
611  loc, hw::ArrayType::get(rewriter.getI8Type(), bytes.size()),
612  rewriter.getArrayAttr(bytes));
613  auto manifestLogic =
614  rewriter.create<sv::LogicOp>(loc, manifestConstant.getType());
615  rewriter.create<sv::AssignOp>(loc, manifestLogic, manifestConstant);
616  auto manifest = rewriter.create<sv::ReadInOutOp>(loc, manifestLogic);
617 
618  // Then instantiate the external module.
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()))}));
625  });
626 
627  rewriter.setInsertionPoint(op);
628  rewriter.create<hw::InstanceOp>(loc, manifestMod, "__manifest",
629  ArrayRef<Value>({}));
630 
631  rewriter.eraseOp(op);
632  return success();
633 }
634 void ESItoHWPass::runOnOperation() {
635  auto top = getOperation();
636  auto *ctxt = &getContext();
637 
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))))
648  signalPassFailure();
649 
650  // Set up a conversion and give it a set of laws.
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>();
657 
658  pass1Target.addIllegalOp<WrapSVInterfaceOp, UnwrapSVInterfaceOp>();
659  pass1Target.addIllegalOp<PipelineStageOp>();
660  pass1Target.addIllegalOp<CompressedManifestOp>();
661 
662  // Add all the conversion patterns.
663  ESIHWBuilder esiBuilder(top);
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);
671 
672  if (platform == Platform::cosim)
673  pass1Patterns.insert<CosimManifestLowering>(ctxt);
674  else if (platform == Platform::fpga)
675  pass1Patterns.insert<ManifestRomLowering>(ctxt);
676  else
677  pass1Patterns.insert<RemoveOpLowering<CompressedManifestOp>>(ctxt);
678 
679  // Run the conversion.
680  if (failed(
681  applyPartialConversion(top, pass1Target, std::move(pass1Patterns))))
682  signalPassFailure();
683 
684  ConversionTarget pass2Target(*ctxt);
685  pass2Target.addLegalDialect<comb::CombDialect>();
686  pass2Target.addLegalDialect<HWDialect>();
687  pass2Target.addLegalDialect<SVDialect>();
688  pass2Target.addIllegalDialect<ESIDialect>();
689 
690  RewritePatternSet pass2Patterns(ctxt);
691  pass2Patterns.insert<CanonicalizerOpLowering<UnwrapFIFOOp>>(ctxt);
692  pass2Patterns.insert<CanonicalizerOpLowering<WrapFIFOOp>>(ctxt);
693  pass2Patterns.insert<RemoveWrapUnwrap>(ctxt);
694  if (failed(
695  applyPartialConversion(top, pass2Target, std::move(pass2Patterns))))
696  signalPassFailure();
697 }
698 
699 std::unique_ptr<OperationPass<ModuleOp>> circt::esi::createESItoHWPass() {
700  return std::make_unique<ESItoHWPass>();
701 }
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:65
static constexpr char validStr[]
Definition: PassDetails.h:85
static constexpr char readyStr[]
Definition: PassDetails.h:86
static constexpr char dataStr[]
Definition: PassDetails.h:85
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
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:34
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:102
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
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:49
This holds the name, type, direction of a module's ports.