Skip to content

The previous tick

With pre(x), the value x had one tick ago, a chart can react to changes instead of levels: detect edges, fire one-tick pulses and count.

A doorbell should ring once per press, however long the finger stays on the button. A light switch should toggle once per press, not once per tick. Reacting to a change needs two values: the one now and the one a tick ago. So far a chart could only see the first. This chapter adds the second, with pre, and then shows count delays, which are built on it.

ChangeSimulation

Diagram

Diagram of ChangeStates: Watch (initial, superstate). Transitions: none.Changeinput inttempoutput intprevious, deltaWatchimmediate during/ previous = pre(temp); delta = temp - pre(temp)

Code

scchart Change {
input int temp
output int previous, delta
initial state Watch {
immediate during do previous = pre(temp); delta = temp - pre(temp)
}
}
Tick 0 of 5
Inputs for tick 1
Timing diagram of Change
Tickinput int tempoutput int previousoutput int deltaActive 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, 5 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 5 ticks:

  1. temp = 20Tick 1: there is no previous tick, so pre(temp) is the default for an int, 0. That makes delta 20, as if the temperature had jumped from 0.
  2. temp = 21pre(temp) is 20, the value temp had when tick 1 ended. delta is 1.
  3. temp stays 21 (inputs keep their value), so pre(temp) is 21 and delta is 0.
  4. temp = 19A drop of 2: delta is −2.
  5. temp = 22pre(temp) is 19: delta is 3.
previous is always last tick’s temp. Look at tick 1, where there is no last tick.

pre(temp) is the value temp had at the end of the previous tick. The chart computes it in an immediate during action, so it runs in every tick including the first (see Entry, during and exit actions):

Tick temp previous delta
1 20 0 20
2 21 20 1
3 21 (held) 21 0
4 19 21 −2
5 22 19 3

pre works on inputs, outputs and local variables alike, and it fits into any expression: temp - pre(temp) is “how much did temp change since the last tick”. Writes in the current tick don’t affect pre(x): it reads a copy that was taken when the previous tick ended, so a chart may both write x and read pre(x) in the same tick.

In tick 1, delta is 20, as if the temperature had jumped from 0. There is no previous tick, so what does pre give there? temp is an input without an initial value. Predict what happens for a variable that does have one, then run it and open the explanation:

TankPrediction

Diagram

Diagram of TankStates: Fill (initial, superstate). Transitions: none.Tankoutput intlevel = 50output intbeforeFillimmediate during/ before = pre(level)during/ level += 10

Code

scchart Tank {
output int level = 50
output int before
initial state Fill {
immediate during do before = pre(level)
during do level += 10
}
}

level starts at 50 and a delayed during action adds 10 every tick. What is before, which is pre(level), after ticks 1 and 2?

Plays the scenario on the diagram, then marks each answer.
Explanation

before is 0 after tick 1 and 50 after tick 2. level is 50 throughout tick 1, but pre(level) in tick 1 is 0, not 50. From tick 2 on it is what you expect: 50 in tick 2 while level has already moved on to 60, 60 in tick 3, and so on.

In tick 1, KIELER fills the gap with the default of the variable’s type: 0 for an int, false for a bool, even for a variable with an initial value.

The same happens whenever a scope starts afresh: a variable declared inside a superstate gets a new past each time the superstate is entered, so in that entry tick pre of it is the default again. Inputs, outputs and the chart’s other top-level variables belong to the chart itself, which is entered only once, in tick 1.

A rising edge is the tick in which a bool goes from false to true, a falling edge the tick in which it goes from true to false. With pre both are one expression: pressed below is button && !pre(button), “true now and false a tick ago”, and released is !button && pre(button).

EdgesSimulation

Diagram

Diagram of EdgesStates: Watch (initial, superstate). Transitions: none.Edgesinput boolbuttonoutput boolpressed, releasedWatchimmediate during/ pressed = button && !pre(button); released = !button && pre(button)

