CIRCT 20.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 = 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
203struct LowerSeqFIFOPass
204 : public circt::seq::impl::LowerSeqFIFOBase<LowerSeqFIFOPass> {
205 void runOnOperation() override;
206};
207
208} // namespace
209
210void 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
226std::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)
create(data_type, value)
Definition hw.py:433
std::unique_ptr< mlir::Pass > createLowerSeqFIFOPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition seq.py:1