counter/counter.rs
1//! An example of a very simple machine-check system.
2//!
3//! The used structs and implementations must be enclosed
4//! within a module on which the machine_description macro
5//! is applied.
6
7#[machine_check::machine_description]
8mod machine_module {
9 // Currently, all used non-global paths must be imported
10 // within the machine_description macro.
11 //
12 // Only machine-check types and traits, plus a few std
13 // traits, can be used.
14 use ::machine_check::{BitvectorArray, Ext, Unsigned};
15 use ::std::{
16 clone::Clone,
17 cmp::{Eq, PartialEq},
18 fmt::Debug,
19 hash::Hash,
20 };
21
22 // It is required to derive the following traits
23 // on the structs that will be used for machine-check
24 // verification.
25 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
26 pub struct Input {
27 increment: Unsigned<1>,
28 unused: BitvectorArray<16, 8>,
29 }
30
31 // Input implementation signifies that the struct
32 // can be used as input to a system implementing Machine.
33 impl ::machine_check::Input for Input {}
34
35 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
36 pub struct State {
37 value: Unsigned<8>,
38 unused: BitvectorArray<16, 8>,
39 }
40
41 // State implementation signifies that the struct
42 // can be used as state of a system implementing Machine.
43 impl ::machine_check::State for State {}
44
45 // The system can contain data that will be unchanging,
46 // but usable within the machine-stepping functions.
47 //
48 // This one does not contain any such data.
49 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
50 pub struct System {}
51
52 // Machine implementation makes it possible to formally
53 // verify the system.
54 impl ::machine_check::Machine for System {
55 // The type of inputs to the finite-state machine.
56 type Input = Input;
57 // The type of states of the finite-state machine.
58 type State = State;
59
60 // Machine initialization. Given an input, a state is generated.
61 // The function must be pure, i.e. give the same result with the
62 // same parameters.
63 //
64 // Here, the value is initialized to zero, and the unused array
65 // is taken from the input, i.e. can have any values.
66 fn init(&self, input: &Input) -> State {
67 State {
68 value: Unsigned::<8>::new(0),
69 unused: Clone::clone(&input.unused),
70 }
71 }
72
73 // Machine step. Given a state and an input, the next state is generated.
74 // Again, the function must be pure.
75 //
76 // Here, the value is incremented if input increment field is 1.
77 // If it reaches 157, it is immediately reset to 0. The unused array
78 // is again taken from the input, i.e. can have any values, and the
79 // values do not have to match the previous ones.
80 fn next(&self, state: &State, input: &Input) -> State {
81 // The increment is extended to 8 bits (zero-extension because
82 // it is Unsigned), then added to the value in the current state.
83 // Currently, it must be called using associated function call,
84 // i.e. Ext::<8>::ext(a), rather than method call input.increment.ext()
85 let mut next_value = state.value + Ext::<8>::ext(input.increment);
86 // If the next value is 157, it is immediately set to 0.
87 if next_value == Unsigned::<8>::new(157) {
88 next_value = Unsigned::<8>::new(0);
89 }
90
91 // The clone function is one of the few std functions supported
92 // by machine-check. Currently, the functions can only be called
93 // using associated function call, i.e. Clone::clone(&a),
94 // rather than the usually used method call a.clone().
95 let unused = Clone::clone(&input.unused);
96
97 // The new state is constructed with the new value and unused fields.
98 State {
99 value: next_value,
100 unused,
101 }
102 }
103 }
104}
105
106// Main entry point of the executable.
107fn main() {
108 // Construct the system. This one has no unchanging data.
109 let system = machine_module::System {};
110 // Run machine-check with the constructed system.
111 machine_check::run(system);
112}