Skip to content

Signals

Pure and valued signals: present or absent in each tick, emitting and testing them, and combining values.

A variable remembers. But much of what a controller deals with is not memory, it is events: a button was pressed, a sensor pulsed, a coin dropped, an alarm must sound now. You can model an event with a bool variable, and then you have to remember to set it back to false afterwards. SCCharts has a better tool, borrowed from the synchronous language Esterel: the signal. A signal is present or absent in each tick, and it is absent unless something emits it in that very tick.

This chapter covers pure signals, which have nothing but their presence, and valued signals, which also carry a value from tick to tick.

Flash emits the output signal flash and sets the bool variable pressed on the same transition. Watch what each does in the tick after.

FlashSimulation

Diagram

Diagram of FlashStates: Ready (initial). Transitions: Ready to Ready: press / flash; pressed = true.Flashinput boolpressoutput signalflashoutput boolpressed = falseReadypress/ flash;pressed = true

Code

scchart Flash {
input bool press
output signal flash
output bool pressed = false
initial state Ready
if press do flash; pressed = true go to Ready
}
Tick 0 of 5
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Flash
Tickinput bool pressoutput signal flashoutput bool pressedActive 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. press = falseTick 1 enters Ready. flash is absent: a signal is absent unless something emits it in this tick.
  2. press = truepress is true, so the self-loop emits flash and sets pressed. Both are true at the end of this tick.
  3. press = falseNothing emits flash, so it is absent again. pressed is a variable: it keeps the value true until something assigns it.
  4. Still flash absent, pressed true.
  5. press = trueflash is present again, for this tick only.
flash is present only in the ticks that emit it; pressed stays true.
Tick press flash after pressed after
1 false absent false
2 true present true
3 false absent true
4 false (held) absent true
5 true present true

That is the whole idea. At the start of every tick an output or local signal is absent again; it is present in a tick if and only if the chart emits it in that tick. A variable, by contrast, keeps its value until something assigns it. Nobody has to switch flash off.

A pure signal is declared with signal and no type: output signal flash for an output, input signal ring for an input, plain signal found for a local signal that only the chart itself sees. Several at once work as for variables: input signal A, B.

To emit a signal, write its name as an effect: do flash, or do flash; beep for two. There is no emit keyword in SCTX. Emitting a signal twice in a tick is the same as emitting it once: it is present.

To test a signal, use its name as a condition: it is true when the signal is present. !A tests for absence, and && and || combine tests as for bools.

BothSimulation

Diagram

Diagram of BothStates: Watch (initial). Transitions: Watch to Watch: 1: A && B / both; Watch to Watch: 2: A && !B / onlyA.Bothinput signalA, Boutput signalboth, onlyAWatch1: A && B/ both2: A && !B/ onlyA

Code

scchart Both {
input signal A, B
output signal both, onlyA
initial state Watch
if A && B do both go to Watch
if A && !B do onlyA go to Watch
}
Tick 0 of 5
Inputs for tick 1 Input signal: present while on. It keeps its value until you change it. Input signal: present while on. It keeps its value until you change it.
Timing diagram of Both
Tickinput signal Ainput signal Boutput signal bothoutput signal onlyAActive 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. A = false, B = falseTick 1 enters Watch. Its transitions are delayed.
  2. A = trueA is present and B absent: priority 1 (A && B) fails, priority 2 (A && !B) fires and emits onlyA.
  3. B = trueBoth present: priority 1 emits both. onlyA is absent again, because nothing emitted it in this tick.
  4. A = falseA is absent, so neither transition fires and neither output is present.
  5. B alone still triggers nothing.
both needs A and B in the same tick; onlyA needs A without B.

A signal declaration can’t give a pure signal a starting presence: output signal S = true compiles, but the = true is ignored and S is absent until emitted.

In a running system the environment decides afresh in every tick whether an input signal is present. In KIELER’s simulator and in SCCharts Lab an input signal is an input like any other: it keeps the presence you gave it until you change it. Doorbell chimes once for every tick in which ring is present, and the tour shows what happens when you leave ring switched on.

DoorbellSimulation

Diagram

Diagram of DoorbellStates: Quiet (initial). Transitions: Quiet to Quiet: ring / chime; rings += 1.Doorbellinput signalringoutput signalchimeoutput intrings = 0Quietring/ chime;rings += 1

