Skip to content

Latches

Remembering a request until it has been served, with one writer per variable, and deciding what happens when a request and its service coincide.

A request is often over long before anyone can act on it. You press a lift’s call button for a fraction of a second; the lift may take half a minute to come. Something has to remember that the button was pressed, show it (the lamp in the button), and forget it again once the lift has arrived. That something is a latch: it is set by a request and reset by the service.

This page builds a latch for a lift call button, looks at the two questions every latch has to answer (who wins when a request and its service arrive in the same tick, and who may write the remembered value), and shows the same latch as a one-line equation.

The memory is a state. Idle means “no call”, Waiting means “a call is registered”. The lamp is on exactly while Waiting is active, thanks to its entry and exit actions:

CallButtonSimulation

Diagram

Diagram of CallButtonStates: Idle (initial), Waiting (superstate). Transitions: Idle to Waiting: button; Waiting to Idle: arrived.CallButtoninput boolbuttoninput boolarrivedoutput boollampIdleWaitingentry/ lamp = trueexit/ lamp = falsebuttonarrived

Code

scchart CallButton {
input bool button // the call button on this floor
input bool arrived // the lift is here, doors open
output bool lamp // "call registered"
initial state Idle
if button go to Waiting
state Waiting {
entry do lamp = true
exit do lamp = false
}
if arrived go to Idle
}
Tick 0 of 14
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of CallButton
Tickinput bool buttoninput bool arrivedoutput bool lampActive state of the chart
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 14 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 14 ticks:

  1. button = false, arrived = falseTick 1: no call, the lamp is off.
  2. button = trueSomeone presses the button: Idle moves to Waiting, whose entry action lights the lamp.
  3. button = falseThe button is released, but the chart is still in Waiting: the state is the memory. The lamp stays on.
  4. Still waiting for the lift.
  5. button = trueA second press while the call is registered changes nothing: Waiting has no transition for the button.
  6. button = falseWaiting.
  7. arrived = trueThe lift arrives: if arrived go to Idle fires, and Waiting’s exit action switches the lamp off. The call has been served.
  8. button = trueThe doors are still open, and someone presses the button anyway. Idle sees the press and lights the lamp, although the lift is right here.
  9. button = falseDoors still open: the call is served at once, and the lamp goes off again. A one-tick flash for a call that never needed registering.
  10. arrived = falseThe lift leaves.
  11. button = trueA new call.
  12. button = falseWaiting.
  13. button = true, arrived = trueThe lift arrives in the same tick as another press: if arrived fires, and the press is served along with the earlier call.
  14. button = false, arrived = falseIdle.
The press in tick 2 lasts one tick; the lamp stays on until the lift arrives in tick 7. Then watch ticks 8 and 9.

The press in tick 2 lasts a single tick, but the chart stays in Waiting until arrived, so the lamp stays on. A second press while waiting (tick 5) changes nothing, because Waiting has no transition that looks at the button. That is usually what you want: a lift call is either registered or it is not.

Ticks 8 and 9 show a flaw. The lift is standing at the floor with its doors open when somebody presses the button. Idle sees the press and registers a call, and in the next tick, the doors still being open, Waiting serves it again. The lamp flashes for one tick, for a call that never needed registering.

Every latch has to decide what happens when the set and the reset arrive together. There are two places where they can meet:

  • In Idle: a press while the lift is already here. The press is served at once, so it should not be registered: if button && !arrived go to Waiting.
  • In Waiting: the lift arrives in the very tick someone presses again. The lift is here, so this press is served together with the call that was waiting: if arrived go to Idle stays as it is.
CallButton and CallButtonFixedComparison

Diagrams

CallButton

Diagram of CallButtonStates: Idle (initial), Waiting (superstate). Transitions: Idle to Waiting: button; Waiting to Idle: arrived.CallButtoninput boolbuttoninput boolarrivedoutput boollampIdleWaitingentry/ lamp = trueexit/ lamp = falsebuttonarrived

