HCACK=0은 wafer가 움직였다는 뜻이 아니다
Host가 RCMD=START로 S2F41 Host Command Send를 보내고 S2F42를 HCACK=0으로 받는다. MES는 lot을 running으로 바꾼다. 10초 뒤 tool은 pod clamp에서 alarm을 내고 아무것도 처리하지 않는다. 누군가 "SECS가 고장났다"고 티켓을 연다.
고장난 것은 없다. SEMI E5에서 HCACK=0은 command performed, 즉 transaction 안에서 끝나는 명령에만 맞는 code다. 실제 작업이 수 초에서 수 분 걸리는 START라면 tool은 HCACK=4(accept했고 완료는 나중에 event로 알림)를 돌려주고, host는 reply가 아니라 그 event를 기다려야 했다. Acknowledge code와 물리적 결과는 서로 다른 사실이고, SECS-II는 둘에 각각 다른 code를 준다. 이 둘을 하나로 취급하면 시운전은 MES record를 되감는 작업이 된다.
추상화가 아니라 message를 읽는다
SEMI E5 stream 2에서 S2F41 Host Command Send(HCS)의 body는 이 모양이다.
L,2
<A RCMD>
L,n ← parameter가 없으면 L,0
L,2
<A CPNAME>
< CPVAL>
S2F42 Host Command Acknowledge(HCA)가 대칭으로 답한다.
L,2
<B HCACK> ← binary 1 byte
L,n ← HCACK≠3이면 보통 L,0
L,2
<A CPNAME>
<B CPACK> ← 역시 binary 1 byte
HCACK과 CPACK이 binary(B) 1 byte라는 점은 parser를 직접 쓸 때 실제로 걸린다. U1로 기대하고 format byte를 비교하면 안 맞는다. CPVAL은 반대다. E5가 하나의 format으로 못 박지 않으니 parameter마다 어떤 item format으로 주고받을지를 interface 사양서에 적어 둬야 한다. Tool 두 대가 갈리는 자리가 여기다.
Object 지향적인 대상(E87 carrier, E94 control job)에는 enhanced form인 S2F49/S2F50을 쓴다. S2F49 Enhanced Remote Command에는 DATAID와 OBJSPEC가 앞에 붙어서, tool에 맨 command를 쏘는 대신 특정 object를 지정할 수 있다.
외워둘 만한 HCACK code (E5):
- 0 — OK, performed (transaction 안에서 완료)
- 1 — command가 존재하지 않음
- 2 — 지금은 수행 불가 (state가 맞지 않음)
- 3 — parameter가 하나 이상 invalid — 이제 CPACK list를 읽는다
- 4 — accept했고 완료는 collection event로 나중에 알림
- 5 — 이미 요청한 상태라 reject
- 6 — 그런 object가 없음 (enhanced form)
E5가 정의하는 값은 여기까지다. 그 위 값이 오면 표준이 아니라 vendor 확장이니 매뉴얼을 찾아야 한다.
CPACK은 parameter별로 S2F42 reply list에 담긴다:
- 1 — parameter name이 존재하지 않음
- 2 — CPVAL 값이 illegal
- 3 — CPVAL format이 illegal
HCACK=3에 CPACK list가 채워져 오는 것이 설계할 수 있는 가장 쓸모 있는 reject다. 어느 CPNAME이 왜 실패했는지 정확히 짚어준다. Parameter 문제를 전부 맨 HCACK=2로 뭉개는 tool은 매번 controller log를 뒤지게 만든다. CPACK list를 돌려주게 vendor에게 요구해라 — 표준에 있는 내용이고, 빼는 건 나중에 갚는 지름길이다.
HCACK=4는 "accept했지만 안 끝났다"의 계약이다
쓸모 있는 remote command 대부분은 빠르게 accept되고 느리게 완료된다. HCACK=4로 S2F42가 온 뒤에도 tool은 clamp, pump down, recipe load, interlock 해제를 해야 한다. 깔끔한 설계는 이런 완료를 미리 정의해 둔 collection event에 연결한다 — S2F33으로 report를 정의하고, S2F35로 link하고, S2F37로 enable해 둔다. 그러면 START의 완료는 S2F42 reply가 아니라 CEID(ProcessStart나 lot-started event)로 도착한다.
MES lot 상태는 S2F42가 아니라 CEID로 바꾼다. Recipe select의 "완료"는 acknowledge가 아니라 current-PPID status variable이 요청한 PPID와 같아지고 recipe alarm이 없는 상태다. Accept만으로 lot을 올리면, 뒤에 local interlock에서 막힌 tool 때문에 MES는 시작도 안 한 lot을 running이라고 주장하게 된다.
Parameter보다 state를 먼저, motion보다 parameter를 먼저 검사한다
SEMI E30의 control state model은 OFFLINE을 sub-state(equipment offline, attempt online, host offline)로, ONLINE을 LOCAL과 REMOTE로 나눈다. 장비 동작을 바꾸는 remote command가 합법인 곳은 ONLINE/REMOTE뿐이다. Tool이 ONLINE/LOCAL이면 정답은 HCACK=2다.
여기서 진단이 꼬이는 이유가 하나 있다. ONLINE/LOCAL에서도 status 조회는 그대로 답한다. S1F3을 던지면 S1F4가 정상적으로 온다. 그래서 "통신은 살아 있는데 command만 안 먹는다"가 되고, 통신 문제를 의심하기 시작한다. Host diagnostic이 control state를 LOCAL이라고 보여주지 않으면, operator가 조용히 panel을 잡고 있는 동안 누군가는 존재하지 않는 comms fault를 쫓는다.
싼 검사부터 비싼 검사 순으로 배치해서 나쁜 command가 hardware를 움직이기 전에 죽게 한다:
- ONLINE/REMOTE인가? 아니면 HCACK=2
- Command가 존재하는가? 아니면 HCACK=1
- 모든 CPNAME을 알고, 모든 CPVAL이 range와 format에 맞는가? 아니면 HCACK=3 + CPACK
- Process state가 허용하는가 (idle, active job 없음)? 아니면 HCACK=2
- 이제서야 sequence를 시작하고 HCACK=4를 돌려준다
제 몫을 하는 parameter 검사:
| Parameter | 확인 | Reject 방식 |
|---|---|---|
| Recipe / PPID | 존재, 해당 tool에서 enable, revision rule 일치 | HCACK=3 또는 recipe-not-found |
| Carrier / slot id | Format, 기대한 E87 위치에 존재, 이미 사용 안 됨 | CPACK=2 |
| Port number | 범위 안, 현재 사용 가능 | CPACK=2 |
| Numeric setpoint | Range, 단위, 소수점, engineering limit | CPACK=2 |
중요 parameter를 조용히 default 처리하지 마라. Host가 carrier id를 빼먹으면 오래된 내부 값을 물려받지 말고 CPACK=1을 돌려줘서 host가 자기 실수를 보게 한다.
Wire에 실제로 올려 본 S2F41
여기까지는 code 표 이야기다. Header는 직접 보는 편이 빠르다. Simulator의 passive listener(:5501)에 socket을 열고 Select를 끝낸 뒤 S2F41을 한 발 보냈다. Body는 손으로 SECS-II item으로 encode했다.
TX Select.req 00 00 00 0A 00 0B 00 00 00 01 00 00 02 01
RX Select.rsp 00 00 00 0A 00 0B 00 00 00 02 00 00 02 01 (+3ms)
Byte 3이 00, Select Status 0이니 SELECTED다. 이제 data message.
TX S2F41 W=1 (44 bytes)
00 00 00 28 00 0B 82 29 00 00 00 00 02 02
01 02 41 05 53 54 41 52 54 01 01 01 02 41
04 50 50 49 44 41 09 45 54 43 48 5F 42 41
53 45
장비 RX decode: len 40, sessionId 11, stream 2, wbit true,
function 41, ptype 0, stype 0, systemBytes 514
앞 네 byte 00 00 00 28은 길이 접두어다. 0x28 = 40이고, header 10 + body 30이다. 길이 접두어 자신은 이 40에 안 들어가니 socket에 실제로 나간 건 44 byte다. 00 0B는 SessionID 11.
Header Byte 2가 82다. 최상위 bit이 W-bit, 하위 7 bit이 Stream이니 stream 2에 reply 요구. Byte 3의 29가 십진수 41, Function 41이다. SType이 00이라서 control message가 아니라 data message이고, PType 00은 body가 SECS-II encoding이라는 뜻이다.
Body 30 byte가 위에서 그린 item tree 그대로다.
| Byte | 뜻 |
|---|---|
01 02 | L,2 — 최상위 list, 항목 2개 |
41 05 + 53 54 41 52 54 | A,5 START — RCMD |
01 01 | L,1 — parameter 쌍 1개 |
01 02 | L,2 — CPNAME/CPVAL 쌍 |
41 04 + 50 50 49 44 | A,4 PPID — CPNAME |
41 09 + 45 54 43 48 5F 42 41 53 45 | A,9 ETCH_BASE — CPVAL |
Format byte 읽는 법은 E5가 정해 둔 대로다. 상위 6 bit이 format code, 하위 2 bit이 뒤따르는 길이 byte 개수다. 0x41 = 0100 0001 → format code 16(ASCII), 길이 byte 1개. 0x01 = 0000 0001 → format code 0(List), 길이 byte 1개. 그래서 41 05가 "ASCII 5 byte", 01 02가 "항목 2개짜리 list"다.
그리고 S2F42는 오지 않았다. 3초를 기다렸고 아무것도 없었다.
정직하게 짚어 둔다. 이 listener는 data message에 아예 답하지 않는다. SType 1(Select.req), 5(Linktest.req), 9(Separate.req)만 처리한다. 그러니 침묵의 원인은 HCACK 판단이 아니라 미구현이고, 이 capture를 tool의 command 처리 동작에 대한 증거로 쓰면 안 된다. Capture가 증명하는 범위는 encoding까지다.
다만 host 쪽에서 보이는 그림은 진짜 장애와 구분이 안 된다. Request는 나갔고, 장비는 RX를 log했고, reply는 없다. 다음 절이 정확히 그 상태에서 무엇을 하면 안 되는지에 관한 이야기다.
Retry: T3 만료는 lost reply지 무시된 command가 아니다
HSMS reply timeout T3는 S2F42가 안 올 때 발동한다. SEMI E37은 이 값을 기본값으로 강제하지 않는다. Timeout parameter 표에 typical 45초라고 적어 두었을 뿐이고 link별로 설정 가능한 값이다. Vendor가 대개 45초를 그대로 실어 보내니 현장에서는 기본값처럼 보이지만, T3 = 30초로 들어온 장비도 규격을 지킨 것이다. Hookup 전에 양쪽 실제 설정값을 적어 둔다.
T3 만료는 lost reply지 반드시 lost command는 아니다. Tool은 accept하고 이미 시작했을 수 있다. START를 무턱대고 다시 보내면 같은 lot에 process가 두 번 걸린다.
- Idempotent한가? Status query(S1F3)는 반복해도 안전하지만
START는 아니다. - 다시 보내기 전에 조회하고 reconcile한다: S1F3로 selected SV를 읽거나 PPExecName / active-command variable을 보고 실제로 무슨 일이 있었는지 확인한다.
- Enhanced remote command에 host가 지정한
DATAID를 실어서 duplicate를 감지할 수 있게 한다. - Retry 상한을 둔다. 도달하면 loop 대신 operator alarm을 올린다.
Motion, carrier transfer, recipe start, process command에서 blind automatic retry는 dropped packet 하나를 double-processed lot으로 만드는 바로 그 동작이다.
일부러 reject path를 증명한다
정상 command만 시험하면 reject path 전체가 미검증으로 남는다 — 그리고 사람을 새벽에 부르는 건 그 reject path다. 아래는 simulator, dry-run, 안전한 idle state에서 돌린다. Negative test가 실제 motion을 만들면 안 된다:
- ONLINE/LOCAL로 두고 command 전송 → HCACK=2 기대.
- EXECUTING 중에
START→ HCACK=2, 이미 요청 상태면 5. - 존재하지 않는 PPID → HCACK=3 또는 recipe-not-found.
- 범위를 벗어난 numeric CPVAL → HCACK=3, CPACK=2.
- 필수 CPNAME 누락 → HCACK=3, CPACK=1.
- Valid command를 보낸 뒤 완료 CEID 전에 HSMS link를 끊기 → host가 실패로 단정하지 않고 reconcile하는지 확인.
- 같은 command 두 번 → duplicate 처리 확인.
- Interlock이나 alarm active → HCACK=2.
Host 화면에 있어야 하는 것
- GEM communication state (E37 selected 여부)와 control state — ONLINE/REMOTE vs LOCAL
- Equipment process state (E30 processing model)
- Active alarm summary (ALID)
- Active control job, process job, carrier, PPID (E94 / E40 / E87)
- 진행 중인 command와 마지막으로 돌려준 HCACK
여기서 "failure" 대부분은 protocol 문제가 아니다. Tool이 그 command가 불법인 state에 앉아 있는데 host에는 아무것도 안 보이는 것이다.
Handover 전에 command마다 S2F41 → S2F42 → 완료 CEID로 이어지는 message trace를 한 번씩 남기고, 옆에 equipment state와 MES record를 붙여라. 그리고 reject case에도 똑같이 한다. Wrong-state나 bad-parameter command에 대해 그 trace를 못 만들면 reject path는 증명되지 않은 것이고, 이는 새벽에 사람을 부를 가능성이 가장 큰 절반을 한 번도 시험하지 않았다는 뜻이다.
장비를 붙잡기 전에 자기 driver가 S2F41을 어떤 byte로 조립하는지부터 확인하고 싶으면 SECS/GEM 시뮬레이터에 socket을 열고 위 capture를 그대로 재현해 보면 된다. RCMD와 CPNAME/CPVAL 쌍이 생각한 자리에 들어가는지, format byte가 맞는지까지는 여기서 잡힌다. HCACK 판단은 안 잡힌다. 그건 tool 몫이다.