15#include "mlir/Pass/Pass.h"
16#include "mlir/Transforms/DialectConversion.h"
17#include "llvm/ADT/TypeSwitch.h"
21#define GEN_PASS_DEF_LOWERSEQFIFO
22#include "circt/Dialect/Seq/SeqPasses.h.inc"
33 using OpConversionPattern::OpConversionPattern;
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);
60 rewriter, loc, nextCount,
clk, rst,
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);
66 rewriter, loc, rdAddrNext,
clk, rst,
69 rewriter, loc, wrAddrNext,
clk, rst,
77 uint64_t rdLatency = mem.getRdLatency();
78 uint64_t numInflightStages = rdLatency > 1 ? rdLatency - 1 : 0;
81 if (numInflightStages > 0) {
82 nextUsed = bb.
get(countType);
84 rewriter, loc, nextUsed,
clk, rst,
88 Value readData = seq::ReadPortOp::create(
89 rewriter, loc, hlmem, llvm::SmallVector<Value>{rdAddr},
90 adaptor.getRdEn(), mem.getRdLatency());
91 seq::WritePortOp::create(rewriter, loc, hlmem,
92 llvm::SmallVector<Value>{wrAddr},
93 adaptor.getInput(), adaptor.getWrEn(),
97 comb::ICmpOp fifoFull = comb::ICmpOp::create(
98 rewriter, loc, comb::ICmpPredicate::eq, used, countTcFull);
99 fifoFull->setAttr(
"sv.namehint", rewriter.getStringAttr(
"fifo_full"));
100 comb::ICmpOp fifoEmpty = comb::ICmpOp::create(
101 rewriter, loc, comb::ICmpPredicate::eq, count, countTc0);
102 fifoEmpty->setAttr(
"sv.namehint", rewriter.getStringAttr(
"fifo_empty"));
105 auto notRdEn = comb::createOrFoldNot(rewriter, loc, adaptor.getRdEn());
106 auto notWrEn = comb::createOrFoldNot(rewriter, loc, adaptor.getWrEn());
107 Value rdEnNandWrEn = comb::AndOp::create(rewriter, loc, notRdEn, notWrEn);
108 Value rdEnAndNotWrEn =
109 comb::AndOp::create(rewriter, loc, adaptor.getRdEn(), notWrEn);
110 Value wrEnAndNotRdEn =
111 comb::AndOp::create(rewriter, loc, adaptor.getWrEn(), notRdEn);
113 auto addCountTc1 = comb::AddOp::create(rewriter, loc, count, countTc1);
114 Value wrEnNext = comb::MuxOp::create(rewriter, loc, fifoFull,
119 auto countEqTc0 = comb::ICmpOp::create(
120 rewriter, loc, comb::ICmpPredicate::eq, count, countTc0);
121 auto subCountTc1 = comb::SubOp::create(rewriter, loc, count, countTc1);
123 Value rdEnNext = comb::MuxOp::create(rewriter, loc, countEqTc0,
130 comb::MuxOp::create(rewriter, loc, rdEnAndNotWrEn, rdEnNext, count);
131 auto nextMux = comb::MuxOp::create(rewriter, loc, wrEnAndNotRdEn, wrEnNext,
133 nextCount.
setValue(comb::MuxOp::create(rewriter, loc, rdEnNandWrEn,
135 static_cast<Value
>(nextCount).getDefiningOp()->setAttr(
136 "sv.namehint", rewriter.getStringAttr(
"fifo_count_next"));
140 comb::AndOp::create(rewriter, loc, adaptor.getWrEn(),
141 comb::createOrFoldNot(rewriter, loc, fifoFull));
142 auto addWrAddrPtrTc1 = comb::AddOp::create(rewriter, loc, wrAddr, ptrTc1);
144 auto wrAddrNextNoRollover = comb::MuxOp::create(rewriter, loc, wrAndNotFull,
145 addWrAddrPtrTc1, wrAddr);
147 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::eq,
148 wrAddrNextNoRollover, ptrTcFull);
149 wrAddrNext.
setValue(comb::MuxOp::create(rewriter, loc, isMaxAddrWr, ptrTc0,
150 wrAddrNextNoRollover));
151 static_cast<Value
>(wrAddrNext)
153 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"fifo_wr_addr_next"));
155 auto notFifoEmpty = comb::createOrFoldNot(rewriter, loc, fifoEmpty);
156 Value rdAndNotEmpty =
157 comb::AndOp::create(rewriter, loc, adaptor.getRdEn(), notFifoEmpty);
158 auto addRdAddrPtrTc1 = comb::AddOp::create(rewriter, loc, rdAddr, ptrTc1);
159 auto rdAddrNextNoRollover = comb::MuxOp::create(
160 rewriter, loc, rdAndNotEmpty, addRdAddrPtrTc1, rdAddr);
162 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::eq,
163 rdAddrNextNoRollover, ptrTcFull);
164 rdAddrNext.
setValue(comb::MuxOp::create(rewriter, loc, isMaxAddrRd, ptrTc0,
165 rdAddrNextNoRollover));
166 static_cast<Value
>(rdAddrNext)
168 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"fifo_rd_addr_next"));
171 if (numInflightStages > 0) {
173 Value retire = rdAndNotEmpty;
176 for (uint64_t i = 0; i < numInflightStages; ++i)
179 "fifo_rd_inflight_dly" + std::to_string(i));
181 auto notRetire = comb::createOrFoldNot(rewriter, loc, retire);
182 auto notWrAndNotFull = comb::createOrFoldNot(rewriter, loc, wrAndNotFull);
184 comb::AndOp::create(rewriter, loc, wrAndNotFull, notRetire);
186 comb::AndOp::create(rewriter, loc, retire, notWrAndNotFull);
187 auto usedPlus1 = comb::AddOp::create(rewriter, loc, used, countTc1);
188 auto usedMinus1 = comb::SubOp::create(rewriter, loc, used, countTc1);
190 comb::MuxOp::create(rewriter, loc, decUsed, usedMinus1, used);
192 comb::MuxOp::create(rewriter, loc, incUsed, usedPlus1, usedDecMux);
193 usedNextMux->setAttr(
"sv.namehint",
194 rewriter.getStringAttr(
"fifo_used_next"));
199 llvm::SmallVector<Value> results;
202 results.push_back(readData);
204 results.push_back(fifoFull);
206 results.push_back(fifoEmpty);
208 if (
auto almostFull = mem.getAlmostFullThreshold()) {
210 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::uge, used,
212 almostFull.value())));
213 static_cast<Value
>(results.back())
215 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"fifo_almost_full"));
218 if (
auto almostEmpty = mem.getAlmostEmptyThreshold()) {
220 comb::ICmpOp::create(rewriter, loc, comb::ICmpPredicate::ule, count,
222 almostEmpty.value())));
223 static_cast<Value
>(results.back())
225 ->setAttr(
"sv.namehint", rewriter.getStringAttr(
"fifo_almost_empty"));
229 Value clkI1 = seq::FromClockOp::create(rewriter, loc,
clk);
230 Value notEmptyAndRden = comb::createOrFoldNot(
232 comb::AndOp::create(rewriter, loc, adaptor.getRdEn(), fifoEmpty));
233 verif::ClockedAssertOp::create(
234 rewriter, loc, notEmptyAndRden, verif::ClockEdge::Pos, clkI1,
236 rewriter.getStringAttr(
"FIFO empty when read enabled"));
237 Value notFullAndWren = comb::createOrFoldNot(
239 comb::AndOp::create(rewriter, loc, adaptor.getWrEn(), fifoFull));
240 verif::ClockedAssertOp::create(
241 rewriter, loc, notFullAndWren, verif::ClockEdge::Pos, clkI1,
243 rewriter.getStringAttr(
"FIFO full when write enabled"));
245 rewriter.replaceOp(mem, results);
250struct LowerSeqFIFOPass
251 :
public circt::seq::impl::LowerSeqFIFOBase<LowerSeqFIFOPass> {
252 void runOnOperation()
override;
257void LowerSeqFIFOPass::runOnOperation() {
258 MLIRContext &ctxt = getContext();
259 ConversionTarget target(ctxt);
262 target.addIllegalOp<seq::FIFOOp>();
263 target.addLegalDialect<seq::SeqDialect, hw::HWDialect, comb::CombDialect,
264 verif::VerifDialect>();
265 RewritePatternSet patterns(&ctxt);
266 patterns.add<FIFOLowering>(&ctxt);
269 applyPartialConversion(getOperation(), target, std::move(patterns))))
Backedge get(mlir::Type resultType, mlir::LocationAttr optionalLoc={})
Create a typed backedge.
void setValue(mlir::Value)
create(cls, result_type, reset=None, reset_value=None, name=None, sym_name=None, **kwargs)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.