CallButtonFixed

Diagram of CallButtonFixedStates: Idle (initial), Waiting (superstate). Transitions: Idle to Waiting: button && !arrived; Waiting to Idle: arrived.CallButtonFixedinput boolbuttoninput boolarrivedoutput boollampIdleWaitingentry/ lamp = trueexit/ lamp = falsebutton && !arrivedarrived

Code

CallButton

scchart CallButton {
input bool button // the call button on this floor
input bool arrived // the lift is here, doors open
output bool lamp // "call registered"
initial state Idle
if button go to Waiting
state Waiting {
entry do lamp = true
exit do lamp = false
}
if arrived go to Idle
}

CallButtonFixed

scchart CallButtonFixed {
input bool button // the call button on this floor
input bool arrived // the lift is here, doors open
output bool lamp // "call registered"
initial state Idle
if button && !arrived go to Waiting
state Waiting {
entry do lamp = true
exit do lamp = false
}
if arrived go to Idle
}
Tick 0 of 14
Inputs for tick 1, for both charts Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of CallButton and CallButtonFixed, no ticks yet
Tickinput bool buttoninput bool arrivedCallButton: output bool lampCallButton: Active state of the chartCallButtonFixed: output bool lampCallButtonFixed: Active state of the chart
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 14 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 14 ticks, the same inputs for both charts:

  1. button = false, arrived = falseTick 1: no call in either chart; both lamps are off.
  2. button = trueA press: both charts move from Idle to Waiting, whose entry action lights the lamp.
  3. button = falseThe button is released, but both charts stay in Waiting: the state is the memory.
  4. Waiting for the lift.
  5. button = trueA second press while a call is registered changes nothing: Waiting has no transition for the button.
  6. button = falseWaiting.
  7. arrived = trueThe lift arrives: if arrived go to Idle fires in both, and the exit action switches the lamp off.
  8. button = trueA press while the doors are still open. CallButton’s Idle tests only button, registers the press and lights its lamp. CallButtonFixed tests button && !arrived, which is false: its lamp stays off.
  9. button = falseThe doors are still open, so CallButton serves its needless call at once: a one-tick flash of the lamp.
  10. arrived = falseThe lift leaves.
  11. button = trueA new call in both charts.
  12. button = falseWaiting.
  13. button = true, arrived = trueThe lift arrives in the same tick as another press. Both charts are in Waiting, whose only transition is if arrived go to Idle: the press is served together with the earlier call, and both lamps go off.
  14. button = false, arrived = falseIdle.
Tick 8: a press while the doors are open. Only the fixed version leaves the lamp off.

First and Fixed are lamp in CallButton and in CallButtonFixed:

Tick Inputs First Fixed
7 arrived false false
8 button, arrived (held) true false
9 arrived (held) false false
13 button, arrived false false

Here the reset wins in both places. That is right for a lift, but it is a decision, not a law. A latch whose reset wins is called reset-dominant; one whose set wins is set-dominant.

Think of an alarm that a fault switches on and an operator acknowledges with ack. If the operator acknowledges while the fault is still present, a reset-dominant latch switches the alarm off, and the still-present fault switches it straight back on in the next tick. The acknowledgement is wasted, and the alarm flickers. A set-dominant latch refuses the acknowledgement for as long as the fault lasts:

AlarmResetWins and AlarmSetWinsComparison

Diagrams

AlarmResetWins

Diagram of AlarmResetWinsStates: Normal (initial), Alarm (superstate). Transitions: Normal to Alarm: fault; Alarm to Normal: ack.AlarmResetWinsinput boolfault, ackoutput boolalarmNormalAlarmentry/ alarm = trueexit/ alarm = falsefaultack

AlarmSetWins

Diagram of AlarmSetWinsStates: Normal (initial), Alarm (superstate). Transitions: Normal to Alarm: fault; Alarm to Normal: ack && !fault.AlarmSetWinsinput boolfault, ackoutput boolalarmNormalAlarmentry/ alarm = trueexit/ alarm = falsefaultack && !fault

