Reputation: 26647
I verify an 8-bit adder with this code. When I print the number of reachable states are 1 if the main module is empty and 2^32 if the entire main module is included. What makes this difference in reported number of reachable states? For a 4-bit adder the reported number of reachable states were 2^16. It seems logical that the input states are 2^n if n is the number of bits. Where do all the other states come from? I noticed it is when I add the line a : adder (in1, in2);
that the states increase, but that was only experimentally verified that this is what increasese the number of states and not the adder module itself. Why?
MODULE bit-adder (in1, in2, cin)
VAR
sum : boolean;
cout : boolean;
ASSIGN
next (sum) := (in1 xor in2) xor cin;
next (cout) := (in1 & in2) | ((in1 | in2) & cin);
MODULE adder (in1, in2)
VAR
bit0 : bit-adder (in1[0], in2[0], FALSE);
bit1 : bit-adder (in1[1], in2[1], bit0.cout);
bit2 : bit-adder (in1[2], in2[2], bit1.cout);
bit3 : bit-adder (in1[3], in2[3], bit2.cout);
bit4 : bit-adder (in1[4], in2[4], bit3.cout);
bit5 : bit-adder (in1[5], in2[5], bit4.cout);
bit6 : bit-adder (in1[6], in2[6], bit5.cout);
bit7 : bit-adder (in1[7], in2[7], bit6.cout);
DEFINE
sum0 := bit0.sum;
sum1 := bit1.sum;
sum2 := bit2.sum;
sum3 := bit3.sum;
sum4 := bit4.sum;
sum5 := bit5.sum;
sum6 := bit6.sum;
sum7 := bit7.sum;
overflow := bit7.cout;
MODULE main
VAR
in1 : array 0 .. 7 of boolean;
in2 : array 0 .. 7 of boolean;
a : adder (in1, in2);
ASSIGN
next (in1[0]) := in1[0];
next (in1[1]) := in1[1];
next (in1[2]) := in1[2];
next (in1[3]) := in1[3];
next (in1[4]) := in1[4];
next (in1[5]) := in1[5];
next (in1[6]) := in1[6];
next (in1[7]) := in1[7];
next (in2[0]) := in2[0];
next (in2[1]) := in2[1];
next (in2[2]) := in2[2];
next (in2[3]) := in2[3];
next (in2[4]) := in2[4];
next (in2[5]) := in2[5];
next (in2[6]) := in2[6];
next (in2[7]) := in2[7];
DEFINE
op1 := toint (in1[0]) + 2 * toint (in1[1]) + 4 * toint (in1[2]) + 8 * toint
(in1[3]) + 16 * toint (in1[4]) + 32 * toint (in1[5]) + 64 * toint (in1[6]) + 128 * toint
(in1[7]);
op2 := toint (in2[0]) + 2 * toint (in2[1]) + 4 * toint (in2[2]) + 8 * toint
(in2[3]) + 16* toint (in2[4]) + 32 * toint (in2[5]) + 64 * toint (in2[6]) + 128 * toint
(in2[7]);
sum := toint (a.sum0) + 2 * toint (a.sum1) + 4 * toint (a.sum2) + 8 * toint
(a.sum3) +16 * toint (a.sum4) + 32 * toint (a.sum5) + 64 * toint (a.sum6) + 128 * toint
(a.sum7) + 256 * toint (a.overflow);
LTLSPEC F op1 + op2 = sum
Upvotes: 2
Views: 127
Reputation: 7342
Empty Main Module. If you don't include (directly or indirectly) something in the main module, then it is simply ignored. You can think of it like defining a class in C++ and never instantiating/using it anywhere else: it can be optimised away by the compiler without affecting the execution of the system.
8-bit adder.
nuXmv > print_reachable_states
######################################################################
system diameter: 1
reachable states: 4.29497e+09 (2^32) out of 4.29497e+09 (2^32)
######################################################################
This is my take on the example:
Module bit-adder
has two boolean
variables, both of these are free to take any legal value in the domain of a boolean
variable, depending on the specific inputs of the Module (i.e. the values in in1
, in2
and cin
).
Module adder
has eight bit-adder
sub-modules and a number of defined fields, which don't really count when the purpose is to estimate the state space. This module does not add any particular constraint on the possible states assumed by the variables in the bit-adders
, so the only thing that matters is that by combining eight bit-adders
together it has a potential space of 2^16
states.
Module main
has one adder
module and two 8-bit
arrays which model the inputs. These inputs are arbitrarily chosen in the first state and remain fixed forever, so they add 2^16
possible initial states to the system. The adder
itself overall has 2^16
reachable states. The combination of both things yields a space of 2^32
states.
Addendum. In the above output, nuXmv warns you that the system diameter is only 1
. This is due to the fact that both sum
and cout
can assume an arbitrary value in the initial state, so for any choice of in1
and in2
, there exists at least one initial state in which the values of sum
and cout
of each bit-adder
in the system coincide with the correct sum, without any computation being necessary. This is obviously an unrealistic stretch. A better approach would be to force both sum
and cout
being initialised to FALSE
:
nuXmv > print_reachable_states
######################################################################
system diameter: 9
reachable states: 259539 (2^17.9856) out of 4.29497e+09 (2^32)
######################################################################
You can see that this time around the system diameter has increased to 9. This is obviously due to the fact that in this very simple encoding of a circuit adder the carry-bit
is propagated along the diagonal, and each propagation step takes one transition. The number of reachable states has also been reduced thanks to the fact we have discarded some configurations by fixing the values of cout
and sum
.
Upvotes: 1