Code

scchart Edges {
input bool button
output bool pressed, released
initial state Watch {
immediate during do pressed = button && !pre(button); released = !button && pre(button)
}
}
Tick 0 of 8
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Edges
Tickinput bool buttonoutput bool pressedoutput bool releasedActive 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, 8 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 8 ticks:

  1. button = falseNot pressed. pre(button) is false in tick 1 (the default).
  2. button = truePressed now, not pressed before: a rising edge, so pressed is true for this tick.
  3. Still held: button and pre(button) are both true, so no edge.
  4. Still held: no edge.
  5. button = falseReleased now, pressed before: a falling edge, released is true.
  6. Not pressed, and not pressed before: nothing.
  7. button = trueA new press: pressed again.
  8. button = falseA one-tick press gives a rising edge and, one tick later, a falling edge.
pressed and released are each true for exactly one tick per change, however long the button is held.
Tick button pressed released
1 false false false
2 true true false
3 true (held) false false
4 true (held) false false
5 false false true
6 false (held) false false
7 true true false
8 false false true

pressed is a one-tick pulse: true in one tick, false in the next, whatever the input does. Ticks 7 and 8 show the shortest press there is, one tick long: a rising edge, and a falling edge right after. Because pre(button) is false in tick 1, a button that is already held in tick 1 counts as a rising edge in tick 1.

Edges matter most in triggers. Here is the toggle from Thinking in ticks next to a version that triggers on the rising edge, both with the button held for three ticks:

Toggle and EdgeToggleComparison

Diagrams

Toggle

Diagram of ToggleStates: Off (initial), On. Transitions: Off to On: button / light = true; On to Off: button / light = false.Toggleinput boolbuttonoutput boollight = falseOffOnbutton/ light = truebutton/ light = false

EdgeToggle

Diagram of EdgeToggleStates: Off (initial), On. Transitions: Off to On: button && !pre(button) / light = true; On to Off: button && !pre(button) / light = false.EdgeToggleinput boolbuttonoutput boollight = falseOffOnbutton && !pre(button)/ light = truebutton && !pre(button)/ light = false

Code

Toggle

scchart Toggle {
input bool button
output bool light = false
initial state Off
if button do light = true go to On
state On
if button do light = false go to Off
}

EdgeToggle

scchart EdgeToggle {
input bool button
output bool light = false
initial state Off
if button && !pre(button) do light = true go to On
state On
if button && !pre(button) do light = false go to Off
}
Tick 0 of 7
Inputs for tick 1, for both charts Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Toggle and EdgeToggle, no ticks yet
Tickinput bool buttonToggle: output bool lightToggle: Active state of the chartEdgeToggle: output bool lightEdgeToggle: 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. button = falseTick 1 enters Off in both charts.
  2. button = trueThe button goes down. For Toggle button is true; for EdgeToggle it is a rising edge, button && !pre(button). Both go from Off to On: the light is on.
  3. The button is still held. Toggle sees button true in On and goes straight back to Off. EdgeToggle sees pre(button) true: no edge, so it stays On.
  4. Still held: Toggle flips back to On, and will flicker for as long as the button is held. EdgeToggle still sees no edge.
  5. button = falseReleased. Neither chart reacts to a falling button. Toggle’s light is wherever the flicker left it, here on.
  6. button = trueA new press, a new rising edge: both charts go from On to Off.
  7. button = falseReleased. EdgeToggle toggled exactly once per press.
Toggle: if button. EdgeToggle: if button && !pre(button). The button is held from tick 2 to tick 4: watch light in both charts.
Tick button light in Toggle light in EdgeToggle
1 false false false
2 true true true
3 true (held) false true
4 true (held) true true
5 false true true
6 true false false
7 false false false

