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

Why a SECS/GEM Recipe Upload Should Start With S7F1, Not S7F3

A 300-byte PPBODY is 330 bytes on the socket. A real S7F1 and S7F3 capture, and what a host loses by skipping the grant step.

SECS/GEMMESSCADATroubleshootingChecklists

The first recipe upload anyone writes is a single S7F3. Put the PPID in, put the PPBODY in, send it. Then a 400 KB body goes out in full and S7F4 comes back as a refusal — and the host has already spent all 400 KB. Storage full, PPID already present, tool not in a state to take it: different reasons, same bill. That is what S7F1 is for in SEMI E5. It asks whether the body will be accepted before the body goes on the wire.

The 330 bytes one S7F3 put on the socket — 4-byte length prefix, 10-byte HSMS header, L,2, an 11-byte PPID item, a 3-byte B item header, and the 300-byte PPBODY that S7F1's LENGTH counted S7F3 — PPID ETCH_FAST, PPBODY 300 4 10 2 11 3 300 00 0001 46 00 0B 87 03 … 01 02 41 09 45 54 … 22 01 2C 3B 20 56 58 2D 39 30 30 30 … lengthprefix HSMS header L,2 A[9] PPID B item header PPBODY S7F1 LENGTH = 300 330 bytes on the socket

S7 in E5 is a two-step conversation

SEMI E5 collects process program management into Stream 7, and the upload path is split across two primaries.

  • S7F1 Process Program Load Inquire — body is L,2 { PPID, LENGTH }. "I want to send this much under this name; will you take it?"
  • S7F2 Process Program Grant — answers with a one-byte PPGNT. Zero is granted. The non-zero values separate reasons like already have it, no space, invalid PPID, and busy — but read the number-to-reason mapping straight out of E5's PPGNT table rather than guessing it.
  • S7F3 Process Program Send — L,2 { PPID, PPBODY }. This one carries the body.
  • S7F4 Process Program Acknowledge — ACKC7. Zero is accepted here too.

Download is the mirror image. S7F5 asks for a PPID and S7F6 returns L,2 { PPID, PPBODY }. Delete is S7F17/S7F18, and the list of PPIDs the tool currently holds is S7F19/S7F20.

From the host side, the thing that matters is that S7F1 is a negotiation, not a formality. Telling the tool the LENGTH up front lets it allocate, or refuse on the spot if it has no room. Skip the step and that decision moves to after the body has arrived.

S7F1 on the wire

I opened a socket straight to the simulator's passive listener on :5501, finished Select, then sent S7F1. PPID is ETCH_FAST and the PPBODY I intended to send is 300 bytes.

TX  Select.req    00 00 00 0A 00 0B 00 00 00 01 00 00 07 01
RX  Select.rsp    00 00 00 0A 00 0B 00 00 00 02 00 00 07 01

That is SELECTED. Now S7F1.

TX  S7F1  W=1     00 00 00 1D 00 0B 87 01 00 00 00 00 07 02
                  01 02
                  41 09 45 54 43 48 5F 46 41 53 54
                  B1 04 00 00 01 2C
    tool decode   stream 7, wbit true, function 1, ptype 0, stype 0, systemBytes 1794

Reading from the front: 00 00 00 1D is the 29 bytes that follow — header 10 plus body 19. 00 0B is SessionID 11. 87 is Stream 7 with the W-bit set, since the top bit is W (Header Byte 2), and 01 is Function 1.

Those 19 body bytes are L,2 { PPID, LENGTH } exactly.

  • 01 02 — List, two elements.
  • 41 09 plus 45 54 43 48 5F 46 41 53 54 — an A item, length 9, ETCH_FAST.
  • B1 04 plus 00 00 01 2C — a U4 item, length 4, value 300.

0x2C is 44 and 0x01 2C is 300. That 300 is the byte count of the PPBODY about to follow.

What LENGTH counts and what the socket carries are different numbers

I sent S7F3 on the same socket, carrying the 300-byte PPBODY.

