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>
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
# 17 — E116 + E120 + E39: Performance, CEM, objects
|
||||
|
||||
← [16 E90 + E157 — Substrate and module tracking](16_e90_e157_substrates_modules.md) | [Back to index](00_index.md) | Next: [18 E84 — Parallel I/O handoff](18_e84_parallel_io.md) →
|
||||
|
||||
Three smaller GEM 300 standards in one chapter. Each is narrow in
|
||||
scope but load-bearing for production fab operations.
|
||||
|
||||
- **E116** — Equipment Performance Tracking. Time-buckets per
|
||||
equipment state for OEE / utilisation reporting.
|
||||
- **E120** — Common Equipment Model. A generic typed object
|
||||
hierarchy the host can query.
|
||||
- **E39** — Object Services. CRUD-style messages (`S14F*`) that
|
||||
operate over E120 (and other) object types.
|
||||
|
||||
---
|
||||
|
||||
## E116 — Equipment Performance Tracking
|
||||
|
||||
### What it does
|
||||
|
||||
In a fab, **equipment utilisation** is a primary KPI. Tools cost
|
||||
$10–100M; idle minutes are visible on quarterly P&L statements.
|
||||
E116 standardises how equipment reports *how much time it spent
|
||||
in each state* so MES dashboards can compute OEE (Overall Equipment
|
||||
Effectiveness) without per-vendor logic.
|
||||
|
||||
### The states
|
||||
|
||||
```cpp
|
||||
// include/secsgem/gem/ept_state.hpp:22
|
||||
enum class EptState : uint8_t {
|
||||
NonScheduledTime = 0, // not in the schedule (weekend, planned down)
|
||||
UnscheduledDowntime = 1, // in schedule, but broken (alarm, fault)
|
||||
ScheduledDowntime = 2, // in schedule, planned maintenance
|
||||
Engineering = 3, // running engineering / qualification work
|
||||
Standby = 4, // ready, awaiting material
|
||||
Productive = 5, // actively processing
|
||||
};
|
||||
```
|
||||
|
||||
These are the SEMI E116 §6.2 standard states. Per-state events:
|
||||
|
||||
```cpp
|
||||
enum class EptEvent {
|
||||
Begin_NonScheduledTime,
|
||||
Begin_UnscheduledDowntime,
|
||||
Begin_ScheduledDowntime,
|
||||
Begin_Engineering,
|
||||
Begin_Standby,
|
||||
Begin_Productive,
|
||||
};
|
||||
```
|
||||
|
||||
### What the FSM records
|
||||
|
||||
[`EptStateMachine`](../include/secsgem/gem/ept_state.hpp) is a
|
||||
"what kind of time is this" classifier rather than a strict
|
||||
lifecycle. Any state can transition to any other. What it
|
||||
tracks: **how long was the equipment in each state**.
|
||||
|
||||
The store accumulates time-buckets:
|
||||
|
||||
```cpp
|
||||
class EptStore {
|
||||
// For each EptState, accumulated wall-clock duration.
|
||||
std::array<std::chrono::seconds, 6> bucket_;
|
||||
|
||||
// Current state + when it was entered (so the dwell so far is
|
||||
// counted as part of the current bucket on read).
|
||||
};
|
||||
```
|
||||
|
||||
A host querying "how much Productive time today?" gets the bucket
|
||||
value for `Productive`, plus the dwell of the current state if
|
||||
that state is Productive.
|
||||
|
||||
### Wire
|
||||
|
||||
E116 doesn't define its own S/F messages. Like E90, state changes
|
||||
fire as **CEIDs** the host has subscribed to.
|
||||
|
||||
Tests:
|
||||
[`tests/test_ept.cpp`](../tests/test_ept.cpp) (7 cases — initial
|
||||
state, transitions, bucket accumulation including current dwell,
|
||||
reset, same-state no-op).
|
||||
|
||||
### When EPT transitions happen
|
||||
|
||||
EPT classification is *application logic*. The library doesn't
|
||||
decide that processing a PJ = Productive — the EAP does, by
|
||||
explicitly calling `EptStateMachine::on_event(Begin_Productive)`
|
||||
when a PJ starts. Typical wiring:
|
||||
|
||||
```
|
||||
PJ Processing → EPT Productive
|
||||
PJ Paused → EPT Standby (or Engineering, depending on cause)
|
||||
Alarm category 2 (equipment safety) → EPT UnscheduledDowntime
|
||||
Maintenance recipe running → EPT ScheduledDowntime
|
||||
```
|
||||
|
||||
The [`examples/pvd_tool/main.cpp`](../examples/pvd_tool/main.cpp) §5
|
||||
shows one concrete wiring; chapter
|
||||
[41](41_integration_hardware_mes_production.md) discusses the
|
||||
production patterns.
|
||||
|
||||
---
|
||||
|
||||
## E120 — Common Equipment Model
|
||||
|
||||
### What it does
|
||||
|
||||
E120 defines a **generic typed object hierarchy** the equipment can
|
||||
expose to the host. The motivation: every E30/GEM 300 standard
|
||||
defines its own object type (Carrier, Substrate, ProcessJob,
|
||||
ControlJob, Alarm, …), each with its own attributes. E120 says
|
||||
"all of these are *objects* with the same hierarchical structure;
|
||||
let's standardise how the host queries them."
|
||||
|
||||
### The object types
|
||||
|
||||
```cpp
|
||||
// include/secsgem/gem/store/cem_objects.hpp:27
|
||||
enum class CemObjectType : uint8_t {
|
||||
Equipment = 1,
|
||||
IOProcessor = 2,
|
||||
IODevice = 3,
|
||||
SubsystemController = 4,
|
||||
Subsystem = 5,
|
||||
Module = 6,
|
||||
// ... more
|
||||
};
|
||||
```
|
||||
|
||||
Each object has:
|
||||
|
||||
- A unique `OBJID` (ASCII string).
|
||||
- A type from the enum above.
|
||||
- A `parent_objid` (or empty for root — Equipment).
|
||||
- A typed attribute bag.
|
||||
|
||||
That builds the hierarchy:
|
||||
|
||||
```
|
||||
Equipment "PVD-1"
|
||||
├── IOProcessor "IOP-1"
|
||||
│ ├── IODevice "Sensor-Pressure-A"
|
||||
│ └── IODevice "Sensor-Temp-A"
|
||||
└── SubsystemController "SubC-1"
|
||||
├── Subsystem "Vacuum"
|
||||
│ └── Module "Pump-1"
|
||||
└── Subsystem "Gas-Manifold"
|
||||
```
|
||||
|
||||
The host can walk this tree, read attributes, and update its own
|
||||
asset model.
|
||||
|
||||
### Code
|
||||
|
||||
Store:
|
||||
[`include/secsgem/gem/store/cem_objects.hpp`](../include/secsgem/gem/store/cem_objects.hpp).
|
||||
Tests:
|
||||
[`tests/test_cem_objects.cpp`](../tests/test_cem_objects.cpp) (3
|
||||
cases — create, lookup, child enumeration).
|
||||
|
||||
### Wire
|
||||
|
||||
E120 itself doesn't define messages — it defines the *data model*.
|
||||
The wire access is **E39 Object Services**.
|
||||
|
||||
---
|
||||
|
||||
## E39 — Object Services
|
||||
|
||||
### What it does
|
||||
|
||||
E39 generalises "get attribute of an object" and "set attribute of
|
||||
an object" into one message family — `S14F*` — that works across
|
||||
**any object type** (E120 hierarchy, E40 process jobs, E94 control
|
||||
jobs, E87 carriers, …).
|
||||
|
||||
### The messages
|
||||
|
||||
| S/F | Direction | Purpose |
|
||||
|-------|-----------|----------------------------------------------------------|
|
||||
| S14F1 | H → E | GetAttr. Body: object type + OBJID + attribute name list. |
|
||||
| S14F2 | E → H | GetAttr reply. Body: attribute values + OBJACK byte. |
|
||||
| S14F3 | H → E | SetAttr. Body: object type + OBJID + name/value pairs. |
|
||||
| S14F4 | E → H | SetAttr reply. |
|
||||
|
||||
OBJACK = 0 means accepted; non-zero means error.
|
||||
|
||||
E39 is the **uniform API** for object introspection — same shape
|
||||
of message whether the host is reading a Carrier attribute, an
|
||||
Alarm attribute, or a Process Module attribute.
|
||||
|
||||
### Code
|
||||
|
||||
Handlers live in
|
||||
[`include/secsgem/gem/host_command_registry.hpp`](../include/secsgem/gem/host_command_registry.hpp)
|
||||
and the generated message catalog.
|
||||
|
||||
Tests are bundled into
|
||||
[`tests/test_cem_objects.cpp`](../tests/test_cem_objects.cpp) and
|
||||
[`tests/test_messages.cpp`](../tests/test_messages.cpp) — the
|
||||
`S14F1`/`S14F2` round-trip is exercised against multiple object
|
||||
types.
|
||||
|
||||
### Why E39 exists separately from E120
|
||||
|
||||
The split is the SEMI typical-shape: one standard defines the
|
||||
*data model*, a separate standard defines the *wire access*. This
|
||||
way E39 can extend to objects defined in other standards (E40 PJs,
|
||||
E94 CJs, E87 carriers) without E120 having to know about them.
|
||||
|
||||
In code, each object store registers itself with a generic
|
||||
attribute-resolver; `S14F1` handlers look up the right resolver by
|
||||
object type.
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
You now know how the equipment reports *time* (E116), *structure*
|
||||
(E120), and *attribute access* (E39). The next chapter is the
|
||||
last GEM 300 standard with its own state machine — the **parallel
|
||||
I/O handshake** that physically hands carriers between robot and
|
||||
load port.
|
||||
|
||||
Next: [→ 18 E84 — Parallel I/O handoff](18_e84_parallel_io.md)
|
||||
Reference in New Issue
Block a user