Skip to content

Counters and timeouts

Counting ticks, timeouts measured in ticks, frequency division, and when to use a count delay instead of a counter.

A synchronous chart has no clock of its own; it has ticks. If your system ticks at a fixed rate, say every 10 ms, then counting ticks is measuring time: 100 ticks are a second. This page shows the two ways SCCharts gives you to count: count delays (if 4 true go to …), where KIELER keeps the count for you, and counters, ordinary int variables you increment yourself. It uses both for timeouts and for frequency division, and ends with a comparison to help you choose.

Everything here assumes the ticks come at a steady rate. If they do not, see Time for clocks that measure real time.

The simplest counter is a variable and a during action:

TickCounterSimulation

Diagram

Diagram of TickCounterStates: none. Transitions: none.TickCounteroutput inttickNo = 0, later = 0immediate during/ tickNo++during/ later++

Code

scchart TickCounter {
output int tickNo = 0, later = 0
immediate during do tickNo++
during do later++
}
Tick 0 of 5
Timing diagram of TickCounter
Tickoutput int tickNooutput int later
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. Tick 1. The immediate during action runs in the tick its state (here: the chart itself) is entered, so tickNo is already 1. The plain during action waits for the next tick, so later is still 0.
  2. Both actions run: tickNo is 2, later is 1.
  3. tickNo is always the number of the current tick.
  4. later counts the ticks after the first: one less.
  5. Tick 5.
tickNo is the number of the current tick; later is always one less.

The two actions differ only in the first tick. A during action runs in every tick in which its state is active except the tick it was entered; for actions written directly in the chart, that is tick 1. immediate during runs in the entry tick as well. So immediate during do tickNo++ makes tickNo the number of the current tick, and during do later++ counts the ticks after the first. Pick whichever matches the question you ask: “which tick is this?” or “how many ticks have passed since we started?”.

Many buildings have a stairwell light on a timer: press the button and the light stays on for a while, then goes off by itself. Pressing again while it is on restarts the time. With a count delay that is three transitions:

StairLightSimulation

Diagram

Diagram of StairLightStates: Off (initial), On (superstate). Transitions: Off to On: button; On to On: 1: button; On to Off: 2: 4 true.StairLightinput boolbuttonoutput boollightOffOnentry/ light = trueexit/ light = falsebutton1: button2: 4 true

Code

scchart StairLight {
input bool button
output bool light
initial state Off
if button go to On
state On {
entry do light = true
exit do light = false
}
if button go to On
if 4 true go to Off
}
Tick 0 of 17
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of StairLight
Tickinput bool buttonoutput bool lightActive 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, 17 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 17 ticks:

  1. button = falseTick 1: dark.
  2. button = truePressed: On is entered and its entry action switches the light on.
  3. button = falseif 4 true counts its first tick (the entry tick does not count).
  4. button = falseTwo.
  5. button = falseThree.
  6. button = falseFour: the timeout fires and On’s exit action switches the light off. On for ticks 2 to 5: four ticks.
  7. button = falseDark.
  8. button = truePressed: on.
  9. button = falseOne.
  10. button = falseTwo.
  11. button = truePressed again before the timeout. if button go to On is written first, so it wins: On is left and entered again, and the re-entry restarts the count. The exit and entry actions run, but the light ends the tick on.
  12. button = falseOne, counted from the new entry.
  13. button = falseTwo.
  14. button = falseThree.
  15. button = falseFour: off. The second press bought four more ticks from tick 11.
  16. button = falseDark.
  17. button = falseDark.
Warning: KIELER warns about a potential instantaneous loop here, but compiles and runs the chart correctly
  • Warning, line 8, column 9KIELER says: Instantaneous loop detected!KIELER's loop check works on the compiled chart, in which count delays and entry and exit actions 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 On. The chart works as written.About this message
Ticks 2 to 5: four ticks of light. In tick 11 a second press restarts the count, and the light stays on until tick 14.

if 4 true go to Off is a count delay: it fires in the fourth tick in which its trigger (here true) holds while On is active. Like any delayed transition it ignores the entry tick, so it fires four ticks after the tick On was entered. The restart comes for free: if button go to On is a self-loop, and every entry of a state restarts its count delays. It is written first, so a press in the very tick the timeout would fire still wins and keeps the light on. (Time builds the same light with a clock, which measures real time.)