Code

AlarmResetWins

scchart AlarmResetWins {
input bool fault, ack
output bool alarm
initial state Normal
if fault go to Alarm
state Alarm {
entry do alarm = true
exit do alarm = false
}
if ack go to Normal
}

AlarmSetWins

scchart AlarmSetWins {
input bool fault, ack
output bool alarm
initial state Normal
if fault go to Alarm
state Alarm {
entry do alarm = true
exit do alarm = false
}
if ack && !fault go to Normal
}
Tick 0 of 7
Inputs for tick 1, for both charts Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of AlarmResetWins and AlarmSetWins, no ticks yet
Tickinput bool faultinput bool ackAlarmResetWins: output bool alarmAlarmResetWins: Active state of the chartAlarmSetWins: output bool alarmAlarmSetWins: Active state of the chart
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 7 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 7 ticks, the same inputs for both charts:

  1. fault = false, ack = falseTick 1: no alarm in either chart.
  2. fault = trueA fault: both alarms go on.
  3. ack = trueThe operator acknowledges while the fault is still present. AlarmResetWins takes if ack and switches its alarm off. AlarmSetWins tests ack && !fault, which is false: its alarm stays on.
  4. ack = falseAlarmResetWins is back in Normal, sees the fault again and raises the alarm once more: the acknowledgement was wasted. AlarmSetWins is still alarming.
  5. fault = falseThe fault clears. Both alarms stay latched until someone acknowledges.
  6. ack = trueAcknowledged, with the fault gone: both alarms go off.
  7. ack = falseNormal.
Tick 3: ack while the fault is still present. The reset-dominant alarm goes off and comes back; the set-dominant one stays on.

In both charts the alarm stays latched after the fault itself has gone (tick 5): the operator must still acknowledge it. That is the point of a latch.

In SCCharts, who wins is decided by the triggers you write, and by priority when several transitions of one state are enabled together: the transition written first is taken. So decide the coincidence explicitly, and put a tick in your test scenario where the two arrive together.

So far one region does all the work. It is tempting to split it: one region that switches the lamp on when the button is pressed, another that switches it off when the lift arrives. KIELER refuses:

TwoWritersSimulation

Diagram

Diagram of TwoWritersStates: Watch (initial), Watch (initial). Transitions: Watch to Watch: button / lamp = true; Watch to Watch: arrived / lamp = false.TwoWritersinput boolbutton, arrivedoutput boollampButtonWatchbutton/ lamp = trueLiftWatcharrived/ lamp = false

Code

scchart TwoWriters {
input bool button, arrived
output bool lamp
region Button {
initial state Watch
if button do lamp = true go to Watch
}
region Lift {
initial state Watch
if arrived do lamp = false go to Watch
}
}

KIELER rejects this chart

  • Line 7, column 20Two concurrent regions assign different values to lampKIELER says: The SCG is NOT asc-schedulable!KIELER does not check whether the two assignments can happen in the same tick: two regions that run side by side must not both assign lamp. Let one region own lamp, and let the other regions send it requests with signals.About this message
KIELER rejects this chart: two concurrent regions write lamp.

Official KIELER reports Can't schedule from … (several times) and The SCG is NOT asc-schedulable!; SCCharts Lab adds the readable version:

Circular dependency involving lamp prevents scheduling this tick.

Two concurrent regions may not write different values to the same variable, and KIELER checks that without looking at the triggers: it does not matter that button and arrived might never be true together. (Why the compiler says no explains the rule and its exceptions; writing the same value from several regions, lamp = true and lamp = true, is allowed.)

The cure is a principle worth adopting everywhere: every variable has exactly one owner, the one region that writes it. Other regions do not write the variable; they send requests to its owner, and a signal is the natural way to send one:

OneOwnerSimulation

Diagram

