Features for High-Availability and Safety Systems#
In high-availability systems you often need to detect serious faults that occur outside the state-machine implementation yet still corrupt its behaviour. Examples include a runaway pointer overwriting critical variables, a supply brown-out flipping a bit in memory, or a defective RAM cell. Detecting and recovering from these conditions is a system-wide design concern. The code generator can still help by making inconsistencies in the state machine itself easier to catch. It offers the following mechanisms.
Undefined-state error handler#
You can plug in handler code on the default branch of the generated switch on the state variable. That path runs when the state variable holds a value that does not correspond to any defined state, which helps catch corruption or logic errors. Enable this with the configuration key UnknownStateHandler.
Example:
switch (instanceVar->stateVar) {
case S1:
...
case S2:
...
default:
error_handler(); /* your error handler */
break;
}Validating state transitions#
The generator can optionally emit a validate function that checks whether a transition from the current state to a requested target state is allowed—that is, whether it exists in the modelled diagram. Set ValidationCall=yes in the configuration file to enable it.
The generated state machine calls a user-supplied handler; that handler should call the generated validate function. You decide what happens when a transition is rejected (log, safe state, reset, and so on). The validation table itself is generated for you.
On a single CPU, validation runs on the same core as the state machine. In a redundant setup you can also run the validate logic on a second CPU.
Example state machine (excerpt):
case S22:
if (msg == (TESTCASE_EVENT_T)ev32) {
/* Transition from S22 to S21 */
testcaseValidationHandler(S22, S21, instanceVar->inst_id);
evConsumed = 1U;
/* OnExit code of state S22 */
...Example of a user-defined handler:
/* your handler */
void testcaseValidationHandler(uint16_t from, uint16_t to, uint8_t machineId)
{
uint8_t retCode = testcaseValidate(from, to); /* allowed transition? */
if (retCode != 0U) {
/* transition not allowed */
reboot(); /* or whatever fits your system */
} else {
printf("Transition allowed\n");
}
}Generated validation code for an example machine:
#define SIZE_TRANSITION_TABLE (31U)
#define TRANSITION(from,to) (uint16_t)((from * 127) + to)
/* Hash table of valid transitions */
static const uint16_t valid_transitions[] = {
16249U , /* S1->S3 */
10880U , /* S111->S111 */
9728U , /* S11->S11 */
10489U , /* S132->S131 */
15360U , /* S3->S3 */
9754U , /* S11->S12 */
13030U , /* S12->S11 */
13056U , /* S12->S12 */
6680U , /* S13->S11 */
9704U , /* S11->S13 */
16181U , /* S1->S13 */
10892U , /* S111->S112 */
15292U , /* S3->S13 */
12404U , /* S112->S111 */
6724U , /* S13->S3 */
5745U , /* S22->S21 */
6731U , /* S13->S1 */
3855U , /* S21->S22 */
1009U , /* S4->S3 */
15247U , /* S3->S4 */
914U , /* S4->FINAL0 */
1016U , /* S4->S1 */
896U , /* S4->S4 */
5461U , /* S2->S1 */
16159U , /* S1->S21 */
9694U , /* S11->S2 */
9682U , /* S11->S21 */
9779U , /* S11->S1 */
16205U , /* S1->S11 */
16256U , /* S1->S1 */
16171U , /* S1->S2 */
};
uint8_t testcaseValidate(uint16_t from, uint16_t to)
{
uint16_t hash = TRANSITION(from, to);
uint16_t x;
for (x = 0U; x < SIZE_TRANSITION_TABLE; x++) {
if (valid_transitions[x] == hash) {
return 0U;
}
}
/* return an error */
return 1U;
}The validate code relies on a few predefined types. Define them in a header that matches your platform and pull it in with the AdditionalValidateIncludes configuration parameter.
State and event values with Hamming distance#
States and events can be emitted in ascending order with a configurable Hamming distance between successive numeric codes. If one or more bits flip accidentally, the state variable is more likely to fall into a value that matches no case label, so execution drops into the default path and your error handler can run. See UseHammingCodesForEvents, UseHammingCodesForStates, and HammingDistance.
Example configuration:
UseHammingCodesForEvents=yes
UseHammingCodesForStates=yes
HammingDistance=3Example generated enum spacing:
/* Events which can be sent to the state machine */
typedef enum {
evInnerS3 = 0U,
ev3434 = 7U,
ev111 = 25U,
evInnerS111 = 30U,
evBH = 42U,
evBG = 45U,
evBF = 51U,
...
TESTCASE_NO_MSG = 461U
} TESTCASE_EVENT_T;If you have questions or suggestions, send us an email.