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.
Present for one tick
Section titled “Present for one 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.
Diagram
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 | input bool press | output signal flash | output bool pressed | Active state of the chart |
|---|
Guided tour, 5 ticks:
- press = falseTick 1 enters
Ready.flashis absent: a signal is absent unless something emits it in this tick. - press = true
pressis true, so the self-loop emitsflashand setspressed. Both are true at the end of this tick. - press = falseNothing emits
flash, so it is absent again.pressedis a variable: it keeps the value true until something assigns it. - Still
flashabsent,pressedtrue. - press = true
flashis 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.
Declaring, emitting and testing
Section titled “Declaring, emitting and testing”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.
Diagram
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 | input signal A | input signal B | output signal both | output signal onlyA | Active state of the chart |
|---|
Guided tour, 5 ticks:
- A = false, B = falseTick 1 enters
Watch. Its transitions are delayed. - A = true
Ais present andBabsent: priority 1 (A && B) fails, priority 2 (A && !B) fires and emitsonlyA. - B = trueBoth present: priority 1 emits
both.onlyAis absent again, because nothing emitted it in this tick. - A = false
Ais absent, so neither transition fires and neither output is present. Balone 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.
Input signals in the simulator
Section titled “Input signals in the simulator”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.
Diagram
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 | input signal ring | output signal chime | output int rings | Active state of the chart |
|---|
Guided tour, 6 ticks:
- ring = falseTick 1 enters
Quiet. The transition is delayed, andringis absent anyway. - ring = true
ringis present, so the chart emitschimeand counts the ring. - Nobody touched
ring, but in the simulator an input keeps its last value, soringis still present:chimeagain, andringsis 2. - ring = false
ringis set absent, and only now does the chiming stop. - ring = true
ringis present again:chime, andringsis 3. - ring = false
ringis absent again, so there is no chime: a press that lasts exactly one tick.
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.
Signals or variables?
Section titled “Signals or variables?”| 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.
Local signals
Section titled “Local signals”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.
Diagram
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 | input int reading | output signal alarm | local signal high | Active state of the chart |
|---|
Guided tour, 5 ticks:
- reading = 20Tick 1 enters
Idle. Its transition is delayed, so nothing is measured yet. Idle→Measure→Report→Idle, all in this tick. The reading is 20, soMeasuretakes its second transition, nobody emitshigh, andReportdoes not emitalarm.- reading = 130130 is over 100:
Measure’s first transition emitshigh. Later in the same chainReporttestshigh, finds it present, and emitsalarm. - The reading is still 130 (inputs keep their value), so the chain runs the same way again:
highandalarmare present in this tick too. - reading = 90
highwas present in tick 4, but a local signal is absent again at the start of every tick. Nobody emits it now, so there is noalarm.
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.
Valued signals
Section titled “Valued signals”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.
Diagram
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 | input bool sample | input int reading | output signal int level | output int last | Active state of the chart |
|---|
Guided tour, 5 ticks:
- sample = false, reading = 7Tick 1:
levelis absent and its value is its initial value, 0. - sample = true
sampleis true: the chart emitslevelwith the value ofreading, 7.val(level)already sees the new value in the same tick, solastis 7. - sample = false, reading = 9Nothing emits
level, so it is absent, but its value is still 7:val(level)keeps the last emitted value.readingchanged to 9, which does not matter until the next emission. - sample = trueEmitted again, now with 9.
- 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).
Emitting more than once in a tick
Section titled “Emitting more than once in a tick”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.
Diagram
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 | input bool a | output signal int total | output signal int latest | Active state of the chart |
|---|
Guided tour, 4 ticks:
- a = falseTick 1 enters
Run. Nothing is emitted. - a = trueBoth signals are emitted twice in one transition.
totalhascombine +, so its emissions add up: 1 + 2 = 3.latesthas no combine function, and within one transition the last emission wins: 2. - a = falseBoth absent; both keep their values, 3 and 2.
- a = true
totalstarts from 0 again in every tick, so it is 3 again, not 6.
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 withop, starting afresh in each tick from a start value, the operator’s neutral element. That is whytotalis 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.
Building on the last value
Section titled “Building on the last value”A natural first counter emits the signal’s value plus one:
Diagram
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 23
val(count)is read to emitcountin the same tick, butval(count)has to wait for every emission ofcountKIELER says:The SCG is NOT asc-schedulable!
To build on the value ofcount, use last tick's value:count(pre(val(count)) + 1).About this message
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.
Diagram
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 | input bool press | output signal int count | Active state of the chart |
|---|
Guided tour, 5 ticks:
- press = trueTick 1 enters
Run; the self-loop is delayed.countis absent with value 0. pre(val(count))is the valuecounthad at the end of the last tick, 0, so the chart emitscount(1).- press = falseNo emission:
countis absent, its value stays 1. - press = true
pre(val(count))is 1, socountis emitted with 2. - And with 3.
count goes up by one in every tick in which press is true.Signals and termination
Section titled “Signals and termination”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:
Diagrams
Once
OnceBool
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 | input signal start | Once: output int runs | Once: Active state of the chart | OnceBool: output int runs | OnceBool: Active state of the chart |
|---|
Guided tour, 5 ticks, the same inputs for both charts:
- start = falseTick 1 enters
Waitingin both charts. - start = true
startcomes, and both charts count it and enter their final stateDone.OnceBoolends the tick in a final state, so it terminates.Onceis in its final state too, but it declares a signal, so KIELER’s simulator does not let it terminate. startis still on.OnceBoolhas terminated and checks nothing.Onceis still running, soDone’s transition fires and takes it back toWaiting.Oncecounts a second run;OnceBoolstays at 1 for good.- start = false
startis off.Oncerests inDone, still running; the nextstartwould 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.
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.
Try it
Section titled “Try it”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}Check your understanding
Section titled “Check your understanding”Diagram
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.
| Tick | tip | amount |
|---|---|---|
| 1 | absent | 0 |
| 2 | present | 2 |
| 3 | present | 5 |
| 4 | absent | 1 |
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.
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.
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 withval(V). The value persists while the signal is absent. - Several emissions in one tick: without a combine function the last one wins; with
combine opthey 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.