Skip to content

Commit 46793f4

Browse files
authored
[circt-bmc][VerifToSMT] Add initial value support (#7903)
1 parent dbd1d7c commit 46793f4

File tree

4 files changed

+78
-23
lines changed

4 files changed

+78
-23
lines changed

integration_test/circt-bmc/seq-errors.mlir

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
// REQUIRES: libz3
22
// REQUIRES: circt-bmc-jit
33

4-
// RUN: circt-bmc %s -b 10 --module Counter --shared-libs=%libz3 | FileCheck %s --check-prefix=COUNTER
5-
// COUNTER: Assertion can be violated!
4+
// Test with two bounds - one that doesn't run long enough for the counter to reach 3, and one that does
5+
// RUN: circt-bmc %s -b 2 --module Counter --shared-libs=%libz3 | FileCheck %s --check-prefix=COUNTER2
6+
// COUNTER2: Bound reached with no violations!
7+
// RUN: circt-bmc %s -b 10 --module Counter --shared-libs=%libz3 | FileCheck %s --check-prefix=COUNTER10
8+
// COUNTER10: Assertion can be violated!
69

710
hw.module @Counter(in %clk: !seq.clock, out count: i2) {
11+
%init = seq.initial () {
12+
%c0_i2 = hw.constant 0 : i2
13+
seq.yield %c0_i2 : i2
14+
} : () -> !seq.immutable<i2>
815
%c1_i2 = hw.constant 1 : i2
916
%regPlusOne = comb.add %reg, %c1_i2 : i2
10-
%reg = seq.compreg %regPlusOne, %clk : i2
11-
// Condition - count should never reach 3 (deliberately not true)
12-
// FIXME: add an initial condition here once we support them, currently it
13-
// can be violated on the first cycle as 3 is a potential initial value.
14-
// Can also use this to check bounds are behaving as expected.
17+
%reg = seq.compreg %regPlusOne, %clk initial %init : i2
18+
// Condition - count should never reach 3
1519
%c3_i2 = hw.constant 3 : i2
1620
%lt = comb.icmp ult %reg, %c3_i2 : i2
1721
verif.assert %lt : i1

lib/Conversion/VerifToSMT/VerifToSMT.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "circt/Conversion/VerifToSMT.h"
1010
#include "circt/Conversion/HWToSMT.h"
1111
#include "circt/Dialect/SMT/SMTOps.h"
12+
#include "circt/Dialect/SMT/SMTTypes.h"
1213
#include "circt/Dialect/Seq/SeqTypes.h"
1314
#include "circt/Dialect/Verif/VerifOps.h"
1415
#include "circt/Support/Namespace.h"
@@ -206,8 +207,8 @@ struct VerifBoundedModelCheckingOpConversion
206207
if (failed(rewriter.convertRegionTypes(&op.getCircuit(), *typeConverter)))
207208
return failure();
208209

209-
unsigned numRegs =
210-
cast<IntegerAttr>(op->getAttr("num_regs")).getValue().getZExtValue();
210+
unsigned numRegs = op.getNumRegs();
211+
auto initialValues = op.getInitialValues();
211212

212213
auto initFuncTy = rewriter.getFunctionType({}, initOutputTy);
213214
// Loop and init output types are necessarily the same, so just use init
@@ -272,9 +273,19 @@ struct VerifBoundedModelCheckingOpConversion
272273
if (isa<seq::ClockType>(oldTy)) {
273274
inputDecls.push_back(initVals[initIndex++]);
274275
clockIndexes.push_back(curIndex);
275-
} else {
276-
inputDecls.push_back(rewriter.create<smt::DeclareFunOp>(loc, newTy));
276+
continue;
277277
}
278+
if (curIndex >= oldCircuitInputTy.size() - numRegs) {
279+
auto initVal =
280+
initialValues[curIndex - oldCircuitInputTy.size() + numRegs];
281+
if (auto initIntAttr = dyn_cast<IntegerAttr>(initVal)) {
282+
inputDecls.push_back(rewriter.create<smt::BVConstantOp>(
283+
loc, initIntAttr.getValue().getSExtValue(),
284+
cast<smt::BitVectorType>(newTy).getWidth()));
285+
continue;
286+
}
287+
}
288+
inputDecls.push_back(rewriter.create<smt::DeclareFunOp>(loc, newTy));
278289
}
279290

280291
auto numStateArgs = initVals.size() - initIndex;
@@ -434,6 +445,19 @@ void ConvertVerifToSMTPass::runOnOperation() {
434445
WalkResult assertionCheck = getOperation().walk(
435446
[&](Operation *op) { // Check there is exactly one assertion and clock
436447
if (auto bmcOp = dyn_cast<verif::BoundedModelCheckingOp>(op)) {
448+
// We also currently don't support initial values on registers that
449+
// don't have integer inputs.
450+
auto regTypes = TypeRange(bmcOp.getCircuit().getArgumentTypes())
451+
.take_back(bmcOp.getNumRegs());
452+
for (auto [regType, initVal] :
453+
llvm::zip(regTypes, bmcOp.getInitialValues())) {
454+
if (!isa<IntegerType>(regType) && !isa<UnitAttr>(initVal)) {
455+
op->emitError("initial values are currently only supported for "
456+
"registers with integer types");
457+
signalPassFailure();
458+
return WalkResult::interrupt();
459+
}
460+
}
437461
// Check only one clock is present in the circuit inputs
438462
auto numClockArgs = 0;
439463
for (auto argType : bmcOp.getCircuit().getArgumentTypes())

test/Conversion/VerifToSMT/verif-to-smt-errors.mlir

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,26 @@ func.func @multiple_clocks() -> (i1) {
122122
}
123123
func.return %bmc : i1
124124
}
125+
126+
// -----
127+
128+
func.func @multiple_clocks() -> (i1) {
129+
// expected-error @below {{initial values are currently only supported for registers with integer types}}
130+
%bmc = verif.bmc bound 10 num_regs 1 initial_values [0]
131+
init {
132+
%c0_i1 = hw.constant 0 : i1
133+
%clk = seq.to_clock %c0_i1
134+
verif.yield %clk : !seq.clock
135+
}
136+
loop {
137+
^bb0(%clk: !seq.clock):
138+
verif.yield %clk: !seq.clock
139+
}
140+
circuit {
141+
^bb0(%clk: !seq.clock, %arg0: !hw.array<2xi32>):
142+
%true = hw.constant true
143+
verif.assert %true : i1
144+
verif.yield %arg0 : !hw.array<2xi32>
145+
}
146+
func.return %bmc : i1
147+
}

test/Conversion/VerifToSMT/verif-to-smt.mlir

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -93,33 +93,37 @@ func.func @test_lec(%arg0: !smt.bv<1>) -> (i1, i1, i1) {
9393
// CHECK: smt.push 1
9494
// CHECK: [[F0:%.+]] = smt.declare_fun : !smt.bv<32>
9595
// CHECK: [[F1:%.+]] = smt.declare_fun : !smt.bv<32>
96+
// CHECK: [[C42_BV32:%.+]] = smt.bv.constant #smt.bv<42> : !smt.bv<32>
97+
// CHECK: [[ARRAYFUN:%.+]] = smt.declare_fun : !smt.array<[!smt.bv<1> -> !smt.bv<32>]>
9698
// CHECK: [[C0_I32:%.+]] = arith.constant 0 : i32
9799
// CHECK: [[C1_I32:%.+]] = arith.constant 1 : i32
98100
// CHECK: [[C10_I32:%.+]] = arith.constant 10 : i32
99101
// CHECK: [[FALSE:%.+]] = arith.constant false
100102
// CHECK: [[TRUE:%.+]] = arith.constant true
101-
// CHECK: [[FOR:%.+]]:5 = scf.for [[ARG0:%.+]] = [[C0_I32]] to [[C10_I32]] step [[C1_I32]] iter_args([[ARG1:%.+]] = [[INIT]]#0, [[ARG2:%.+]] = [[F0]], [[ARG3:%.+]] = [[F1]], [[ARG4:%.+]] = [[INIT]]#1, [[ARG5:%.+]] = [[FALSE]])
103+
// CHECK: [[FOR:%.+]]:7 = scf.for [[ARG0:%.+]] = [[C0_I32]] to [[C10_I32]] step [[C1_I32]] iter_args([[ARG1:%.+]] = [[INIT]]#0, [[ARG2:%.+]] = [[F0]], [[ARG3:%.+]] = [[F1]], [[ARG4:%.+]] = [[C42_BV32]], [[ARG5:%.+]] = [[ARRAYFUN]], [[ARG6:%.+]] = [[INIT]]#1, [[ARG7:%.+]] = [[FALSE]])
102104
// CHECK: smt.pop 1
103105
// CHECK: smt.push 1
104-
// CHECK: [[CIRCUIT:%.+]]:2 = func.call @bmc_circuit([[ARG1]], [[ARG2]], [[ARG3]])
106+
// CHECK: [[CIRCUIT:%.+]]:4 = func.call @bmc_circuit([[ARG1]], [[ARG2]], [[ARG3]], [[ARG4]], [[ARG5]])
105107
// CHECK: [[SMTCHECK:%.+]] = smt.check sat {
106108
// CHECK: smt.yield [[TRUE]]
107109
// CHECK: } unknown {
108110
// CHECK: smt.yield [[TRUE]]
109111
// CHECK: } unsat {
110112
// CHECK: smt.yield [[FALSE]]
111113
// CHECK: }
112-
// CHECK: [[ORI:%.+]] = arith.ori [[SMTCHECK]], [[ARG5]]
113-
// CHECK: [[LOOP:%.+]]:2 = func.call @bmc_loop([[ARG1]], [[ARG4]])
114+
// CHECK: [[ORI:%.+]] = arith.ori [[SMTCHECK]], [[ARG7]]
115+
// CHECK: [[LOOP:%.+]]:2 = func.call @bmc_loop([[ARG1]], [[ARG6]])
114116
// CHECK: [[F2:%.+]] = smt.declare_fun : !smt.bv<32>
115117
// CHECK: [[OLDCLOCKLOW:%.+]] = smt.bv.not [[ARG1]]
116118
// CHECK: [[BVPOSEDGE:%.+]] = smt.bv.and [[OLDCLOCKLOW]], [[LOOP]]#0
117119
// CHECK: [[BVTRUE:%.+]] = smt.bv.constant #smt.bv<-1> : !smt.bv<1>
118120
// CHECK: [[BOOLPOSEDGE:%.+]] = smt.eq [[BVPOSEDGE]], [[BVTRUE]]
119-
// CHECK: [[NEWREG:%.+]] = smt.ite [[BOOLPOSEDGE]], [[CIRCUIT]]#1, [[ARG3]]
120-
// CHECK: scf.yield [[LOOP]]#0, [[F2]], [[NEWREG]], [[LOOP]]#1, [[ORI]]
121+
// CHECK: [[NEWREG1:%.+]] = smt.ite [[BOOLPOSEDGE]], [[CIRCUIT]]#1, [[ARG3]]
122+
// CHECK: [[NEWREG2:%.+]] = smt.ite [[BOOLPOSEDGE]], [[CIRCUIT]]#2, [[ARG4]]
123+
// CHECK: [[NEWREG3:%.+]] = smt.ite [[BOOLPOSEDGE]], [[CIRCUIT]]#3, [[ARG5]]
124+
// CHECK: scf.yield [[LOOP]]#0, [[F2]], [[NEWREG1]], [[NEWREG2]], [[NEWREG3]], [[LOOP]]#1, [[ORI]]
121125
// CHECK: }
122-
// CHECK: [[XORI:%.+]] = arith.xori [[FOR]]#4, [[TRUE]]
126+
// CHECK: [[XORI:%.+]] = arith.xori [[FOR]]#6, [[TRUE]]
123127
// CHECK: smt.yield [[XORI]]
124128
// CHECK: }
125129
// CHECK: return [[BMC]]
@@ -143,19 +147,19 @@ func.func @test_lec(%arg0: !smt.bv<1>) -> (i1, i1, i1) {
143147
// CHECK: [[C5:%.+]] = builtin.unrealized_conversion_cast [[NARG]] : i1 to !smt.bv<1>
144148
// CHECK: return [[C4]], [[C5]]
145149
// CHECK: }
146-
// CHECK: func.func @bmc_circuit([[ARGO:%.+]]: !smt.bv<1>, [[ARG1:%.+]]: !smt.bv<32>, [[ARG2:%.+]]: !smt.bv<32>)
150+
// CHECK: func.func @bmc_circuit([[ARGO:%.+]]: !smt.bv<1>, [[ARG1:%.+]]: !smt.bv<32>, [[ARG2:%.+]]: !smt.bv<32>, [[ARG3:%.+]]: !smt.bv<32>, [[ARG4:%.+]]: !smt.array<[!smt.bv<1> -> !smt.bv<32>]>)
147151
// CHECK: [[C6:%.+]] = builtin.unrealized_conversion_cast [[ARG2]] : !smt.bv<32> to i32
148152
// CHECK: [[C7:%.+]] = builtin.unrealized_conversion_cast [[ARG1]] : !smt.bv<32> to i32
149153
// CHECK: [[CN1_I32:%.+]] = hw.constant -1 : i32
150154
// CHECK: [[ADD:%.+]] = comb.add [[C7]], [[C6]]
151155
// CHECK: [[XOR:%.+]] = comb.xor [[C6]], [[CN1_I32]]
152156
// CHECK: [[C9:%.+]] = builtin.unrealized_conversion_cast [[XOR]] : i32 to !smt.bv<32>
153157
// CHECK: [[C10:%.+]] = builtin.unrealized_conversion_cast [[ADD]] : i32 to !smt.bv<32>
154-
// CHECK: return [[C9]], [[C10]]
158+
// CHECK: return [[C9]], [[C10]], [[ARG3]], [[ARG4]]
155159
// CHECK: }
156160

157161
func.func @test_bmc() -> (i1) {
158-
%bmc = verif.bmc bound 10 num_regs 1 initial_values [unit]
162+
%bmc = verif.bmc bound 10 num_regs 3 initial_values [unit, 42, unit]
159163
init {
160164
%c0_i1 = hw.constant 0 : i1
161165
%clk = seq.to_clock %c0_i1
@@ -171,12 +175,12 @@ func.func @test_bmc() -> (i1) {
171175
verif.yield %newclk, %newStateArg : !seq.clock, i1
172176
}
173177
circuit {
174-
^bb0(%clk: !seq.clock, %arg0: i32, %state0: i32):
178+
^bb0(%clk: !seq.clock, %arg0: i32, %state0: i32, %state1: i32, %state2: !hw.array<2xi32>):
175179
%c-1_i32 = hw.constant -1 : i32
176180
%0 = comb.add %arg0, %state0 : i32
177181
// %state0 is the result of a seq.compreg taking %0 as input
178182
%2 = comb.xor %state0, %c-1_i32 : i32
179-
verif.yield %2, %0 : i32, i32
183+
verif.yield %2, %0, %state1, %state2 : i32, i32, i32, !hw.array<2xi32>
180184
}
181185
func.return %bmc : i1
182186
}

0 commit comments

Comments
 (0)