Code

scchart Doorbell {
input signal ring
output signal chime
output int rings = 0
initial state Quiet
if ring do chime; rings += 1 go to Quiet
}
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 ringoutput signal chimeoutput 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. ring = falseTick 1 enters Quiet. The transition is delayed, and ring is absent anyway.
  2. ring = truering is present, so the chart emits chime and counts the ring.
  3. Nobody touched ring, but in the simulator an input keeps its last value, so ring is still present: chime again, and rings is 2.
  4. ring = falsering is set absent, and only now does the chiming stop.
  5. ring = truering is present again: chime, and rings is 3.
  6. ring = falsering is absent again, so there is no chime: a press that lasts exactly one tick.
Tick 3: nobody touched ring, so it is still present, and the bell chimes again.
Tick ring chime after rings after
1 absent absent 0
2 present present 1
3 present (held) present 2
4 absent absent 2
5 present present 3
6 absent absent 3

So to make a “press” that lasts exactly one tick, set the input present, step, and set it absent again before the next step. The same holds if the chart emits one of its own input signals: the signal then stays present until the environment sets it absent. Leave inputs to the environment.

What differs Pure signal Bool variable
Declaration signal done bool done
Setting it do done (emit) do done = true (assign)
In the next tick absent, unless emitted again keeps its value
Good for events: “this happened now” state: “this is how things are”

Signals have one more advantage that will matter in Concurrent regions: any number of parts of a chart may emit the same signal in the same tick, and every part that tests it in that tick sees it present.

The two can’t be mixed up in the code. KIELER rejects an emission of a variable and an assignment to a signal:

In an expression, a signal’s name stands for its presence, so in arithmetic it counts as 1 when present and 0 when absent.

A signal declared without input or output is local. It is reset to absent in every tick, like an output, but it is not part of the chart’s interface (KIELER’s simulator still lists it, as it lists local variables). Even in the charts you know so far, a local signal can pass a fact along a chain of immediate transitions within a tick: a transition that emits high lets a later transition of the chain test high.

GaugeSimulation

Diagram

Diagram of GaugeStates: Idle (initial), Measure, Report. Transitions: Idle to Measure; Measure to Report: 1: reading > 100 / high; Measure to Report: 2:; Report to Idle: 1: high / alarm; Report to Idle: 2:.Gaugeinput intreadingoutput signalalarmsignalhighIdleMeasureReport1: reading > 100/ high2:1: high/ alarm2:

Code

scchart Gauge {
input int reading
output signal alarm
signal high
initial state Idle
go to Measure
state Measure
immediate if reading > 100 do high go to Report
immediate go to Report
state Report
immediate if high do alarm go to Idle
immediate go to Idle
}
Tick 0 of 5
Inputs for tick 1
Timing diagram of Gauge
Tickinput int readingoutput signal alarmlocal signal highActive 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. reading = 20Tick 1 enters Idle. Its transition is delayed, so nothing is measured yet.
  2. Idle → Measure → Report → Idle, all in this tick. The reading is 20, so Measure takes its second transition, nobody emits high, and Report does not emit alarm.
  3. reading = 130130 is over 100: Measure’s first transition emits high. Later in the same chain Report tests high, finds it present, and emits alarm.
  4. The reading is still 130 (inputs keep their value), so the chain runs the same way again: high and alarm are present in this tick too.
  5. reading = 90high was present in tick 4, but a local signal is absent again at the start of every tick. Nobody emits it now, so there is no alarm.
From tick 2 the chain Idle → Measure → Report → Idle runs in every tick. Watch the local signal high in ticks 3 to 5.

Measure decides whether the reading is high, and Report, later in the same tick, acts on the decision without looking at reading again. In a chart this small you could test reading twice; the point is that the fact travels along the chain within the tick, and that nothing carries it into the next one: in tick 5 nobody emits high, so it is absent.

Local signals really come into their own when several regions run side by side and need to tell each other what happened in this tick; that is Concurrent regions.

A valued signal is a signal that also carries a value. Declare it with a type, and optionally an initial value: output signal int level = 0. Emit it with a value in parentheses, level(7) or level(reading), and read the value with val(level). Its presence works exactly as for a pure signal; its value persists until the next emission.

