← Articles
SECS/GEM/16 min read/— views

Prove a Full GEM Startup Handshake Without Any Equipment

Run all ten SEMI E30 startup steps, S1F13 to S2F31, with one npx command against a simulator: the wire capture, what each PASS proves, a refused Select.

SECS/GEMMESSCADATroubleshootingChecklists

The host code is written. It sends S1F13, receives S6F11, parses it, writes it to the database. The problem is that there is no equipment to point it at, and the vendor FAT is in three weeks.

Testing for the first time at FAT means debugging your host driver with four vendor engineers standing behind you. The failures that show up there are not exotic. COMMACK comes back non-zero, S2F33 is rejected outright, the tool never goes online. All of them are checkable from your desk today.

One command does it.

npx secs-gem-host connect <host>:<port> --report

The target is the HSMS passive listener behind the SECS/GEM simulator; a local instance works the same way, just point at its IP and port. With --report the tool splits SEMI E30 startup into ten steps, prints a PASS/FAIL row for each, and exits 1 if any step fails — which also means you can drop it straight into CI.

The ten E30 startup steps, from S1F13/S1F14 COMMACK to S2F31/S2F32 TIACK: what the host sends and the ack the equipment answers with HostEquipment 123 456 789 10 S1F13S1F14 · COMMACK S1F17S1F18 · ONLACK S1F3S1F4 · SV values S1F11S1F12 · SVID namelist S2F29S2F30 · EC namelist S2F33 S2F35S2F36 · LRACK S2F37S2F38 · ERACK S5F3S5F4 · ACKC5 S2F31S2F32 · TIACK S2F34 · DRACK VID mismatch → DRACK 4

The table it actually prints

Output of a run on 2026-09-09, secs-gem-host 0.2.0 against the simulator's listener at 127.0.0.1:5501. Nothing synthesised — every line below has a matching equipment-side RX packet, and the bytes are in the next section.

Step Sent    Expect  Got     Ack  Time(ms)    Result  Note
------------------------------------------------------------------------
1    S1F13   S1F14   S1F14   0    1/45000     PASS    VX-9000 Plasma Etcher/SECSGEM-1.4.1
2    S1F17   S1F18   S1F18   0    1/45000     PASS
3    S1F3    S1F4    S1F4    -    1/45000     PASS    4 values
4    S1F11   S1F12   S1F12   -    2/45000     PASS    4 SVIDs
5    S2F29   S2F30   S2F30   -    0/45000     PASS    2 ECs
6    S2F33   S2F34   S2F34   0    3/45000     PASS
7    S2F35   S2F36   S2F36   0    1/45000     PASS
8    S2F37   S2F38   S2F38   0    1/45000     PASS
9    S5F3    S5F4    S5F4    0    5/45000     PASS
10   S2F31   S2F32   S2F32   0    1/45000     PASS
------------------------------------------------------------------------
E30 startup: 10/10 passed
flag: model VIDs not in equipment SVID list: 10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011

The note on step 1, VX-9000 Plasma Etcher/SECSGEM-1.4.1, is the MDLN and SOFTREV the equipment put in its S1F14. The tool does not only check COMMACK; it checks that those two fields are actually populated.

A - in the Ack column is not a failure. S1F4, S1F12 and S2F30 carry no one-byte acknowledgement code at all. Those steps are judged on "is the body a list" and "is the namelist non-empty".

The 45000 in the Time(ms) denominator is the T3 reply timeout. E37's timer table gives T3 a typical value of 45 s — typical, not a mandated default — and this tool takes it as its own default; --t3 changes it. The 1 ms is a loopback number. On real equipment, if that column climbs into seconds, that is itself worth reporting. For what each timer protects, see T3, T5, T6, T7 and T8.

The bytes behind the table

The run above went over a socket, so the equipment kept its own copy of it. What follows is the simulator's /api/export/pcap output for that run, unedited: 27 packets, both sides. Every host frame is an equipment-side RX record, which is how I know these are wire bytes and not a rendering of a message object. The raw export is committed at content/demos/prove-e30-startup-handshake-in-one-command.json.

One flag added for the capture: --device-id 11, so the SessionID on the wire matches the simulator's. The bare command works too — this listener does not check the field.

Select, before any of the ten steps

H→E  00 00 00 0A 00 0B 00 00 00 01 00 00 00 01
E→H  00 00 00 0A 00 0B 00 00 00 02 00 00 00 01

Length 00 00 00 0A, SessionID 00 0B (11), Header Bytes 2-3 00 00, PType 00, SType 01 out and 02 back, SystemBytes 00 00 00 01 echoed. On a Select.rsp, Byte 3 is the Select Status, and 00 is accepted (SEMI E37). Step 1 does not exist until this line.

Step 1 — S1F13 → S1F14, COMMACK 0

H→E  00 00 00 1C 00 0B 81 0D 00 00 00 00 00 02
     01 02 41 07 47 45 4D 48 4F 53 54 41 05 30 2E 31 2E 30

