recovery/recovery.rs
1//! A configurable system where the recovery property holds or not depending on a command-line argument.
2//!
3//! This system keeps the maximum value of the input `value` in its state.
4//! If the command-line argument `--system-enable-reset` is given, it can reset the maximum value
5//! to zero based on the input `reset`.
6//!
7//! We can verify whether the *recovery* property `AG![EF![max_value == 0]]` holds, i.e. considering that
8//! we are in any reachable state, we can reach the state where `max_value` equals zero. To complicate
9//! things a bit, there is an unused state field `unused` that is loaded in each cycle from the input
10//! `unused` and a free-running counter `free_counter` that does not interact with `max_value`.
11//!
12//! Without the command-line argument `--system-enable-reset`, **machine-check** will quickly determine
13//! the recovery property does not hold. When using the argument, it will not be able to determine it
14//! quickly unless `--strategy decay` is used, which lets it ignore the free-running counter.
15//!
16use clap::Args;
17use machine_check::Bitvector;
18
19#[machine_check::machine_description]
20mod machine_module {
21 use ::machine_check::{Bitvector, Unsigned};
22 use ::std::{
23 clone::Clone,
24 cmp::{Eq, PartialEq},
25 fmt::Debug,
26 hash::Hash,
27 };
28
29 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
30 pub struct Input {
31 /// A value input that we track the maximum value of.
32 value: Unsigned<4>,
33 /// A reset signal. May or may not be actually enabled.
34 reset: Bitvector<1>,
35 /// An unused input.
36 unused: Bitvector<4>,
37 }
38
39 impl ::machine_check::Input for Input {}
40
41 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
42 pub struct State {
43 /// The maximum value of the input `value`.
44 ///
45 /// Can be reset by the input `reset` if `enable_reset` is chosen.
46 max_value: Unsigned<4>,
47 /// An unused value.
48 unused: Bitvector<4>,
49 /// An irrelevant free-running counter.
50 free_counter: Unsigned<4>,
51 }
52
53 impl ::machine_check::State for State {}
54
55 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
56 pub struct System {
57 pub enable_reset: Bitvector<1>,
58 }
59
60 impl ::machine_check::Machine for System {
61 type Input = Input;
62 type State = State;
63 #[allow(non_snake_case)]
64 fn init(&self, _input: &Input) -> State {
65 State {
66 max_value: Unsigned::<4>::new(0),
67 unused: Bitvector::<4>::new(0),
68 free_counter: Unsigned::<4>::new(0),
69 }
70 }
71
72 fn next(&self, state: &State, input: &Input) -> State {
73 let input_value = input.value;
74
75 // If the maximum value is smaller than the input value,
76 // update it to the input value.
77 let mut next_max = state.max_value;
78 if next_max < input_value {
79 next_max = input_value;
80 }
81 // If the reset input is asserted and it is actually enabled in the system,
82 // reset the maximum value to zero.
83 if (input.reset & self.enable_reset) == Bitvector::<1>::new(1) {
84 next_max = Unsigned::<4>::new(0);
85 }
86
87 // Increment the free-running counter. It will wrap around eventually.
88 let free_counter = state.free_counter + Unsigned::<4>::new(1);
89 State {
90 max_value: next_max,
91 unused: input.unused,
92 free_counter,
93 }
94 }
95 }
96}
97
98#[derive(Args)]
99struct SystemArgs {
100 #[arg(long = "system-enable-reset")]
101 enable_reset: bool,
102}
103
104fn main() {
105 let (run_args, system_args) = machine_check::parse_args::<SystemArgs>(std::env::args());
106 if system_args.enable_reset {
107 println!("Reset input is enabled");
108 } else {
109 println!("Reset input is disabled");
110 }
111 let enable_reset = Bitvector::<1>::new(system_args.enable_reset as u64);
112 machine_check::execute(machine_module::System { enable_reset }, run_args);
113}