CIRCT 21.0.0git
Loading...
Searching...
No Matches
VerifToSMT.cpp
Go to the documentation of this file.
1//===- VerifToSMT.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
14#include "mlir/Conversion/ReconcileUnrealizedCasts/ReconcileUnrealizedCasts.h"
15#include "mlir/Dialect/Arith/IR/Arith.h"
16#include "mlir/Dialect/Func/IR/FuncOps.h"
17#include "mlir/Dialect/SCF/IR/SCF.h"
18#include "mlir/Dialect/SMT/IR/SMTOps.h"
19#include "mlir/Dialect/SMT/IR/SMTTypes.h"
20#include "mlir/IR/ValueRange.h"
21#include "mlir/Pass/Pass.h"
22#include "mlir/Transforms/DialectConversion.h"
23#include "llvm/ADT/SmallVector.h"
24
25namespace circt {
26#define GEN_PASS_DEF_CONVERTVERIFTOSMT
27#include "circt/Conversion/Passes.h.inc"
28} // namespace circt
29
30using namespace mlir;
31using namespace circt;
32using namespace hw;
33
34//===----------------------------------------------------------------------===//
35// Conversion patterns
36//===----------------------------------------------------------------------===//
37
38namespace {
39/// Lower a verif::AssertOp operation with an i1 operand to a smt::AssertOp,
40/// negated to check for unsatisfiability.
41struct VerifAssertOpConversion : OpConversionPattern<verif::AssertOp> {
42 using OpConversionPattern<verif::AssertOp>::OpConversionPattern;
43
44 LogicalResult
45 matchAndRewrite(verif::AssertOp op, OpAdaptor adaptor,
46 ConversionPatternRewriter &rewriter) const override {
47 Value cond = typeConverter->materializeTargetConversion(
48 rewriter, op.getLoc(), smt::BoolType::get(getContext()),
49 adaptor.getProperty());
50 Value notCond = rewriter.create<smt::NotOp>(op.getLoc(), cond);
51 rewriter.replaceOpWithNewOp<smt::AssertOp>(op, notCond);
52 return success();
53 }
54};
55
56/// Lower a verif::AssumeOp operation with an i1 operand to a smt::AssertOp
57struct VerifAssumeOpConversion : OpConversionPattern<verif::AssumeOp> {
58 using OpConversionPattern<verif::AssumeOp>::OpConversionPattern;
59
60 LogicalResult
61 matchAndRewrite(verif::AssumeOp op, OpAdaptor adaptor,
62 ConversionPatternRewriter &rewriter) const override {
63 Value cond = typeConverter->materializeTargetConversion(
64 rewriter, op.getLoc(), smt::BoolType::get(getContext()),
65 adaptor.getProperty());
66 rewriter.replaceOpWithNewOp<smt::AssertOp>(op, cond);
67 return success();
68 }
69};
70
71/// Lower a verif::LecOp operation to a miter circuit encoded in SMT.
72/// More information on miter circuits can be found, e.g., in this paper:
73/// Brand, D., 1993, November. Verification of large synthesized designs. In
74/// Proceedings of 1993 International Conference on Computer Aided Design
75/// (ICCAD) (pp. 534-537). IEEE.
76struct LogicEquivalenceCheckingOpConversion
77 : OpConversionPattern<verif::LogicEquivalenceCheckingOp> {
79 verif::LogicEquivalenceCheckingOp>::OpConversionPattern;
80
81 LogicalResult
82 matchAndRewrite(verif::LogicEquivalenceCheckingOp op, OpAdaptor adaptor,
83 ConversionPatternRewriter &rewriter) const override {
84 Location loc = op.getLoc();
85 auto *firstOutputs = adaptor.getFirstCircuit().front().getTerminator();
86 auto *secondOutputs = adaptor.getSecondCircuit().front().getTerminator();
87
88 if (firstOutputs->getNumOperands() == 0) {
89 // Trivially equivalent
90 Value trueVal =
91 rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(true));
92 rewriter.replaceOp(op, trueVal);
93 return success();
94 }
95
96 auto unusedResult = op.use_empty();
97
98 // Solver will only return a result when it is used to check the returned
99 // value.
100 smt::SolverOp solver;
101 if (unusedResult)
102 solver = rewriter.create<smt::SolverOp>(loc, TypeRange{}, ValueRange{});
103 else
104 solver = rewriter.create<smt::SolverOp>(loc, rewriter.getI1Type(),
105 ValueRange{});
106
107 rewriter.createBlock(&solver.getBodyRegion());
108
109 // First, convert the block arguments of the miter bodies.
110 if (failed(rewriter.convertRegionTypes(&adaptor.getFirstCircuit(),
111 *typeConverter)))
112 return failure();
113 if (failed(rewriter.convertRegionTypes(&adaptor.getSecondCircuit(),
114 *typeConverter)))
115 return failure();
116
117 // Second, create the symbolic values we replace the block arguments with
118 SmallVector<Value> inputs;
119 for (auto arg : adaptor.getFirstCircuit().getArguments())
120 inputs.push_back(rewriter.create<smt::DeclareFunOp>(loc, arg.getType()));
121
122 // Third, inline the blocks
123 // Note: the argument value replacement does not happen immediately, but
124 // only after all the operations are already legalized.
125 // Also, it has to be ensured that the original argument type and the type
126 // of the value with which is is to be replaced match. The value is looked
127 // up (transitively) in the replacement map at the time the replacement
128 // pattern is committed.
129 rewriter.mergeBlocks(&adaptor.getFirstCircuit().front(), solver.getBody(),
130 inputs);
131 rewriter.mergeBlocks(&adaptor.getSecondCircuit().front(), solver.getBody(),
132 inputs);
133 rewriter.setInsertionPointToEnd(solver.getBody());
134
135 // Fourth, convert the yielded values back to the source type system (since
136 // the operations of the inlined blocks will be converted by other patterns
137 // later on and we should make sure the IR is well-typed after each pattern
138 // application), and build the 'assert'.
139 SmallVector<Value> outputsDifferent;
140 for (auto [out1, out2] :
141 llvm::zip(firstOutputs->getOperands(), secondOutputs->getOperands())) {
142 Value o1 = typeConverter->materializeTargetConversion(
143 rewriter, loc, typeConverter->convertType(out1.getType()), out1);
144 Value o2 = typeConverter->materializeTargetConversion(
145 rewriter, loc, typeConverter->convertType(out1.getType()), out2);
146 outputsDifferent.emplace_back(
147 rewriter.create<smt::DistinctOp>(loc, o1, o2));
148 }
149
150 rewriter.eraseOp(firstOutputs);
151 rewriter.eraseOp(secondOutputs);
152
153 Value toAssert;
154 if (outputsDifferent.size() == 1)
155 toAssert = outputsDifferent[0];
156 else
157 toAssert = rewriter.create<smt::OrOp>(loc, outputsDifferent);
158
159 rewriter.create<smt::AssertOp>(loc, toAssert);
160
161 // Fifth, check for satisfiablility and report the result back.
162 // If no operation uses the result of this solver, we leave our check
163 // operations empty. If the result is used, we create a check operation with
164 // the result type of the operation and yield the result of the check
165 // operation.
166 if (unusedResult) {
167 auto checkOp = rewriter.create<smt::CheckOp>(loc, TypeRange{});
168 rewriter.createBlock(&checkOp.getSatRegion());
169 rewriter.create<smt::YieldOp>(loc);
170 rewriter.createBlock(&checkOp.getUnknownRegion());
171 rewriter.create<smt::YieldOp>(loc);
172 rewriter.createBlock(&checkOp.getUnsatRegion());
173 rewriter.create<smt::YieldOp>(loc);
174 rewriter.setInsertionPointAfter(checkOp);
175 rewriter.create<smt::YieldOp>(loc);
176
177 // Erase as operation is replaced by an operator without a return value.
178 rewriter.eraseOp(op);
179 } else {
180 Value falseVal =
181 rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(false));
182 Value trueVal =
183 rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(true));
184 auto checkOp = rewriter.create<smt::CheckOp>(loc, rewriter.getI1Type());
185 rewriter.createBlock(&checkOp.getSatRegion());
186 rewriter.create<smt::YieldOp>(loc, falseVal);
187 rewriter.createBlock(&checkOp.getUnknownRegion());
188 rewriter.create<smt::YieldOp>(loc, falseVal);
189 rewriter.createBlock(&checkOp.getUnsatRegion());
190 rewriter.create<smt::YieldOp>(loc, trueVal);
191 rewriter.setInsertionPointAfter(checkOp);
192 rewriter.create<smt::YieldOp>(loc, checkOp->getResults());
193
194 rewriter.replaceOp(op, solver->getResults());
195 }
196
197 return success();
198 }
199};
200
201/// Lower a verif::BMCOp operation to an MLIR program that performs the bounded
202/// model check
203struct VerifBoundedModelCheckingOpConversion
204 : OpConversionPattern<verif::BoundedModelCheckingOp> {
205 using OpConversionPattern<verif::BoundedModelCheckingOp>::OpConversionPattern;
206
207 VerifBoundedModelCheckingOpConversion(TypeConverter &converter,
208 MLIRContext *context, Namespace &names,
209 bool risingClocksOnly)
210 : OpConversionPattern(converter, context), names(names),
211 risingClocksOnly(risingClocksOnly) {}
212 LogicalResult
213 matchAndRewrite(verif::BoundedModelCheckingOp op, OpAdaptor adaptor,
214 ConversionPatternRewriter &rewriter) const override {
215 Location loc = op.getLoc();
216 SmallVector<Type> oldLoopInputTy(op.getLoop().getArgumentTypes());
217 SmallVector<Type> oldCircuitInputTy(op.getCircuit().getArgumentTypes());
218 // TODO: the init and loop regions should be able to be concrete instead of
219 // symbolic which is probably preferable - just need to convert back and
220 // forth
221 SmallVector<Type> loopInputTy, circuitInputTy, initOutputTy,
222 circuitOutputTy;
223 if (failed(typeConverter->convertTypes(oldLoopInputTy, loopInputTy)))
224 return failure();
225 if (failed(typeConverter->convertTypes(oldCircuitInputTy, circuitInputTy)))
226 return failure();
227 if (failed(typeConverter->convertTypes(
228 op.getInit().front().back().getOperandTypes(), initOutputTy)))
229 return failure();
230 if (failed(typeConverter->convertTypes(
231 op.getCircuit().front().back().getOperandTypes(), circuitOutputTy)))
232 return failure();
233 if (failed(rewriter.convertRegionTypes(&op.getInit(), *typeConverter)))
234 return failure();
235 if (failed(rewriter.convertRegionTypes(&op.getLoop(), *typeConverter)))
236 return failure();
237 if (failed(rewriter.convertRegionTypes(&op.getCircuit(), *typeConverter)))
238 return failure();
239
240 unsigned numRegs = op.getNumRegs();
241 auto initialValues = op.getInitialValues();
242
243 auto initFuncTy = rewriter.getFunctionType({}, initOutputTy);
244 // Loop and init output types are necessarily the same, so just use init
245 // output types
246 auto loopFuncTy = rewriter.getFunctionType(loopInputTy, initOutputTy);
247 auto circuitFuncTy =
248 rewriter.getFunctionType(circuitInputTy, circuitOutputTy);
249
250 func::FuncOp initFuncOp, loopFuncOp, circuitFuncOp;
251
252 {
253 OpBuilder::InsertionGuard guard(rewriter);
254 rewriter.setInsertionPointToEnd(
255 op->getParentOfType<ModuleOp>().getBody());
256 initFuncOp = rewriter.create<func::FuncOp>(loc, names.newName("bmc_init"),
257 initFuncTy);
258 rewriter.inlineRegionBefore(op.getInit(), initFuncOp.getFunctionBody(),
259 initFuncOp.end());
260 loopFuncOp = rewriter.create<func::FuncOp>(loc, names.newName("bmc_loop"),
261 loopFuncTy);
262 rewriter.inlineRegionBefore(op.getLoop(), loopFuncOp.getFunctionBody(),
263 loopFuncOp.end());
264 circuitFuncOp = rewriter.create<func::FuncOp>(
265 loc, names.newName("bmc_circuit"), circuitFuncTy);
266 rewriter.inlineRegionBefore(op.getCircuit(),
267 circuitFuncOp.getFunctionBody(),
268 circuitFuncOp.end());
269 auto funcOps = {&initFuncOp, &loopFuncOp, &circuitFuncOp};
270 // initOutputTy is the same as loop output types
271 auto outputTys = {initOutputTy, initOutputTy, circuitOutputTy};
272 for (auto [funcOp, outputTy] : llvm::zip(funcOps, outputTys)) {
273 auto operands = funcOp->getBody().front().back().getOperands();
274 rewriter.eraseOp(&funcOp->getFunctionBody().front().back());
275 rewriter.setInsertionPointToEnd(&funcOp->getBody().front());
276 SmallVector<Value> toReturn;
277 for (unsigned i = 0; i < outputTy.size(); ++i)
278 toReturn.push_back(typeConverter->materializeTargetConversion(
279 rewriter, loc, outputTy[i], operands[i]));
280 rewriter.create<func::ReturnOp>(loc, toReturn);
281 }
282 }
283
284 auto solver =
285 rewriter.create<smt::SolverOp>(loc, rewriter.getI1Type(), ValueRange{});
286 rewriter.createBlock(&solver.getBodyRegion());
287
288 // Call init func to get initial clock values
289 ValueRange initVals =
290 rewriter.create<func::CallOp>(loc, initFuncOp)->getResults();
291
292 // Initial push
293 rewriter.create<smt::PushOp>(loc, 1);
294
295 // InputDecls order should be <circuit arguments> <state arguments>
296 // <wasViolated>
297 // Get list of clock indexes in circuit args
298 size_t initIndex = 0;
299 SmallVector<Value> inputDecls;
300 SmallVector<int> clockIndexes;
301 for (auto [curIndex, oldTy, newTy] :
302 llvm::enumerate(oldCircuitInputTy, circuitInputTy)) {
303 if (isa<seq::ClockType>(oldTy)) {
304 inputDecls.push_back(initVals[initIndex++]);
305 clockIndexes.push_back(curIndex);
306 continue;
307 }
308 if (curIndex >= oldCircuitInputTy.size() - numRegs) {
309 auto initVal =
310 initialValues[curIndex - oldCircuitInputTy.size() + numRegs];
311 if (auto initIntAttr = dyn_cast<IntegerAttr>(initVal)) {
312 inputDecls.push_back(rewriter.create<smt::BVConstantOp>(
313 loc, initIntAttr.getValue().getSExtValue(),
314 cast<smt::BitVectorType>(newTy).getWidth()));
315 continue;
316 }
317 }
318 inputDecls.push_back(rewriter.create<smt::DeclareFunOp>(loc, newTy));
319 }
320
321 auto numStateArgs = initVals.size() - initIndex;
322 // Add the rest of the init vals (state args)
323 for (; initIndex < initVals.size(); ++initIndex)
324 inputDecls.push_back(initVals[initIndex]);
325
326 Value lowerBound =
327 rewriter.create<arith::ConstantOp>(loc, rewriter.getI32IntegerAttr(0));
328 Value step =
329 rewriter.create<arith::ConstantOp>(loc, rewriter.getI32IntegerAttr(1));
330 Value upperBound =
331 rewriter.create<arith::ConstantOp>(loc, adaptor.getBoundAttr());
332 Value constFalse =
333 rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(false));
334 Value constTrue =
335 rewriter.create<arith::ConstantOp>(loc, rewriter.getBoolAttr(true));
336 inputDecls.push_back(constFalse); // wasViolated?
337
338 // TODO: swapping to a whileOp here would allow early exit once the property
339 // is violated
340 // Perform model check up to the provided bound
341 auto forOp = rewriter.create<scf::ForOp>(
342 loc, lowerBound, upperBound, step, inputDecls,
343 [&](OpBuilder &builder, Location loc, Value i, ValueRange iterArgs) {
344 // Drop existing assertions
345 builder.create<smt::PopOp>(loc, 1);
346 builder.create<smt::PushOp>(loc, 1);
347
348 // Execute the circuit
349 ValueRange circuitCallOuts =
350 builder
351 .create<func::CallOp>(
352 loc, circuitFuncOp,
353 iterArgs.take_front(circuitFuncOp.getNumArguments()))
354 ->getResults();
355
356 // If we have a cycle up to which we ignore assertions, we need an
357 // IfOp to track this
358 // First, save the insertion point so we can safely enter the IfOp
359
360 auto insideForPoint = builder.saveInsertionPoint();
361 // We need to still have the yielded result of the op in scope after
362 // we've built the check
363 Value yieldedValue;
364 auto ignoreAssertionsUntil =
365 op->getAttrOfType<IntegerAttr>("ignore_asserts_until");
366 if (ignoreAssertionsUntil) {
367 auto ignoreUntilConstant = builder.create<arith::ConstantOp>(
368 loc, rewriter.getI32IntegerAttr(
369 ignoreAssertionsUntil.getValue().getZExtValue()));
370 auto shouldIgnore = builder.create<arith::CmpIOp>(
371 loc, arith::CmpIPredicate::ult, i, ignoreUntilConstant);
372 auto ifShouldIgnore = builder.create<scf::IfOp>(
373 loc, builder.getI1Type(), shouldIgnore, true);
374 // If we should ignore, yield the existing value
375 builder.setInsertionPointToEnd(
376 &ifShouldIgnore.getThenRegion().front());
377 builder.create<scf::YieldOp>(loc, ValueRange(iterArgs.back()));
378 builder.setInsertionPointToEnd(
379 &ifShouldIgnore.getElseRegion().front());
380 yieldedValue = ifShouldIgnore.getResult(0);
381 }
382
383 auto checkOp =
384 rewriter.create<smt::CheckOp>(loc, builder.getI1Type());
385 {
386 OpBuilder::InsertionGuard guard(builder);
387 builder.createBlock(&checkOp.getSatRegion());
388 builder.create<smt::YieldOp>(loc, constTrue);
389 builder.createBlock(&checkOp.getUnknownRegion());
390 builder.create<smt::YieldOp>(loc, constTrue);
391 builder.createBlock(&checkOp.getUnsatRegion());
392 builder.create<smt::YieldOp>(loc, constFalse);
393 }
394
395 Value violated = builder.create<arith::OrIOp>(
396 loc, checkOp.getResult(0), iterArgs.back());
397
398 // If we've packaged everything in an IfOp, we need to yield the
399 // new violated value
400 if (ignoreAssertionsUntil) {
401 builder.create<scf::YieldOp>(loc, violated);
402 // Replace the variable with the yielded value
403 violated = yieldedValue;
404 }
405
406 // If we created an IfOp, make sure we start inserting after it again
407 builder.restoreInsertionPoint(insideForPoint);
408
409 // Call loop func to update clock & state arg values
410 SmallVector<Value> loopCallInputs;
411 // Fetch clock values to feed to loop
412 for (auto index : clockIndexes)
413 loopCallInputs.push_back(iterArgs[index]);
414 // Fetch state args to feed to loop
415 for (auto stateArg : iterArgs.drop_back().take_back(numStateArgs))
416 loopCallInputs.push_back(stateArg);
417 ValueRange loopVals =
418 builder.create<func::CallOp>(loc, loopFuncOp, loopCallInputs)
419 ->getResults();
420
421 size_t loopIndex = 0;
422 // Collect decls to yield at end of iteration
423 SmallVector<Value> newDecls;
424 for (auto [oldTy, newTy] :
425 llvm::zip(TypeRange(oldCircuitInputTy).drop_back(numRegs),
426 TypeRange(circuitInputTy).drop_back(numRegs))) {
427 if (isa<seq::ClockType>(oldTy))
428 newDecls.push_back(loopVals[loopIndex++]);
429 else
430 newDecls.push_back(builder.create<smt::DeclareFunOp>(loc, newTy));
431 }
432
433 // Only update the registers on a clock posedge unless in rising
434 // clocks only mode
435 // TODO: this will also need changing with multiple clocks - currently
436 // it only accounts for the one clock case.
437 if (clockIndexes.size() == 1) {
438 SmallVector<Value> regInputs = circuitCallOuts.take_back(numRegs);
439 if (risingClocksOnly) {
440 // In rising clocks only mode we don't need to worry about whether
441 // there was a posedge
442 newDecls.append(regInputs);
443 } else {
444 auto clockIndex = clockIndexes[0];
445 auto oldClock = iterArgs[clockIndex];
446 // The clock is necessarily the first value returned by the loop
447 // region
448 auto newClock = loopVals[0];
449 auto oldClockLow = builder.create<smt::BVNotOp>(loc, oldClock);
450 auto isPosedgeBV =
451 builder.create<smt::BVAndOp>(loc, oldClockLow, newClock);
452 // Convert posedge bv<1> to bool
453 auto trueBV = builder.create<smt::BVConstantOp>(loc, 1, 1);
454 auto isPosedge =
455 builder.create<smt::EqOp>(loc, isPosedgeBV, trueBV);
456 auto regStates =
457 iterArgs.take_front(circuitFuncOp.getNumArguments())
458 .take_back(numRegs);
459 SmallVector<Value> nextRegStates;
460 for (auto [regState, regInput] :
461 llvm::zip(regStates, regInputs)) {
462 // Create an ITE to calculate the next reg state
463 // TODO: we create a lot of ITEs here that will slow things down
464 // - these could be avoided by making init/loop regions concrete
465 nextRegStates.push_back(builder.create<smt::IteOp>(
466 loc, isPosedge, regInput, regState));
467 }
468 newDecls.append(nextRegStates);
469 }
470 }
471
472 // Add the rest of the loop state args
473 for (; loopIndex < loopVals.size(); ++loopIndex)
474 newDecls.push_back(loopVals[loopIndex]);
475
476 newDecls.push_back(violated);
477
478 builder.create<scf::YieldOp>(loc, newDecls);
479 });
480
481 Value res = rewriter.create<arith::XOrIOp>(loc, forOp->getResults().back(),
482 constTrue);
483 rewriter.create<smt::YieldOp>(loc, res);
484 rewriter.replaceOp(op, solver.getResults());
485 return success();
486 }
487
488 Namespace &names;
489 bool risingClocksOnly;
490};
491
492} // namespace
493
494//===----------------------------------------------------------------------===//
495// Convert Verif to SMT pass
496//===----------------------------------------------------------------------===//
497
498namespace {
499struct ConvertVerifToSMTPass
500 : public circt::impl::ConvertVerifToSMTBase<ConvertVerifToSMTPass> {
501 using Base::Base;
502 void runOnOperation() override;
503};
504} // namespace
505
507 RewritePatternSet &patterns,
508 Namespace &names,
509 bool risingClocksOnly) {
510 patterns.add<VerifAssertOpConversion, VerifAssumeOpConversion,
511 LogicEquivalenceCheckingOpConversion>(converter,
512 patterns.getContext());
513 patterns.add<VerifBoundedModelCheckingOpConversion>(
514 converter, patterns.getContext(), names, risingClocksOnly);
515}
516
517void ConvertVerifToSMTPass::runOnOperation() {
518 ConversionTarget target(getContext());
519 target.addIllegalDialect<verif::VerifDialect>();
520 target.addLegalDialect<smt::SMTDialect, arith::ArithDialect, scf::SCFDialect,
521 func::FuncDialect>();
522 target.addLegalOp<UnrealizedConversionCastOp>();
523
524 // Check BMC ops contain only one assertion (done outside pattern to avoid
525 // issues with whether assertions are/aren't lowered yet)
526 SymbolTable symbolTable(getOperation());
527 WalkResult assertionCheck = getOperation().walk(
528 [&](Operation *op) { // Check there is exactly one assertion and clock
529 if (auto bmcOp = dyn_cast<verif::BoundedModelCheckingOp>(op)) {
530 // We also currently don't support initial values on registers that
531 // don't have integer inputs.
532 auto regTypes = TypeRange(bmcOp.getCircuit().getArgumentTypes())
533 .take_back(bmcOp.getNumRegs());
534 for (auto [regType, initVal] :
535 llvm::zip(regTypes, bmcOp.getInitialValues())) {
536 if (!isa<IntegerType>(regType) && !isa<UnitAttr>(initVal)) {
537 op->emitError("initial values are currently only supported for "
538 "registers with integer types");
539 signalPassFailure();
540 return WalkResult::interrupt();
541 }
542 }
543 // Check only one clock is present in the circuit inputs
544 auto numClockArgs = 0;
545 for (auto argType : bmcOp.getCircuit().getArgumentTypes())
546 if (isa<seq::ClockType>(argType))
547 numClockArgs++;
548 // TODO: this can be removed once we have a way to associate reg
549 // ins/outs with clocks
550 if (numClockArgs > 1) {
551 op->emitError(
552 "only modules with one or zero clocks are currently supported");
553 return WalkResult::interrupt();
554 }
555 SmallVector<mlir::Operation *> worklist;
556 int numAssertions = 0;
557 op->walk([&](Operation *curOp) {
558 if (isa<verif::AssertOp>(curOp))
559 numAssertions++;
560 if (auto inst = dyn_cast<InstanceOp>(curOp))
561 worklist.push_back(symbolTable.lookup(inst.getModuleName()));
562 });
563 // TODO: probably negligible compared to actual model checking time
564 // but cacheing the assertion count of modules would speed this up
565 while (!worklist.empty()) {
566 auto *module = worklist.pop_back_val();
567 module->walk([&](Operation *curOp) {
568 if (isa<verif::AssertOp>(curOp))
569 numAssertions++;
570 if (auto inst = dyn_cast<InstanceOp>(curOp))
571 worklist.push_back(symbolTable.lookup(inst.getModuleName()));
572 });
573 if (numAssertions > 1)
574 break;
575 }
576 if (numAssertions > 1) {
577 op->emitError(
578 "bounded model checking problems with multiple assertions are "
579 "not yet "
580 "correctly handled - instead, you can assert the "
581 "conjunction of your assertions");
582 return WalkResult::interrupt();
583 }
584 }
585 return WalkResult::advance();
586 });
587 if (assertionCheck.wasInterrupted())
588 return signalPassFailure();
589 RewritePatternSet patterns(&getContext());
590 TypeConverter converter;
592
593 SymbolCache symCache;
594 symCache.addDefinitions(getOperation());
595 Namespace names;
596 names.add(symCache);
597
599 risingClocksOnly);
600
601 if (failed(mlir::applyPartialConversion(getOperation(), target,
602 std::move(patterns))))
603 return signalPassFailure();
604}
A namespace that is used to store existing names and generate new names in some scope within the IR.
Definition Namespace.h:30
void add(mlir::ModuleOp module)
Definition Namespace.h:48
void addDefinitions(mlir::Operation *top)
Populate the symbol cache with all symbol-defining operations within the 'top' operation.
Definition SymCache.cpp:23
Default symbol cache implementation; stores associations between names (StringAttr's) to mlir::Operat...
Definition SymCache.h:85
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void populateVerifToSMTConversionPatterns(TypeConverter &converter, RewritePatternSet &patterns, Namespace &names, bool risingClocksOnly)
Get the Verif to SMT conversion patterns.
void populateHWToSMTTypeConverter(TypeConverter &converter)
Get the HW to SMT type conversions.
Definition HWToSMT.cpp:184
Definition hw.py:1
Definition seq.py:1