docs: chapters 14–19 — GEM 300 standards (Part 2 complete)

Six more chapters finishing Part 2.  Together with chapters 10–13
they document every SEMI standard this codebase implements.

14 — E40 + E94: process jobs (8-state lifecycle, S16F11/F5/F7/F9
on the wire) and control jobs (CJ wraps PJs with batch policy,
S14F9/S16F27 messages).  Worked cascade showing how CJSTART
propagates through the PJ FSM and triggers S6F11 CEIDs at each
transition.

15 — E87 carriers: three orthogonal sub-machines (CarrierID,
SlotMap, CarrierAccess) per carrier and three more (Transfer,
Reservation, Association) per load port.  S3F17 CarrierAction
strings + CAACK codes, S3F19 SlotMap verify, the 5-state slot
encoding, multi-port concurrency.

16 — E90 + E157: substrate tracking via three orthogonal axes
(STS / SPS / SubstrateIDStatus) and module process tracking
(NotExecuting / GeneralExecuting / StepExecuting / StepCompleted).
End-to-end PVD example showing E40 + E157 + E90 transitions
cascading into CEIDs.

17 — E116 + E120 + E39: equipment performance time-buckets across
six states, common equipment model object hierarchy, S14F1/F3
GetAttr/SetAttr as the uniform wire access for any object type
across multiple standards.

18 — E84 parallel I/O: ten signal lines, the 9-state handshake
FSM, the three TA1/TA2/TA3 timing-critical timers, why a physical
handshake gets modeled in software (testability, timer enforcement,
CEID emission, multi-port concurrency), the pure-FSM + asio-adapter
split.