E→H  00 00 00 37 00 0B 01 0E 00 00 00 00 00 02
     01 02 21 01 00 01 02 41 15 56 58 2D 39 30 30 30 20 50 6C 61 73
     6D 61 20 45 74 63 68 65 72 41 0D 53 45 43 53 47 45 4D 2D 31 2E
     34 2E 31

Byte 2 81 is the W-bit on stream 1, Byte 3 0D is function 13. The body is L[2]{A "GEMHOST", A "0.1.0"} — the host's own MDLN and SOFTREV. Back comes L[2]{B 0, L[2]{A "VX-9000 Plasma Etcher", A "SECSGEM-1.4.1"}}: 21 01 00 is COMMACK 0, and those two strings are byte-for-byte what the table printed in its Note column.

Step 2 — S1F17 → S1F18, ONLACK 0

H→E  00 00 00 0A 00 0B 81 11 00 00 00 00 00 03
E→H  00 00 00 0D 00 0B 01 12 00 00 00 00 00 03 21 01 00

The request is a 14-byte header with no body at all; Byte 3 11 is 17. The reply carries three body bytes, 21 01 00 — a bare binary item, not a list the way S1F14's body is. A parser that unwraps every reply as an L[2] breaks here, on a step that passed.

Step 3 — S1F3 with a zero-length list

H→E  00 00 00 0C 00 0B 81 03 00 00 00 00 00 04 01 00

The whole body is 01 00: an L item of length zero. SEMI E5 reads that as "all SVs", and the S1F4 that came back holds four (A "Idle", two 8-byte floats, A "ETCH_BASE") — the 4 values in the table.

Step 6 — the delete-all nobody sees, then DRACK 0

H→E  00 00 00 14 00 0B 82 21 00 00 00 00 00 07 01 02 B1 04 00 00 00 01 01 00
E→H  00 00 00 0D 00 0B 02 22 00 00 00 00 00 07 21 01 00

L[2]{U4 1, L[0]} — DATAID 1 and an empty report list, the message that wipes every report definition on the equipment. B1 is format code 44 (U4) with one length byte; 01 00 is the empty list. DRACK 0, and the table never shows this transaction at all.

The definitions follow on SystemBytes 00 00 00 08 as a 136-byte S2F33 carrying VIDs 00 00 27 11 through 00 00 27 1B — 10001 to 10011. The answer:

E→H  00 00 00 0D 00 0B 02 22 00 00 00 00 00 08 21 01 00

DRACK 0 again, for eleven VIDs this equipment never published in step 4. That one byte is the subject of the next section.

Step 9 — ALED and the zero-length ALID list

H→E  00 00 00 11 00 0B 85 03 00 00 00 00 00 0B 01 02 21 01 80 B1 00

L[2]{B 0x80, U4[]}. 21 01 80 is ALED with its high bit set — bit 8 in E5's numbering, the enable bit. B1 00 is a U4 item of length zero: no ALID at all, which E5 reads as every alarm. B1 04 00 00 00 00 would have been ALID 0, a different request entirely.

Step 10 — the 16-character timestamp

H→E  00 00 00 1C 00 0B 82 1F 00 00 00 00 00 0C
     41 10 32 30 32 36 30 39 30 39 30 36 30 33 31 39 39 31

41 10 is an ASCII item of length 16 and the characters are 2026090906031991 — YYYYMMDDhhmmsscc. E5's TIME also has a 12-character form; equipment that accepts only one of the two is a real failure mode, and this run exercises only the 16.

Teardown

H→E  00 00 00 0A 00 0B 00 00 00 09 00 00 00 0D

SType 9, Separate.req, no reply. Communication state drops to NOT COMMUNICATING and the socket closes. The control state does not move — the simulator is still ON-LINE REMOTE from step 2, which is why running this command twice in a row is not the same test twice.

If Select is refused, there is no table

I set the simulator's selectRefuse fault to 3 and ran the identical command.

H→E  00 00 00 0A 00 0B 00 00 00 01 00 00 00 01
E→H  00 00 00 0A 00 0B 00 03 00 02 00 00 00 01
connect failed: Select.rsp status 3
$ echo $?
1

Byte 3 is 03 where the good run had 00; SEMI E37 names 3 Connect Exhaust. Everything else in the frame is well formed — same length, same SessionID, SystemBytes echoed. The exit code is still 1, so CI still fails, but no table is printed: the ten rows only exist once the session has been SELECTED. If something downstream parses that table, give it the empty case. Which Select Status means what, code by code, is in an accepted Select and a refused one.

What each of the ten steps proves

