40df3067a4
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>
307 lines
10 KiB
Markdown
307 lines
10 KiB
Markdown
# 15 — E87: Carriers and load ports
|
|
|
|
← [14 E40 + E94 — Process and control jobs](14_e40_e94_jobs.md) | [Back to index](00_index.md) | Next: [16 E90 + E157 — Substrate and module tracking](16_e90_e157_substrates_modules.md) →
|
|
|
|
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?
|
|
|
|
```cpp
|
|
// 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?
|
|
|
|
```cpp
|
|
// 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?
|
|
|
|
```cpp
|
|
// 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`](../include/secsgem/gem/carrier_state.hpp)
|
|
composes the three; tests in
|
|
[`tests/test_carrier_state.cpp`](../tests/test_carrier_state.cpp)
|
|
(11 cases) and
|
|
[`tests/test_e87_wire_scenarios.cpp`](../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
|
|
|
|
```cpp
|
|
// 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
|
|
|
|
```cpp
|
|
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
|
|
|
|
```cpp
|
|
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`](../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**:
|
|
|
|
```cpp
|
|
// 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`](../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`](../tests/test_e87_wire_scenarios.cpp);
|
|
the happy path by
|
|
[`tests/test_carriers.cpp`](../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`](../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](36_persistence_validation_metrics.md) 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**.
|
|
|
|
Next: [→ 16 E90 + E157 — Substrate and module tracking](16_e90_e157_substrates_modules.md)
|