Diagram of OneOwnerStates: Watch (initial), Watch (initial), Idle (initial), Waiting (superstate). Transitions: Watch to Watch: hallButton / request; Watch to Watch: carButton / request; Idle to Waiting: request && !arrived; Waiting to Idle: arrived.OneOwnerinput boolhallButton, carButton, arrivedoutput boollampsignalrequestHallWatchhallButton/ requestCarWatchcarButton/ requestLatchIdleWaitingentry/ lamp = trueexit/ lamp = falserequest && !arrivedarrived

Code

scchart OneOwner {
input bool hallButton, carButton, arrived
output bool lamp
signal request
region Hall {
initial state Watch
if hallButton do request go to Watch
}
region Car {
initial state Watch
if carButton do request go to Watch
}
region Latch {
initial state Idle
if request && !arrived go to Waiting
state Waiting {
entry do lamp = true
exit do lamp = false
}
if arrived go to Idle
}
}
Tick 0 of 10
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of OneOwner
Tickinput bool hallButtoninput bool carButtoninput bool arrivedoutput bool lampActive state of region HallActive state of region CarActive state of region Latch
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 10 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 10 ticks:

  1. hallButton = false, carButton = false, arrived = falseTick 1: nothing.
  2. hallButton = trueThe hall button: Hall emits request, and Latch, in the same tick, registers the call.
  3. hallButton = falseReleased: Latch remembers.
  4. carButton = trueSomeone inside the car presses the button for this floor too: Car emits request, which Waiting ignores.
  5. carButton = false, arrived = trueThe lift arrives: Latch clears the lamp. It is the only region that writes lamp.
  6. arrived = falseThe lift leaves.
  7. hallButton = true, carButton = trueBoth buttons in the same tick: both regions emit request. Several regions may emit one signal; the emissions simply add up to “present”.
  8. hallButton = false, carButton = falseWaiting.
  9. arrived = trueServed.
  10. arrived = falseIdle.
Two buttons, one owner: Hall and Car emit request, and only Latch writes lamp.

Hall and Car each emit request, and several regions may emit the same signal in one tick (tick 7): a signal is present if anyone emits it. Latch reads request in the same tick, after every emission, and owns lamp alone. The owner is also the one place where the coincidence rules live, which is a good reason to keep it that way. A shared device works the same way: the arbiter that hands a printer to one computer at a time is the one owner of the variable that says who has it.

If you have met latches in digital logic, you may prefer to write one as an equation. The lamp is on if it was on in the previous tick or the button is pressed, unless the lift is here:

LatchEquationSimulation

Diagram

Diagram of LatchEquationStates: none. Transitions: none.LatchEquationinput boolbutton, arrivedoutput boollampimmediate during/ lamp = (pre(lamp) || button) && !arrived

Code

scchart LatchEquation {
input bool button, arrived
output bool lamp
immediate during do lamp = (pre(lamp) || button) && !arrived
}
Tick 0 of 14
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of LatchEquation
Tickinput bool buttoninput bool arrivedoutput bool lamp
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 14 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 14 ticks:

  1. button = false, arrived = falseTick 1: lamp = (false or false) and not false: off. In tick 1, pre(lamp) is false.
  2. button = truePressed: button makes the bracket true and arrived is false: on, in the tick of the press.
  3. button = falseReleased: pre(lamp) keeps the bracket true. The previous tick’s lamp holds the lamp on.
  4. Held by pre(lamp).
  5. button = trueA second press: still on.
  6. button = falseOn.
  7. arrived = truearrived: && !arrived switches the lamp off, whatever the bracket says.
  8. button = trueA press while the lift is here: !arrived keeps the lamp off.
  9. button = falseOff.
  10. arrived = falseThe lift leaves; pre(lamp) is false, so the lamp stays off.
  11. button = trueA new call: on.
  12. button = falseOn.
  13. button = true, arrived = truePress and arrival in the same tick: the arrival wins.
  14. button = false, arrived = falseOff.