StepWhat a PASS provesWhat a FAIL usually means in a fab
1. S1F13 → S1F14COMMACK 0 came back and both MDLN and SOFTREV are populated. SECS-II conversation exists from here onA non-zero COMMACK means the equipment is not ready to accept this host. No reply at all is an HSMS problem, not a SECS one
2. S1F17 → S1F18The equipment accepted remote controlThe tool's panel is in LOCAL, or nobody pressed online. The most deflating failure at a FAT
3. S1F3 → S1F4Values actually come back. An empty SVID list reads as "all of them" in SEMI E5A body that is not a list means this tool never implemented S1F3, or answers with S9F5
4. S1F11 → S1F12The SVID namelist is not empty. Steps 6 to 8 are judged against what arrives hereEquipment that will not publish its own list. Every later report definition then rests on the interface manual alone
5. S2F29 → S2F30Equipment constants can be readIf the plan was to manage ECs from the host, that plan is already broken
6. S2F33 → S2F34DRACK 0. The report definitions really landed on the equipmentDRACK 4 — one of the VIDs requested does not exist. One wrong ID rejects the whole message
7. S2F35 → S2F36LRACK 0. The RPTIDs are linked to CEIDsIn SEMI E5, LRACK 4 means a CEID does not exist, 5 an RPTID does not, 3 that a link for that CEID is already defined — you appended without deleting first
8. S2F37 → S2F38ERACK 0. Events are enabled. CEED true with an empty CEID list enables all of themERACK 1, a CEID you named does not exist. Skip this step and the definitions are perfect and no S6F11 ever arrives
9. S5F3 → S5F4ACKC5 0. Alarm reporting is on. ALED with the high bit set (0x80) plus a zero-length ALID item means all alarms, not ALID 0A host that gets everything except alarms is usually a host that never sent this message
10. S2F31 → S2F32TIACK 0. The equipment took the host's time. The tool sends the 16-character YYYYMMDDhhmmsscc formThe tool refuses to have its clock set. Decide now whether event timestamps get replaced by host receive time

Step 2 has one exception. The tool passes ONLACK 2 as well as 0 — re-onlining equipment that is already online is not a failure worth flagging. SEMI E5 defines ONLACK 2 as equipment already on-line, but this run never produced one: the simulator answers every S1F17 with a fixed 21 01 00, so the 2 branch is code that has not executed here.

Step 6 also does something the table does not show. Before sending the definitions, it sends one S2F33 carrying a DATAID and an empty report list — the message that deletes every report definition and event link on the equipment. That is what stops you from stacking your configuration on top of whatever the previous integrator left behind. The ack from that delete-all is noted but never counted toward the result. The full DRACK, LRACK and ERACK code tables are in defining, linking and enabling collection events.

Why a 10/10 run still raises a flag

The line under the table is the most valuable output of the run.

flag: model VIDs not in equipment SVID list: 10001, 10002, ... 10011

Just before step 6, the tool compares the S1F12 namelist it collected in step 4 against the VIDs in its own model. The simulator published four SVIDs; the tool's default model builds reports out of VIDs 10001 through 10011. Not one of them overlaps.

DRACK still came back 0. The simulator accepted VIDs it has never heard of. The score is 10/10 and only the flag records it.

Real equipment answers DRACK 4 here, and that is the failure that eats the most human time during startup. One bad VID rejects the entire S2F33, and a host driver that never inspects DRACK logs "report setup complete" and moves on. The configuration then exists only on the host, never on the tool, and stays that way for weeks.

So the flag is something to resolve, not to ignore. Put the SVIDs, CEIDs and RPTIDs from the equipment's interface manual into one JSON file and pass it with --model.

npx secs-gem-host connect <host>:<port> --report --model ./vx9000.json

Now the SVID list the equipment actually published in step 4 is checked against the IDs you intend to use, in the same run. Clearing that flag before the FAT is cheaper than meeting DRACK 4 for the first time on the day.

What this run does not prove

Worth drawing the line explicitly.

  • It is not evidence about the equipment. The simulator returning DRACK 0 for VIDs it does not know is the proof of that. What the table proves is that your host driver builds ten transactions to spec, parses the replies, and actually inspects the ack codes.
  • Timing does not reproduce. 1 ms is a loopback figure. Whether MES finishes its database write inside a 45-second T3 on real equipment is a separate question.
  • RPTID remapping during recovery, spooling, the real content of S6F11 — only equipment does those.
  • A failure before SELECTED is not a startup result at all. The refused-Select run above printed one line and no rows. If Select succeeds and the equipment then goes quiet, that is a different problem — start with selected but not communicating.

The order to work in over three weeks

  1. Run it against the simulator as-is. Anything short of 10/10 is a host driver bug you have today.
  2. Build the model JSON from the SVIDs, CEIDs and RPTIDs in the equipment's interface manual.
  3. Run again with --model and confirm the flag is gone.
  4. Wire that command into CI. The exit code is already 1/0, so there is nothing else to write.
  5. On FAT day, change <host>:<port> to the real tool and run the same command. That one table becomes the minutes of the meeting.

The tool is on npm as secs-gem-host, source at github.com/jungyoseok/secs-gem-host, MIT. Running secs-gem-host mcp exposes the same engine as an MCP server, so an AI agent can be told "connect to this tool, run startup, and explain the step that failed" and do it directly.

If you need something to connect to, the SECS/GEM simulator is up right now.

npx secs-gem-host connect <host>:<port> --report