19 — E42 + E148 + S5F9–F18: formatted recipes (S7F23/F25 typed
PPBODY), time synchronization with 16-char + 14-char accepted on
set, exception recovery as a persistent multi-step host-supervised
FSM (Posted → Recovering → Cleared with abort/retry).  Revisits
the auto-S9 family and contrasts S9 (transport) vs S5F9
(application).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 20:14:42 +02:00
parent 858ca22975
commit 40df3067a4
6 changed files with 1587 additions and 0 deletions
+280
View File
@@ -0,0 +1,280 @@
# 18 — E84: Parallel I/O handoff
← [17 E116 + E120 + E39 — Performance, CEM, objects](17_e116_e120_e39_objects.md) | [Back to index](00_index.md) | Next: [19 E42 + E148 + S9 — Misc](19_e42_e148_s9_misc.md) →
E84 is unusual in the GEM 300 suite: it's the only standard that's
**not SECS at all**. Not a wire format, not a message catalog —
ten *physical wires* between the AMHS robot and the load port,
asserted at CMOS voltage levels with strict timing.
Why? Because dropping a $20 000 FOUP is catastrophic, and you
can't afford to coordinate the kinematics over TCP — too much
latency, too many failure modes. The handshake has to be
deterministic in hardware.
This chapter:
- The ten signal lines and what each one means.
- The handshake state machine.
- The three timing-critical timers (TA1, TA2, TA3).
- How the codebase models a physical-layer handshake as software
(and why it does).
---
## The ten signals
Each signal is one **single-bit boolean** asserted on a physical
wire. Four go from the equipment to the AMHS; six go from the
AMHS to the equipment:
```cpp
// include/secsgem/gem/e84_state.hpp:28
enum class E84Signal : uint8_t {
CS_0 = 0, // AMHS -> equip: carrier stage select 0
CS_1 = 1, // AMHS -> equip: carrier stage select 1
VALID = 2, // AMHS -> equip: handshake start
TR_REQ = 3, // AMHS -> equip: transfer request
BUSY = 4, // AMHS -> equip: transfer in progress
COMPT = 5, // AMHS -> equip: transfer complete
L_REQ = 6, // equip -> AMHS: load request (port ready to receive)
U_REQ = 7, // equip -> AMHS: unload request (port ready to release)
READY = 8, // equip -> AMHS: ready
ES = 9, // either: emergency stop
};
```
- **CS_0 + CS_1**: two bits encoding which port the AMHS is
addressing (CS = Carrier Select). Tools with up to 4 ports can
be indexed by two bits.
- **VALID**: the AMHS asserts this when CS bits are stable —
"you can read me now."
- **TR_REQ**: AMHS is requesting a transfer.
- **BUSY**: AMHS is actively moving the carrier. Goes high when
the robot starts lowering / lifting.
- **COMPT**: AMHS has finished the kinematic operation.
- **L_REQ**: equipment is ready to *receive* a carrier.
- **U_REQ**: equipment is ready to *release* a carrier.
- **READY**: equipment kinematic interlocks are satisfied.
- **ES**: Emergency Stop. Either side can assert. If asserted,
every state machine on both sides goes to a safe state.
Defined in
[`include/secsgem/gem/e84_state.hpp`](../include/secsgem/gem/e84_state.hpp).
Stored in `E84SignalSet` as a 10-bit bitmap.
---
## The handshake state machine
```cpp
// include/secsgem/gem/e84_state.hpp:63
enum class E84State : uint8_t {
Idle = 0, // no signals
CarrierPresent = 1, // CS asserted; no VALID yet
ValidAsserted = 2, // CS + VALID; equipment hasn't ack'd
LoadReady = 3, // VALID + L_REQ; port ready to receive
UnloadReady = 4, // VALID + U_REQ; port ready to release
Transferring = 5, // BUSY asserted; transfer happening
Complete = 6, // COMPT asserted; AMHS done
EmergencyStop = 7, // ES asserted
HandoffFault = 8, // a timer expired
};
```
The happy path for an **inbound load**:
```
Idle ─CS asserted─► CarrierPresent ─VALID asserted─► ValidAsserted
(equipment
decides: yes,
I can take it)
L_REQ asserted
LoadReady
TR_REQ asserted
BUSY asserted
Transferring
(robot lowers
carrier onto
port; takes a
few seconds)
BUSY de-asserted
COMPT asserted
Complete
(signals all
drop back; CS
de-asserted)
Idle
```
An **outbound unload** follows the same pattern but uses `U_REQ`
instead of `L_REQ`, and ends with the carrier moving *off* the
port.
The FSM is **event-driven**: every transition is triggered by one
signal change, not by a clock tick. `E84StateMachine::on_signal_change()`
re-evaluates the bitmap and emits a state transition if one is due.
Tests:
[`tests/test_e84.cpp`](../tests/test_e84.cpp) (6 cases — every
happy-path transition);
[`tests/test_e84_ports.cpp`](../tests/test_e84_ports.cpp) (5 cases
— per-port store).
---
## The three TA timers
These are why E84 matters more than "the AMHS lifts the carrier."
Without timer enforcement, a stuck signal could leave the
mechanical handoff frozen mid-motion — the robot holding the
carrier, neither side noticing the other has gone quiet.
```cpp
struct E84Timeouts {
std::chrono::milliseconds ta1{0};
std::chrono::milliseconds ta2{0};
std::chrono::milliseconds ta3{0};
};
```
(Spec defaults are 2 s / 2 s / 60 s; tool builders tune per
port.)
### TA1
Armed: on entering `ValidAsserted` (AMHS asserted VALID).
Cancelled: on entering `LoadReady` or `UnloadReady` (equipment
asserted L_REQ or U_REQ).
Bounds: **how long may the equipment take to respond to VALID?**
If TA1 expires the AMHS doesn't know whether the equipment is busy,
broken, or asleep — fault.
### TA2
Armed: on entering `LoadReady` or `UnloadReady`.
Cancelled: on entering `Transferring` (AMHS asserted BUSY).
Bounds: **how long may the AMHS take to start moving once the
port is ready?** Prevents the equipment holding its port idle
forever waiting for an AMHS that's stuck.
### TA3
Armed: on entering `Transferring`.
Cancelled: on entering `Complete`.
Bounds: **how long may the actual transfer take?** If the robot
freezes mid-motion, TA3 catches it.
### What happens on timeout
The FSM transitions to `HandoffFault` with the relevant
`E84Fault` reason:
```cpp
enum class E84Fault : uint8_t {
None = 0,
TA1Expired = 1,
TA2Expired = 2,
TA3Expired = 3,
};
```
The equipment fires an alarm (configurable ALID per port), the
EAP brings up the operator panel, and someone has to physically
inspect.
Tested by
[`tests/test_e84_timers.cpp`](../tests/test_e84_timers.cpp) (12
cases — every timer armed/cancelled/expired path).
---
## Why model a physical handshake in software
The wires are real. The signals are CMOS-level on opto-isolated
24 V lines. But the software needs to:
1. **Test the protocol logic without a real load port.** Spinning
up actual hardware for unit tests is impossible.
2. **Drive the timer enforcement.** Even if the wires are
physical, the timers TA1/TA2/TA3 are wall-clock and need a
software clock to track.
3. **Emit CEIDs alongside transitions.** When the port goes
`Transferring`, the equipment also wants to fire `CarrierIn`
over SECS-II — the same way E40/E87/E90 transitions do.
4. **Model multi-port concurrency.** A 4-port tool has four
independent E84 FSMs running in parallel; they have to be
modeled distinctly.
The codebase ships **two implementations**:
### Pure FSM (testable)
[`E84StateMachine`](../include/secsgem/gem/e84_state.hpp) is the
IO-free FSM. Inputs: signal change events. Outputs: state
transitions + timer arm/cancel requests. No wall clock.
This is what tests drive — they feed signal events in, expect
transitions out, and synthetically expire timers.
### asio adapter (production)
[`E84AsioTimers`](../include/secsgem/gem/e84_asio_timers.hpp)
wraps the FSM with real `asio::steady_timer`s. When the FSM
requests `arm(TA1, 2s)`, the adapter schedules a wall-clock timer;
when 2 s pass and nothing's cancelled it, the adapter feeds the
expiry event back into the FSM.
This is what runs in production — connected to a GPIO driver
that pulses the actual wires.
Tested by
[`tests/test_e84_asio_timers.cpp`](../tests/test_e84_asio_timers.cpp)
(4 cases — every timer fires on real wall clock).
---
## How E84 connects to the rest of GEM
E84 itself only manages the physical handoff. Once a carrier is
docked, *SECS messages* take over:
1. E84 reaches `Complete` → equipment fires CEID `CarrierArrived`
(configured in `data/equipment.yaml`).
2. CEID `CarrierArrived` fires → S6F11 (host informed).
3. Host sees S6F11 → looks up carrier ID → optionally sends
S3F19 SlotMapVerify (E87).
4. Host sends S3F17 `ProceedWithCarrier` → CarrierAccess goes
InAccess (E87).
5. Processing happens (E40 + E90 + E157).
6. All wafers done → equipment fires CEID `CarrierComplete`.
7. Host sends S3F17 `CarrierOut`.
8. AMHS comes back; E84 runs in reverse to unload.
E84 is the **bookend** at both ends of the carrier flow. Without
it, the carrier never docks and never undocks; without the SECS
messages after step 1, nothing knows the carrier arrived.
---
## Where to go next
You now know every state-machine-bearing standard in the GEM 300
suite. One more chapter wraps up the remaining narrow ones —
formatted process programs, distributed time sync, and the
exception recovery streams.
Next: [→ 19 E42 + E148 + S9 — Misc](19_e42_e148_s9_misc.md)