Tick for tick the same as CallButtonFixed for these inputs.

pre(lamp) is the lamp’s value at the end of the previous tick, so the equation holds the lamp on from one tick to the next; && !arrived makes it reset-dominant. To make it set-dominant, move the button outside: lamp = button || (pre(lamp) && !arrived). The two equations differ only in a tick where a press and the lift come together:

LatchEquation and LatchEquationSetComparison

Diagrams

LatchEquation

Diagram of LatchEquationStates: none. Transitions: none.LatchEquationinput boolbutton, arrivedoutput boollampimmediate during/ lamp = (pre(lamp) || button) && !arrived

LatchEquationSet

Diagram of LatchEquationSetStates: none. Transitions: none.LatchEquationSetinput boolbutton, arrivedoutput boollampimmediate during/ lamp = button || pre(lamp) && !arrived

Code

LatchEquation

scchart LatchEquation {
input bool button, arrived
output bool lamp
immediate during do lamp = (pre(lamp) || button) && !arrived
}

LatchEquationSet

scchart LatchEquationSet {
input bool button, arrived
output bool lamp
immediate during do lamp = button || (pre(lamp) && !arrived)
}
Tick 0 of 6
Inputs for tick 1, for both charts Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of LatchEquation and LatchEquationSet, no ticks yet
Tickinput bool buttoninput bool arrivedLatchEquation: output bool lampLatchEquationSet: output bool lamp
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 6 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 6 ticks, the same inputs for both charts:

  1. button = false, arrived = falseTick 1: the lamp is off in both charts.
  2. button = trueA press: on in both.
  3. button = falseReleased: pre(lamp) holds the lamp on in both.
  4. button = true, arrived = trueA press while the lift is here. In LatchEquation, && !arrived switches the lamp off; in LatchEquationSet the press is outside the bracket and wins, so the lamp stays on.
  5. button = falsearrived alone: off in both.
  6. arrived = falseThe lift leaves. Off in both.
Tick 4: a press while the lift is here. The reset-dominant LatchEquation keeps the lamp off; the set-dominant LatchEquationSet keeps it on.

The equation is compact, and it is easy to get the dominance wrong by moving a bracket. The two-state version is easier to extend, for example with a state for “lamp blinking because the lift is on its way”. There is one more difference: the equation runs in an immediate during action, so it already reacts to a press in tick 1, whereas the delayed transition out of Idle does not.

Two floors

The starter registers calls on floor 1. Extend it to floor 2, with its own button, lamp and arrival input (button2, lamp2, at2). The two calls must be independent: serving floor 1 must not clear the call on floor 2. Keep one writer per lamp. To test it: a call on floor 1 in tick 2 and on floor 2 in tick 3; the lift at floor 1 in tick 5 clears lamp1 only, and at floor 2 in tick 7 clears lamp2.

Show the starter chart
scchart TwoFloors {
input bool button1, button2 // call buttons on floors 1 and 2
input bool at1, at2 // the lift is at floor 1 / floor 2, doors open
output bool lamp1, lamp2
initial state Idle
if button1 && !at1 go to Waiting
state Waiting {
entry do lamp1 = true
exit do lamp1 = false
}
if at1 go to Idle
}
Show a solution
scchart TwoFloors {
input bool button1, button2 // call buttons on floors 1 and 2
input bool at1, at2 // the lift is at floor 1 / floor 2, doors open
output bool lamp1, lamp2
region Floor1 {
initial state Idle
if button1 && !at1 go to Waiting
state Waiting {
entry do lamp1 = true
exit do lamp1 = false
}
if at1 go to Idle
}
region Floor2 {
initial state Idle
if button2 && !at2 go to Waiting
state Waiting {
entry do lamp2 = true
exit do lamp2 = false
}
if at2 go to Idle
}
}

Take a number

