CIRCT  20.0.0git
LowerSeqFIFO.cpp
Go to the documentation of this file.
1 //===- LowerSeqFIFO.cpp - seq.fifo 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 
10 #include "circt/Dialect/SV/SVOps.h"
15 #include "mlir/Pass/Pass.h"
16 #include "mlir/Transforms/DialectConversion.h"
17 #include "llvm/ADT/TypeSwitch.h"
18 
19 namespace circt {
20 namespace seq {
21 #define GEN_PASS_DEF_LOWERSEQFIFO
22 #include "circt/Dialect/Seq/SeqPasses.h.inc"
23 } // namespace seq
24 } // namespace circt
25 
26 using namespace circt;
27 using namespace seq;
28 
29 namespace {
30 
31 struct FIFOLowering : public OpConversionPattern<seq::FIFOOp> {
32 public:
33  using OpConversionPattern::OpConversionPattern;
34 
35  LogicalResult
36  matchAndRewrite(seq::FIFOOp mem, OpAdaptor adaptor,
37  ConversionPatternRewriter &rewriter) const final {
38  Location loc = mem.getLoc();
39  Type eltType = adaptor.getInput().getType();
40  Value clk = adaptor.getClk();
41  Value rst = adaptor.getRst();
42  BackedgeBuilder bb(rewriter, loc);
43  size_t depth = mem.getDepth();
44  Type countType = rewriter.getIntegerType(llvm::Log2_64_Ceil(depth + 1));
45  Type ptrType = rewriter.getIntegerType(llvm::Log2_64_Ceil(depth));
46  Backedge rdAddrNext = bb.get(ptrType);
47  Backedge wrAddrNext = bb.get(ptrType);
48  Backedge nextCount = bb.get(countType);
49 
50  // ====== Some constants ======
51  Value countTcFull = rewriter.create<hw::ConstantOp>(loc, countType, depth);
52  Value countTc1 = rewriter.create<hw::ConstantOp>(loc, countType, 1);
53  Value countTc0 = rewriter.create<hw::ConstantOp>(loc, countType, 0);
54  Value ptrTc0 = rewriter.create<hw::ConstantOp>(loc, ptrType, 0);
55  Value ptrTc1 = rewriter.create<hw::ConstantOp>(loc, ptrType, 1);
56  Value ptrTcFull = rewriter.create<hw::ConstantOp>(loc, ptrType, depth);
57 
58  // ====== Hardware units ======
59  Value count = rewriter.create<seq::CompRegOp>(
60  loc, nextCount, clk, rst,
61  rewriter.create<hw::ConstantOp>(loc, countType, 0), "fifo_count");
62  seq::HLMemOp hlmem = rewriter.create<seq::HLMemOp>(
63  loc, clk, rst, "fifo_mem",
64  llvm::SmallVector<int64_t>{static_cast<int64_t>(depth)}, eltType);
65  Value rdAddr = rewriter.create<seq::CompRegOp>(
66  loc, rdAddrNext, clk, rst,
67  rewriter.create<hw::ConstantOp>(loc, ptrType, 0), "fifo_rd_addr");
68  Value wrAddr = rewriter.create<seq::CompRegOp>(
69  loc, wrAddrNext, clk, rst,
70  rewriter.create<hw::ConstantOp>(loc, ptrType, 0), "fifo_wr_addr");
71 
72  Value readData = rewriter.create<seq::ReadPortOp>(
73  loc, hlmem, llvm::SmallVector<Value>{rdAddr}, adaptor.getRdEn(),
74  mem.getRdLatency());
75  rewriter.create<seq::WritePortOp>(loc, hlmem,
76  llvm::SmallVector<Value>{wrAddr},
77  adaptor.getInput(), adaptor.getWrEn(),
78  /*latency*/ 1);
79 
80  // ====== some more constants =====
81  comb::ICmpOp fifoFull = rewriter.create<comb::ICmpOp>(
82  loc, comb::ICmpPredicate::eq, count, countTcFull);
83  fifoFull->setAttr("sv.namehint", rewriter.getStringAttr("fifo_full"));
84  comb::ICmpOp fifoEmpty = rewriter.create<comb::ICmpOp>(
85  loc, comb::ICmpPredicate::eq, count, countTc0);
86  fifoEmpty->setAttr("sv.namehint", rewriter.getStringAttr("fifo_empty"));
87 
88  // ====== Next-state count ======
89  auto notRdEn = comb::createOrFoldNot(loc, adaptor.getRdEn(), rewriter);
90  auto notWrEn = comb::createOrFoldNot(loc, adaptor.getWrEn(), rewriter);
91  Value rdEnNandWrEn = rewriter.create<comb::AndOp>(loc, notRdEn, notWrEn);
92  Value rdEnAndNotWrEn =
93  rewriter.create<comb::AndOp>(loc, adaptor.getRdEn(), notWrEn);
94  Value wrEnAndNotRdEn =
95  rewriter.create<comb::AndOp>(loc, adaptor.getWrEn(), notRdEn);
96 
97  auto countEqTcFull = rewriter.create<comb::ICmpOp>(
98  loc, comb::ICmpPredicate::eq, count, countTcFull);
99  auto addCountTc1 = rewriter.create<comb::AddOp>(loc, count, countTc1);
100  Value wrEnNext = rewriter.create<comb::MuxOp>(loc, countEqTcFull,
101  // keep value
102  count,
103  // increment
104  addCountTc1);
105  auto countEqTc0 = rewriter.create<comb::ICmpOp>(
106  loc, comb::ICmpPredicate::eq, count, countTc0);
107  auto subCountTc1 = rewriter.create<comb::SubOp>(loc, count, countTc1);
108 
109  Value rdEnNext = rewriter.create<comb::MuxOp>(loc, countEqTc0,
110  // keep value
111  count,
112  // decrement
113  subCountTc1);
114 
115  auto nextInnerMux =
116  rewriter.create<comb::MuxOp>(loc, rdEnAndNotWrEn, rdEnNext, count);
117  auto nextMux = rewriter.create<comb::MuxOp>(loc, wrEnAndNotRdEn, wrEnNext,
118  nextInnerMux);
119  nextCount.setValue(rewriter.create<comb::MuxOp>(
120  loc, rdEnNandWrEn, /*keep value*/ count, nextMux));
121  static_cast<Value>(nextCount).getDefiningOp()->setAttr(
122  "sv.namehint", rewriter.getStringAttr("fifo_count_next"));
123 
124  // ====== Read/write pointers ======
125  Value wrAndNotFull = rewriter.create<comb::AndOp>(
126  loc, adaptor.getWrEn(), comb::createOrFoldNot(loc, fifoFull, rewriter));
127  auto addWrAddrPtrTc1 = rewriter.create<comb::AddOp>(loc, wrAddr, ptrTc1);
128 
129  auto wrAddrNextNoRollover = rewriter.create<comb::MuxOp>(
130  loc, wrAndNotFull, addWrAddrPtrTc1, wrAddr);
131  auto isMaxAddrWr = rewriter.create<comb::ICmpOp>(
132  loc, comb::ICmpPredicate::eq, wrAddrNextNoRollover, ptrTcFull);
133  wrAddrNext.setValue(rewriter.create<comb::MuxOp>(loc, isMaxAddrWr, ptrTc0,
134  wrAddrNextNoRollover));
135  static_cast<Value>(wrAddrNext)
136  .getDefiningOp()
137  ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_wr_addr_next"));
138 
139  auto notFifoEmpty = comb::createOrFoldNot(loc, fifoEmpty, rewriter);
140  Value rdAndNotEmpty =
141  rewriter.create<comb::AndOp>(loc, adaptor.getRdEn(), notFifoEmpty);
142  auto addRdAddrPtrTc1 = rewriter.create<comb::AddOp>(loc, rdAddr, ptrTc1);
143  auto rdAddrNextNoRollover = rewriter.create<comb::MuxOp>(
144  loc, rdAndNotEmpty, addRdAddrPtrTc1, rdAddr);
145  auto isMaxAddrRd = rewriter.create<comb::ICmpOp>(
146  loc, comb::ICmpPredicate::eq, rdAddrNextNoRollover, ptrTcFull);
147  rdAddrNext.setValue(rewriter.create<comb::MuxOp>(loc, isMaxAddrRd, ptrTc0,
148  rdAddrNextNoRollover));
149  static_cast<Value>(rdAddrNext)
150  .getDefiningOp()
151  ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_rd_addr_next"));
152 
153  // ====== Result values ======
154  llvm::SmallVector<Value> results;
155 
156  // Data
157  results.push_back(readData);
158  // Full
159  results.push_back(fifoFull);
160  // Empty
161  results.push_back(fifoEmpty);
162 
163  if (auto almostFull = mem.getAlmostFullThreshold()) {
164  results.push_back(rewriter.create<comb::ICmpOp>(
165  loc, comb::ICmpPredicate::uge, count,
166  rewriter.create<hw::ConstantOp>(loc, countType, almostFull.value())));
167  static_cast<Value>(results.back())
168  .getDefiningOp()
169  ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_almost_full"));
170  }
171 
172  if (auto almostEmpty = mem.getAlmostEmptyThreshold()) {
173  results.push_back(rewriter.create<comb::ICmpOp>(
174  loc, comb::ICmpPredicate::ule, count,
175  rewriter.create<hw::ConstantOp>(loc, countType,
176  almostEmpty.value())));
177  static_cast<Value>(results.back())
178  .getDefiningOp()
179  ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_almost_empty"));
180  }
181 
182  // ====== Protocol checks =====
183  Value clkI1 = rewriter.create<seq::FromClockOp>(loc, clk);
184  Value notEmptyAndRden = comb::createOrFoldNot(
185  loc, rewriter.create<comb::AndOp>(loc, adaptor.getRdEn(), fifoEmpty),
186  rewriter);
187  rewriter.create<verif::ClockedAssertOp>(
188  loc, notEmptyAndRden, verif::ClockEdge::Pos, clkI1, /*enable=*/Value(),
189  rewriter.getStringAttr("FIFO empty when read enabled"));
190  Value notFullAndWren = comb::createOrFoldNot(
191  loc, rewriter.create<comb::AndOp>(loc, adaptor.getWrEn(), fifoFull),
192  rewriter);
193  rewriter.create<verif::ClockedAssertOp>(
194  loc, notFullAndWren, verif::ClockEdge::Pos, clkI1,
195  /*enable=*/Value(),
196  rewriter.getStringAttr("FIFO full when write enabled"));
197 
198  rewriter.replaceOp(mem, results);
199  return success();
200  }
201 };
202 
203 struct LowerSeqFIFOPass
204  : public circt::seq::impl::LowerSeqFIFOBase<LowerSeqFIFOPass> {
205  void runOnOperation() override;
206 };
207 
208 } // namespace
209 
210 void LowerSeqFIFOPass::runOnOperation() {
211  MLIRContext &ctxt = getContext();
212  ConversionTarget target(ctxt);
213 
214  // Lowering patterns must lower away all HLMem-related operations.
215  target.addIllegalOp<seq::FIFOOp>();
216  target.addLegalDialect<seq::SeqDialect, hw::HWDialect, comb::CombDialect,
217  verif::VerifDialect>();
218  RewritePatternSet patterns(&ctxt);
219  patterns.add<FIFOLowering>(&ctxt);
220 
221  if (failed(
222  applyPartialConversion(getOperation(), target, std::move(patterns))))
223  signalPassFailure();
224 }
225 
226 std::unique_ptr<Pass> circt::seq::createLowerSeqFIFOPass() {
227  return std::make_unique<LowerSeqFIFOPass>();
228 }
Instantiate one of these and use it to build typed backedges.
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
Backedge is a wrapper class around a Value.
void setValue(mlir::Value)
def create(data_type, value)
Definition: hw.py:433
Value createOrFoldNot(Location loc, Value value, OpBuilder &builder, bool twoState=false)
Create a `‘Not’' gate on a value.
Definition: CombOps.cpp:48
std::unique_ptr< mlir::Pass > createLowerSeqFIFOPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21
Definition: seq.py:1