8a55137e57
Interface cleanup so the report_* family matches the typo-safe ethos of eq.names instead of leaking raw protobuf errors on a misspelled value. - Milestone / ModuleState / JobState: importable str-enums (member == its wire name, so plain strings still work) — autocomplete + a typo-checked happy path. The clean rule: equipment-specific *names* live on eq.names; fixed protocol *value-sets* are enums. - _enum_value(): resolves an enum-or-string arg client-side and, on a bad value, raises ValueError with a close-match hint *before* the wire. Wired into report_job / report_substrate / report_module / request_control_state (all previously raised a raw protobuf ValueError). - Equipment is now a context manager (with Equipment(...) as eq: ...). - examples/wafer_tool.py: a cluster tool tracking one wafer through one module end-to-end (E90 + E157), showing the enums + context manager. - tests/test_enums.py: asserts the enums stay in lockstep with the proto and that the typo path is helpful. Wired into run_interop.sh (pyclient step). - Interop drives both the enum and string forms on the wire + the ValueError typo path. Docs (ch16/ch42) updated; names-vs-enums rule documented. All Python unit tests + 25 pyclient interop checks pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
246 lines
8.1 KiB
Markdown
246 lines
8.1 KiB
Markdown
# 16 — E90 + E157: Substrate and module tracking
|
||
|
||
← [15 E87 — Carriers and load ports](15_e87_carriers.md) | [Back to index](00_index.md) | Next: [17 E116 + E120 + E39 — Performance, CEM, objects](17_e116_e120_e39_objects.md) →
|
||
|
||
E87 (chapter 15) tracks the **container**. E90 and E157 track
|
||
what's *inside* the container — every individual wafer (substrate)
|
||
and every process module the wafer passes through.
|
||
|
||
This chapter is shorter than the others in Part 2 because the
|
||
ideas overlap E87 (the same three-orthogonal-axes pattern repeats)
|
||
and E40 (state events drive S6F11 CEIDs).
|
||
|
||
---
|
||
|
||
## E90 — Substrate tracking
|
||
|
||
### What it tracks
|
||
|
||
One state-bearing record **per wafer**, identified by a substrate
|
||
ID (an ASCII string, often the laser-etched serial number).
|
||
|
||
E90 has **three orthogonal axes** — the same pattern as E87's
|
||
carrier (chapter 15 §3):
|
||
|
||
#### 1. Substrate State (STS) — location
|
||
|
||
```cpp
|
||
// include/secsgem/gem/substrate_state.hpp:26
|
||
enum class SubstrateState : uint8_t {
|
||
AtSource = 0, // in its origin carrier slot
|
||
AtWork = 1, // in-process at a module
|
||
AtDestination = 2, // delivered to final location
|
||
NoState = 255,
|
||
};
|
||
```
|
||
|
||
Events: `Acquire` (Source→Work), `Release` (Work→Destination),
|
||
`Return` (Work→Source, for unprocessed return).
|
||
|
||
#### 2. Substrate Processing State (SPS) — lifecycle
|
||
|
||
```cpp
|
||
enum class SubstrateProcessingState : uint8_t {
|
||
NeedsProcessing = 0,
|
||
InProcess = 1,
|
||
Processed = 2,
|
||
Aborted = 3,
|
||
Stopped = 4,
|
||
Rejected = 5,
|
||
Lost = 6,
|
||
Skipped = 7,
|
||
NoState = 255,
|
||
};
|
||
```
|
||
|
||
Events: `StartProcessing`, `EndProcessing`, `Abort`, `Stop`,
|
||
`Reject`, `ReportLost`, `Skip`.
|
||
|
||
#### 3. Substrate ID Status — identity confidence
|
||
|
||
```cpp
|
||
enum class SubstrateIDStatus : uint8_t {
|
||
NotConfirmed = 0,
|
||
WaitingForHost = 1,
|
||
Confirmed = 2,
|
||
Mismatched = 3,
|
||
NoState = 255,
|
||
};
|
||
```
|
||
|
||
Mirrors the `CarrierIDStatus` pattern from E87 — same problem
|
||
(equipment reads ID, host may need to verify), same shape of
|
||
solution.
|
||
|
||
### Why three axes?
|
||
|
||
The same reason E87 has three: these aspects evolve **at different
|
||
times** and **for different reasons**.
|
||
|
||
- A wafer can be `AtWork` + `NeedsProcessing` (just arrived,
|
||
recipe hasn't started).
|
||
- A wafer can be `AtWork` + `InProcess` (recipe running).
|
||
- A wafer can be `AtSource` + `Processed` + `Confirmed` (back in
|
||
its carrier slot after processing — typical end state).
|
||
|
||
Putting all three in one enum would multiply to ~30 valid
|
||
combinations. Three independent FSMs with ~3 events each is much
|
||
cleaner.
|
||
|
||
### Code
|
||
|
||
State machines:
|
||
[`include/secsgem/gem/substrate_state.hpp`](../include/secsgem/gem/substrate_state.hpp)
|
||
defines `SubstrateStateMachine`, which composes the three.
|
||
|
||
Store:
|
||
[`include/secsgem/gem/store/substrates.hpp`](../include/secsgem/gem/store/substrates.hpp)
|
||
holds one record per substrate ID, with a Location string the
|
||
application updates as the wafer moves.
|
||
|
||
Tests:
|
||
[`tests/test_substrates.cpp`](../tests/test_substrates.cpp) (14
|
||
cases — every axis, every event); persistence in
|
||
[`tests/test_substrate_persistence.cpp`](../tests/test_substrate_persistence.cpp)
|
||
(7 cases).
|
||
|
||
CEID-on-wire emission ("Substrate StartProcessing fires the
|
||
configured SubstrateInProcess CEID") is verified by
|
||
[`tests/test_wire_ceid_emission.cpp`](../tests/test_wire_ceid_emission.cpp).
|
||
|
||
### Wire interaction
|
||
|
||
E90 doesn't define its own S/F messages — substrate state changes
|
||
fire as **CEIDs** that the host has subscribed to via the standard
|
||
E30 §6.6 Dynamic Event Report Configuration (chapter 13). So:
|
||
|
||
- Equipment fires `Acquire` event on substrate `W-2026-06-09-A47`.
|
||
- `SubstrateStateMachine` transitions Source → Work.
|
||
- The state-change handler looks up the configured CEID for
|
||
"SubstrateInProcess" (from `data/equipment.yaml`).
|
||
- That CEID fires → `compose_reports_for(ceid)` → `S6F11`.
|
||
|
||
Host gets one S6F11 per wafer transition. In a 25-wafer FOUP
|
||
that's 25–50 events per processing pass. Persistent reports +
|
||
spool (chapter 13 Additionals) handle the burst.
|
||
|
||
---
|
||
|
||
## E157 — Module Process Tracking
|
||
|
||
### What it tracks
|
||
|
||
One state-bearing record **per process module**. A cluster tool
|
||
has multiple modules (Chamber A, Chamber B, Pre-clean, …); each
|
||
runs its own recipe step in parallel or sequence. E157 lets the
|
||
host see *which module is in which step of which recipe right
|
||
now*.
|
||
|
||
### The states
|
||
|
||
```cpp
|
||
// include/secsgem/gem/module_state.hpp:20
|
||
enum class ModuleState : uint8_t {
|
||
NotExecuting = 0,
|
||
GeneralExecuting = 1, // setup, pre-process, post-process
|
||
StepExecuting = 2, // actively running a recipe step
|
||
StepCompleted = 3,
|
||
NoState = 255,
|
||
};
|
||
```
|
||
|
||
Events: `StartGeneral`, `StartStep`, `CompleteStep`, `Reset`,
|
||
`Abort`.
|
||
|
||
Notice this is a much simpler FSM than E90 — one axis only. That's
|
||
because modules are more deterministic than substrates: a module
|
||
is either running a step or it isn't; substrates can be in many
|
||
overlapping conditions.
|
||
|
||
### Code
|
||
|
||
[`include/secsgem/gem/module_state.hpp`](../include/secsgem/gem/module_state.hpp)
|
||
defines `ModuleStateMachine`.
|
||
|
||
Store:
|
||
[`include/secsgem/gem/store/modules.hpp`](../include/secsgem/gem/store/modules.hpp).
|
||
|
||
Tests:
|
||
[`tests/test_modules.cpp`](../tests/test_modules.cpp) (5 cases).
|
||
|
||
### How E157 plays with E40 and E90
|
||
|
||
Concrete example. A PVD tool with three modules (Chamber A, B,
|
||
C); host submits PJ for wafer W-1, recipe says "process at
|
||
Chamber B for 90 seconds":
|
||
|
||
```
|
||
1. PJ-1 transitions Queued → SettingUp → WaitingForStart → Processing.
|
||
(E40 FSM, chapter 14)
|
||
2. Equipment fires ModuleEvent::StartGeneral on Chamber B.
|
||
ModuleState: NotExecuting → GeneralExecuting.
|
||
(E157 FSM)
|
||
3. Equipment fires SubstrateEvent::Acquire on W-1.
|
||
Substrate STS: AtSource → AtWork.
|
||
(E90 FSM)
|
||
4. Recipe step begins. ModuleEvent::StartStep on Chamber B.
|
||
ModuleState: GeneralExecuting → StepExecuting.
|
||
Substrate SPS: NeedsProcessing → InProcess.
|
||
5. ...90 seconds pass...
|
||
6. Recipe step ends. ModuleEvent::CompleteStep on Chamber B.
|
||
ModuleState: StepExecuting → StepCompleted.
|
||
Substrate SPS: InProcess → Processed.
|
||
7. Substrate released. SubstrateEvent::Release on W-1.
|
||
Substrate STS: AtWork → AtDestination.
|
||
8. PJ-1: ProcessComplete.
|
||
```
|
||
|
||
Each of the eight transitions fires a CEID, which fires an S6F11
|
||
event report. The host sees the **complete trace** of where every
|
||
wafer was at every moment.
|
||
|
||
---
|
||
|
||
## Daemon path (Python client)
|
||
|
||
If your tool uses the daemon (`secs_gemd`) and the Python client, the
|
||
E90 and E157 RPCs are wrapped as two methods:
|
||
|
||
```python
|
||
from secsgem_client import Equipment, Milestone, ModuleState
|
||
eq = Equipment("localhost:50051")
|
||
|
||
# E90 — substrate journey (daemon drives FSMs, fires CEIDs automatically)
|
||
eq.report_substrate("WFR-001", Milestone.ARRIVED, carrier_id="FOUP-7", slot=3)
|
||
eq.report_substrate("WFR-001", Milestone.AT_WORK)
|
||
eq.report_substrate("WFR-001", Milestone.PROCESSING)
|
||
eq.report_substrate("WFR-001", Milestone.PROCESSED)
|
||
eq.report_substrate("WFR-001", Milestone.AT_DESTINATION)
|
||
|
||
# E157 — module state (module is auto-created on first report)
|
||
eq.report_module("CHAMBER-A", ModuleState.GENERAL_EXECUTING)
|
||
eq.report_module("CHAMBER-A", ModuleState.STEP_EXECUTING)
|
||
eq.report_module("CHAMBER-A", ModuleState.STEP_COMPLETED)
|
||
eq.report_module("CHAMBER-A", ModuleState.NOT_EXECUTING)
|
||
```
|
||
|
||
`Milestone` / `ModuleState` are importable enums (each member equals its
|
||
plain-string name, so `"ARRIVED"` works just as well). The daemon's
|
||
`ReportSubstrate` handler validates FSM transitions: a substrate that never
|
||
`ARRIVED` is rejected with `INVALID_OBJECT`, and a **duplicate** `ARRIVED`
|
||
with `CANNOT_DO_NOW` (`substrate '...' already exists`) — it never silently
|
||
re-creates over a wafer's live state. A complete worked example is
|
||
[clients/python/examples/wafer_tool.py](../clients/python/examples/wafer_tool.py).
|
||
|
||
---
|
||
|
||
## Where to go next
|
||
|
||
You now know how every component of in-flight material is
|
||
tracked. The next chapter covers the three smaller GEM 300
|
||
standards that round out the suite: equipment performance time
|
||
tracking, the common equipment model, and generic object
|
||
services.
|
||
|
||
Next: [→ 17 E116 + E120 + E39 — Performance, CEM, objects](17_e116_e120_e39_objects.md)
|