Files
secs-gem/docs/18_e84_parallel_io.md
raphael 40df3067a4 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>
2026-06-09 20:14:42 +02:00

10 KiB

18 — E84: Parallel I/O handoff

17 E116 + E120 + E39 — Performance, CEM, objects | Back to index | Next: 19 E42 + E148 + S9 — Misc

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:

// 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. Stored in E84SignalSet as a 10-bit bitmap.


The handshake state machine

// 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 (6 cases — every happy-path transition); 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.

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:

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 (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 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 wraps the FSM with real asio::steady_timers. 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 (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