Toggle flickers for as long as the button is held: in every tick button is true, so whichever state the chart is in, its transition fires. On a real controller a tick may last a millisecond and a press a few hundred, so the light would end up on or off at random. EdgeToggle toggles exactly once per press. When one press should do one thing, trigger on the edge. Edges and pulses collects more patterns of this kind. Edges help in testing too: in SCCharts Lab a breakpoint can be a condition that uses pre, so button != pre(button) pauses the simulation on every edge of button (Test your chart has more on breakpoints).

For a signal, pre(S) asks whether S was present in the previous tick. For a valued signal V, pre(V) is its presence in the previous tick and pre(val(V)) its value at the end of the previous tick (which, like any signal value, stays put while the signal is absent). A doorbell that chimes once per press and counts its rings:

DoorbellSimulation

Diagram

Diagram of DoorbellStates: Watch (initial, superstate). Transitions: none.Doorbellinput signalpressoutput signalchimeoutput signal intringsWatchimmediate duringpress && !pre(press)/ chime; rings((pre(val(rings)) + 1))

Code

scchart Doorbell {
input signal press
output signal chime
output signal int rings
initial state Watch {
immediate during if press && !pre(press) do chime; rings(pre(val(rings)) + 1)
}
}
Tick 0 of 6
Inputs for tick 1 Input signal: present while on. It keeps its value until you change it.
Timing diagram of Doorbell
Tickinput signal pressoutput signal chimeoutput signal int ringsActive 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, 6 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 6 ticks:

  1. press = falseNo press. rings has the value 0 (the default).
  2. press = truepress is present and pre(press) is absent: a new press. The chart emits chime and emits rings with pre(val(rings)) + 1 = 1.
  3. press is still present (held), but it was present last tick too: no chime. rings is absent this tick; its value stays 1.
  4. press = falseReleased.
  5. press = trueA second press: chime, and rings carries 2.
  6. press = falseReleased. The value of rings stays 2 until the next emission.
chime is emitted on the first tick of each press. rings carries the number of rings so far.
Tick press chime rings val(rings)
1 absent absent absent 0
2 present present present 1
3 present (held) absent absent 1
4 absent absent absent 1
5 present present present 2
6 absent absent absent 2

The emission rings(pre(val(rings)) + 1) builds on last tick’s value. rings(val(rings) + 1) would read the value it is about to set, and KIELER rejects it, as you saw in Signals. Reading the previous value is the standard way to build on a signal’s value.

pre can be nested: pre(pre(x)) is the value two ticks ago. A long press is a button held now, one tick ago and two ticks ago:

LongPressSimulation

Diagram

Diagram of LongPressStates: Watch (initial, superstate). Transitions: none.LongPressinput boolbuttonoutput boollongPressWatchimmediate during/ longPress = button && pre(button) && pre(pre(button))

Code

scchart LongPress {
input bool button
output bool longPress
initial state Watch {
immediate during do longPress = button && pre(button) && pre(pre(button))
}
}
Tick 0 of 8
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of LongPress
Tickinput bool buttonoutput bool longPressActive 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, 8 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 8 ticks:

  1. button = falseNot pressed.
  2. button = truePressed in this tick only so far.
  3. Pressed now and one tick ago.
  4. Pressed now, one tick ago and two ticks ago (pre(pre(button))): a long press.
  5. Still a long press.
  6. button = falseReleased.
  7. button = truePressed again.
  8. Only two ticks so far: pre(pre(button)) is false, so this is not a long press yet.
longPress needs three ticks in a row. Ticks 7 and 8 are a press of two ticks: not long enough.

Each pre adds one remembered value to the compiled program. To look ten ticks back, count instead: with a counter variable, or with a count delay.

A count delay puts a number in front of a trigger: if 3 knock go to Open fires in the third tick in which knock holds while the state is active. A door that opens after three knocks:

KnockSimulation

Diagram

Diagram of KnockStates: Shut (initial), Away, Open. Transitions: Shut to Open: 1: 3 knock / open = true; Shut to Away: 2: away; Away to Shut: !away.Knockinput boolknock, awayoutput boolopen = falseShutAwayOpen1: 3 knock/ open = true2: away!away

