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