Two consequences of that restart are worth knowing:

  • Holding the button keeps the light on. In every tick of a long press if button go to On fires again and restarts the count; the four ticks start when the button is released. If that is not what you want, restart only on a rising edge, if button && !pre(button) go to On (see edges).
  • The count belongs to the state. KIELER keeps it in a hidden variable that it sets to 0 whenever On is entered. You cannot read it, show it, or change it.

Here is the stairwell light with an explicit counter, left, the number of ticks the light has left:

StairLight and StairLightCounterComparison

Diagrams

StairLight

Diagram of StairLightStates: Off (initial), On (superstate). Transitions: Off to On: button; On to On: 1: button; On to Off: 2: 4 true.StairLightinput boolbuttonoutput boollightOffOnentry/ light = trueexit/ light = falsebutton1: button2: 4 true

StairLightCounter

Diagram of StairLightCounterStates: Off (initial), On. Transitions: Off to On: button / light = true; left = 4; On to On: 1: button / left = 4; On to Off: 2: left == 1 / left = 0; light = false; On to On: 3: / left--.StairLightCounterinput boolbuttonoutput boollightoutput intleft = 0OffOnbutton/ light = true;left = 41: button/ left = 42: left == 1/ left = 0;light = false3: / left--

Code

StairLight

scchart StairLight {
input bool button
output bool light
initial state Off
if button go to On
state On {
entry do light = true
exit do light = false
}
if button go to On
if 4 true go to Off
}

StairLightCounter

scchart StairLightCounter {
input bool button
output bool light
output int left = 0
initial state Off
if button do light = true; left = 4 go to On
state On
if button do left = 4 go to On
if left == 1 do left = 0; light = false go to Off
do left-- go to On
}
Tick 0 of 17
Inputs for tick 1, for both charts Boolean input: true while on. It keeps its value until you change it.
Timing diagram of StairLight and StairLightCounter, no ticks yet
Tickinput bool buttonStairLight: output bool lightStairLight: Active state of the chartStairLightCounter: output bool lightStairLightCounter: output int leftStairLightCounter: 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, 17 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

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

  1. button = falseTick 1: both lights are off, and left is 0.
  2. button = truePressed. StairLight enters On, whose entry action switches the light on. StairLightCounter switches it on in the transition and sets left to 4.
  3. button = falseStairLight’s count delay counts its first tick: the entry tick does not count. StairLightCounter’s transitions are delayed too, so they are tried for the first time now: left counts down to 3.
  4. button = falseTwo ticks counted; left is 2.
  5. button = falseThree; left is 1.
  6. button = falseBoth switch off: if 4 true completes in StairLight, and left == 1 fires in StairLightCounter, which sets left back to 0. The light was on in ticks 2 to 5.
  7. button = falseDark.
  8. button = truePressed: both lights on, left is 4.
  9. button = falseOne tick counted; left is 3.
  10. button = falseTwo; left is 2.
  11. button = truePressed again before the timeout. In both charts the press is On’s first transition, a self-loop. In StairLight the re-entry restarts the count delay; StairLightCounter sets left back to 4. The light stays on.
  12. button = falseOne tick counted from the new press; left is 3.
  13. button = falseTwo; left is 2.
  14. button = falseThree; left is 1.
  15. button = falseOff in both, four ticks after the second press.
  16. button = falseDark.
  17. button = falseDark.
Warning: KIELER warns about a potential instantaneous loop here, but compiles and runs the chart correctly (StairLight)
  • Warning in StairLight, line 8, column 9KIELER says: Instantaneous loop detected!KIELER's loop check works on the compiled chart, in which count delays and entry and exit actions 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 On. The chart works as written.About this message
Both turn the light off in ticks 6 and 15. Only the counter version shows how much time is left.

The transitions out of On say, in priority order: a press sets left back to 4; on the last tick, switch off; otherwise count down. With a press in tick 2, the two charts agree tick for tick. Delay and Counter are light in StairLight and in StairLightCounter:

Tick Delay Counter left
1 false false 0
2 true true 4
3 true true 3
4 true true 2
5 true true 1
6 false false 0

The counter version is longer, but you can watch left in the simulator, other parts of the chart can read it (to make the light warn before it goes off, say), and the limit could come from a variable instead of being a number written into the transition.

Note where left is declared: at the top of the chart. On is re-entered by its self-loop, and a variable declared inside a state is initialised again on every entry of that state. Predict what that does to a counter:

