02 — The cast of characters: equipment, EAP, MES, fab planner, AMHS, operator. Who initiates which conversation, why the equipment is the passive side of HSMS by convention, how the AMHS handshake is out-of-band relative to SECS. Cross-references the relevant namespace and test files for each actor. 03 — Vocabulary + a wafer's journey: follows one 300 mm wafer end-to-end through a fab and labels every SECS message and acronym that fires. Introduces SVID / DVID / ECID / CEID / RPTID / ALID / PPID / MDLN / SOFTREV / HCACK / ALCD / OFLACK / CAACK / SMACK / etc. in context rather than as a list. Includes one-screen reference tables for the remaining acknowledge codes, T-timers in all four contexts (HSMS / SECS-I / E84 / E30 communication state), and a stream-by-stream summary. Part 1 (Foundations) of the guided tour is now complete — a reader who reads chapters 01–03 can describe the protocol stack, identify the actors, and recognise every acronym they'll meet in Part 2. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
28 KiB
03 — Vocabulary + a wafer's journey
← 02 The cast of characters | Back to index | Next: 10 E5 — SECS-II data items →
The SEMI standards bury everything in acronyms. Three-letter, four-
letter, sometimes the same letter pattern (ACKC5, ACKC6, ACKC7,
ACKC10) but with completely different semantics depending on which
stream you're in. Most readers learn them by absorbing them over
years of integration work.
This chapter accelerates that. We follow one 300 mm wafer from
the moment it enters the fab to the moment it leaves as a finished
die, and at every step we name every acronym that fires, what it
means, and where it lives in this codebase. By the end you'll have
seen SVID, CEID, ALID, PPID, HCACK, ALCD, RPTID,
OFLACK, MDLN, SOFTREV, CAACK, SMACK, and the rest in
context — not as a vocabulary list.
If you're confident with the vocabulary already, skip to Part 2, Chapter 10 (SECS-II encoding).
The setup
- The wafer: an unpatterned 300 mm silicon disc, 775 µm thick,
with a serial number
W-2026-06-09-A47etched on the bevel. - The carrier: a Front-Opening Universal Pod (FOUP) that
holds 25 wafers in vertical slots. Our wafer is in slot 14. The
FOUP's bar code reads
C-31415. - The tools:
- PVD-1 (physical vapour deposition — deposits a metal layer)
- LITHO-3 (photolithography — patterns the metal layer)
- ETCH-7 (plasma etch — removes uncovered metal)
- The host: a fab-wide MES called
meta-fab.example. - The recipe:
RECIPE-Cu-Afor PVD-1,RECIPE-193nm-Xfor LITHO-3,RECIPE-CL2-Bfor ETCH-7.
For brevity we'll only show the wafer's first pass through PVD-1. The same pattern repeats for LITHO-3 and ETCH-7.
Stage 1 — Establishing communications (already done)
Before any wafer arrives, PVD-1 and meta-fab.example have already HSMS-SELECTed each other. That's the once-per-power-on dance:
host (active) equipment (passive)
───────────── ───────────────────
TCP SYN ─────────────────────────► (bind on :5000)
◄──── TCP SYN-ACK
HSMS Select.req (sessionID=0) ───►
◄──── HSMS Select.rsp (SELECT_STATUS=0=accept)
[transport state: SELECTED]
S1F13 Establish Communications ──►
◄──── S1F14 (COMMACK=0=accepted, [MDLN, SOFTREV])
[GEM communication state: COMMUNICATING]
This introduces three acronyms:
MDLN— Model Name. An ASCII string up to 20 chars identifying the equipment model. PVD-1 returns"ACME-PVD-3000".SOFTREV— Software Revision. ASCII string up to 20 chars identifying the firmware / EAP version. PVD-1 returns"1.4.2".COMMACK— Communication Acknowledge. One byte; 0 = accepted, 1 = denied. Defined in E30 §6.5.
Where: see equipment.yaml device block; emission flows through
gem::Router →
secsgem::secs2::Message.
Stage 2 — The carrier arrives at PVD-1's load port
The AMHS overhead hoist swings FOUP C-31415 onto load port 1.
Before anything SECS happens, the E84 handshake runs on the
physical I/O lines:
AMHS robot load port 1
────────── ───────────
CS_0 asserted ───────────────────► (carrier select bit 0)
CS_1 asserted ───────────────────► (carrier select bit 1)
VALID asserted ──────────────────► (lines above are stable)
◄──── L_REQ asserted (LOAD allowed)
TR_REQ asserted ─────────────────► (transfer requested)
◄──── READY asserted (kinematic interlocks ok)
BUSY asserted ───────────────────► (placement in progress)
… mechanical placement happens (~5 seconds) …
BUSY de-asserted ────────────────► (placement done)
◄──── COMPT asserted (complete)
CONT asserted ───────────────────► (carrier connected to load port)
This introduces the E84 line-name acronyms (VALID, CS_0, CS_1,
TR_REQ, READY, BUSY, COMPT, CONT, L_REQ, U_REQ, ES)
and three timer names:
TA1— armed whenVALIDasserts; the load port must respond withL_REQwithinTA1. Default ~2 seconds.TA2— armed whenL_REQasserts;TR_REQmust follow withinTA2. Default ~2 seconds.TA3— armed whenBUSYasserts (transfer in progress); the whole transfer must finish withinTA3. Default ~60 seconds.
Any of these timing out → both sides go to HandoffFault and the
operator gets paged. No FOUP gets dropped because the protocol
guarantees both sides agreed on every step.
Where: include/secsgem/gem/e84.hpp
defines the FSM; include/secsgem/gem/e84_timers.hpp
defines the timer enforcement; chapter 18
walks the whole handshake.
Stage 3 — The carrier is on the load port; PVD-1 tells the MES
The E84 handshake gave PVD-1 a docked carrier. Now SECS messages flow:
PVD-1 (equipment) meta-fab (host)
───────────────── ───────────────
S6F11 CarrierArrived ────────────►
CEID = 10001
DATAID = 1
[ {RPTID=100, V=[CarrierID="C-31415", PortID=1]} ]
◄──── S6F12 (ACKC6=0=accepted)
S3F19 Slot Map Verify ───────────►
CARRIERID = "C-31415"
[ slot_state[1..25] ]
◄──── S3F20 (SMACK=0=match)
New acronyms in this stage:
CEID— Collection Event ID. An identifier (any unsigned width; we'll useU4) for a noteworthy thing that happened. CEIDs are defined in the equipment's YAML and the MES learns them viaS1F23/F24. CEID 10001 =CarrierArrivedper E87.RPTID— Report ID. A bundle of variables. When CEID 10001 fires, the MES gets back the values of every variable linked to every report linked to CEID 10001. Reports are defined by the host viaS2F33and linked to CEIDs viaS2F35.DATAID— Data ID, a per-host transaction counter. Lets the host correlate report data to a specific request.ACKC6— Acknowledge Code 6. S6F12 reply byte. 0 = accepted, anything else = MES couldn't process the event.CARRIERID— Carrier ID, an ASCII string. Matches what the AMHS told us via E84.SMACK— Slot Map Acknowledge. S3F20 reply byte. 0 = matches what the MES expected, 1 = mismatch. Defined in E87.CAACK— Carrier Action Acknowledge (we'll see this one shortly). S3F18 reply byte for carrier-action commands.
Note the pattern: every primary message ends in an odd function (F11, F19), every reply ends in the next even function (F12, F20). This is invariant across SECS-II. See chapter 10 for the encoding details.
Where: gem::CarrierStore in include/secsgem/gem/carrier_store.hpp;
the E87 wire tests in tests/test_e87_wire_scenarios.cpp.
Stage 4 — The host authorises processing
meta-fab (host) PVD-1 (equipment)
─────────────── ─────────────────
S3F17 CarrierAction ─────────────►
CARRIERACTION = "ProceedWithCarrier"
CARRIERID = "C-31415"
◄──── S3F18 (CAACK=0=accepted)
CARRIERACTION— an ASCII string from a fixed E87 set:ProceedWithCarrier,CancelCarrier,CarrierOut, …CAACK— Carrier Action Acknowledge. S3F18 reply byte. 0 = accepted, 1 = unknown carrier, 2 = invalid action, 3 = invalid state, 4 = mismatch, 5 = unknown.
The host could have sent CancelCarrier here instead and PVD-1
would have rejected the FOUP without processing. That decision
lives entirely on the host side.
Stage 5 — The host queues a process job
meta-fab (host) PVD-1 (equipment)
─────────────── ─────────────────
S16F11 PRJobCreate ──────────────►
PRJobID = "PJ-2026-06-09-001"
MF = "Substrate"
PRMtlOutSpec = []
PRRecipeMethod = "RecipeOnly"
RCPSpec = "RECIPE-Cu-A"
PRProcessStart = false (we'll start it explicitly later)
PRMtlnameList = ["W-2026-06-09-A47"]
◄──── S16F12 (PRJobAck=0)
S14F9 CreateControlJob ─────────►
CJobID = "CJ-2026-06-09-001"
PRJobIDList = ["PJ-2026-06-09-001"]
◄──── S14F10 (OBJACK=0)
New acronyms:
PRJobID— Process Job ID, an ASCII string the host invents for tracking. Sometimes calledPJID.MF— Material Format. ASCII;Substrate,Carrier,SubstrateLocation. Tells the equipment what scale the job is about.RCPSpec— Recipe specification. References a recipe by ID (PPID, below).PPID— Process Program ID. The recipe's identifier. In our case"RECIPE-Cu-A".PRJobAck— S16F12 reply byte. 0 = accepted, non-zero values for each failure mode.CJobID— Control Job ID. A control job wraps one or more process jobs and adds scheduling semantics (start order, abort policy, dependency on other CJs).OBJACK— Object Acknowledge. S14F10 reply byte. Generic E39 object-services ack: 0 = accepted, 1 = error.
E40 governs process jobs; E94 governs control jobs above them.
Where: include/secsgem/gem/process_jobs.hpp,
include/secsgem/gem/control_jobs.hpp.
The state machines are loaded from
data/process_job_state.yaml and
data/control_job_state.yaml.
See chapter 14 for the lifecycle in full.
Stage 6 — The host configures event reports
Before processing starts, the MES wants to subscribe to specific events. This is a three-message dance:
meta-fab (host) PVD-1 (equipment)
─────────────── ─────────────────
S2F33 DefineReport ──────────────►
DATAID = 2
[ { RPTID=200, VID=[1, 2, 5] } ] (link RPTID 200 to SVIDs 1,2,5)
◄──── S2F34 (DRACK=0=accepted)
S2F35 LinkEvent ─────────────────►
DATAID = 3
[ { CEID=300, RPTID=[200] } ] (when CEID 300 fires, send RPTID 200)
◄──── S2F36 (LRACK=0=accepted)
S2F37 EnableEvent ───────────────►
CEED = true
CEID = [300] (enable CEID 300)
◄──── S2F38 (ERACK=0=accepted)
New acronyms:
SVID— Status Variable ID. An identifier (U4typical) for a long-lived value the host can read at any time — current control state, chamber pressure, wafer counter, recipe in progress, clock. Roughly: instance variables that survive across events.DVID— Data Variable ID. Same shape, but only meaningful at the moment an event fires. E.g. the temperature at the time theProcessStartedevent was emitted. Not readable independently viaS1F3; only delivered as part of a report.ECID— Equipment Constant ID. Same shape, but the host can set it (within declaredmin/maxbounds) viaS2F15. Settings that survive power-cycle: nominal chamber pressure, T3 timeout, T7 timeout, etc.VID— Variable ID. A generic SVID-or-DVID, used in report definitions.CEED— Collection Event Enable Disable. Boolean.true= enable the listed CEIDs,false= disable them.DRACK— Define Report Acknowledge. S2F34 reply.LRACK— Link Report Acknowledge. S2F36 reply.ERACK— Enable Report Acknowledge. S2F38 reply.
Acknowledge bytes are distinct enums per stream/function. DRACK = 0 = accepted; LRACK = 0 = accepted; ERACK = 0 = accepted —
same value, but each one has its own enumeration of failure codes
(3 = at least one CEID does not exist, etc.). Don't reuse one
stream's enum for another's.
Where: include/secsgem/gem/report_store.hpp,
include/secsgem/gem/event_store.hpp.
The configuration flow is the heart of E30 §6.6 Dynamic Event Report
Configuration; see chapter 13.
Stage 7 — Processing begins
meta-fab (host) PVD-1 (equipment)
─────────────── ─────────────────
S2F41 RemoteCommand ─────────────►
RCMD = "START"
CPNAME[] / CPVAL[] = []
◄──── S2F42 (HCACK=0=accepted)
S6F11 ProcessStarted ──────────►
CEID = 300
DATAID = 4
[ {RPTID=200,
V=[ControlState="OnlineRemote",
Clock="20260609173000",
WaferCount=147]} ]
◄──── S6F12 (ACKC6=0)
New acronyms:
RCMD— Remote Command name. ASCII."START","STOP","PAUSE","ABORT","VENT", etc. Equipment-vendor-defined.CPNAME/CPVAL— Command Parameter name / value pairs. Empty list here; some commands take parameters (e.g.RCMD="CHANGE-RECIPE", CPNAME="PPID", CPVAL="RECIPE-Cu-B").HCACK— Host Command Acknowledge. S2F42 reply. 0 = accepted, 1 = invalid command, 2 = cannot perform now, 3 = at least one parameter is invalid, 4 = accepted-and-will-finish-later, 5 = rejected, 6 = invalid object.
The S6F11(CEID=300) that fires next is the event report
defined three messages earlier. The MES correlated this by:
- Earlier sent
S2F33→ equipment now knows that "RPTID 200 = [SVID 1, SVID 2, SVID 5]." - Earlier sent
S2F35→ equipment now knows that "when CEID 300 fires, the report payload should include RPTID 200." - Earlier sent
S2F37→ CEID 300 is enabled, so when the processing logic fires it, anS6F11actually leaves the wire. (If CEID 300 had been left disabled, the processing logic would still fire it but the wire would stay quiet.)
Where: include/secsgem/gem/host_command_registry.hpp
maps RCMD strings to handlers; the report-emission machinery lives
in EquipmentDataModel (include/secsgem/gem/data_model.hpp)
via compose_reports_for(ceid). Wire-level tests:
tests/test_wire_ceid_emission.cpp.
Stage 8 — An alarm fires
Mid-processing, the chamber pressure sensor reads above its
configured ECID="ChamberPressureMax" threshold. The EAP's alarm
monitor decides this is alarm-worthy and calls
alarms.set(ALID=42):
PVD-1 (equipment) meta-fab (host)
───────────────── ───────────────
S5F1 AlarmReport ────────────────►
ALCD = 0x84 (bit 7 set + category 4 = process)
ALID = 42
ALTX = "Chamber pressure above max threshold"
◄──── S5F2 (ACKC5=0)
ALID— Alarm ID. An identifier (U4typical) for one named alarm in the equipment's alarm directory.ALCD— Alarm Code. One byte. Bit 7 = "set" (1) or "clear" (0). Lower 7 bits = category (1 = personal safety, 2 = equipment safety, 3 = parameter control warning, 4 = parameter control error, 5 = irrecoverable error, 6 = equipment status warning, 7 = attention flag, 8 = data integrity, others reserved). E5 §13.ALTX— Alarm Text. ASCII description, up to 120 chars per E5 §13.ACKC5— Acknowledge Code 5. S5F2 reply. 0 = accepted.
If the host had previously disabled ALID 42 (via S5F3 ALED=0x00), this S5F1 wouldn't have left the wire — the equipment
would still note the alarm internally (so S5F5 would list it), but
the host wouldn't get pinged.
When the pressure returns to range, a second S5F1 fires with
ALCD=0x04 (bit 7 cleared) and the same ALID, signalling "alarm
cleared."
Where: include/secsgem/gem/alarm_store.hpp;
the dispatcher gates emission on the enable list in
include/secsgem/gem/alarm_dispatcher.hpp.
Stage 9 — Processing completes
PVD-1 (equipment) meta-fab (host)
───────────────── ───────────────
S6F11 ProcessCompleted ──────────►
CEID = 301
DATAID = 5
[ {RPTID=200, V=[…]} ]
◄──── S6F12 (ACKC6=0)
Same pattern as ProcessStarted — just a different CEID for a
different lifecycle moment.
The MES sees CEID=301 and updates its tracking: process job
PJ-2026-06-09-001 is now ProcessComplete per E40. It clears its
"in progress" counter and updates wafer W-2026-06-09-A47's
location in E90 substrate tracking.
Stage 10 — Carrier transfers out
meta-fab (host) PVD-1 (equipment)
─────────────── ─────────────────
S3F25 CarrierTransfer ───────────►
CARRIERID = "C-31415"
PortID = 2 (transfer from LP1 to LP2 = outbound)
◄──── S3F26 (CAACK=0)
PVD-1 (equipment) meta-fab (host)
───────────────── ───────────────
S6F11 CarrierTransfered ─────────►
CEID = 10002
[ … ]
◄──── S6F12 (ACKC6=0)
Then E84 runs in reverse: the AMHS robot couples to the load port,
the parallel I/O lines hand control back, the OHT hoist lifts the
FOUP, and C-31415 heads to LITHO-3 for the next process step.
Wait, what other acronyms exist?
The journey above covered the most common acronyms but skipped a handful that show up in other contexts. A reference list for the rest:
Acknowledge codes you haven't met yet
| Code | Stream | Where | 0 = accepted, then… |
|---|---|---|---|
OFLACK |
1 | S1F16 reply to "Request Offline" | 0=accept, 1=already offline |
ONLACK |
1 | S1F18 reply to "Request Online" | 0=accept, 1=not allowed, 2=already online |
ACKC7 |
7 | S7F4 / S7F18 (recipe send/delete) | 0=accept, 1=permission denied, 2=length err,3=matrix err,4=PPID not found,5=mode unsupported,6=other |
ACKC10 |
10 | S10F2 / F4 / F6 (terminal services) | 0=accept, 1=not displayed, 2=no terminal |
CMDA |
2 | S2F22 reply to legacy S2F21 |
0=ok, 1=invalid command, 2=cannot do now,3=invalid arg |
TIACK |
2 | S2F32 reply to "Set Clock" | 0=accept, 1=err not done |
EAC |
2 | S2F16 reply to "Set EC values" | 0=accept, 1=≥1 constant out of range, 2=busy, 3=≥1 constant unknown |
RSPACK |
2 | S2F44 reply to "Set Spool Streams" | 0=accept, 1=spool not supported, 2=≥1 stream unknown |
RSDA |
6 | S6F24 reply to "Spool Data Send" | 0=ok, 1=denied |
PPGNT |
7 | S7F2 reply to "PP Load Inquire" | 0=permit, 1=already have, 2=no room, 3=invalid PPID, 4=mode unsupported, 5=PP non-existent, 6=other |
Control codes you haven't met
| Code | Stream | Where | What it means |
|---|---|---|---|
RSDC |
6 | S6F23 host command to spool | 0=transmit spooled, 1=purge spool |
ALED |
5 | S5F3 host enable/disable alarm | bit 7 set = enable, bit 7 cleared = disable |
TID |
10 | S10F1/F3/F5 terminal display | Which terminal screen to address (0 = main) |
TEXT |
10 | S10F1/F3/F5 terminal display | The ASCII text payload |
Object-services codes (E39)
| Code | Stream | Where | What it means |
|---|---|---|---|
OBJSPEC |
various | S2F49, S14F1 | An "object specifier" — a typed path identifying a target object |
OBJACK |
14 | S14F2 / F10 / F12 reply | 0=ok, 1=command failed |
CPACK |
2 | S2F42 reply (modern) | Per-parameter ack for each CPNAME/CPVAL in the command |
CEPACK |
2 | S2F50 reply (enhanced) | Per-parameter ack for enhanced remote commands |
All the T-timers in one place
You'll meet timer acronyms in three different contexts. They use the same letters with different meanings — pin them down:
HSMS T-timers (E37 §10)
These bound the network behaviour. Only fire when something is slow or stuck.
| Name | Default | What it bounds |
|---|---|---|
T3 |
45 s | Reply timeout for a W=1 primary message |
T5 |
10 s | How long active side waits between connect attempts |
T6 |
5 s | Control-transaction (Select / Linktest) reply timeout |
T7 |
10 s | Passive side: max time without Select.req after TCP |
T8 |
5 s | Max time between bytes of a single frame |
Chapter 11 covers each one in detail.
SECS-I T-timers (E4 §10)
These bound the serial-block behaviour. Distinct from HSMS T-timers despite the name overlap.
| Name | Default | What it bounds |
|---|---|---|
T1 |
500 ms | Inter-character timeout within one block |
T2 |
10 s | Protocol timer (handshake state) |
T3 |
45 s | Reply timeout for a W=1 primary message |
T4 |
45 s | Inter-block timeout in a multi-block message |
T3 exists in both HSMS and SECS-I with the same semantics — it's
load-bearing in both transports.
E84 timers (E84 §6)
These bound the physical handoff timing. Distinct again.
| Name | Default | What it bounds |
|---|---|---|
TA1 |
~2 s | VALID → L_REQ |
TA2 |
~2 s | L_REQ → TR_REQ |
TA3 |
~60 s | BUSY → transfer complete |
E30 communication-state timers (E30 §6.5)
These bound the application-level establish-communications loop:
| Name | Default | What it bounds |
|---|---|---|
T_CRA |
45 s | Wait for S1F14 (Comm Request Acknowledge) reply after S1F13 |
T_DELAY |
10 s | Retry interval after a failed S1F13 round-trip |
Defined in include/secsgem/gem/communication_state.hpp;
tested in tests/test_communication_state.cpp.
Stream-by-stream summary
The streams you'll meet most often, with one sentence each:
| Stream | What it's for | Most-used messages |
|---|---|---|
| S1 | Identification, status, control | S1F1/F2, S1F3/F4, S1F11/F12, S1F13/F14, S1F15-F18, S1F19/F20, S1F21-F24 |
| S2 | Equipment constants, clock, events, commands | S2F13-F18, S2F29-F38, S2F41/F42, S2F43-F50 |
| S3 | Carrier management (E87) | S3F17/F18, S3F19/F20, S3F25-F28 |
| S5 | Alarms, exception recovery | S5F1-F8, S5F9-F18 |
| S6 | Data collection, event reports, spool | S6F11/F12, S6F15/F16, S6F19-F22, S6F23-F26 |
| S7 | Recipe / process program management | S7F1-F6, S7F17-F20, S7F23-F26 |
| S9 | Protocol-error reports (auto-emitted by equipment) | S9F1, S9F3, S9F5, S9F7, S9F9, S9F11, S9F13 |
| S10 | Terminal services | S10F1-F6 |
| S12 | Wafer maps | (per-stream — chapter 10 §6) |
| S14 | Generic object services (E39), control jobs (E94) | S14F1/F2, S14F9-F12 |
| S16 | Process jobs (E40) | S16F5-F8, S16F9, S16F11-F14, S16F27/F28 |
Where every named message lives in code: build/generated/secsgem/gem/messages.hpp
(after a build) — generated from
data/messages.yaml by
tools/generate_messages.py.
Chapter 31 walks the codegen.
You've made it through Part 1
You can now:
- Explain why SECS/GEM exists, and what each of "SECS", "HSMS", and "GEM" actually refers to.
- Name every actor in the fab automation stack and describe who talks to whom.
- Recognise every common acronym (SVID, ECID, DVID, CEID, RPTID, ALID, PPID, MDLN, SOFTREV, HCACK, ALCD, OFLACK, …) and the acknowledge bytes that go with each stream.
- List the T-timers in four different contexts (HSMS, SECS-I, E84, E30 communication state) and not confuse them.
Part 2 of this guide takes one standard at a time, from the ground up: byte-level encoding, wire diagrams, every message, every ack value, the FSMs, and the code that implements each. We start with the foundation everything else stands on — E5 SECS-II, the data-item encoding.