TX  S7F3  W=1     00 00 01 46 00 0B 87 03 00 00 00 00 07 03
                  01 02
                  41 09 45 54 43 48 5F 46 41 53 54
                  22 01 2C
                  3B 20 56 58 2D 39 30 30 30 20 72 65 63 69 70 65 …  (300 bytes)
    tool decode   stream 7, wbit true, function 3, ptype 0, stype 0, systemBytes 1795
    socket bytes  330

The length prefix is 00 00 01 46 = 326, and adding the prefix's own four bytes gives the 330 that went on the socket. The LENGTH I declared was 300. Every one of the 30 bytes of difference is countable — HSMS header 10, L,2 2, the PPID item 11 (41 09 plus nine characters), and the PPBODY's item header 3.

That 3-byte item header is worth a look. 22 means B (binary) format using two length bytes, and the 01 2C after it is 300. The moment a PPBODY passes 255 bytes, one length byte is no longer enough (length bytes in the item header). Real recipes do not stop at 300 bytes, so assume this path uses two or three length bytes from the start.

So do not read S7F1's LENGTH as "the load this transaction puts on the network". It counts PPBODY only. If the tool's buffer arithmetic needs a figure that includes headers, it will be short by exactly that difference.

Block segmentation is not something to worry about here. Under SECS-I (E4) a message goes out in blocks with the data in each block capped at 244 bytes; HSMS (E37) has no such split. One 4-byte length prefix wraps the whole message. Porting E4-era code and keeping the logic that chops the payload into 244-byte pieces gets you a message the tool reads as corrupt, not as truncated.

PPID is not a version

The protocol gives you a name and nothing else. The list S7F20 returns is PPIDs. E5 has no field that tells you whether the ETCH_FAST on the tool is last week's or today's.

The way this bites: the host decides "it's already there" and skips the upload, and what is actually on the tool is two revisions old. Wafers process normally and the MES record looks clean. It just ran a different recipe.

I do one of two things. Either put the revision in the PPID so the name itself is unique (ETCH_FAST_R7), or pull the body back with S7F5 and compare a hash. The first is cheap, the second is certain. On a tool whose storage holds only a handful of programs the first runs out of room quickly, and then it has to be the second. Either way, what has to leave the code is the assumption that the same PPID means the same recipe.

The screen side of recipe parameters is a separate write-up: recipe parameter download validation.

What this capture does not prove

This listener does not answer data messages. It handles SType 1 (Select.req), 5 (Linktest.req) and 9 (Separate.req) only. So neither S7F2 nor S7F4 came back for the two messages above, and that is a missing implementation, not a refusal. I have not verified what PPGNT comes back in an S7F2, or what ACKC7 comes back in an S7F4, against this capture. What the capture proves stops at the encoding the host emitted — which bytes sit where, and that the receiving decoder split them correctly into stream 7, function 1 and function 3.

The per-value meanings of PPGNT and ACKC7 have to come from E5's tables checked against the tool vendor's document. The two do disagree in practice, and when they do the tool is right.

Where to look in host code

  • Send S7F1 first. If PPGNT is non-zero, do not build S7F3. Even with the body already in memory, it does not go on the socket.
  • S7F1's LENGTH is the PPBODY byte count. Do not add item headers or the HSMS header to it.
  • Choose the PPBODY item's length-byte count from its size. Past 255 bytes, one byte will not do.
  • HSMS does not use 244-byte block segmentation. If E4 code was ported, delete that part.
  • Decide a retry interval for a "busy" PPGNT. An immediate retry gets the same answer.
  • Do not skip an upload on PPID equality. Put the revision in the name, or compare bodies with S7F5.
  • Do not blindly resend an S7F3 whose T3 expired. It may only be the reply that was lost, in which case the tool receives the same PPID twice.

The first thing to check in upload code is the capture, not the log. Host logs tend to say S7F3 sent, 400KB, and that line looks identical whether or not an S7F1 preceded it.

The capture above was taken against the passive listener in the SECS/GEM simulator. Open a socket, finish Select, send the same body, and the same bytes come out.