Code

scchart Knock {
input bool knock, away
output bool open = false
initial state Shut
if 3 knock do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}
Tick 0 of 11
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 Knock
Tickinput bool knockinput bool awayoutput bool openActive 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, 11 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 11 ticks:

  1. knock = false, away = falseTick 1 enters Shut. The count delay starts at zero.
  2. knock = trueFirst knock: one tick in which knock holds.
  3. knock = falseA pause does not reset the count: the ticks need not be consecutive.
  4. knock = trueSecond knock.
  5. knock = false, away = trueThe visitor walks away before the third knock: Shut is left.
  6. away = falseBack again: Shut is entered anew, and every entry starts the count from zero.
  7. knock = trueFirst knock of the new visit.
  8. knock = falsePause.
  9. knock = trueSecond knock.
  10. Third tick with knock in this visit: the transition fires and the door opens.
  11. Open.
The visitor knocks twice, walks away, comes back and knocks three times. Watch when the door opens.
Tick knock away State after open
1 false false Shut false
2 true false (held) Shut false
3 false false (held) Shut false
4 true false (held) Shut false
5 false true Away false
6 false (held) false Shut false
7 true false (held) Shut false
8 false false (held) Shut false
9 true false (held) Shut false
10 true (held) false (held) Open true

Read off the rules:

  • The ticks need not be consecutive. Ticks 2 and 4 both count, with a pause between them.
  • Every entry restarts the count. In tick 5 the visitor leaves with two knocks counted. When Shut is entered again in tick 6, the count starts from zero, and it takes three more ticks with knock (7, 9 and 10) to open the door. A self-loop is an entry too, so it restarts the count as well.
  • The entry tick doesn’t count for a delayed count delay, just as a delayed transition is not checked in its source’s entry tick. With immediate if 3 knock, the entry tick counts.

if 1 knock means the same as if knock. if 0 knock is rejected with A count delay of 0 is not allowed on a trigger.. And because true holds in every tick, if 5 true go to Next waits for five ticks after the state was entered: a simple timeout, measured in ticks. Ticks are not seconds, though: how long a tick lasts depends on how often the program is run. Time shows how to work with real time.

Count delays are for transitions. On an entry or exit action a count is a syntax error, and on a during action it is ignored: during if 3 knock do … compiles, but runs exactly as during if knock do …, and KIELER warns (the spelling is KIELER’s):

Count Delays on During Actions are not supported yet. The valued will be ignored by the transformation.

To count in an action, use a counter variable, as KnockByHand does below.

Count delays count ticks, not presses. Predict what a held knock does, then run it and open the explanation:

KnockPrediction

Diagram

Diagram of KnockStates: Shut (initial), Away, Open. Transitions: Shut to Open: 1: 3 knock / open = true; Shut to Away: 2: away; Away to Shut: !away.Knockinput boolknock, awayoutput boolopen = falseShutAwayOpen1: 3 knock/ open = true2: away!away

Code

scchart Knock {
input bool knock, away
output bool open = false
initial state Shut
if 3 knock do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}

knock is true from tick 1 and stays true: one long knock. Is the door open after tick 3? After tick 4?

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

Tickknockaway
1truefalse
2truefalse
3truefalse
4truefalse
Plays the scenario on the diagram, then marks each answer.
Explanation

The door is still shut after tick 3 and open after tick 4. Tick 1 is Shut’s entry tick and doesn’t count; ticks 2, 3 and 4 do, so one long knock opens the door in tick 4.

If you mean three separate knocks, count rising edges: that is the second exercise below.

KIELER has no counting transitions at run time. Its compiler rewrites if 3 knock into a counter and pre, and you can write the same thing by hand:

Knock and KnockByHandComparison

Diagrams

Knock