A latch remembers one request. A queue counter remembers how many: customers take tickets (take), the counter calls them (serve), and waiting says how many are waiting. The starter forgets a service when take and serve arrive in the same tick: it counts the new ticket and ignores the call. Fix it, and make sure that serve with nobody waiting changes nothing. To test it: take in ticks 2 and 4 and serve in tick 6 leave waiting at 1; take and serve together in tick 8 should keep it at 1 (the starter says 2); serve in ticks 10 and 12 should give 0, then still 0.

Show the starter chart
scchart TicketQueue {
input signal take // a customer takes a ticket
input signal serve // the counter calls the next customer
output int waiting = 0
initial state Counting
if take do waiting++ go to Counting
if serve && waiting > 0 do waiting-- go to Counting
}
Show a solution
scchart TicketQueue {
input signal take // a customer takes a ticket
input signal serve // the counter calls the next customer
output int waiting = 0
initial state Counting
if take && serve go to Counting
if take do waiting++ go to Counting
if serve && waiting > 0 do waiting-- go to Counting
}
CallButtonFixedPrediction

Diagram

Diagram of CallButtonFixedStates: Idle (initial), Waiting (superstate). Transitions: Idle to Waiting: button && !arrived; Waiting to Idle: arrived.CallButtonFixedinput boolbuttoninput boolarrivedoutput boollampIdleWaitingentry/ lamp = trueexit/ lamp = falsebutton && !arrivedarrived

Code

scchart CallButtonFixed {
input bool button // the call button on this floor
input bool arrived // the lift is here, doors open
output bool lamp // "call registered"
initial state Idle
if button && !arrived go to Waiting
state Waiting {
entry do lamp = true
exit do lamp = false
}
if arrived go to Idle
}

CallButtonFixed, from the comparison above. In tick 4 a press comes together with the lift; in tick 6 someone presses while the doors are open, and holds the button as the lift leaves in tick 7. Is the lamp on after ticks 4, 6 and 7?

Inputs per tick. A value in grey was not set in that tick: it holds from an earlier one.

Tickbuttonarrived
1falsefalse
2truefalse
3falsefalse
4truetrue
5falsetrue
6truetrue
7truefalse
Plays the scenario on the diagram, then marks each answer.
Explanation

The lamp is off after ticks 4 and 6 and on after tick 7. In tick 4 the press comes with the lift: Waiting looks only at arrived, so the call is served and the exit action switches the lamp off. In tick 6 the doors are open, and Idle’s if button && !arrived is false. In tick 7 the lift leaves while the button is still held: now button && !arrived holds, and the call is registered.

An alarm is latched with an equation. Which one is set-dominant, so that a fault wins over an acknowledgement in the same tick?
Show the answer

Answer C: alarm = fault || (pre(alarm) && !ack)

With fault outside the bracket, a fault switches the alarm on whatever ack says, and ack can only clear an alarm that is held from the previous tick. The first equation is reset-dominant: && !ack switches the alarm off even while the fault lasts. The third is no latch at all: with || !ack the alarm is on in every tick without an acknowledgement.

Two regions both contain a transition with the effect alarm = true, triggered by different sensors. A third region resets the alarm with alarm = false. What does KIELER say?
Show the answer

Answer A: It rejects the chart: concurrent regions write different values

The two alarm = true writes agree and would be accepted on their own. But alarm = false in a concurrent region writes a different value, and KIELER rejects that, whatever the triggers say. Give alarm one owner and let the other regions emit signals to it.

Key ideas

  • A latch remembers a request until it is served. A state is the simplest memory; entry and exit actions keep an output in step with it.
  • Decide what happens when the request and the service arrive in the same tick (reset-dominant or set-dominant), write the decision into the triggers, and test that tick.
  • One writer per variable: concurrent regions may not assign different values to the same variable, even if they never do it in the same tick. Send requests to the owner with signals.
  • x = (pre(x) || set) && !reset is a reset-dominant latch in one line; x = set || (pre(x) && !reset) is set-dominant.

Next, Mode switching: leaving one mode for another without cutting a job short.

Report a mistake on this page