SensorSimulation

Diagram

Diagram of SensorStates: Run (initial). Transitions: Run to Run: 1: sample / level(reading); last = val(level); Run to Run: 2: / last = val(level).Sensorinput boolsampleinput intreadingoutput signal intlevel = 0output intlast = 0Run1: sample/ level(reading);last = val(level)2: / last = val(level)

Code

scchart Sensor {
input bool sample
input int reading
output signal int level = 0
output int last = 0
initial state Run
if sample do level(reading); last = val(level) go to Run
do last = val(level) go to Run
}
Tick 0 of 5
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Sensor
Tickinput bool sampleinput int readingoutput signal int leveloutput int lastActive 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. sample = false, reading = 7Tick 1: level is absent and its value is its initial value, 0.
  2. sample = truesample is true: the chart emits level with the value of reading, 7. val(level) already sees the new value in the same tick, so last is 7.
  3. sample = false, reading = 9Nothing emits level, so it is absent, but its value is still 7: val(level) keeps the last emitted value. reading changed to 9, which does not matter until the next emission.
  4. sample = trueEmitted again, now with 9.
  5. sample = falseAbsent again; the value stays 9.
level is present only when sampled, but val(level), copied into last, keeps the last emitted value.

The last two columns are the signal’s presence and its value after each tick:

Tick sample reading level val(level)
1 false 7 absent 0
2 true 7 (held) present 7
3 false 9 absent 7
4 true 9 (held) present 9
5 false 9 (held) absent 9

last copies val(level) in every tick, so it shows the same values. val(level) already sees an emission made earlier in the same tick: in tick 2, last = val(level) right after level(reading) gives 7.

In this guide’s timing diagram a valued signal has one row, its value. The value is shaded and bold in the ticks in which the signal is present, and grey in the ticks in which it is absent. KIELER and SCCharts Lab list the same information as three entries: level (the presence), level_val (the value, what val(level) reads) and level_curval (the value being built up in the current tick; see combine functions below).

A transition may emit the same valued signal more than once, and in Concurrent regions several regions may emit it in the same tick. Which value counts? That is what a combine function decides. Twice emits two signals twice each; total is declared with combine +, latest without a combine function.

TwiceSimulation

Diagram

Diagram of TwiceStates: Run (initial). Transitions: Run to Run: a / total(1); total(2); latest(1); latest(2).Twiceinput boolaoutput signal inttotalcombine+output signal intlatestRuna/ total(1);total(2);latest(1);latest(2)

Code

scchart Twice {
input bool a
output signal int total combine +
output signal int latest
initial state Run
if a do total(1); total(2); latest(1); latest(2) go to Run
}
Tick 0 of 4
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Twice
Tickinput bool aoutput signal int totaloutput signal int latestActive 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, 4 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 4 ticks:

  1. a = falseTick 1 enters Run. Nothing is emitted.
  2. a = trueBoth signals are emitted twice in one transition. total has combine +, so its emissions add up: 1 + 2 = 3. latest has no combine function, and within one transition the last emission wins: 2.
  3. a = falseBoth absent; both keep their values, 3 and 2.
  4. a = truetotal starts from 0 again in every tick, so it is 3 again, not 6.
With combine +, total is 1 + 2 = 3; without one, latest keeps the last emission, 2.
  • Without a combine function, the last emission wins: latest(1); latest(2) gives 2. The KIELER wiki says that a valued signal emitted several times in a tick needs a combine function; KIELER itself only insists on it when concurrent regions, which come in chapter 8, emit the signal in different ways in the same tick.
  • With combine op, every emission of the tick is combined with op, starting afresh in each tick from a start value, the operator’s neutral element. That is why total is 3 again in tick 4, not 6.
Combine Start value 5 and 6 give
+ 0 11
* 1 30
max −2147483648 6
min 2147483647 5
| 0 7
& 0 0

These are the start values for int signals. A bool signal can be combined with | or &, which start from false and true. combine none is the same as no combine function.

KIELER has some rough edges here. max and min work, but KIELER warns:

Min or max combine operators are currently not supported.

For int signals, & starts from 0, so the result is always 0 (a KIELER bug). And a float signal with any combine function crashes KIELER’s compiler.