Diagram of KnockStates: Shut (initial), Away, Open. Transitions: Shut to Open: 1: 3 knock / open = true; Shut to Away: 2: away; Away to Shut: !away.Knockinput boolknock, awayoutput boolopen = falseShutAwayOpen1: 3 knock/ open = true2: away!away

KnockByHand

Diagram of KnockByHandStates: Shut (initial, superstate), Away, Open. Transitions: Shut to Open: 1: pre(count) == 2 && knock / open = true; Shut to Away: 2: away; Away to Shut: !away.KnockByHandinput boolknock, awayoutput boolopen = falseoutput intcountShutentry/ count = 0duringknock/ count++AwayOpen1: pre(count) == 2 && knock/ open = true2: away!away

Code

Knock

scchart Knock {
input bool knock, away
output bool open = false
initial state Shut
if 3 knock do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}

KnockByHand

scchart KnockByHand {
input bool knock, away
output bool open = false
output int count
initial state Shut {
entry do count = 0
during if knock do count++
}
if pre(count) == 2 && knock do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}
Tick 0 of 11
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 Knock and KnockByHand, no ticks yet
Tickinput bool knockinput bool awayKnock: output bool openKnock: Active state of the chartKnockByHand: output bool openKnockByHand: output int countKnockByHand: 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, 11 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

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

  1. knock = false, away = falseTick 1 enters Shut in both charts. KnockByHand’s entry action sets count = 0.
  2. knock = trueFirst knock. KnockByHand’s during action counts it: count is 1. Its trigger reads pre(count), 0, so it does not fire; Knock’s count delay has one tick too.
  3. knock = falseA pause: count stays 1. The ticks need not be consecutive.
  4. knock = trueSecond knock: count is 2, and pre(count) was 1.
  5. knock = false, away = trueThe visitor walks away before the third knock: both charts leave Shut for Away.
  6. away = falseBack again: Shut is entered anew. KnockByHand’s entry action resets count to 0, and Knock’s count delay starts from zero as well.
  7. knock = trueFirst knock of the new visit: count is 1.
  8. knock = falsePause: count stays 1.
  9. knock = trueSecond knock: count is 2.
  10. Third tick with knock in this visit. KnockByHand sees pre(count) == 2 and knock, and Knock’s count delay reaches 3: both doors open in this same tick.
  11. Both open.
Knock: the count delay. KnockByHand: the same logic with a counter, count, that you can watch. With the same knocks both doors open in tick 10.
output int count // an output, so you can watch it
initial state Shut {
entry do count = 0 // every entry restarts
during if knock do count++ // delayed: skips the entry tick
}
if pre(count) == 2 && knock do open = true go to Open

The trigger asks “two knocks counted before this tick, and a knock now”. Each rule above is visible in the expansion: the entry action restarts the count, and the plain during skips the entry tick. For immediate if 3 knock, the compiler writes immediate during instead.

That expansion has one consequence that nobody would design on purpose:

ImmediateCountSimulation

Diagram

Diagram of ImmediateCountStates: Shut (initial), Away, Open. Transitions: Shut to Open: 1: 3 knock / open = true; Shut to Away: 2: away; Away to Shut: !away.ImmediateCountinput boolknock, awayoutput boolopen = falseShutAwayOpen1: 3 knock/ open = true2: away!away

Code

scchart ImmediateCount {
input bool knock, away
output bool open = false
initial state Shut
immediate if 3 knock do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}
Tick 0 of 5
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 ImmediateCount
Tickinput bool knockinput bool awayoutput bool openActive 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, 5 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 5 ticks:

  1. knock = true, away = falseAn immediate count delay counts the entry tick too: 1.
  2. knock is still true: two counted, one short of opening the door.
  3. knock = false, away = trueThe visitor walks away with two knocks counted.
  4. knock = true, away = falseBack, and knocking: Shut is entered anew, which should restart the count. Yet the door opens at once, after one knock in this visit. The trigger reads pre of the counter, which still holds the 2 from the last visit.
  5. The door stays open.
