machine-check 0.3.0

Formal verification tool for digital systems
Documentation
// An example of a very simple machine-check system.
//
// The used structs and implementations must be enclosed
// within a module on which the machine_description macro
// is applied.

#[machine_check::machine_description]
mod machine_module {
    // Currently, all used non-global paths must be imported
    // within the machine_description macro.
    //
    // Only machine-check types and traits, plus a few std
    // traits, can be used.
    use ::machine_check::{BitvectorArray, Ext, Unsigned};
    use ::std::{
        clone::Clone,
        cmp::{Eq, PartialEq},
        fmt::Debug,
        hash::Hash,
    };

    // It is required to derive the following traits
    // on the structs that will be used for machine-check
    // verification.
    #[derive(Clone, PartialEq, Eq, Hash, Debug)]
    pub struct Input {
        increment: Unsigned<1>,
        unused: BitvectorArray<16, 8>,
    }

    // Input implementation signifies that the struct
    // can be used as input to a system implementing Machine.
    impl ::machine_check::Input for Input {}

    #[derive(Clone, PartialEq, Eq, Hash, Debug)]
    pub struct State {
        value: Unsigned<8>,
        unused: BitvectorArray<16, 8>,
    }

    // State implementation signifies that the struct
    // can be used as state of a system implementing Machine.
    impl ::machine_check::State for State {}

    // The system can contain data that will be unchanging,
    // but usable within the machine-stepping functions.
    //
    // This one does not contain any such data.
    #[derive(Clone, PartialEq, Eq, Hash, Debug)]
    pub struct System {}

    // Machine implementation makes it possible to formally
    // verify the system.
    impl ::machine_check::Machine for System {
        // The type of inputs to the finite-state machine.
        type Input = Input;
        // The type of states of the finite-state machine.
        type State = State;

        // Machine initialization. Given an input, a state is generated.
        // The function must be pure, i.e. give the same result with the
        // same parameters.
        //
        // Here, the value is initialized to zero, and the unused array
        // is taken from the input, i.e. can have any values.
        fn init(&self, input: &Input) -> State {
            State {
                value: Unsigned::<8>::new(0),
                unused: Clone::clone(&input.unused),
            }
        }

        // Machine step. Given a state and an input, the next state is generated.
        // Again, the function must be pure.
        //
        // Here, the value is incremented if input increment field is 1.
        // If it reaches 157, it is immediately reset to 0. The unused array
        // is again taken from the input, i.e. can have any values, and the
        // values do not have to match the previous ones.
        fn next(&self, state: &State, input: &Input) -> State {
            // The increment is extended to 8 bits (zero-extension because
            // it is Unsigned), then added to the value in the current state.
            // Currently, it must be called using associated function call,
            // i.e. Ext::<8>::ext(a), rather than method call input.increment.ext()
            let mut next_value = state.value + Ext::<8>::ext(input.increment);
            // If the next value is 157, it is immediately set to 0.
            if next_value == Unsigned::<8>::new(157) {
                next_value = Unsigned::<8>::new(0);
            }

            // The clone function is one of the few std functions supported
            // by machine-check. Currently, the functions can only be called
            // using associated function call, i.e. Clone::clone(&a),
            // rather than the usually used method call a.clone().
            let unused = Clone::clone(&input.unused);

            // The new state is constructed with the new value and unused fields.
            State {
                value: next_value,
                unused,
            }
        }
    }
}

// Main entry point of the executable.
fn main() {
    // Construct the system. This one has no unchanging data.
    let system = machine_module::System {};
    // Run machine-check with the constructed system.
    machine_check::run(system);
}