18-642:
Modal Systems & Statecharts
9/6/2018
“When you come to a fork in the road,
take it.”
― Yogi Berra
© 2017-2018 Philip Koopman © 2017 Philip Koopman 1
State Intensive Systems
Anti-Patterns:
No detailed design – just code
Deeply nested if statements
instead of switch statements
for state-full code
Mixing mode change logic
with normal output sequences
Detailed design of state-intensive behaviors
Operating modes, e.g., stop, start, run
Inputs that drive sequences of events
Key technique: statecharts (software finite state machine)
© 2017 Philip Koopman 2
3-Speed Fan FlowChart
Example code for 3-speed fan
Draw a flowchart -- how easy is it to understand this code?
Are there any bugs in this code?
// SPDBUTTON: input true on cycle when speed button depressed
// ONOFF: input true one cycle when on/off switch depressed
static uint8_t speed; // 0=off; 1=slow; 2=medium; 3=fast
…..
if(speed == 0)
{ if(SPDBUTTON == 1 || ONOFF == 1) { speed = 1; }
} else if (SPDBUTTON == 1)
{ if (speed == 1) { speed = 2; }
else if (speed == 2) { speed = 3;}
else { speed = 0;}
} else if (ONOFF == 1)
{ speed = 0;} © 2017 Philip Koopman 3
Elements of a Statechart
A statechart is a software tem
s et
Finite State Machine: y
S es
R
Set of states with side effects
Set of guards that cause transitions
– No side effects on transitions STATE ID
Initial state Side Effect
Convert example fan code to statechart
(Next slide has graphic)
Transition
Guard
Define a state for each fan speed Condition
Define transitions
Easier to understand? Any bugs?
© 2017 Philip Koopman 4
Convert to StateChart Exercise: 3-speed fan
Define initial state, side effects, transitions
© 2017 Philip Koopman 5
Corrected Statechart
This is a controller for a
multi-speed motor or
other similar application
Inputs:
SPDBUTTON, ONOFF
Outputs: Speed =
{Stop, Slow, Med, Fast}
State names
(arbitrary labels):
{OFF, SLOW, MEDIUM,
FAST}
System Reset is to state s1
© 2017 Philip Koopman 6
Fan Example Code – part 1
static enum CurrState {OFF, SLOW, MEDIUM, FAST}; // define states
static const uint8_t SpdOff=0; // define speed constant values
static const uint8_t SpdSlow=10;
static const uint8_t SpdMed=15;
static const uint8_t SpdFast=25;
CurrState = OFF; // initialize state machine to OFF
void ProcessStates(void) // run periodically from main loop
{ switch (CurrState)
{ case OFF: // State S1
speed(SpdOff); // Take action in state
// Test arc guards and take transitions
if (SpdButton() == TRUE || OnOffButton() == TRUE) {CurrState = SLOW;}
break; // go to end of switch statement
case SLOW: // State S2
speed(SpdSlow); // take action
if (SpdButton() == TRUE) {CurrState = MEDIUM;}
if (OnOffButton() == TRUE) {CurrState = OFF;}
break;
© 2017 Philip Koopman 7
Fan Example Code – part 2
case MEDIUM: // State S3
speed(SpdMed); // take action
if (SpdButton() == TRUE) {CurrState = FAST;}
if (OnOffButton() == TRUE) {CurrState = OFF;}
break;
case FAST: // State S4
speed(SpdFast); // take action
if (SpdButton() == TRUE) {CurrState = SLOW;}
if (OnOffButton() == TRUE) {CurrState = OFF;}
break;
default: // Error: invalid state
error(INVALID_STATE_ERROR); // should never get here
}
}
© 2017 Philip Koopman 8
Half-Duplex Serial Port Example
RDRF = “Receive Data Register Full” Data byte arrived SCDR = “Serial Comms. Data Reg.”
TDRE = “Transmit Data Register Empty” Done sending XON/XOR Flow Control
4. Receive 1. Transceive
Idle XON Idle
RDRF ~XON RDRF TDRE &&
~RDRF
5. Read
2. Read 3. Write
Data Byte
Data Byte Data Byte
(XOFF)
XOFF ~XOFF
[Valvano 2006] © 2017 Philip Koopman 9
Best Practices for Statecharts
Use statecharts for stateful code
Maps to easier-to-test switch statement
Avoid actions on arcs to simplify code
Move complex behaviors to per-state
subroutine helper functions to
limit cyclomatic complexity
Summary of pitfalls https://goo.gl/ocnSRS
Some code is better as flowchart if there is no state history
Don’t let statechart get too complex
– Might need to decompose into nested or parallel state machines
© 2017 Philip Koopman 10
https://xkcd.com/1195/
https://xkcd.com/844/
© 2017 Philip Koopman 11