Note: KIELER's immediate count can complete as soon as Shut is entered again, if Shut was left one count short, however long ago
  • Note, line 6, column 5KIELER implements immediate if 3 ... with a counter that restarts on entry, but compares the counter's previous value, which is still the old visit's in the tick after a re-entry. A delayed count (without immediate) does not have this problem.
immediate if 3 knock. Two knocks, a walk away, and on return a single knock opens the door.

In tick 4 the visitor comes back and knocks once, and the door opens. Entering Shut did reset the counter to 0 and the knock counted 1, but the trigger reads pre(count), which is still 2: the value when the previous tick ended. The reset only shows in pre from the next tick on. A delayed count delay can’t do this, because its transition is not checked in the entry tick.

ABRO’s reset from Aborts, the self-loop if R abort to ABO around the whole behaviour, is the usual way to start a chart over. KIELER cannot compile a count delay anywhere inside a state with such a loop:

KnockRestartChart

Diagram

Diagram of KnockRestartStates: Door (initial, superstate), Shut (initial), Open. Transitions: Shut to Open: 3 knock / unlock; Door to Door: restart.KnockRestartinput boolknock, restartoutput signalunlockDoorShutOpen3 knock/ unlockrestart

Code

scchart KnockRestart {
input bool knock, restart
output signal unlock
initial state Door {
initial state Shut
if 3 knock do unlock go to Open
state Open
}
if restart abort to Door
}

KIELER rejects this chart

  • Line 7, column 12KIELER cannot compile the count delay if 3 knock on Shut's transition to Open: Door, around it, has the self-loop if restart abort to DoorKIELER says: The SCG is NOT asc-schedulable!KIELER keeps the count in a variable of the state around Shut, and in a tick in which if restart abort to Door leaves Door and enters it again, restarting the count and counting wait for each other. It rejects every count delay inside a state with a self-loop. Count by hand, with a variable declared outside Door: entry do count = 0 and during if knock do count++ on Shut, and if count == 3 on its transition.About this message
if 3 knock inside Door, and if restart abort to Door around it. KIELER rejects the chart.

The messages are the scheduling errors that Why the compiler says no explains, ending in The SCG is NOT asc-schedulable!, although the chart has no concurrent regions. The count delay on its own compiles, and so does the reset loop; together they leave no valid order in the code KIELER generates for the count delay. It makes no difference whether the reset is abort to or go to, or how deep inside the state the count delay sits.

The way out is to count by hand, with a counter declared at the top of the chart, outside the state with the reset loop:

KnockRestartByHandSimulation

Diagram

Diagram of KnockRestartByHandStates: Door (initial, superstate), Shut (initial, superstate), Open. Transitions: Shut to Open: count == 3 / unlock; Door to Door: restart.KnockRestartByHandinput boolknock, restartoutput signalunlockoutput intcountDoorShutentry/ count = 0duringknock/ count++Opencount == 3/ unlockrestart

Code

scchart KnockRestartByHand {
input bool knock, restart
output signal unlock
output int count
initial state Door {
initial state Shut {
entry do count = 0
during if knock do count++
}
if count == 3 do unlock go to Open
state Open
}
if restart abort to Door
}
Tick 0 of 12
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 KnockRestartByHand
Tickinput bool knockinput bool restartoutput signal unlockoutput int countActive state of the chartActive state of the region of Door
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 12 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 12 ticks:

  1. knock = false, restart = falseTick 1 enters Door and Shut. Shut’s entry action sets count to 0.
  2. knock = trueFirst knock: the during action counts it, count is 1.
  3. knock = falseA pause: count stays 1.
  4. knock = trueSecond knock: count is 2.
  5. knock = false, restart = truerestart leaves Door and enters it again in the same tick. Entering Shut sets count back to 0.
  6. knock = true, restart = falseThe first knock after the restart: count is 1.
  7. knock = falseA pause.
  8. knock = trueSecond knock: count is 2.
  9. Third knock: the during action makes count 3, the transition out of Shut reads it after that update, and unlock is present in this tick.
  10. knock = falseIn Open. unlock is a signal, so it was present for that one tick only.
  11. restart = truerestart works from Open too: back in Shut, with count at 0.
  12. restart = falseWaiting for knocks again.
