Files
secs-gem/docs/03_vocabulary_and_a_wafers_journey.md
T
raphael 0355c73211
tests / build-and-test (push) Successful in 2m9s
tests / thread-sanitizer (push) Successful in 2m35s
tests / tshark-dissector (push) Successful in 2m17s
tests / secs4j-interop (push) Successful in 1m6s
tests / libfuzzer (push) Successful in 3m7s
docs: refresh stale file paths after store/ reorg + gen_messages rename
generate_messages.py → gen_messages.py and several gem/ headers moved
under gem/store/ (carrier_store.hpp → store/carriers.hpp, etc.);
e84.hpp split into e84_state.hpp.  The guided-tour chapters still
pointed at the old paths — relink them so the deep-link footnotes
resolve.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 23:23:42 +02:00

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-A47 etched 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-A for PVD-1, RECIPE-193nm-X for LITHO-3, RECIPE-CL2-B for 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::Routersecsgem::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 when VALID asserts; the load port must respond with L_REQ within TA1. Default ~2 seconds.
  • TA2 — armed when L_REQ asserts; TR_REQ must follow within TA2. Default ~2 seconds.
  • TA3 — armed when BUSY asserts (transfer in progress); the whole transfer must finish within TA3. 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_state.hpp defines the FSM; include/secsgem/gem/e84_asio_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 use U4) for a noteworthy thing that happened. CEIDs are defined in the equipment's YAML and the MES learns them via S1F23/F24. CEID 10001 = CarrierArrived per 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 via S2F33 and linked to CEIDs via S2F35.
  • 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/store/carriers.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 called PJID.
  • 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/store/process_jobs.hpp, include/secsgem/gem/store/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 (U4 typical) 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 the ProcessStarted event was emitted. Not readable independently via S1F3; only delivered as part of a report.
  • ECID — Equipment Constant ID. Same shape, but the host can set it (within declared min/max bounds) via S2F15. 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/store/event_reports.hpp (reports + collection events live together — the same store backs S2F33/F35/F37). 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:

  1. Earlier sent S2F33 → equipment now knows that "RPTID 200 = [SVID 1, SVID 2, SVID 5]."
  2. Earlier sent S2F35 → equipment now knows that "when CEID 300 fires, the report payload should include RPTID 200."
  3. Earlier sent S2F37 → CEID 300 is enabled, so when the processing logic fires it, an S6F11 actually 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/store/host_commands.hpp (HostCommandRegistry) 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 (U4 typical) 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/store/alarms.hpp defines AlarmRegistry, which both stores the definitions and gates S5F1 emission on the enable list.


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 VALIDL_REQ
TA2 ~2 s L_REQTR_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/gen_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.

Next: → 10 E5 — SECS-II data items