CounterInsidePrediction

Diagram

Diagram of CounterInsideStates: S (initial, superstate). Transitions: S to S: / n++; shown = n.CounterInsideoutput intshown = 0Sintn = 0/ n++;shown = n

Code

scchart CounterInside {
output int shown = 0
initial state S {
int n = 0
}
do n++; shown = n go to S
}

S declares int n = 0 and loops to itself in every tick with do n++; shown = n go to S. What is shown after ticks 2 and 4?

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

shown is 1 after both ticks. In every tick the self-loop runs n++, which turns 0 into 1, and shown copies the 1; then the re-entry of S sets n back to 0. The counter never gets past 1.

Declare counters one level up, in the chart or in a superstate that stays active, as StairLightCounter does with left. The debouncing page uses the same idea with a counter declared outside its looping state.

Counting is also how you make something slower than the tick. A blinker that is lit for two ticks and dark for two divides the tick rate by four:

BlinkerSimulation

Diagram

Diagram of BlinkerStates: Dark (initial), Lit (superstate). Transitions: Dark to Lit: 2 true; Lit to Dark: 2 true.Blinkeroutput boollampDarkLitentry/ lamp = trueexit/ lamp = false2 true2 true

Code

scchart Blinker {
output bool lamp
initial state Dark
if 2 true go to Lit
state Lit {
entry do lamp = true
exit do lamp = false
}
if 2 true go to Dark
}
Tick 0 of 9
Timing diagram of Blinker
Tickoutput 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, 9 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 9 ticks:

  1. Tick 1: Dark is entered; the lamp is off.
  2. if 2 true has counted one tick.
  3. Two: Lit is entered, the lamp goes on.
  4. One tick in Lit.
  5. Two: back to Dark.
  6. Dark.
  7. Lit.
  8. Lit.
  9. Dark.
Warning: KIELER warns about a potential instantaneous loop here, but compiles and runs the chart correctly
  • Warning, line 4, column 17KIELER says: Instantaneous loop detected!KIELER's loop check works on the compiled chart, in which count delays and entry and exit actions 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 Lit and Dark. The chart works as written.About this message
Lit in ticks 3, 4, 7 and 8: a period of four ticks.

Each state waits for its own count delay and hands over to the other. (KIELER warns about this chart too, as above.) To produce a single-tick pulse every N ticks, a counter and a self-loop are more direct:

DividerSimulation

Diagram

Diagram of DividerStates: Count (initial). Transitions: Count to Count: 1: phase == 2 / phase = 0; beat; Count to Count: 2: / phase++.Divideroutput signalbeatintphase = 0Count1: phase == 2/ phase = 0;beat2: / phase++

Code

scchart Divider {
output signal beat
int phase = 0
initial state Count
if phase == 2 do phase = 0; beat go to Count
do phase++ go to Count
}
Tick 0 of 10
Timing diagram of Divider
Tickoutput signal beatlocal int phaseActive 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, 10 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 10 ticks:

  1. Tick 1: Count is entered; phase is 0. Its transitions are delayed, so nothing else happens.
  2. phase is 0, not 2: the untriggered transition counts up to 1.
  3. 2.
  4. phase == 2: beat is emitted and phase starts again at 0.
  5. 1.
  6. 2.
  7. beat: every third tick.
  8. 1.
  9. 2.
  10. beat.
From tick 2, phase runs 1, 2, 0, 1, 2, 0, … and beat comes in ticks 4, 7 and 10.

phase counts 0, 1, 2, 0, …, and the tick in which it would pass 2 emits beat instead. Because the transitions of Count are delayed, the first beat comes in tick 4: tick 1 only enters the state. The same chart divides events instead of ticks if you put the event into the triggers; the packing exercise below does that.

A count delay, such as if 4 true go to Off, against a counter, an int you update yourself:

What differs Count delay Counter
Length one transition a declaration and two or three transitions
Visible no: KIELER’s hidden variable yes, like any variable
What it counts ticks in which the trigger holds while the state is active, not the entry tick; they need not be consecutive whatever you program
Restarts on every entry of the source state when you assign it
The limit a number literal only any expression: a const, a variable, an input
Where on transitions only anywhere

Rules of thumb: use a count delay for a fixed timeout on a single state (“leave after 50 ticks”). Use a counter when the count must be seen or shared, when the limit is not a fixed number, when the count must survive leaving the state, or when you count across several states.