Warning: KIELER warns about a potential instantaneous loop here, but compiles and runs the chart correctly
  • Warning, line 7, column 19KIELER says: Instantaneous loop detected!KIELER's loop check works on the compiled chart, in which entry and during actions and strong aborts are extra regions and transitions, and warns whenever control could go round a circle there within one tick, without checking whether the triggers prevent it: here the circle runs through Door and Shut. The chart works as written.About this message
The same door with its own counter, count. restart in tick 5 starts the count again, and the third knock after it, in tick 9, unlocks the door.

The transition tests count == 3 itself: it reads count after the during action has updated it, so the door unlocks in the tick of the third knock, as with if 3 knock. KIELER compiles this version with an instantaneous-loop warning, as it does for many charts with a reset loop around actions; the chart behaves as described (more about this warning). Count delay or counter? weighs the two in general.

Count presses, not ticks

Presses should count how often the button is pressed, but holding it down counts every tick. Change the during action so that each press counts once, however long it lasts.

Show the starter chart
scchart Presses {
input bool button
output int presses = 0
initial state Count {
immediate during if button do presses++
}
}
Show a solution
scchart Presses {
input bool button
output int presses = 0
initial state Count {
immediate during if button && !pre(button) do presses++
}
}

Three separate knocks

In Knock, one long knock opens the door. Change the transition so that it opens only after three separate knocks: three rising edges of knock. Keep the count delay; only its trigger needs to change.

Show the starter chart
scchart Knock {
input bool knock, away
output bool open = false
initial state Shut
if 3 knock do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}
Show a hint

if 3 knock counts the ticks in which knock holds. Which trigger holds only in the first tick of each knock?

Show a solution
scchart Knock {
input bool knock, away
output bool open = false
initial state Shut
if 3 knock && !pre(knock) do open = true go to Open
if away go to Away
state Away
if !away go to Shut
state Open
}
A chart declares output int level = 50. What is pre(level) in tick 1?
Show the answer

Answer A: 0, the default for an int

In tick 1 there is no previous tick, and pre gives the default of the type, not the initial value. Tank above shows it: before is 0 in tick 1 and 50 in tick 2.

Which expression is true for exactly one tick when b changes from true to false?
Show the answer

Answer B: !b && pre(b)

!b && pre(b) means “false now, true a tick ago”: a falling edge. b && !pre(b) is the rising edge, and !pre(b) is true in every tick after a tick in which b was false.

In Knock, the visitor knocks twice, walks away (Shut is left) and comes back. How many more ticks with knock open the door?
Show the answer

Answer B: 3

Every entry of Shut restarts the count, so the two earlier knocks are forgotten: it takes three more, as ticks 7, 9 and 10 of the simulation show. (With an immediate count delay it can be different, as the quirk above shows.)

Key ideas

  • pre(x) is the value x had at the end of the previous tick. Writes in the current tick don’t change it.
  • In tick 1, and when a superstate is entered again for its own locals, pre(x) is the default of the type (0, false), not the initial value.
  • b && !pre(b) is a rising edge and !b && pre(b) a falling edge: each is true for exactly one tick. Trigger on the edge when one press should do one thing.
  • pre(S) asks whether a signal was present in the previous tick; pre(val(V)) is a valued signal’s previous value.
  • if n T fires in the n-th tick in which T holds since the state was entered. Only immediate counts the entry tick, and every entry restarts the count. Inside a state with a reset self-loop KIELER rejects count delays: count by hand there.

Next, Why the compiler says no, the last chapter of Learn: the checks behind KIELER’s scheduling errors, and how to change a chart it refuses.

Report a mistake on this page