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>
10 KiB
15 — E87: Carriers and load ports
← 14 E40 + E94 — Process and control jobs | Back to index | Next: 16 E90 + E157 — Substrate and module tracking →
Before a wafer can be processed it has to physically arrive at the tool, dock with the load port, expose its slot contents, get verified, and be authorised by the host. That's six distinct state transitions, each one tracked by SEMI E87 — Carrier Management (2000).
This chapter covers:
- The carrier (FOUP) and the load port — what each is and what state they each track.
- The three carrier state sub-machines: ID, Slot Map, Access.
- The three load-port state sub-machines: Transfer, Reservation, Association.
- The E87 message catalog (S3F*).
- Where each piece is in code.
Vocabulary
- Carrier — a physical container holding wafers. In a 300 mm fab almost always a FOUP (Front-Opening Universal Pod, 25 slots). In smaller fabs sometimes a SMIF pod or a cassette.
- Load port — the equipment-side dock where carriers physically attach. A typical PVD tool has 2 load ports (1 input, 1 output); a lithography stepper might have 4.
- Slot map — the equipment's reading of which of the carrier's N slots contain wafers, expressed as N bytes. Slot states are Empty, CorrectlyOccupied, DoubleSlotted, CrossSlotted, NotRead.
- Carrier ID — bar-coded string on the FOUP (e.g. "C-31415"). Read by the equipment's bar-code reader on docking.
The three carrier state sub-machines
E87 doesn't track "carrier state" as one variable — it tracks three orthogonal aspects:
1. ID Status
How sure are we who this carrier is?
// include/secsgem/gem/carrier_state.hpp:23
enum class CarrierIDStatus : uint8_t {
NotConfirmed = 0,
WaitingForHost = 1,
Confirmed = 2,
IDVerificationFailed = 3,
};
Bar-code reader fires ID_READ_OK → NotConfirmed → Confirmed.
If the reader fails or returns gibberish, ID_READ_FAIL →
NotConfirmed → IDVerificationFailed. Some hosts insist on
verifying the ID themselves (look it up against their LMS); they
hold the carrier in WaitingForHost until they reply with
HOST_ID_CONFIRMED.
2. Slot Map Status
Have we read the carrier's contents?
// include/secsgem/gem/carrier_state.hpp:45
enum class SlotMapStatus : uint8_t {
NotRead = 0,
WaitingForHost = 1,
Read = 2,
SlotMapVerificationFailed = 3,
};
The mapper (an optical sensor reading wafer positions) runs after
ID confirmation. Result: a 25-byte vector (one byte per slot).
Hosts can validate the map against expectation (S3F19 Slot Map
Verify); a mismatch flips to SlotMapVerificationFailed.
3. Access Status
Is the carrier authorised for processing right now?
// include/secsgem/gem/carrier_state.hpp:64
enum class CarrierAccessStatus : uint8_t {
NotAccessed = 0,
InAccess = 1,
CarrierComplete = 2,
CarrierStopped = 3,
};
InAccess means the equipment is currently using slots from this
carrier. CarrierComplete means done; awaiting unloading.
All three sub-machines progress independently. A carrier can
be Confirmed (ID) + Read (Map) + NotAccessed (Access) — and
that's the typical state after docking but before the host
authorises processing.
CarrierStateMachine in
include/secsgem/gem/carrier_state.hpp
composes the three; tests in
tests/test_carrier_state.cpp
(11 cases) and
tests/test_e87_wire_scenarios.cpp
(4 wire scenarios).
The three load-port state sub-machines
The load port has its own three sub-machines:
1. Transfer State
// include/secsgem/gem/load_port_state.hpp:19
enum class LoadPortTransferState : uint8_t {
OutOfService = 0,
ReadyToLoad = 1,
ReadyToUnload = 2,
InService = 3,
};
Physical readiness — is the port mechanically ready to dock a new FOUP, release the current one, or busy?
2. Reservation Status
enum class LoadPortReservationStatus : uint8_t {
NotReserved = 0,
Reserved = 1,
};
Has the host pre-reserved this port for an inbound carrier? Reservation is how the host tells the AMHS "send the carrier to this specific port."
3. Association Status
enum class LoadPortAssociationStatus : uint8_t {
NotAssociated = 0,
Associated = 1,
};
Is there a known Carrier object linked to this port? Becomes
Associated when a carrier docks and the ID is read.
LoadPortStateMachine in
include/secsgem/gem/load_port_state.hpp.
Multi-port + multi-carrier
A real fab tool runs multiple load ports in parallel. A 4-port cluster tool can have:
- Port 1: carrier A in
Associated+InAccess(processing) - Port 2: carrier B in
Associated+CarrierComplete(done, awaiting unload) - Port 3: AMHS robot docking carrier C (
InService) - Port 4:
OutOfService(mechanical fault)
The codebase models this as a per-port store:
// include/secsgem/gem/store/carriers.hpp
class CarrierStore {
// one CarrierStateMachine per Carrier ID
// one LoadPortStateMachine per PortID
};
Tests for the parallel scenario:
tests/test_e87_wire_scenarios.cpp
(4 multi-port scenarios — independence between ports asserted).
The E87 messages
| S/F | Direction | Purpose |
|---|---|---|
| S3F17 | H → E | CarrierAction. Body: CARRIERACTION + CARRIERID. |
| S3F18 | E → H | CAACK reply. |
| S3F19 | H → E | Slot Map Verify. Body: CARRIERID + expected slot states. |
| S3F20 | E → H | SMACK reply. |
| S3F25 | H → E | Carrier Transfer. Move a carrier between ports. |
| S3F26 | E → H | CAACK reply. |
| S3F27 | H → E | Cancel Carrier. Pre-arrival cancellation. |
| S3F28 | E → H | CAACK reply. |
CARRIERACTION strings (S3F17 body)
The dominant E87 messages are S3F17 carrier-action commands. CARRIERACTION is an ASCII string from a fixed E87 set:
| String | Meaning |
|---|---|
ProceedWithCarrier |
Authorise processing. Triggers Access → InAccess. |
CancelCarrier |
Refuse the carrier; equipment doesn't process it. |
CancelCarrierAtPort |
Same but specifies port. |
BypassCarrier |
Process nothing from this carrier (audit slot map only). |
CarrierOut |
Mark CarrierComplete; AMHS will retrieve. |
CarrierReCID |
Re-read the carrier ID (e.g. bar code was iffy). |
CAACK reply codes (S3F18, 1 byte):
| Code | Meaning |
|---|---|
| 0 | Acknowledged. |
| 1 | Invalid command. |
| 2 | Cannot perform now. |
| 3 | Invalid carrier ID. |
| 4 | Invalid port ID. |
| 5 | Carrier ID unknown. |
A typical carrier flow
1. AMHS docks FOUP at port 1.
2. Equipment fires CarrierArrived event (CEID per equipment.yaml)
→ S6F11 (host gets pinged).
3. Equipment reads bar code → CarrierIDStatus: NotConfirmed → Confirmed.
4. Equipment runs slot mapper → SlotMapStatus: NotRead → Read.
5. (Optional) Host sends S3F19 SlotMapVerify with expected contents
→ equipment compares → SMACK = 0 (match) or 1 (mismatch).
6. Host sends S3F17 CARRIERACTION = "ProceedWithCarrier"
→ CarrierAccessStatus: NotAccessed → InAccess.
7. Processing happens. Substrate state changes are tracked by E90
(chapter 16).
8. All substrates done. Equipment fires CarrierComplete event.
9. Host sends S3F17 CARRIERACTION = "CarrierOut"
→ CarrierAccessStatus → CarrierComplete.
10. AMHS retrieves FOUP from port 1. LoadPortAssociation goes back
to NotAssociated; carrier object can be deleted.
The slot-map-mismatch path in step 5 is tested by
tests/test_e87_wire_scenarios.cpp;
the happy path by
tests/test_carriers.cpp (6 cases).
Slot maps in detail
A slot map is a byte-vector with one byte per carrier slot. For a 25-slot FOUP, that's 25 bytes. Byte values:
| Value | State | Meaning |
|---|---|---|
| 0 | Empty |
No wafer detected. |
| 1 | CorrectlyOccupied |
One wafer in the correct vertical position. |
| 2 | DoubleSlotted |
Two wafers in one slot — physical fault. |
| 3 | CrossSlotted |
Wafer at an angle / wrong height. |
| 4 | NotRead |
Sensor couldn't read this slot. |
S3F19 (Slot Map Verify) carries the host's expected slot map. The equipment compares against its read map; if they match, SMACK = 0. Mismatches imply someone interfered with the carrier (or the mapper is broken).
The map is part of the carrier store and persists across restarts.
Persistence
Like all GEM-300 stores, the carrier store is persistent. A file per active carrier + a file per port lets the equipment recover its full E87 state after a restart — including in-progress carriers stranded mid-Access by a power loss.
Tested by
tests/test_carrier_persistence.cpp
(6 cases — write, restart, replay, corrupted-file drop, removal).
Per-store journal pattern is the same across E40, E87, E90, E94, E116; chapter 36 walks the mechanism.
Where to go next
You now know how a carrier arrives, gets authorised, and leaves. But every wafer inside the carrier needs its own tracking — and when wafers move into a process module, their state has to follow them. That's E90 and E157.