CIRCT 22.0.0git
Loading...
Searching...
No Matches
VerifOps.cpp
Go to the documentation of this file.
1//===- VerifOps.cpp -------------------------------------------------------===//
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
16#include "mlir/IR/Builders.h"
17#include "mlir/IR/OpImplementation.h"
18#include "mlir/IR/PatternMatch.h"
19#include "mlir/IR/SymbolTable.h"
20#include "mlir/IR/TypeRange.h"
21#include "mlir/Interfaces/FunctionImplementation.h"
22#include "mlir/Interfaces/SideEffectInterfaces.h"
23#include "llvm/ADT/MapVector.h"
24
25using namespace circt;
26using namespace verif;
27using namespace mlir;
28
29static ClockEdge ltlToVerifClockEdge(ltl::ClockEdge ce) {
30 switch (ce) {
31 case ltl::ClockEdge::Pos:
32 return ClockEdge::Pos;
33 case ltl::ClockEdge::Neg:
34 return ClockEdge::Neg;
35 case ltl::ClockEdge::Both:
36 return ClockEdge::Both;
37 }
38 llvm_unreachable("Unknown event control kind");
39}
40
41//===----------------------------------------------------------------------===//
42// HasBeenResetOp
43//===----------------------------------------------------------------------===//
44
45OpFoldResult HasBeenResetOp::fold(FoldAdaptor adaptor) {
46 // Fold to zero if the reset is a constant. In this case the op is either
47 // permanently in reset or never resets. Both mean that the reset never
48 // finishes, so this op never returns true.
49 if (adaptor.getReset())
50 return BoolAttr::get(getContext(), false);
51
52 // Fold to zero if the clock is a constant and the reset is synchronous. In
53 // that case the reset will never be started.
54 if (!adaptor.getAsync() && adaptor.getClock())
55 return BoolAttr::get(getContext(), false);
56
57 return {};
58}
59
60//===----------------------------------------------------------------------===//
61// AssertLikeOps Canonicalization Patterns
62//===----------------------------------------------------------------------===//
63
64namespace {
65/// Remove `if %true` enable condition from an operation.
66template <typename Op>
67struct RemoveEnableTrue : public OpRewritePattern<Op> {
69
70 LogicalResult matchAndRewrite(Op op,
71 PatternRewriter &rewriter) const override {
72 Value enable = op.getEnable();
73 if (!enable)
74 return failure();
75 auto enableConst = enable.getDefiningOp<hw::ConstantOp>();
76 if (!enableConst || !enableConst.getValue().isOne())
77 return failure();
78
79 rewriter.modifyOpInPlace(op, [&]() { op.getEnableMutable().clear(); });
80 return success();
81 }
82};
83
84/// Delete operation if enable is `false`.
85template <typename Op>
86struct EraseIfEnableFalse : public OpRewritePattern<Op> {
88
89 LogicalResult matchAndRewrite(Op op,
90 PatternRewriter &rewriter) const override {
91 Value enable = op.getEnable();
92 if (!enable)
93 return failure();
94 auto enableConst = enable.getDefiningOp<hw::ConstantOp>();
95 if (!enableConst || !enableConst.getValue().isZero())
96 return failure();
97
98 rewriter.eraseOp(op);
99 return success();
100 }
101};
102
103/// Delete operation if property is `ltl.boolean_constant true` or
104/// `hw.constant true`.
105template <typename Op>
106struct EraseIfPropertyTrue : public OpRewritePattern<Op> {
108
109 LogicalResult matchAndRewrite(Op op,
110 PatternRewriter &rewriter) const override {
111 Value property = op.getProperty();
112
113 // Check for ltl.boolean_constant true
114 if (auto boolConst =
115 property.template getDefiningOp<ltl::BooleanConstantOp>()) {
116 if (boolConst.getValueAttr().getValue()) {
117 rewriter.eraseOp(op);
118 return success();
119 }
120 }
121
122 // Check for hw.constant true (for i1 properties)
123 if (auto hwConst = property.template getDefiningOp<hw::ConstantOp>()) {
124 if (hwConst.getValue().isOne()) {
125 rewriter.eraseOp(op);
126 return success();
127 }
128 }
129
130 return failure();
131 }
132};
133
134/// Convert `op(ltl.clock(prop, clk), en)` to `clocked_op(prop, en, clk)`.
135template <typename TargetOp, typename Op>
136struct LowerToClocked : public OpRewritePattern<Op> {
138
139 LogicalResult matchAndRewrite(Op op,
140 PatternRewriter &rewriter) const override {
141 auto clockOp = op.getProperty().template getDefiningOp<ltl::ClockOp>();
142 if (!clockOp)
143 return failure();
144
145 rewriter.replaceOpWithNewOp<TargetOp>(
146 op, clockOp.getInput(), ltlToVerifClockEdge(clockOp.getEdge()),
147 clockOp.getClock(), op.getEnable(), op.getLabelAttr());
148 return success();
149 }
150};
151} // namespace
152
153//===----------------------------------------------------------------------===//
154// AssertOp
155//===----------------------------------------------------------------------===//
156
157void AssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
158 MLIRContext *context) {
159 results.add<EraseIfEnableFalse<AssertOp>, EraseIfPropertyTrue<AssertOp>,
160 RemoveEnableTrue<AssertOp>,
161 LowerToClocked<ClockedAssertOp, AssertOp>>(context);
162}
163
164//===----------------------------------------------------------------------===//
165// AssumeOp
166//===----------------------------------------------------------------------===//
167
168void AssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
169 MLIRContext *context) {
170 results.add<EraseIfEnableFalse<AssumeOp>, EraseIfPropertyTrue<AssumeOp>,
171 RemoveEnableTrue<AssumeOp>,
172 LowerToClocked<ClockedAssumeOp, AssumeOp>>(context);
173}
174
175//===----------------------------------------------------------------------===//
176// CoverOp
177//===----------------------------------------------------------------------===//
178
179void CoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
180 MLIRContext *context) {
181 // Covers are NOT canonicalized to remove trivially true properties or
182 // constant false enables. See the comment in the test file for details.
183 results
184 .add<RemoveEnableTrue<CoverOp>, LowerToClocked<ClockedCoverOp, CoverOp>>(
185 context);
186}
187
188//===----------------------------------------------------------------------===//
189// ClockedAssertOp
190//===----------------------------------------------------------------------===//
191
192void ClockedAssertOp::getCanonicalizationPatterns(RewritePatternSet &results,
193 MLIRContext *context) {
194 results.add<RemoveEnableTrue<ClockedAssertOp>,
195 EraseIfEnableFalse<ClockedAssertOp>,
196 EraseIfPropertyTrue<ClockedAssertOp>>(context);
197}
198
199//===----------------------------------------------------------------------===//
200// ClockedAssumeOp
201//===----------------------------------------------------------------------===//
202
203void ClockedAssumeOp::getCanonicalizationPatterns(RewritePatternSet &results,
204 MLIRContext *context) {
205 results.add<RemoveEnableTrue<ClockedAssumeOp>,
206 EraseIfEnableFalse<ClockedAssumeOp>,
207 EraseIfPropertyTrue<ClockedAssumeOp>>(context);
208}
209
210//===----------------------------------------------------------------------===//
211// ClockedCoverOp
212//===----------------------------------------------------------------------===//
213
214void ClockedCoverOp::getCanonicalizationPatterns(RewritePatternSet &results,
215 MLIRContext *context) {
216 // Covers are NOT canonicalized to remove trivially true properties or
217 // constant false enables. See the comment in the test file for details.
218 results.add<RemoveEnableTrue<ClockedCoverOp>>(context);
219}
220
221//===----------------------------------------------------------------------===//
222// CircuitRelationCheckOp
223//===----------------------------------------------------------------------===//
224
225template <typename OpTy>
226static LogicalResult verifyCircuitRelationCheckOpRegions(OpTy &op) {
227 if (op.getFirstCircuit().getArgumentTypes() !=
228 op.getSecondCircuit().getArgumentTypes())
229 return op.emitOpError()
230 << "block argument types of both regions must match";
231 if (op.getFirstCircuit().front().getTerminator()->getOperandTypes() !=
232 op.getSecondCircuit().front().getTerminator()->getOperandTypes())
233 return op.emitOpError()
234 << "types of the yielded values of both regions must match";
235
236 return success();
237}
238
239//===----------------------------------------------------------------------===//
240// LogicalEquivalenceCheckingOp
241//===----------------------------------------------------------------------===//
242
243LogicalResult LogicEquivalenceCheckingOp::verifyRegions() {
245}
246
247//===----------------------------------------------------------------------===//
248// RefinementCheckingOp
249//===----------------------------------------------------------------------===//
250
251LogicalResult RefinementCheckingOp::verifyRegions() {
253}
254
255//===----------------------------------------------------------------------===//
256// BoundedModelCheckingOp
257//===----------------------------------------------------------------------===//
258
259LogicalResult BoundedModelCheckingOp::verifyRegions() {
260 if (!getInit().getArgumentTypes().empty())
261 return emitOpError() << "init region must have no arguments";
262 auto *initYieldOp = getInit().front().getTerminator();
263 auto *loopYieldOp = getLoop().front().getTerminator();
264 if (initYieldOp->getOperandTypes() != loopYieldOp->getOperandTypes())
265 return emitOpError()
266 << "init and loop regions must yield the same types of values";
267 if (initYieldOp->getOperandTypes() != getLoop().front().getArgumentTypes())
268 return emitOpError()
269 << "loop region arguments must match the types of the values "
270 "yielded by the init and loop regions";
271 size_t totalClocks = 0;
272 auto circuitArgTy = getCircuit().getArgumentTypes();
273 for (auto input : circuitArgTy)
274 if (isa<seq::ClockType>(input))
275 totalClocks++;
276 auto initYields = initYieldOp->getOperands();
277 // We know init and loop yields match, so only need to check one
278 if (initYields.size() < totalClocks)
279 return emitOpError()
280 << "init and loop regions must yield at least as many clock "
281 "values as there are clock arguments to the circuit region";
282 for (size_t i = 0; i < totalClocks; i++) {
283 if (!isa<seq::ClockType>(initYieldOp->getOperand(i).getType()))
284 return emitOpError()
285 << "init and loop regions must yield as many clock values as "
286 "there are clock arguments in the circuit region "
287 "before any other values";
288 }
289 if (getNumRegs() > 0 && totalClocks == 0)
290 return emitOpError("num_regs is non-zero, but the circuit region has no "
291 "clock inputs to clock the registers");
292 auto initialValues = getInitialValues();
293 if (initialValues.size() != getNumRegs()) {
294 return emitOpError()
295 << "number of initial values must match the number of registers";
296 }
297 for (auto attr : initialValues) {
298 if (!isa<IntegerAttr, UnitAttr>(attr))
299 return emitOpError()
300 << "initial values must be integer or unit attributes";
301 }
302 return success();
303}
304
305//===----------------------------------------------------------------------===//
306// SimulationOp
307//===----------------------------------------------------------------------===//
308
309LogicalResult SimulationOp::verifyRegions() {
310 if (getBody()->getNumArguments() != 2)
311 return emitOpError() << "must have two block arguments";
312 if (!isa<seq::ClockType>(getBody()->getArgument(0).getType()))
313 return emitOpError() << "block argument #0 must be of type `!seq.clock`";
314 if (!getBody()->getArgument(1).getType().isSignlessInteger(1))
315 return emitOpError() << "block argument #1 must be of type `i1`";
316
317 auto *yieldOp = getBody()->getTerminator();
318 if (yieldOp->getNumOperands() != 2)
319 return yieldOp->emitOpError() << "must have two operands";
320 if (!yieldOp->getOperand(0).getType().isSignlessInteger(1))
321 return yieldOp->emitOpError() << "operand #0 must be of type `i1`";
322 if (!yieldOp->getOperand(1).getType().isSignlessInteger(1))
323 return yieldOp->emitOpError() << "operand #1 must be of type `i1`";
324
325 return success();
326}
327
328void SimulationOp::getAsmBlockArgumentNames(Region &region,
329 OpAsmSetValueNameFn setNameFn) {
330 if (region.empty() || region.getNumArguments() != 2)
331 return;
332 setNameFn(region.getArgument(0), "clock");
333 setNameFn(region.getArgument(1), "init");
334}
335
336//===----------------------------------------------------------------------===//
337// Generated code
338//===----------------------------------------------------------------------===//
339
340#define GET_OP_CLASSES
341#include "circt/Dialect/Verif/Verif.cpp.inc"
static std::unique_ptr< Context > context
static InstancePath empty
static ClockEdge ltlToVerifClockEdge(ltl::ClockEdge ce)
Definition VerifOps.cpp:29
static LogicalResult verifyCircuitRelationCheckOpRegions(OpTy &op)
Definition VerifOps.cpp:226
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
Definition LLVM.h:183
Definition seq.py:1
Definition verif.py:1