ABRO
The hello world of synchronous programming: wait for A and B in any order, emit O, and start again whenever R arrives.
ABRO is the first program almost every synchronous language shows. It comes from Esterel, Gérard Berry’s language, and its whole specification fits in two sentences:
Emit
Oas soon as bothAandBhave arrived, in any order. WheneverRarrives, forget everything and start again.
You have built it already: Concurrent regions puts it together step by step, and Aborts explains why its reset is a strong abort. This page treats ABRO as a pattern to reuse. It shows what each part is for, what the chart does in every corner case, a version with variables, the mistakes people make with it, and an immediate version that KIELER gets wrong.
The pattern
Section titled “The pattern”ABRO answers a question that comes up all the time: how do I wait for several things that can happen in any order? A vending machine, for instance, may deliver once a product has been chosen and the payment has gone through, in either order, and a cancel button starts it all again. In C you would juggle flags in a loop. In SCCharts each part of the problem has its own construct:
- One region per thing to wait for. A region waits in its initial state and moves to a final state when its signal arrives. Being in the final state is the region’s memory that its signal has happened; no flag is needed.
- Termination notices when everything has happened. A superstate whose regions are all in final
states terminates, and its
join tofires in that same tick. Here the join’s effect emitsO. - A strong abort around everything restarts it.
if R abort to ABOleavesABOand enters it again from its initial states, whatever was going on inside.
Diagram
Code
scchart ABRO { input signal A, B, R output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O join to Done
state Done } if R abort to ABO}| Tick | input signal A | input signal B | input signal R | output signal O | Active state of the chart | Active state of the region of ABO | Active state of region HandleA (in ABO.WaitAB) | Active state of region HandleB (in ABO.WaitAB) |
|---|
Guided tour, 11 ticks:
- A = false, B = false, R = falseTick 1 enters
ABO, thenWaitAB, thenWaitAandWaitB, all at once. Every transition is delayed, so this tick only sets things up. - B = true
Barrives first this time:HandleBfinishes inDoneB. The order ofAandBdoes not matter. - B = falseNothing present.
DoneBremembers thatBhas happened. - A = true
A:HandleAfinishes too. Both regions are final, so thejoin tofires in this tick and emitsO. - A = false
Ois a signal, so it is absent again: it was present in exactly one tick.ABOnow rests inDone. - A = true, B = true
AandBare ignored:Donewaits forR, nothing else. - A = false, B = false, R = true
R: the strong abortif R abort to ABOleavesABOand enters it again from its initial states.WaitABstarts over inWaitAandWaitB. - R = false, A = true, B = true
AandBin the same tick: both regions finish in this tick, soWaitABterminates andOis emitted at once. - A = false, B = falseBack in
Done, waiting forR. - A = true, B = true, R = trueAll three at once.
Rwins:ABOis re-entered, and the re-enteredWaitAandWaitBcannot react toAandBin their entry tick (their transitions are delayed).Ois absent. - A = false, B = false, R = falseThe
AandBof the last tick are not remembered: ABRO waits for a freshAandB.
To wait for a third thing, add a third region: the join waits for all of them.
Every case, tick by tick
Section titled “Every case, tick by tick”| Tick | Present | What happens | O |
|---|---|---|---|
| 2 | B |
HandleB finishes; DoneB remembers B |
absent |
| 4 | A |
HandleA finishes too: both regions are final, so the join fires |
present |
| 6 | A, B |
ignored: Done waits for R and nothing else |
absent |
| 7 | R |
ABO is left and entered again: both regions wait from the start |
absent |
| 8 | A, B |
both regions finish in the same tick, so the join fires at once | present |
| 10 | A, B, R |
R restarts ABO; A and B are lost |
absent |
Tick 10 deserves a closer look. ABO rests in Done, so nothing inside it is waiting for A or B.
R re-enters ABO, and the newly entered WaitA and WaitB cannot react in their entry tick,
because their transitions are delayed. So A and B in the reset tick are lost, and ABRO waits for
fresh ones. Esterel’s ABRO behaves the same way.
When R arrives while ABO is still waiting, there is a second reason. A strong abort is decided
before anything inside reacts, so HandleA and HandleB never get to look at that tick’s A and
B. With a weak reset, if R go to ABO, they would: the inside finishes its tick first, and if that
completes the join, O is emitted before the restart. Aborts runs
the two versions side by side. “Forget everything” means the strong abort, and that is what Esterel uses.
KIELER compiles the weak version with the warning Instantaneous loop detected! (in SCCharts Lab,
Potential instantaneous loop.), a false alarm here.
Signals or variables
Section titled “Signals or variables”KIELER’s own Quick Start Guide writes ABRO
with bool variables instead of signals. O is then a variable that the join sets to true, and ABO’s
entry action sets it back to false whenever the chart starts or restarts:
Diagram
Code
scchart ABROBool { input bool A, B, R output bool O
initial state ABO { entry do O = false
initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O = true join to Done
state Done } if R abort to ABO}| Tick | input bool A | input bool B | input bool R | output bool O | Active state of the chart | Active state of the region of ABO | Active state of region HandleA (in ABO.WaitAB) | Active state of region HandleB (in ABO.WaitAB) |
|---|
Guided tour, 7 ticks:
- A = false, B = false, R = falseTick 1 enters
ABO; its entry action setsO = false. - A = true
Ais true:HandleAfinishes. - A = false, B = true
Bis true:HandleBfinishes, the join fires and its effect setsO = true. - B = false
Ois a variable, so it keeps the value it was given: it stays true. - R = true
R: the strong abort re-entersABO, and the entry action setsO = falseagain. - R = false, A = true, B = trueBoth in one tick:
O = true. - A = false, B = false
Ostays true until the nextR.
O becomes true in tick 3 and stays true until the reset in tick 5.The behaviour is the same up to what O means. A signal is present only in the tick it is emitted, so
the signal version says “both have arrived, now”. The variable keeps its value, so the bool version
says “both have arrived since the last reset”. Pick the one that matches what the reader of O needs:
an event to react to once, or a condition to look at whenever it likes. Signals
covers the difference in detail.
Common mistakes
Section titled “Common mistakes”The reset on the wrong state. The reset has to sit on a state that contains Done as well. Put it
on WaitAB instead, and it works only until the first O:
Diagram
Code
scchart ABROResetInside { input signal A, B, R output signal O
initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } if R abort to WaitAB do O join to Done
state Done}| Tick | input signal A | input signal B | input signal R | output signal O | Active state of the chart | Active state of region HandleA (in WaitAB) | Active state of region HandleB (in WaitAB) |
|---|
Guided tour, 8 ticks:
- A = false, B = false, R = falseTick 1 enters
WaitAB. - A = true
A:HandleAfinishes. - R = true, A = false
Rwhile waiting: the strong abort restartsWaitAB, so theAof tick 2 is forgotten. - R = false, B = true
Balone gives noO: since the reset, onlyBhas happened. - B = false, A = true
A: now both have happened since the reset, soOis emitted and the chart moves toDone. - A = false, R = true
Rnow does nothing: the abort belongs toWaitAB, which is no longer active, andDonehas no transitions. - R = false, A = true, B = true
AandBare ignored too. This chart can emitOonly once. - A = false, B = falseStuck in
Done.
O the reset works (tick 3); after it (tick 6) nothing can leave Done.Two more mistakes come with compiler messages.
A region without a final state. Forget the final on DoneB, and HandleB can never finish, so
the join never fires. KIELER compiles the chart but warns:
Every region should have a final state whenever its parent state has a termination transition.All or none concurrent regions should have final states.Diagram
Code
scchart ABRONoFinal { input signal A, B, R output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB state DoneB } } do O join to Done
state Done } if R abort to ABO}| Tick | input signal A | input signal B | input signal R | output signal O | Active state of the chart | Active state of the region of ABO | Active state of region HandleA (in ABO.WaitAB) | Active state of region HandleB (in ABO.WaitAB) |
|---|
Guided tour, 4 ticks:
- A = false, B = false, R = falseTick 1 enters
ABO,WaitAB,WaitAandWaitB. - A = true
A:HandleAreaches its final stateDoneA. - A = false, B = true
B:HandleBmoves toDoneB, butDoneBis not a final state.WaitABdoes not terminate, andOstays absent. - B = falseNothing more can happen inside
WaitAB: onlyRcan restart it, andOnever comes. This is what the two warnings are about.
2 warnings
- Warning, line 10, column 21
WaitABcan never finish: regionHandleBhas no final stateKIELER says:All or none concurrent regions should have final states.
A superstate finishes (forjoin to) only when every region has reached a final state.About this message - Warning, line 12, column 14Region
HandleBhas no final state, sojoin to Donecan never fireKIELER says:Every region should have a final state whenever its parent state has a termination transition.
Mark the state where the region is done as final (final state Done).About this message
A and B have both arrived, but DoneB is not final, so O never comes. The two warnings are listed under the simulation.A strong abort after a weak one. Give ABRO an off switch, a weak if off go to Off on ABO, and
the reset must still be written first. A strong abort is decided before the inside reacts, but a weak
abort or a join of higher priority can only be decided after it. Written the other way round, KIELER
rejects the chart:
Diagram
Code
scchart ABROOffSwitch { input signal A, B, R, off output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O join to Done
state Done } if off go to Off if R abort to ABO
state Off}KIELER rejects this chart
- Line 23, column 3A strong abort (
abort to) must come before the other transitions of the stateKIELER says:Causality problem! Strong abort transitions must have a higher priority than weak abort or termination transitions.
A strong abort is checked before the state runs, so it needs a higher priority thango toandjoin totransitions: move it above them.About this message
if off go to Off comes before if R abort to ABO. Swap the two lines and KIELER accepts the chart.The immediate variant
Section titled “The immediate variant”The ABRO above ignores A and B in tick 1 and in the tick of a reset, because the waiting
transitions are delayed. If you want those to count, the obvious change is to make them immediate:
initial state WaitA immediate if A go to DoneAWhat this should mean is clear. With A, B and R in the same tick, the reset re-enters ABO,
the immediate transitions see A and B at once, WaitAB terminates in its entry tick, and the join
emits O in that same tick. KIELER gets this right when A and B arrive in a later tick, but not
when they arrive in the tick in which ABO is entered. Follow the tour to tick 8, then on to the end:
Diagram
Code
scchart ABROImmediate { input signal A, B, R output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA immediate if A go to DoneA final state DoneA } region HandleB { initial state WaitB immediate if B go to DoneB final state DoneB } } do O join to Done
state Done } if R abort to ABO}| Tick | input signal A | input signal B | input signal R | output signal O | Active state of the chart | Active state of the region of ABO | Active state of region HandleA (in ABO.WaitAB) | Active state of region HandleB (in ABO.WaitAB) |
|---|
Guided tour, 12 ticks:
- A = false, B = false, R = falseTick 1 enters everything. With
immediate,AorBwould already count in this tick, but neither is present. - A = true
A:HandleAfinishes. Immediate transitions are checked in later ticks as well. - A = false, B = true
B: both regions are final, the join fires,Ois present. So far exactly like the delayed ABRO. - B = falseResting in
Done. - R = true
Rre-entersABO.AandBare absent, so the regions wait. - R = false, A = true, B = true
AandBin a later tick:O, as expected. - A = false, B = falseResting in
Done. - R = true, A = true, B = true
R,AandBtogether.Rre-entersABO, and in this entry tick the immediate transitions seeAandB: both regions reach their final states. The join should fire and emitO, but KIELER’s netlist compiler loses it:Ois absent. - R = false, A = false, B = falseNothing happens.
- R = true
Ralone, and still no reaction:ABOis not re-entered. - R = false, A = true, B = true
AandB: nothing. The chart is stuck for good. - A = false, B = falseNothing, for ever.
1 warning and 2 notes
- Warning, line 6, column 19KIELER warns about a potential instantaneous loop here: a superstate can be left and entered again in the same tickKIELER says:
Instantaneous loop detected!
KIELER compiles the chart by copying the superstate's first-tick behaviour; see the notes about the termination it loses.About this message - Note, line 9, column 11If
WaitABis entered again in the tick it was left (for example by a reset), KIELER does not let it finish in that tick through this transitionKIELER copies the superstate's first-tick behaviour to break the instantaneous loop, and the copy cannot reach the final state. The guide shows what KIELER does. Make the transition delayed, or reset with a delayed transition, to avoid it. - Note, line 14, column 11If
WaitABis entered again in the tick it was left (for example by a reset), KIELER does not let it finish in that tick through this transitionKIELER copies the superstate's first-tick behaviour to break the instantaneous loop, and the copy cannot reach the final state. The guide shows what KIELER does. Make the transition delayed, or reset with a delayed transition, to avoid it.
O, as expected. Tick 8: R, A and B together, and the join is lost. From then on the chart never reacts again, not even to R.The same happens if A and B are both present in tick 1. This is a defect in KIELER’s
netlist-based compiler, the one its
simulation uses by default. It shows both in the current KIELER sources and in the KIELER extension for
VS Code (version 0.3.0), checked in October 2026 (how the guide checks KIELER).
A superstate that terminates in the tick it is entered, inside a state that a reset re-enters, loses its
join. With a strong reset, as here, the state around it then never reacts again, not even to R. The
only hint is the warning Instantaneous loop detected! (SCCharts Lab: Potential instantaneous loop.).
KIELER’s other compiler, the priority-based one, rejects the chart outright with
The SCG is not IASC-schedulable!. Without the reset loop, terminating in the entry tick works as it
should.
So on a chart like this, treat that warning as a red flag. If you really need A and B in the
reset tick to count, avoid terminating in the entry tick: record A and B in two local flags and
let a third region emit O when both are set.
Diagram
Code
scchart ABROImmediateFlags { input signal A, B, R output signal O
initial state ABO { bool gotA = false, gotB = false
region HandleA { initial state WaitA immediate if A do gotA = true go to DoneA state DoneA } region HandleB { initial state WaitB immediate if B do gotB = true go to DoneB state DoneB } region Emit { initial state Waiting immediate if gotA && gotB do O go to Done state Done } } if R abort to ABO}| Tick | input signal A | input signal B | input signal R | output signal O | Active state of the chart | Active state of region HandleA (in ABO) | Active state of region HandleB (in ABO) | Active state of region Emit (in ABO) |
|---|
Guided tour, 11 ticks:
- A = true, B = true, R = false
AandBin tick 1. EnteringABOsetsgotAandgotBto false; then the immediate transitions seeAandBand set both flags, andEmit, which reads the flags after they are written, emitsOin this same tick. - A = false, B = falseAll three regions rest. No region has a final state and nothing terminates, so there is no join to lose.
- R = true
Rre-entersABO: the flags start again at false. - R = false, A = true
A:gotAis set. - A = false, B = true
B:gotBis set, andEmitsees both flags:O. - B = falseResting.
- R = true, A = true, B = true
R,AandBtogether:ABOis re-entered and, in its entry tick, all three immediate transitions fire.Ois present, as the immediate ABRO intends. - R = false, A = false, B = falseResting.
- R = true
Rstill works: the chart did not get stuck. - R = false, A = true, B = true
AandB:O. - A = false, B = falseResting.
R, A and B together, and O is emitted, because nothing has to terminate.The flags are declared inside ABO, so every entry of ABO, the reset included, sets them back to
false. Emit reads them after the other regions have written them in the same tick, because KIELER
schedules every write of a variable before every read of it in another region (see
Concurrent regions). For everyday use the delayed ABRO is the
better choice: it is the standard version and it holds no surprises.
Try it
Section titled “Try it”A and B, then C
Change ABRO so that, once A and B have both arrived (in any order), it waits for C, and emits
O only when C comes. A C before both have arrived does not count; a C in the same tick as
the second of them does. R still restarts everything. To test it: C in tick 2 changes nothing;
A in tick 3, B in tick 4 and C in tick 6 give O in tick 6; after an R, A in one
tick and B and C together in the next give O in that tick. Hint: the join does not have to
lead to Done.
Show the starter chart
scchart ABThenC { input signal A, B, C, R output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O join to Done
state Done } if R abort to ABO}Show a solution
scchart ABThenC { input signal A, B, C, R output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } join to WaitC
state WaitC immediate if C do O go to Done
state Done } if R abort to ABO}Again and again, without R
Change the starter so that it does not stop after the first O: after each O it should wait for a
fresh A and a fresh B, and emit O again when both have arrived. There is no R. One line
needs to change (and Done, which can then no longer be reached, can go). To test it: A in tick 2,
B in tick 3, then A and B together in ticks 5 and 6 give O in ticks 3, 5 and 6. Hint:
where does the join lead?
Show the starter chart
scchart ABORepeat { input signal A, B output signal O
initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O join to Done
state Done}Show a solution
scchart ABORepeat { input signal A, B output signal O
initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O join to WaitAB}Check your understanding
Section titled “Check your understanding”Diagram
Code
scchart ABRO { input signal A, B, R output signal O
initial state ABO { initial state WaitAB { region HandleA { initial state WaitA if A go to DoneA final state DoneA } region HandleB { initial state WaitB if B go to DoneB final state DoneB } } do O join to Done
state Done } if R abort to ABO}The ABRO from this page. Is O present after ticks 3, 4 and 5?
Inputs per tick. A value in grey was not set in that tick: it holds from an earlier one.
| Tick | A | B | R |
|---|---|---|---|
| 1 | absent | absent | absent |
| 2 | present | absent | present |
| 3 | absent | present | absent |
| 4 | present | absent | absent |
| 5 | present | present | present |
Explanation
O is absent after tick 3, present after tick 4 and absent after tick 5. Tick 2 brought A together
with R: the strong reset won, and the re-entered WaitA could not react to that A in its entry
tick, so it was lost. B alone in tick 3 finishes only HandleB; A in tick 4 finishes HandleA too,
and the join emits O. In tick 5 ABO rests in Done, which ignores A and B, and R restarts it.
Show the answer
Answer C: No: Done ignores A and B, and the restarted waits are delayed
A weak and a strong abort differ in what the inside may still do in the abort tick. Here the inside is
Done, which has no transitions, so there is nothing to finish. The restart enters WaitA and WaitB,
whose delayed transitions ignore their entry tick, so A and B are lost with either reset;
KIELER’s runs of the two versions agree. They differ only while WaitAB is still waiting.
Show the answer
Answer A: true: a variable keeps its value until something assigns it
O is a variable, so it keeps the value the join gave it. Only the entry action of ABO sets it back to
false, and that runs only when ABO is entered: in tick 1 and after each R. In the signal version,
by contrast, O is present in a single tick.
Key ideas
- One region per thing to wait for; a final state remembers that it has happened.
- A superstate terminates when all its regions are in final states, and a
join towithout a trigger fires in that same tick. - A self-loop on a superstate restarts everything inside it. Put the reset on the state that contains everything it must restart.
- A strong reset (
abort to) wins over whatever happens inside in that tick; a weak one (go to) lets the inside finish first, so the reset tick’sAandBcan still complete the old round. Either way the restartedABOdoes not see them: its waits are delayed. - Immediate ABRO gets stuck in KIELER when
AandBarrive in an entry tick. Use the delayed version, or avoid terminating in the entry tick.
Next, Edges and pulses: turning a button’s level into the one tick in which it changes.