A natural first counter emits the signal’s value plus one:

CounterChart

Diagram

Diagram of CounterStates: Run (initial). Transitions: Run to Run: press / count((val(count) + 1)).Counterinput boolpressoutput signal intcountRunpress/ count((val(count) + 1))

Code

scchart Counter {
input bool press
output signal int count
initial state Run
if press do count(val(count) + 1) go to Run
}

KIELER rejects this chart

  • Line 6, column 23val(count) is read to emit count in the same tick, but val(count) has to wait for every emission of countKIELER says: The SCG is NOT asc-schedulable!To build on the value of count, use last tick's value: count(pre(val(count)) + 1).About this message
KIELER rejects this counter: the emission reads the value it is about to set.

KIELER rejects it with Can't schedule from … errors and The SCG is NOT asc-schedulable!, and warns Instantaneous loop detected! (SCCharts Lab: Circular dependency prevents scheduling this tick. and Potential instantaneous loop.). The reason: val(count) in a tick has to wait until every emission of count in that tick has happened, so that it sees the final value, and this emission needs val(count) before it can happen. Neither can go first.

What the counter means is “one more than last tick’s value”. pre(val(count)) says exactly that: the value count had at the end of the previous tick. The previous tick covers pre in full; here is a preview.

CounterPreSimulation

Diagram

Diagram of CounterPreStates: Run (initial). Transitions: Run to Run: press / count((pre(val(count)) + 1)).CounterPreinput boolpressoutput signal intcountRunpress/ count((pre(val(count)) + 1))

Code

scchart CounterPre {
input bool press
output signal int count
initial state Run
if press do count(pre(val(count)) + 1) go to Run
}
Tick 0 of 5
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of CounterPre
Tickinput bool pressoutput signal int countActive 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. press = trueTick 1 enters Run; the self-loop is delayed. count is absent with value 0.
  2. pre(val(count)) is the value count had at the end of the last tick, 0, so the chart emits count(1).
  3. press = falseNo emission: count is absent, its value stays 1.
  4. press = truepre(val(count)) is 1, so count is emitted with 2.
  5. And with 3.
count goes up by one in every tick in which press is true.

Here is a KIELER quirk that has nothing to do with the language: a chart that declares a signal never terminates in KIELER’s simulator, even when it reaches a final state. Any signal will do, input, output or local. Once and OnceBool differ only in the type of start. Both have a final state with a transition back, like Finish in Delayed and immediate transitions, and the tour holds start on from tick 2:

Once and OnceBoolComparison

Diagrams

Once

Diagram of OnceStates: Waiting (initial), Done (final). Transitions: Waiting to Done: start / runs += 1; Done to Waiting: start.Onceinput signalstartoutput intruns = 0WaitingDonestart/ runs += 1start

OnceBool

Diagram of OnceBoolStates: Waiting (initial), Done (final). Transitions: Waiting to Done: start / runs += 1; Done to Waiting: start.OnceBoolinput boolstartoutput intruns = 0WaitingDonestart/ runs += 1start

Code

Once

scchart Once {
input signal start
output int runs = 0
initial state Waiting
if start do runs += 1 go to Done
final state Done
if start go to Waiting
}

OnceBool

scchart OnceBool {
input bool start
output int runs = 0
initial state Waiting
if start do runs += 1 go to Done
final state Done
if start go to Waiting
}
Tick 0 of 5
Inputs for tick 1, for both charts Input signal: present while on. It keeps its value until you change it.
Timing diagram of Once and OnceBool, no ticks yet
Tickinput signal startOnce: output int runsOnce: Active state of the chartOnceBool: output int runsOnceBool: 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, 5 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

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

  1. start = falseTick 1 enters Waiting in both charts.
  2. start = truestart comes, and both charts count it and enter their final state Done. OnceBool ends the tick in a final state, so it terminates. Once is in its final state too, but it declares a signal, so KIELER’s simulator does not let it terminate.
  3. start is still on. OnceBool has terminated and checks nothing. Once is still running, so Done’s transition fires and takes it back to Waiting.
  4. Once counts a second run; OnceBool stays at 1 for good.
  5. start = falsestart is off. Once rests in Done, still running; the next start would send it round again.
