Arrays and for regions
Arrays of inputs and outputs, and for regions, which make one concurrent copy of a region for every index.
A car with four seats has four seatbelt sensors, and the logic for each seat is the same: wait until
that belt is buckled. You could write four identical regions, one per seat, and
four more the day the car gets a fifth seat. A for region writes the region once and lets KIELER
make the copies, one per index, each with its own constant i.
For regions are almost always used with arrays, so this chapter starts with a short reminder of how arrays work.
Arrays, briefly
Section titled “Arrays, briefly”Variables, types and expressions introduced arrays. The parts that matter here:
const int SEATS = 4input bool buckled[SEATS]output bool lamp[SEATS] = {true, true, true, true}-
T name[n]declares an array ofnelements, numbered from 0 ton− 1. The size must be an integer literal or aconst intwith a literal value; anything else is rejected withArray cardinalities must be an int literal or a reference to a constant int object. -
Elements start at their type’s default (
false,0) unless a vector such as{true, true, true, true}gives initial values. A shorter vector sets only the first elements. -
a[2]reads or writes one element. In the simulator, an array input has one value per element. -
A vector assignment
a = {x, y, z}assigns the elements one after another, in index order. Copying a whole array withb = adoes not compile (the C compiler reportsassignment to expression with array type).
Between concurrent regions, elements with different constant indices count as different variables:
two regions writing lamp[0] and lamp[1] do not conflict. An element whose index is a variable,
lamp[k], counts as all of them at once.
One region, four copies
Section titled “One region, four copies”Here are the seatbelts. Checking holds one region, Seat, written once with for i : 0 to 3:
Diagram
Code
scchart Seatbelts { input bool buckled[4] output bool chime = true
initial state Checking { region Seat for i : 0 to 3 { initial state Open if buckled[i] go to Fastened
final state Fastened } } join to Ready
state Ready { entry do chime = false }}| Tick | input bool buckled | output bool chime | Active state of the chart | Active state of region Seat (in Checking), in the copy with i = 0 | Active state of region Seat (in Checking), in the copy with i = 1 | Active state of region Seat (in Checking), in the copy with i = 2 | Active state of region Seat (in Checking), in the copy with i = 3 |
|---|
Guided tour, 6 ticks:
- buckled = true,false,false,falseTick 1 enters
Checkingand four copies of the regionSeat, withi= 0, 1, 2 and 3. Seat 0 is already buckled, but the transitions are delayed, so nothing moves yet. - The copy with
i= 0 seesbuckled[0]and goes toFastened. The other three keep waiting. - buckled = true,true,false,trueSeats 1 and 3 are buckled: their copies finish too, in the same tick. Only seat 2 is still open.
- buckled = false,false,false,falseEveryone unbuckles, but the finished copies stay in
Fastened: this simple chart only waits for each belt once. - buckled = false,false,true,falseSeat 2 is buckled. Now every copy is in its final state, so
Checkingterminates andjoin to Readyfires in the same tick: the chime stops. - In
Ready.
Seat waits for its own belt. In tick 5 the last belt finishes the last copy, and the join fires in the same tick.KIELER replaces the for region by four ordinary regions, one for each value of i from 0 to 3. In each
copy, i is a constant: the first copy tests buckled[0], the second buckled[1], and so on. The
copies run concurrently, like regions you had written out by hand, and join to Ready waits until every
copy has reached its final state. The diagram draws the region once, labelled with the counter and its
range: Seat | i [0, 3].
In the simulation, the diagram marks a state as active when any copy is in it, and a small badge says
how many copies are in it: in tick 3, Open carries ×1 and Fastened ×3. The timing diagram has a lane
for each copy, Seat i=0 to Seat i=3, so you can follow every copy on its own: Seat i=2 is the
one that stays in Open until tick 5.
This chart only waits for each belt once: unbuckling in tick 4 changes nothing. A real seatbelt warning would also have to notice a belt being opened again.
The rule
Section titled “The rule”A for region is written region Name for i : a to b { ... }, or without a name,
region for i : a to b { ... }. KIELER makes one copy of the region for every value of i from a to
b, both ends included:
- In each copy,
iis a constant with that copy’s value. It is replaced by the number itself, so it is not a variable you can watch in the simulator, and assigning to it is an error. - If
ais greater thanb, the copies count down:for i : 3 to 1givesi= 3, 2, 1.for i : 5 to 5gives one copy. - All copies are concurrent regions of the same state, and each has the region’s name.
- A
join toon a state with for regions waits until every copy, and every other region, is in a final state.
The second form runs over the indices of an array: region for i : buckled is the same as
region for i : 0 to 3 when buckled has four elements. Its size may be a literal or a const, so
changing SEATS changes the number of copies:
Diagram
Code
scchart SeatLamps { const int SEATS = 4 input bool buckled[SEATS] output bool lamp[SEATS] = {true, true, true, true} output int fastened = 0
region Seat for i : buckled { initial state Open if buckled[i] do lamp[i] = false; fastened++ go to Fastened
final state Fastened }}| Tick | input bool buckled | output bool lamp | output int fastened | Active state of region Seat, in the copy with i = 0 | Active state of region Seat, in the copy with i = 1 | Active state of region Seat, in the copy with i = 2 | Active state of region Seat, in the copy with i = 3 |
|---|
Guided tour, 5 ticks:
- buckled = false,false,false,false
for i : buckledmakes one copy per element ofbuckled:SEATSis 4, soi= 0 to 3. All four lamps are on. - buckled = true,false,true,falseSeats 0 and 2: their copies switch off their own lamp and both add 1 to
fastened. Concurrent++on one variable is fine; the copies write different elements oflamp, so they do not conflict. - buckled = false,false,false,falseNothing new.
- buckled = true,true,true,trueSeats 1 and 3 finish (
fastened= 4). Every region of the chart is now final, so the chart itself terminates. - A terminated chart does nothing more.
lamp[i], and both add to fastened in the same tick.Each copy writes its own element, lamp[0] to lamp[3], so the copies never conflict over lamp, and
fastened++ from several copies in one tick is fine: concurrent increments add up (see
Why the compiler says no). Without a join to around it, the copies are regions of
the chart itself, and when the last one finishes in tick 4, the whole chart terminates.
Copies have no order to rely on
Section titled “Copies have no order to rely on”Concurrent regions have no order: data decides what runs first. Copies of a for region are no different.
As long as their writes do not depend on order (++, +=, writes to their own element), you never see an
order. If they do, KIELER may still accept the chart, and the order it picks is not the one you would
guess:
Diagram
Code
scchart CopyOrder { output int log = 0
region First { initial state W immediate do log = log * 10 + 9 go to D final state D } region R for i : 3 to 1 { initial state W immediate do log = log * 10 + i go to D final state D } region Last { initial state W immediate do log = log * 10 + 7 go to D final state D }}| Tick | output int log | Active state of region First | Active state of region R, in the copy with i = 3 | Active state of region R, in the copy with i = 2 | Active state of region R, in the copy with i = 1 | Active state of region Last |
|---|
Guided tour, 1 tick:
- Every region runs its immediate transition in tick 1, and each one multiplies
logby 10 and adds a digit. KIELER runs these concurrent updates in region order:First(9),Last(7), then the copies ofRfori= 3, 2, 1. Solog= 97321: the copies come after all the other regions.
3 warnings: This assignment reads log, so the compiler treats it as an update of log
- Warning, line 6, column 20KIELER says:
This assignment reads log, so the compiler treats it as an update of log (a relative write, as in the deprecated old SC syntax). Write it as log *= 10; log += 9 to say so. In one region it means the same. It only matters when another region also writes log in the same tick: this assignment then runs after that write instead of conflicting with it.
Write it aslog *= 10; log += 9: in one region it means the same, and it says that it is an update.About this message - Warning, line 11, column 20KIELER says:
This assignment reads log, so the compiler treats it as an update of log (a relative write, as in the deprecated old SC syntax). Write it as log *= 10; log += i to say so. In one region it means the same. It only matters when another region also writes log in the same tick: this assignment then runs after that write instead of conflicting with it.
Write it aslog *= 10; log += i: in one region it means the same, and it says that it is an update.About this message - Warning, line 16, column 20KIELER says:
This assignment reads log, so the compiler treats it as an update of log (a relative write, as in the deprecated old SC syntax). Write it as log *= 10; log += 7 to say so. In one region it means the same. It only matters when another region also writes log in the same tick: this assignment then runs after that write instead of conflicting with it.
Write it aslog *= 10; log += 7: in one region it means the same, and it says that it is an update.About this message
log: First (9), the copies of R for i = 3, 2, 1, written in the middle, and Last (7). The result is 97321: the copies run after all the other regions.Each region runs log = log * 10 + digit, which KIELER treats as an update of log. Official KIELER
warns
This assignment looks like old relative write SC syntax and is considered deprecated.and SCCharts Lab says what it means:
This assignment reads log, so the compiler treats it as an update of log (a relative write, as in the deprecated old SC syntax).KIELER runs the updates of a tick in region order, with the copies after all the other regions of
the state. The timing diagram cannot show that order: it lists the lanes as the regions are written,
First, the copies, then Last. Don’t write concurrent updates whose result depends on the order.
Limits
Section titled “Limits”KIELER expands a for region by copying it, and it is strict about what it can copy. Keep for regions flat and simple.
Two more things to avoid: assigning to the counter (i = 5) is rejected with
Cannot replace left hand side of assignment by a value., and dataflow for i : ... is accepted by the
editor but cannot be compiled.
Variables in a for region
Section titled “Variables in a for region”Be careful where a for region’s variables are declared. A declaration at the top of the for region
belongs to the enclosing state, like any region’s declarations, so all copies share one k. If each
copy writes its own value into it in the same tick, KIELER rejects the chart as a
write–write conflict:
Diagram
Code
scchart ForSharedLocal { output int sum = 0 region for i : 1 to 3 { int k = 0 initial state W immediate do k = i; sum += k go to D final state D }}KIELER rejects this chart
- Line 6, column 20The copies of the for-region assign different values to
k, which they shareKIELER says:The SCG is NOT asc-schedulable!
KIELER makes one copy of the for-region for each value ofi, but a variable declared in the region is declared once, outside the copies: they all write the samekin the same tick. Declarekin a state inside the region (initial state W { int k = 0 ... }), or use an update such ask += 1.About this message
int k is declared once, for all three copies, and each copy assigns a different value to it in tick 1.A variable each copy should have for itself goes inside a state of the for region. Then every copy gets its own, and its initial value may use the counter:
Diagram
Code
scchart ForLocalPerCopy { output int sum = 0 region for i : 1 to 3 { initial state W { int k = i * 2 initial state X immediate do sum += k go to Y final state Y } join to D final state D }}| Tick | output int sum | Active state of the chart, in the copy with i = 1 | Active state of the region of W, in the copy with i = 1 | Active state of the chart, in the copy with i = 2 | Active state of the region of W, in the copy with i = 2 | Active state of the chart, in the copy with i = 3 | Active state of the region of W, in the copy with i = 3 |
|---|
Guided tour, 1 tick:
- Declared inside a state of the for region,
kexists once per copy: 2 + 4 + 6 = 12. Every region of the chart finishes in tick 1, so the chart terminates.
W declares its own k = i * 2: the copies add 2, 4 and 6, and sum is 12 after tick 1.At the top of the for region, an initial value that uses the counter (int k = i) crashes the
compiler the same way as nested for regions (SCCharts Lab’s message; no answer at all in the KIELER VS
Code extension).
Try it
Section titled “Try it”Turn the keys
LaunchKeys launches when all three keys have been turned, in any order. It has one hand-written region
per key. Replace the three regions with a single for region, so that giving key a fourth element is
the only change needed for a fourth key. The behaviour must stay exactly the same, so the playground’s
comparison with the model solution matches even before you start: use it to check that your for region
still behaves like the three regions.
Show the starter chart
scchart LaunchKeys { input bool key[3] output bool launch = false
initial state Arming { region Key0 { initial state Off if key[0] go to Turned
final state Turned } region Key1 { initial state Off if key[1] go to Turned
final state Turned } region Key2 { initial state Off if key[2] go to Turned
final state Turned } } join to Launch
state Launch { entry do launch = true }}Show a solution
scchart LaunchKeys { input bool key[3] output bool launch = false
initial state Arming { region Key for i : key { initial state Off if key[i] go to Turned
final state Turned } } join to Launch
state Launch { entry do launch = true }}Check your understanding
Section titled “Check your understanding”Show the answer
Answer B: Four: 2, 3, 4 and 5
Both ends are included, so i takes the values 2, 3, 4 and 5, one copy each. In each copy i is that
constant.
Show the answer
Answer D: region for i : A where A is declared input int A[N] with const int N = 3
The range form needs integer literals, so 0 to N is rejected, but the array form accepts an array whose
size is a literal const. Nesting crashes the compiler, and the counter is a constant that cannot be
assigned.
Key ideas
region R for i : a to bmakes one concurrent copy ofRfor eachifromatob, both included; in each copy,iis a constant.region R for i : Aruns over the indices of arrayA, from 0 to its size minus 1.- Copies that write their own array element, or add to a shared counter, never conflict.
- Copies are concurrent, like hand-written regions: don’t write concurrent updates whose result depends on their order.
- Bounds must be integer literals (use the array form to size by a
const), for regions cannot be nested, and a variable each copy needs for itself is declared inside a state of the copy.
Next, Dataflow regions: equations that are worked out again in every tick.