# 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)