CIRCT 22.0.0git
Loading...
Searching...
No Matches
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
15#include "mlir/Pass/Pass.h"
16#include "mlir/Transforms/DialectConversion.h"
17#include "llvm/ADT/TypeSwitch.h"
18
19namespace circt {
20namespace seq {
21#define GEN_PASS_DEF_LOWERSEQFIFO
22#include "circt/Dialect/Seq/SeqPasses.h.inc"
23} // namespace seq
24} // namespace circt
25
26using namespace circt;
27using namespace seq;
28
29namespace {
30
31struct FIFOLowering : public OpConversionPattern<seq::FIFOOp> {
32public:
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 = hw::ConstantOp::create(rewriter, loc, countType, depth);
52 Value countTc1 = hw::ConstantOp::create(rewriter, loc, countType, 1);
53 Value countTc0 = hw::ConstantOp::create(rewriter, loc, countType, 0);
54 Value ptrTc0 = hw::ConstantOp::create(rewriter, loc, ptrType, 0);
55 Value ptrTc1 = hw::ConstantOp::create(rewriter, loc, ptrType, 1);
56 Value ptrTcFull = hw::ConstantOp::create(rewriter, loc, ptrType, depth);
57
58 // ====== Hardware units ======
59 Value count = seq::CompRegOp::create(
60 rewriter, loc, nextCount, clk, rst,
61 hw::ConstantOp::create(rewriter, loc, countType, 0), "fifo_count");
62 seq::HLMemOp hlmem = seq::HLMemOp::create(
63 rewriter, loc, clk, rst, "fifo_mem",
64 llvm::SmallVector<int64_t>{static_cast<int64_t>(depth)}, eltType);
65 Value rdAddr = seq::CompRegOp::create(
66 rewriter, loc, rdAddrNext, clk, rst,
67 hw::ConstantOp::create(rewriter, loc, ptrType, 0), "fifo_rd_addr");
68 Value wrAddr = seq::CompRegOp::create(
69 rewriter, loc, wrAddrNext, clk, rst,
70 hw::ConstantOp::create(rewriter, loc, ptrType, 0), "fifo_wr_addr");
71
72 Value readData = seq::ReadPortOp::create(
73 rewriter, loc, hlmem, llvm::SmallVector<Value>{rdAddr},
74 adaptor.getRdEn(), mem.getRdLatency());
75 seq::WritePortOp::create(rewriter, loc, hlmem,
76 llvm::SmallVector<Value>{wrAddr},
77 adaptor.getInput(), adaptor.getWrEn(),
78 /*latency*/ 1);
79
80 // ====== some more constants =====
81 comb::ICmpOp fifoFull = comb::ICmpOp::create(
82 rewriter, loc, comb::ICmpPredicate::eq, count, countTcFull);
83 fifoFull->setAttr("sv.namehint", rewriter.getStringAttr("fifo_full"));
84 comb::ICmpOp fifoEmpty = comb::ICmpOp::create(
85 rewriter, 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 = comb::AndOp::create(rewriter, loc, notRdEn, notWrEn);
92 Value rdEnAndNotWrEn =
93 comb::AndOp::create(rewriter, loc, adaptor.getRdEn(), notWrEn);
94 Value wrEnAndNotRdEn =
95 comb::AndOp::create(rewriter, loc, adaptor.getWrEn(), notRdEn);
96
97 auto countEqTcFull = comb::ICmpOp::create(
98 rewriter, loc, comb::ICmpPredicate::eq, count, countTcFull);
99 auto addCountTc1 = comb::AddOp::create(rewriter, loc, count, countTc1);
100 Value wrEnNext = comb::MuxOp::create(rewriter, loc, countEqTcFull,
101 // keep value
102 count,
103 // increment
104 addCountTc1);
105 auto countEqTc0 = comb::ICmpOp::create(
106 rewriter, loc, comb::ICmpPredicate::eq, count, countTc0);
107 auto subCountTc1 = comb::SubOp::create(rewriter, loc, count, countTc1);
108
109 Value rdEnNext = comb::MuxOp::create(rewriter, loc, countEqTc0,
110 // keep value
111 count,
112 // decrement
113 subCountTc1);
114
115 auto nextInnerMux =
116 comb::MuxOp::create(rewriter, loc, rdEnAndNotWrEn, rdEnNext, count);
117 auto nextMux = comb::MuxOp::create(rewriter, loc, wrEnAndNotRdEn, wrEnNext,
118 nextInnerMux);
119 nextCount.setValue(comb::MuxOp::create(rewriter, loc, rdEnNandWrEn,
120 /*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 =
126 comb::AndOp::create(rewriter, loc, adaptor.getWrEn(),
127 comb::createOrFoldNot(loc, fifoFull, rewriter));
128 auto addWrAddrPtrTc1 = comb::AddOp::create(rewriter, loc, wrAddr, ptrTc1);
129
130 auto wrAddrNextNoRollover = comb::MuxOp::create(rewriter, loc, wrAndNotFull,
131 addWrAddrPtrTc1, wrAddr);
132 auto isMaxAddrWr =
133 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::eq,
134 wrAddrNextNoRollover, ptrTcFull);
135 wrAddrNext.setValue(comb::MuxOp::create(rewriter, loc, isMaxAddrWr, ptrTc0,
136 wrAddrNextNoRollover));
137 static_cast<Value>(wrAddrNext)
138 .getDefiningOp()
139 ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_wr_addr_next"));
140
141 auto notFifoEmpty = comb::createOrFoldNot(loc, fifoEmpty, rewriter);
142 Value rdAndNotEmpty =
143 comb::AndOp::create(rewriter, loc, adaptor.getRdEn(), notFifoEmpty);
144 auto addRdAddrPtrTc1 = comb::AddOp::create(rewriter, loc, rdAddr, ptrTc1);
145 auto rdAddrNextNoRollover = comb::MuxOp::create(
146 rewriter, loc, rdAndNotEmpty, addRdAddrPtrTc1, rdAddr);
147 auto isMaxAddrRd =
148 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::eq,
149 rdAddrNextNoRollover, ptrTcFull);
150 rdAddrNext.setValue(comb::MuxOp::create(rewriter, loc, isMaxAddrRd, ptrTc0,
151 rdAddrNextNoRollover));
152 static_cast<Value>(rdAddrNext)
153 .getDefiningOp()
154 ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_rd_addr_next"));
155
156 // ====== Result values ======
157 llvm::SmallVector<Value> results;
158
159 // Data
160 results.push_back(readData);
161 // Full
162 results.push_back(fifoFull);
163 // Empty
164 results.push_back(fifoEmpty);
165
166 if (auto almostFull = mem.getAlmostFullThreshold()) {
167 results.push_back(
168 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::uge, count,
169 hw::ConstantOp::create(rewriter, loc, countType,
170 almostFull.value())));
171 static_cast<Value>(results.back())
172 .getDefiningOp()
173 ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_almost_full"));
174 }
175
176 if (auto almostEmpty = mem.getAlmostEmptyThreshold()) {
177 results.push_back(
178 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::ule, count,
179 hw::ConstantOp::create(rewriter, loc, countType,
180 almostEmpty.value())));
181 static_cast<Value>(results.back())
182 .getDefiningOp()
183 ->setAttr("sv.namehint", rewriter.getStringAttr("fifo_almost_empty"));
184 }
185
186 // ====== Protocol checks =====
187 Value clkI1 = seq::FromClockOp::create(rewriter, loc, clk);
188 Value notEmptyAndRden = comb::createOrFoldNot(
189 loc, comb::AndOp::create(rewriter, loc, adaptor.getRdEn(), fifoEmpty),
190 rewriter);
191 verif::ClockedAssertOp::create(
192 rewriter, loc, notEmptyAndRden, verif::ClockEdge::Pos, clkI1,
193 /*enable=*/Value(),
194 rewriter.getStringAttr("FIFO empty when read enabled"));
195 Value notFullAndWren = comb::createOrFoldNot(
196 loc, comb::AndOp::create(rewriter, loc, adaptor.getWrEn(), fifoFull),
197 rewriter);
198 verif::ClockedAssertOp::create(
199 rewriter, loc, notFullAndWren, verif::ClockEdge::Pos, clkI1,
200 /*enable=*/Value(),
201 rewriter.getStringAttr("FIFO full when write enabled"));
202
203 rewriter.replaceOp(mem, results);
204 return success();
205 }
206};
207
208struct LowerSeqFIFOPass
209 : public circt::seq::impl::LowerSeqFIFOBase<LowerSeqFIFOPass> {
210 void runOnOperation() override;
211};
212
213} // namespace
214
215void LowerSeqFIFOPass::runOnOperation() {
216 MLIRContext &ctxt = getContext();
217 ConversionTarget target(ctxt);
218
219 // Lowering patterns must lower away all HLMem-related operations.
220 target.addIllegalOp<seq::FIFOOp>();
221 target.addLegalDialect<seq::SeqDialect, hw::HWDialect, comb::CombDialect,
222 verif::VerifDialect>();
223 RewritePatternSet patterns(&ctxt);
224 patterns.add<FIFOLowering>(&ctxt);
225
226 if (failed(
227 applyPartialConversion(getOperation(), target, std::move(patterns))))
228 signalPassFailure();
229}
230
231std::unique_ptr<Pass> circt::seq::createLowerSeqFIFOPass() {
232 return std::make_unique<LowerSeqFIFOPass>();
233}
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)
create(data_type, value)
Definition hw.py:433
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
Definition seq.py:157
std::unique_ptr< mlir::Pass > createLowerSeqFIFOPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition seq.py:1