In this lesson, you will learn how to leverage formal model checking to mathematically prove that your embedded state machines are free of reachability bugs or invalid state transitions.
From Testing to Proof
Standard unit tests and Hardware-in-the-Loop (HIL) testing check for the presence of bugs by executing specific inputs. Formal verification, however, uses mathematical modeling to explore every possible state sequence in your system. In embedded Rust, we typically use model checkers like Kani or TLA+ to define a transition system where we assert that certain "unsafe" states are unreachable.
Defining the State Machine
Consider a critical state machine for an embedded motor controller. You want to prove that the motor can never be "Running" while the "Emergency Stop" sensor is active.
In Rust, you model the logic using a simple enum:
#[derive(Copy, Clone, PartialEq)]
enum SystemState { Idle, Running, Stopped }
fn check_transition(state: SystemState, e_stop: bool) {
if e_stop {
// Assert that we can never be in a Running state if e_stop is true
assert!(state != SystemState::Running);
}
}
By passing this code through a formal verifier, the tool doesn't just run the function once; it constructs a proof representing all combinations of state and e_stop. If there is any path—even an obscure combination of interrupt-driven state changes—that allows the system to be Running while e_stop is true, the solver will provide a counter-example trace.
Why Verification Matters
In embedded systems, some failure modes occur only after thousands of specific, interleaved events. Writing test cases to trigger these is often impossible. By abstracting your logic into a state machine and running a model checker, you move from "it seems to work under these tests" to "the logic is mathematically incapable of entering an invalid state." This is the highest level of assurance for critical control loops, effectively shifting your safety guarantee from empirical observation to logical necessity.
Key Takeaway: Formal verification uses exhaustive state exploration to mathematically guarantee that your embedded logic maintains safety invariants across every possible execution path.