Note: KIELER never reports this chart as terminated, because it declares signals (start) (Once)
  • Note in Once, line 1, column 9In KIELER's simulation a chart that declares a signal never terminates, even when every top-level region reaches a final state. The guide shows what KIELER does.
Both reach their final state in tick 2. OnceBool terminates there; Once, which declares a signal, carries on and counts again.

OnceBool behaves as a chart without signals should: it terminates in tick 2, and from then on it checks nothing, so runs stays 1. Once never terminates, so Done’s transition fires in tick 3, and runs reaches 2 in tick 4.

The cause is the bookkeeping KIELER’s simulation adds to every chart, combined with the way it translates signals: together they wrap the chart in an extra state that is never final. Official KIELER and SCCharts Lab behave the same. Most controllers never terminate anyway; if yours must, use bool variables at its top level instead of signals.

An alarm that switches itself off

Alarm should raise alarm in every tick from tick 2 on in which temperature is above 90, and not otherwise (its transition is delayed, so tick 1 cannot raise it). As written, alarm is a bool variable and stays true for ever once it has been set. Make alarm a signal, so that it is present exactly in the hot ticks.

Show the starter chart
scchart Alarm {
input int temperature
output bool alarm = false
initial state Watch
if temperature > 90 do alarm = true go to Watch
}
Show a solution
scchart Alarm {
input int temperature
output signal alarm
initial state Watch
if temperature > 90 do alarm go to Watch
}

Add up the bonuses

Bonus emits points(10) for gold and points(5) for silver. When both arrive in the same tick, points should be 15, but it is 5. Fix it by adding two words to a declaration.

Show the starter chart
scchart Bonus {
input bool gold, silver
output signal int points
initial state Play
if gold && silver do points(10); points(5) go to Play
if gold do points(10) go to Play
if silver do points(5) go to Play
}
Show a solution
scchart Bonus {
input bool gold, silver
output signal int points combine +
initial state Play
if gold && silver do points(10); points(5) go to Play
if gold do points(10) go to Play
if silver do points(5) go to Play
}
TipJarPrediction

Diagram

Diagram of TipJarStates: Open (initial). Transitions: Open to Open: tip / received(amount); thanks.TipJarinput signaltipinput intamountoutput signal intreceived = 0output signalthanksOpentip/ received(amount);thanks

Code

scchart TipJar {
input signal tip
input int amount
output signal int received = 0
output signal thanks
initial state Open
if tip do received(amount); thanks go to Open
}

tip is switched on in tick 2 and only switched off in tick 4, and amount changes in every tick.

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

Ticktipamount
1absent0
2present2
3present5
4absent1
Plays the scenario on the diagram, then marks each answer.
Explanation

In tick 3 nobody switched tip off, so it is still present and the transition fires again, emitting received with the new amount, 5. In tick 4 nothing is emitted, so thanks is absent, and received is absent too but keeps its value, 5.

output signal int total combine + is emitted as total(2); total(3) in tick 2, and not at all in tick 3. After tick 3, is total present, and what is val(total)?
Show the answer

Answer B: Absent, 5

Presence lasts one tick, so total is absent in tick 3. The value persists until the next emission: the combined value of tick 2, 2 + 3 = 5.

Which effect counts presses of press in a valued signal count?
Show the answer

Answer B: count(pre(val(count)) + 1)

count(val(count) + 1) is rejected: the emission would have to wait for its own result. count(count + 1) compiles, but there count is the signal’s presence, not its value; it is still absent when the value is computed, so the chart emits 1 every time. pre(val(count)), last tick’s value, is what a counter needs.

Key ideas

  • A signal is present in a tick if and only if something emits it in that tick; output and local signals start every tick absent.
  • Emit with the signal’s name as an effect (do S), test with its name as a condition (if S, !S). Emitting a variable or assigning a signal is an error.
  • In the simulator, an input signal stays present until you set it absent.
  • A valued signal adds a value: emit with V(e), read with val(V). The value persists while the signal is absent.
  • Several emissions in one tick: without a combine function the last one wins; with combine op they are combined from the operator’s start value.
  • To build on a signal’s value, use last tick’s: pre(val(V)).
  • In KIELER’s simulator, a chart that declares a signal never terminates.

Next, Superstates: states that contain a whole state machine of their own.

Report a mistake on this page