How a count delay counts is the subject of The previous tick: the ticks need not be consecutive (debouncing shows how to demand ticks in a row), every entry restarts the count, if 0 A is rejected, if 1 A means if A, and an immediate count delay can fire too early when its state is left and entered again. One limit matters when you choose: count delays work only on transitions. KIELER accepts during if 3 A do …, but treats it as during if A do … and warns (sic):

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

On entry and exit actions a count is a syntax error. To count inside an action, use a counter.

A short flash

Change the blinker so that the lamp is lit for one tick and dark for three, so that it flashes once every four ticks. The starter is lit in ticks 3, 4, 7 and 8; your version should be lit in ticks 4 and 8 only.

Show the starter chart
scchart Blinker {
output bool lamp
initial state Dark
if 2 true go to Lit
state Lit {
entry do lamp = true
exit do lamp = false
}
if 2 true go to Dark
}
Show a solution
scchart Blinker {
output bool lamp
initial state Dark
if 3 true go to Lit
state Lit {
entry do lamp = true
exit do lamp = false
}
go to Dark
}

Boxes of four

Items pass a light barrier on a conveyor belt; sensor is true while an item is in front of it. The starter counts the items (once per item, thanks to the edge). Extend it to pack them in boxes of four: when the fourth item of a box passes, emit boxFull and start counting the next box from 0. To test it: items in ticks 2, 4 to 5, 7 and 9 fill the first box, so boxFull comes in tick 9 and inBox is 0 again; items in ticks 11 and 13 make it 1 and 2.

Show the starter chart
scchart Packer {
input bool sensor
output int inBox = 0
output signal boxFull
initial state Counting
if sensor && !pre(sensor) do inBox++ go to Counting
}
Show a solution
scchart Packer {
input bool sensor
output int inBox = 0
output signal boxFull
initial state Counting
if sensor && !pre(sensor) && inBox == 3 do inBox = 0; boxFull go to Counting
if sensor && !pre(sensor) do inBox++ go to Counting
}
StairLightPrediction

Diagram

Diagram of StairLightStates: Off (initial), On (superstate). Transitions: Off to On: button; On to On: 1: button; On to Off: 2: 4 true.StairLightinput boolbuttonoutput boollightOffOnentry/ light = trueexit/ light = falsebutton1: button2: 4 true

Code

scchart StairLight {
input bool button
output bool light
initial state Off
if button go to On
state On {
entry do light = true
exit do light = false
}
if button go to On
if 4 true go to Off
}

The stairwell light with a new sequence of presses. Is the light on after ticks 7, 8, 15 and 16?

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

Tickbutton
1false
2true
3false
4true
5false
6false
7false
8false
9false
10true
11true
12true
13false
14false
15false
16false
Plays the scenario on the diagram, then marks each answer.
Explanation

The light is on after tick 7, off after tick 8, on after tick 15 and off after tick 16. The press in tick 4 re-enters On and restarts the count, so the four ticks are 5 to 8. From tick 10 the button is held for three ticks, and every tick of the press restarts the count again; the four ticks begin when it is released, in tick 13, and end in tick 16.

state Wait has the transition if 5 true go to Done. Wait is entered in tick 10. In which tick does the transition fire, if Wait is not left before?
Show the answer

Answer C: Tick 15

A delayed count delay does not count the entry tick. It counts ticks 11, 12, 13, 14 and 15, and fires in the fifth of them, tick 15: five ticks after the entry.

The stairwell light’s time should be adjustable: the chart gets an input input int limit. How do you write the timeout?
Show the answer

Answer A: With a counter, set to limit on every press

The count of a count delay must be a number literal, so if limit true is a syntax error. A counter can start from any expression: set left = limit where StairLightCounter sets left = 4.

Key ideas

  • With a steady tick rate, counting ticks measures time.
  • during skips the tick its state is entered; immediate during does not.
  • A count delay if N true fires N ticks after its state was entered, restarts on every entry, and hides its count. A self-loop that restarts it gives a retriggerable timeout.
  • A counter is a variable you update: visible, shareable and free to compare with anything. Declare it outside any state that is re-entered.
  • Frequency division is counting with a hand-over: two states with count delays, or a counter that wraps.

Next, Latches: remembering a request until it has been served.

Report a mistake on this page