CIRCT  19.0.0git
LowerSeqHLMem.cpp
Go to the documentation of this file.
1 //===- LowerSeqHLMem.cpp - seq.hlmem lowering -----------------------------===//
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 // This pass pattern matches lowering patterns on seq.hlmem ops and referencing
10 // ports.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "PassDetails.h"
15 #include "circt/Dialect/SV/SVOps.h"
18 #include "mlir/Transforms/DialectConversion.h"
19 #include "llvm/ADT/TypeSwitch.h"
20 
21 using namespace circt;
22 using namespace seq;
23 
24 namespace {
25 
26 struct SimpleBehavioralMemoryLowering
27  : public OpConversionPattern<seq::HLMemOp> {
28  // A simple behavioral SV implementation of a HLMemOp. This is intended as a
29  // fall-back pattern if any other higher benefit/target-specific patterns
30  // failed to match.
31 public:
32  using OpConversionPattern::OpConversionPattern;
33 
34  LogicalResult
35  matchAndRewrite(seq::HLMemOp mem, OpAdaptor adaptor,
36  ConversionPatternRewriter &rewriter) const final {
37 
38  // Only support unidimensional memories.
39  auto memType = mem.getMemType();
40  if (memType.getShape().size() != 1)
41  return rewriter.notifyMatchFailure(
42  mem, "only unidimensional memories are supported");
43  auto size = memType.getShape()[0];
44 
45  // Gather up the referencing ops.
46  llvm::SmallVector<seq::ReadPortOp> readOps;
47  llvm::SmallVector<seq::WritePortOp> writeOps;
48  for (auto *user : mem.getHandle().getUsers()) {
49  auto res = llvm::TypeSwitch<Operation *, LogicalResult>(user)
50  .Case([&](seq::ReadPortOp op) {
51  readOps.push_back(op);
52  return success();
53  })
54  .Case([&](seq::WritePortOp op) {
55  writeOps.push_back(op);
56  return success();
57  })
58  .Default([&](Operation *op) { return failure(); });
59  if (failed(res))
60  return rewriter.notifyMatchFailure(user, "unsupported port type");
61  }
62 
63  auto clk = mem.getClk();
64  auto rst = mem.getRst();
65  auto memName = mem.getName();
66 
67  // Create the SV memory.
68  hw::UnpackedArrayType memArrType =
69  hw::UnpackedArrayType::get(memType.getElementType(), size);
70  auto svMem =
71  rewriter.create<sv::RegOp>(mem.getLoc(), memArrType, mem.getNameAttr())
72  .getResult();
73 
74  // Create write ports by gathering up the write port inputs and
75  // materializing the writes inside a single always ff block.
76  struct WriteTuple {
77  Location loc;
78  Value addr;
79  Value data;
80  Value en;
81  };
82  llvm::SmallVector<WriteTuple> writeTuples;
83  for (auto writeOp : writeOps) {
84  if (writeOp.getLatency() != 1)
85  return rewriter.notifyMatchFailure(
86  writeOp, "only supports write ports with latency == 1");
87  auto addr = writeOp.getAddresses()[0];
88  auto data = writeOp.getInData();
89  auto en = writeOp.getWrEn();
90  writeTuples.push_back({writeOp.getLoc(), addr, data, en});
91  rewriter.eraseOp(writeOp);
92  }
93 
94  auto hwClk = rewriter.create<seq::FromClockOp>(clk.getLoc(), clk);
95  rewriter.create<sv::AlwaysFFOp>(
96  mem.getLoc(), sv::EventControl::AtPosEdge, hwClk, ResetType::SyncReset,
97  sv::EventControl::AtPosEdge, rst, [&] {
98  for (auto [loc, address, data, en] : writeTuples) {
99  Value a = address, d = data; // So the lambda can capture.
100  Location l = loc;
101  // Perform write upon write enable being high.
102  rewriter.create<sv::IfOp>(loc, en, [&] {
103  Value memLoc =
104  rewriter.create<sv::ArrayIndexInOutOp>(l, svMem, a);
105  rewriter.create<sv::PAssignOp>(l, memLoc, d);
106  });
107  }
108  });
109 
110  // Create read ports. When latency > 1, we perform the read 1 cycle before
111  // the latency deadline, and register the read output. By doing so, we avoid
112  // start a critical path right after the combinational read.
113  // TODO: When latency > 2, and assuming that the exact read time is flexible
114  // within the latency window, we could decide on whether to buffer the read
115  // data or the read address more, based on which is narrower (saving area).
116  for (auto [ri, readOp] : llvm::enumerate(readOps)) {
117  rewriter.setInsertionPointAfter(readOp);
118  auto loc = readOp.getLoc();
119 
120  auto readAddress = readOp.getAddresses()[0];
121  unsigned latency = readOp.getLatency();
122  unsigned addressDelayCycles = latency - 1;
123  if (latency > 0) {
124  // Materialize any delays on the read address.
125  for (unsigned i = 0; i < addressDelayCycles; ++i) {
126  readAddress = rewriter.create<seq::CompRegOp>(
127  loc, readAddress, clk,
128  rewriter.getStringAttr(memName + "_rdaddr" + std::to_string(ri) +
129  "_dly" + std::to_string(i)));
130  }
131  }
132 
133  // Create a combinational read.
134  Value memLoc =
135  rewriter.create<sv::ArrayIndexInOutOp>(loc, svMem, readAddress);
136  Value readData = rewriter.create<sv::ReadInOutOp>(loc, memLoc);
137  if (latency > 0) {
138  // Register the read data.
139  readData = rewriter.create<seq::CompRegOp>(
140  loc, readData, clk,
141  rewriter.getStringAttr(memName + "_rd" + std::to_string(ri) +
142  "_reg"));
143  }
144  rewriter.replaceOp(readOp, {readData});
145  }
146 
147  rewriter.eraseOp(mem);
148  return success();
149  }
150 };
151 
152 #define GEN_PASS_DEF_LOWERSEQHLMEM
153 #include "circt/Dialect/Seq/SeqPasses.h.inc"
154 
155 struct LowerSeqHLMemPass : public impl::LowerSeqHLMemBase<LowerSeqHLMemPass> {
156  void runOnOperation() override;
157 };
158 
159 } // namespace
160 
161 void LowerSeqHLMemPass::runOnOperation() {
162  hw::HWModuleOp top = getOperation();
163 
164  MLIRContext &ctxt = getContext();
165  ConversionTarget target(ctxt);
166 
167  // Lowering patterns must lower away all HLMem-related operations.
168  target.addIllegalOp<seq::HLMemOp, seq::ReadPortOp, seq::WritePortOp>();
169  target.addLegalDialect<sv::SVDialect, seq::SeqDialect>();
170  RewritePatternSet patterns(&ctxt);
171  patterns.add<SimpleBehavioralMemoryLowering>(&ctxt);
172 
173  if (failed(applyPartialConversion(top, target, std::move(patterns))))
174  signalPassFailure();
175 }
176 
177 std::unique_ptr<Pass> circt::seq::createLowerSeqHLMemPass() {
178  return std::make_unique<LowerSeqHLMemPass>();
179 }
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
std::unique_ptr< mlir::Pass > createLowerSeqHLMemPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: seq.py:1