4 from nmigen
import Elaboratable
, Signal
, Module
5 from nmigen
.asserts
import Assert
, Assume
, Cover
, Past
, Stable
6 from nmigen
.cli
import rtlil
8 from nmutil
.formaltest
import FHDLTestCase
9 from nmutil
.gtkw
import write_gtkw
11 from soc
.experiment
.alu_fsm
import Shifter
14 # This defines a module to drive the device under test and assert
15 # properties about its outputs
16 class Driver(Elaboratable
):
27 m
.submodules
.dut
= dut
= Shifter(8)
28 # Coverage condition (one bit for each coverage case)
30 # input data to the shifter
36 # transaction count for each port
41 # keep data and valid stable, until accepted
42 with m
.If(Past(dut
.p
.i_valid
) & ~
Past(dut
.p
.o_ready
)):
44 Assume(Stable(dut
.op
.sdir
)),
45 Assume(Stable(dut
.p
.data_i
.data
)),
46 Assume(Stable(dut
.p
.data_i
.shift
)),
47 Assume(Stable(dut
.p
.i_valid
)),
49 # force reading the output in a reasonable time,
50 # necessary to pass induction
51 with m
.If(Past(dut
.n
.o_valid
) & ~
Past(dut
.n
.i_ready
)):
52 comb
+= Assume(dut
.n
.i_ready
)
53 # capture transferred input data
54 with m
.If(dut
.p
.o_ready
& dut
.p
.i_valid
):
56 data_i
.eq(dut
.p
.data_i
.data
),
57 shift_i
.eq(dut
.p
.data_i
.shift
),
58 op_sdir
.eq(dut
.op
.sdir
),
59 # increment write counter
60 write_cnt
.eq(write_cnt
+ 1),
62 # calculate the expected result
63 dut_data_i
= dut
.p
.data_i
.data
64 dut_shift_i
= dut
.p
.data_i
.shift
[:4]
65 dut_sdir
= dut
.op
.sdir
67 sync
+= expected
.eq(dut_data_i
>> dut_shift_i
)
69 sync
+= expected
.eq(dut_data_i
<< dut_shift_i
)
70 # Check for dropped inputs, by ensuring that there are no more than
71 # one work item ever in flight at any given time.
72 # Whenever the unit is busy (not ready) the read and write counters
73 # will differ by exactly one unit.
74 m
.d
.comb
+= Assert((read_cnt
+ ~dut
.p
.o_ready
) & 0xF == write_cnt
)
75 # Check for liveness. It will ensure that the FSM is not stuck, and
76 # will eventually produce some result.
77 # In this case, the delay between o_ready being negated and o_valid
78 # being asserted has to be less than 16 cycles.
79 with m
.If(~dut
.p
.o_ready
& ~dut
.n
.o_valid
):
80 m
.d
.sync
+= live_cnt
.eq(live_cnt
+ 1)
82 m
.d
.sync
+= live_cnt
.eq(0)
83 m
.d
.comb
+= Assert(live_cnt
< 16)
84 # check coverage as output data is accepted
85 with m
.If(dut
.n
.i_ready
& dut
.n
.o_valid
):
86 # increment read counter
87 sync
+= read_cnt
.eq(read_cnt
+ 1)
89 comb
+= Assert(dut
.n
.data_o
.data
== expected
)
90 # cover zero data, with zero and non-zero shift
92 with m
.If(data_i
== 0):
93 with m
.If(shift_i
== 0):
95 with m
.If(shift_i
[:3].any() & ~shift_i
[3]):
97 # cover non-zero data, with zero and non-zero shift
99 with m
.If(data_i
!= 0):
100 with m
.If(shift_i
== 0):
102 with m
.If(shift_i
[:3].any() & ~shift_i
[3]):
108 with m
.If(shift_i
[3] != 0):
110 # cover non-zero shift giving non-zero result
111 with m
.If(data_i
.any() & shift_i
.any() & dut
.n
.data_o
.data
.any()):
113 # dummy condition, to avoid optimizing-out the counters
114 with m
.If((write_cnt
!= 0) |
(read_cnt
!= 0)):
116 # check that each condition above occurred at least once
117 comb
+= Cover(cov
.all())
121 class ALUFSMTestCase(FHDLTestCase
):
122 def test_formal(self
):
125 'p_data_i[7:0]', 'p_shift_i[7:0]', 'op__sdir',
126 'p_i_valid', 'p_o_ready',
128 'n_o_valid', 'n_i_ready',
129 ('formal', {'module': 'top'}, [
130 'write_cnt[3:0]', 'read_cnt[3:0]', 'cov[7:0]'
134 'test_formal_cover_alu_fsm.gtkw',
135 os
.path
.dirname(__file__
) +
136 '/proof_alu_fsm_formal/engine_0/trace0.vcd',
142 'test_formal_bmc_alu_fsm.gtkw',
143 os
.path
.dirname(__file__
) +
144 '/proof_alu_fsm_formal/engine_0/trace.vcd',
150 'test_formal_induct_alu_fsm.gtkw',
151 os
.path
.dirname(__file__
) +
152 '/proof_alu_fsm_formal/engine_0/trace_induct.vcd',
158 self
.assertFormal(module
, mode
="prove", depth
=18)
159 self
.assertFormal(module
, mode
="cover", depth
=32)
164 vl
= rtlil
.convert(dut
, ports
=[])
165 with
open("alu_fsm.il", "w") as f
:
169 if __name__
== '__main__':