Handshakes
Request and acknowledge between concurrent regions, within one tick and across ticks, and the causality cycle that catches the classic four-phase handshake.
Two parts of a system that work together need a way to say “please do this” and “done”. A controller asks a motor driver to move; a client asks a server for data; a producer offers an item and a consumer takes it. The exchange is a handshake: a request in one direction, an acknowledgement in the other.
In SCCharts the two parts are usually concurrent regions, and a handshake between
them can happen in two ways: within a single tick, or spread over several. This page shows both, then
the four-phase handshake that hardware designers know, which KIELER rejects in its obvious form, and the
pre that fixes it.
A handshake within one tick
Section titled “A handshake within one tick”Here a client sends a request (the local signal req) when it is told to start, and waits for the
acknowledgement ack. The server acknowledges every request at once:
Diagram
Code
scchart SameTick { input bool start output int served = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle }
region Server { initial state Listening if req do ack go to Listening }}| Tick | input bool start | output int served | Active state of region Client | Active state of region Server |
|---|
Guided tour, 10 ticks:
- start = falseTick 1: the client is
Idle, the serverListening. - start = true
start: the client emitsreqand moves toWaiting. In the same tick the server seesreqand emitsack, and the client’s immediate transition seesack:servedis 1. A whole round trip in one tick. - start = falseIdle again.
- start = falseIdle.
- start = trueAnother request, another round trip in one tick: 2.
- start = falseIdle.
- start = falseIdle.
- start = falseIdle.
- start = falseIdle.
- start = falseIdle.
In tick 2 everything happens at once: the client emits req and enters Waiting; the server sees
req and emits ack; and the client’s immediate transition out of Waiting sees ack and counts
the job. Regions are not run one after the other in the order you wrote them. KIELER orders the work
inside a tick by its data: every emission of a signal comes before every test of it, and each region keeps
its own order. Here that gives one consistent schedule:
- Client: emit
req, enterWaiting. - Server: test
req, emitack. - Client: test
ack,served++, back toIdle.
Note the immediate on the client’s transition. Without it, Waiting could not react in the tick it is
entered, and the acknowledgement of tick 2 would be missed: ack is a signal, present only in the tick
it is emitted. Here is the client without it:
Diagrams
SameTick
SameTickDelayed
Code
SameTick
scchart SameTick { input bool start output int served = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle }
region Server { initial state Listening if req do ack go to Listening }}SameTickDelayed
scchart SameTickDelayed { input bool start output int served = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting if ack do served++ go to Idle }
region Server { initial state Listening if req do ack go to Listening }}| Tick | input bool start | SameTick: output int served | SameTick: Active state of region Client | SameTick: Active state of region Server | SameTickDelayed: output int served | SameTickDelayed: Active state of region Client | SameTickDelayed: Active state of region Server |
|---|
Guided tour, 7 ticks, the same inputs for both charts:
- start = falseTick 1: both clients are
Idle. - start = true
start: in both charts the client emitsreqand entersWaiting, and the server answers withackin the same tick.SameTick’s immediate transition sees it and counts the job.SameTickDelayed’sWaitingwas entered in this tick, so its delayed transition is not checked yet. - start = false
ackis gone:SameTickDelayed’s client waits. - start = falseIt waits for an acknowledgement that will never come.
- start = true
startagain:SameTickserves it in this tick (servedis 2).SameTickDelayedignores it: its client is not inIdle. - start = false
SameTickDelayedis stuck inWaiting. - start = falseStuck:
servedstays 0.
immediate if ack sees it; the other waits for ever.A handshake across ticks
Section titled “A handshake across ticks”Real servers take time. This one goes to Working on a request and acknowledges two ticks later. The
client is unchanged:
Diagrams
SameTick
SlowServer
Code
SameTick
scchart SameTick { input bool start output int served = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle }
region Server { initial state Listening if req do ack go to Listening }}SlowServer
scchart SlowServer { input bool start output int served = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle }
region Server { initial state Ready if req go to Working
state Working if 2 true do ack go to Ready }}| Tick | input bool start | SameTick: output int served | SameTick: Active state of region Client | SameTick: Active state of region Server | SlowServer: output int served | SlowServer: Active state of region Client | SlowServer: Active state of region Server |
|---|
Guided tour, 10 ticks, the same inputs for both charts:
- start = falseTick 1: both clients are
Idle. - start = true
start: both clients emitreqand enterWaiting.SameTick’s server acknowledges in the same tick, and its client’simmediate if ackseesackat once:servedis 1.SlowServer’s server moves toWorkinginstead, and its client waits. - start = false
SlowServer’s server is working;SameTickis idle again. - start = false
SlowServer’s server finishes and emitsack. Its client is still inWaiting, andimmediate if ackis checked in every tick it waits, so it catchesackat once:servedis 1 in both charts now. - start = true
startagain:SameTickdoes the whole round trip in this tick (servedis 2);SlowServer’s server starts working. - start = falseWorking.
- start = false
ackfrom the slow server:servedis 2 in both. - start = falseIdle.
- start = falseIdle.
- start = falseIdle. The client is the same in both charts:
immediate if ackworks with either server.
The client does not need to know which kind of server it talks to. immediate if ack is checked in the
tick Waiting is entered and in every later tick, so it catches an acknowledgement in the same tick or
any number of ticks later. Writing the waiting transition this way makes the client work with both.
Because req and ack are signals, each lasts one tick: they are events. The server must notice
the request in the tick it is made, and here it does. Ready tests req in every tick except the one
in which it is entered, and in that tick the client, which has just been served, cannot ask yet. If the
server could be busy when a request arrives, the request would be lost; the latch
pattern is how you keep it.
The four-phase handshake
Section titled “The four-phase handshake”Hardware often uses levels instead of events: the client raises req and keeps it up until it sees
ack; the server raises ack and keeps it up until it sees req drop. That is four phases: req up,
ack up, req down, ack down. Each side owns one bool and watches the other’s. Written directly,
it looks like this, and KIELER rejects it:
Diagram
Code
scchart FourPhaseCycle { input bool start output bool req, ack
region Client { initial state Idle if start do req = true go to Waiting
state Waiting if ack do req = false go to Releasing
state Releasing if !ack go to Idle }
region Server { initial state Ready if req do ack = true go to Busy
state Busy if !req do ack = false go to Ready }}KIELER rejects this chart
- Line 18, column 10
reqandackdepend on each other within one tick: each waits for the otherKIELER says:The SCG is NOT asc-schedulable!
reqis set on line 10 and read on line 18;reqis set on line 10 and read on line 21;ackis set on line 18 and read on line 10. Within a tick every write of a variable, and every emission of a signal, comes before it is read. Break the cycle withpre(req)(last tick's value), by making one side wait a tick, or by moving the logic into one region.About this message
The messages are Can't schedule from … (many times) and The SCG is NOT asc-schedulable!; SCCharts Lab
adds (the two names may come in either order):
Circular dependency involving ack, req prevents scheduling this tick.The reason is the order rule from above, applied to both directions at once:
- The client’s transition out of
Waitingreadsackand then writesreq. - The server’s transitions read
reqand then writeack. - Within a tick, every write of a variable must come before every read of it in another region.
So the server’s write of ack must come before the client reads ack; the client reads ack before it
writes req; that write must come before the server reads req; and the server reads req before it
writes ack. Each step must happen before the next, and the chain leads back to where it started. No
order satisfies it, so KIELER refuses the chart. It decides this from the reads and writes alone,
without asking which states are active or which triggers are true: if the accesses could form such a
cycle within one tick, the chart is rejected.
Breaking the cycle with pre
Section titled “Breaking the cycle with pre”The cycle needs both sides to react to each other in the same tick. Let one side react to the other’s
value from the previous tick, and the cycle is gone. pre(req) is req at the end of the last tick, so
it does not depend on anything written in this tick:
Diagram
Code
scchart FourPhase { input bool start output bool req, ack
region Client { initial state Idle if start do req = true go to Waiting
state Waiting if ack do req = false go to Releasing
state Releasing if !ack go to Idle }
region Server { initial state Ready if pre(req) do ack = true go to Busy
state Busy if !pre(req) do ack = false go to Ready }}| Tick | input bool start | output bool req | output bool ack | Active state of region Client | Active state of region Server |
|---|
Guided tour, 11 ticks:
- start = falseTick 1: idle, both flags down.
- start = true
start: the client raisesreqand waits. - start = falseThe server sees
pre(req), the request of the last tick, and raisesack. The client seesackin the same tick (the server writes it before the client reads it) and lowersreq. - start = falseThe server sees that
reqwas lowered (pre(req)is false) and lowersack; the client seesackdown and is idle again. Three ticks for the complete handshake. - start = falseIdle.
- start = falseIdle.
- start = falseIdle.
- start = trueA new handshake.
- start = false
ackup,reqdown. - start = false
ackdown. - start = falseIdle.
req up in tick 2, ack up and req down in tick 3, ack down in tick 4.With start in tick 2, tick by tick (the states are those after the tick):
| Tick | req |
ack |
Client | Server |
|---|---|---|---|---|
| 1 | false | false | Idle | Ready |
| 2 | true | false | Waiting | Ready |
| 3 | false | true | Releasing | Busy |
| 4 | false | false | Idle | Ready |
In tick 3 the server reacts to the request raised in tick 2, and the client, which reads ack directly,
reacts to the server in the same tick. In tick 4 the server sees that req dropped in tick 3. Each pre
costs a tick, so the handshake is a little slower. In return it is accepted, and the timing is clear:
information flows one way within a tick, and the way back crosses a tick boundary.
You can put the pre on either side; the exercise below puts it on the client. What you cannot do is
have both sides read each other’s current value and write their own in response.
Try it
Section titled “Try it”Break the cycle on the other side
The starter is the rejected four-phase handshake. Make KIELER accept it by changing only the client,
not the server. Then step through your version with start in tick 2: in which ticks do req and
ack go up and down now, compared with FourPhase?
Show the starter chart
scchart FourPhaseClient { input bool start output bool req, ack
region Client { initial state Idle if start do req = true go to Waiting
state Waiting if ack do req = false go to Releasing
state Releasing if !ack go to Idle }
region Server { initial state Ready if req do ack = true go to Busy
state Busy if !req do ack = false go to Ready }}Show a solution
scchart FourPhaseClient { input bool start output bool req, ack
region Client { initial state Idle if start do req = true go to Waiting
state Waiting if pre(ack) do req = false go to Releasing
state Releasing if !pre(ack) go to Idle }
region Server { initial state Ready if req do ack = true go to Busy
state Busy if !req do ack = false go to Ready }}Give up after a while
This server ignores requests while it is not online, and the starter’s client then waits for ever.
Make the client give up when no acknowledgement has come three ticks after the request: count the failure
in failed and go back to Idle, so that the next start works again. To test it: with online true,
start in tick 2 is served in tick 4. Switch online off in tick 5 and give start in tick 6: the
starter’s client waits for ever, and ignores a new start (tick 12) even after online is back (tick
11). Your client should give up in tick 9 (failed is 1), and the start in tick 12 should be served
in tick 14.
Show the starter chart
scchart GiveUp { input bool start, online output int served = 0, failed = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle }
region Server { initial state Ready if req && online go to Working
state Working if 2 true do ack go to Ready }}Show a solution
scchart GiveUp { input bool start, online output int served = 0, failed = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle if 3 true do failed++ go to Idle }
region Server { initial state Ready if req && online go to Working
state Working if 2 true do ack go to Ready }}Check your understanding
Section titled “Check your understanding”Diagram
Code
scchart SlowServer { input bool start output int served = 0 signal req, ack
region Client { initial state Idle if start do req go to Waiting
state Waiting immediate if ack do served++ go to Idle }
region Server { initial state Ready if req go to Working
state Working if 2 true do ack go to Ready }}The slow server, with start held from tick 2 on. served is a number: what is it after ticks 4, 6 and 10?
Inputs per tick. A value in grey was not set in that tick: it holds from an earlier one.
| Tick | start |
|---|---|
| 1 | false |
| 2 | true |
| 3 | true |
| 4 | true |
| 5 | true |
| 6 | true |
| 7 | true |
| 8 | true |
| 9 | true |
| 10 | true |
Explanation
served is 1 after tick 4, still 1 after tick 6, and 3 after tick 10. The request of tick 2 is
acknowledged two ticks later, in tick 4. start is still held, so the client, back in Idle, requests
again in tick 5 and is served in tick 7, and the third request is served in tick 10: one handshake
every three ticks.
Show the answer
Answer D: Nothing: served counts the same
The slow server acknowledges two ticks after the request, never in the tick Waiting is entered, which
is the only tick a delayed transition skips. KIELER’s run of the delayed client is the same as
SlowServer’s, tick for tick: served becomes 1 in tick 4 and 2 in tick 7. The immediate matters
for a server that may answer in the same tick, as in SameTick; with it, the client works with both.
Show the answer
Answer B: No: the reads and writes form a cycle
KIELER’s check is static: it looks at which variables each region may read and write within a tick, not
at which states can be active together. A reads y and then writes x; B reads x and then writes y.
Every write must precede the other region’s reads, so the reads and writes form a cycle, and the chart is
rejected. Reading pre(x) or pre(y) on one side breaks it.
Key ideas
- A handshake is a request and an acknowledgement between two parts, usually concurrent regions.
- Within one tick, KIELER orders every write (or emission) before every read in other regions, so a request can be answered and the answer used in the same tick.
immediate if ackwaits for an acknowledgement that may come in the same tick or later.- If both sides read the other’s current value and write their own in response, the accesses form a causality cycle, and KIELER rejects the chart, whatever the states and triggers.
- Break the cycle with
preon one side: the way back then crosses a tick boundary.
Next, Watchdogs: noticing that something has stopped happening.