설비는 잘 돌았는데 event 이력에 구멍이 있다
Wet bench 한 대가 밤새 carrier 12개를 처리했다. 아침에 MES를 보니 process start는 모든 lot에 남아 있는데 process complete는 3분의 2 정도만 있었다. Alarm도 없고 끊긴 적도 없다. HSMS session은 밤새 selected 상태였다. 설비 엔지니어의 답은 늘 나오는 그 답이었다. "밤새 링크 살아 있었으니 MES 문제다."
아니었다. 설비가 recovery sequence 중에 report 정의를 다시 만들었고, 그 뒤 report에 붙은 RPTID가 host가 process complete CEID에 link한 적 없는 번호였다. 설비는 process complete마다 S6F11을 계속 보냈다. 다만 report list가 빈 채로 보냈다. MES는 message를 정상적으로 parse했고, 안에 lot ID가 없으니 쓸 수 없는 것으로 버렸다.
Collection event 문제는 대개 이 모양이다. 전송도 되고 parse도 되는데 내용이 비었거나 틀렸고, 어느 상태 화면에도 그 사실이 안 보인다.
성공해야 하는 message는 하나가 아니라 셋이다
SEMI E30의 dynamic event reporting은 SEMI E5 message로 오가는 3단계 handshake다. 각 단계마다 1바이트짜리 acknowledgement code가 돌아오는데 다들 습관적으로 무시한다. 코드 목록이 짧아서 driver에 그대로 넣어도 된다. "setup 완료"만 찍을 이유가 없다.
| 단계 | 요청 | 응답 | 코드(SEMI E5) |
|---|---|---|---|
| Report 정의 | S2F33 | S2F34, DRACK | 0 accept · 1 공간 부족 · 2 format 오류 · 3 RPTID 중복 정의 · 4 VID가 존재하지 않음 |
| Event에 link | S2F35 | S2F36, LRACK | 0 accept · 1 공간 부족 · 2 format 오류 · 3 CEID link 이미 존재 · 4 CEID가 존재하지 않음 · 5 RPTID가 존재하지 않음 |
| Event enable | S2F37 | S2F38, ERACK | 0 accept · 1 CEID가 존재하지 않음 |
ERACK은 정말 저게 전부다. Accept 아니면 "지정한 CEID가 없다", 그 외에는 정의된 코드가 없다. Enable 단계에는 공간 부족 같은 항목이 아예 없으니, 다른 이유로 실패하면 알려줄 코드 자체가 없다는 뜻이다.
맨 아래 응답은 따로 한 줄 쓸 값어치가 있다. S6F12는 ACKC6을 싣는데, E5는 여기에 오류 분류를 두지 않았다. 0이면 수락, 0이 아니면 host가 report를 받지 않았다는 뜻이고 이유를 적을 field가 없다. Host가 0이 아닌 값을 돌려준다면 이유는 host 쪽에서 남겨야 한다. 설비는 끝까지 모른다.
Startup에서 사람을 잡는 건 DRACK 4다. Host가 그 software version에 없는 VID를 요청하면, 표준상 잘못된 변수 하나만 빠지는 게 아니라 S2F33 전체가 거절된다. Host driver가 DRACK을 확인하지 않고 "report setup 완료"만 찍으면, 설정은 host에만 있고 설비에는 없는 상태로 몇 주를 간다.
LRACK 3(CEID link 이미 존재)은 반대쪽 문제다. 먼저 지우지 않고 기존 link 위에 덧붙이려 할 때 나온다. Startup에서 쓰는 순서는 이렇다.
S2F37,CEED = FALSE에 CEID list를 비워 전체 event를 disable.S2F35로 CEID별 report list를 비우거나, 다음 단계에 맡긴다.S2F33에DATAID를 넣고 report list를 비워서 전송 — 설비의 report 정의와 event link가 전부 삭제된다.- 실제 report 정의로
S2F33재전송. DRACK 확인. S2F35로 link. LRACK 확인.S2F37,CEED = TRUE에 필요한 CEID list. ERACK 확인.
3번은 파괴적으로 보여서 다들 건너뛴다. 건너뛰면 이전 통합 업체가 남긴 VID가 report에 그대로 남아 있는 이유를 하루 종일 찾게 된다.
그리고 define과 link가 끝나도 6번을 안 하면 아무것도 안 나간다. "Report는 설정됐는데 event를 enable한 적이 없다" — 매뉴얼에도 있고 host 설정에도 있는데 traffic capture에만 없는 CEID의 1순위 원인이다.
같은 순서를 byte로 보기
위의 1, 3, 4, 5, 6번을 socket에 그대로 올려 본 것이다. Host 쪽은 Python client, 설비 쪽은 SECS/GEM 시뮬레이터의 HSMS passive listener(127.0.0.1:5501, SessionID 11, fault mode 없음)다. 아래 frame은 전부 설비 쪽 RX log에 남았다. Message 객체를 그려낸 게 아니라 실제로 선을 탄 byte다. 원본 export는 content/demos/secs-gem-collection-event-report-notes.json에 넣어 두었다.
Data message가 유효하려면 먼저 SELECTED여야 하니 Select부터.
TX 00 00 00 0A 00 0B 00 00 00 01 00 00 00 14 Select.req
RX 00 00 00 0A 00 0B 00 00 00 02 00 00 00 14 Select.rsp, byte 3 = 00 = 수락
1번, 전체 event disable. S2F37에 CEED = FALSE와 길이 0짜리 CEID list.
TX 00 00 00 11 00 0B 82 25 00 00 00 00 00 15 01 02 25 01 00 01 00
82가 header byte 2다. W-bit 0x80에 Stream 2. 25는 header byte 3, Function 37. 25 01 00이 BOOLEAN item이다. Format code 8진수 11에 length byte 1개, payload 1바이트, 값 0 = FALSE. 그다음 01 00이 원소 0개짜리 list. E5는 이 빈 list를 모든 CEID로 읽는다. 17바이트 frame 하나로 설비 전체가 조용해진다는 뜻이다.
3번, report 정의와 link 전부 삭제. S2F33에 DATAID와 길이 0짜리 report list.
TX 00 00 00 14 00 0B 82 21 00 00 00 00 00 16 01 02 B1 04 00 00 00 01 01 00
전부 20바이트다. 그리고 다들 건너뛰는 그 message다. 진짜 define과 얼마나 안 다른지 보라. 설비의 report 설정을 밀어버리는 것과 거기에 얹는 것의 차이가 01 00이냐 01 02냐다.
4번, report 두 개를 정의하는 define.
TX 00 00 00 46 00 0B 82 21 00 00 00 00 00 17
01 02
B1 04 00 00 00 02 U4 DATAID = 2
01 02 L,2 report 정의 2개
01 02
B1 04 00 00 00 65 U4 RPTID = 101
01 03
B1 04 00 00 00 C9 U4 VID 201
B1 04 00 00 00 CA U4 VID 202
B1 04 00 00 00 CB U4 VID 203
01 02
B1 04 00 00 00 66 U4 RPTID = 102
01 02
B1 04 00 00 00 C9 U4 VID 201
B1 04 00 00 00 CC U4 VID 204
B1은 format code 8진수 54(U4)에 length byte 1개. 그러니 B1 04는 payload 4바이트, U4 값 하나다. List는 다르다. E5에서 list의 길이 field는 바이트가 아니라 원소 개수를 센다. 01 03은 3바이트가 아니라 3개다. 여기를 틀려도 TCP는 그대로 실어 보내고 받는 쪽 log에도 깨끗하게 찍힌다. 다음에 누가 "RX log 깨끗하다"를 message가 맞다는 근거로 내밀면 이걸 기억하면 된다.
여기 VID는 host가 요청한 값일 뿐이다. 201이 그 설비에 실제로 있는지를 답하는 게 DRACK 4이고, 답은 message 단위로 온다. 없는 VID 하나를 넣으면 위의 여섯 개가 통째로 거절된다.
5번과 6번, link와 enable.
TX 00 00 00 3A 00 0B 82 23 00 00 00 00 00 18
01 02 B1 04 00 00 00 03
01 02
01 02 B1 04 00 00 04 4D 01 01 B1 04 00 00 00 65
01 02 B1 04 00 00 04 4E 01 02 B1 04 00 00 00 65 B1 04 00 00 00 66
TX 00 00 00 1D 00 0B 82 25 00 00 00 00 00 19
01 02 25 01 01 01 02 B1 04 00 00 04 4D B1 04 00 00 04 4E
00 00 04 4D가 CEID 1101, 04 4E가 1102다. 이 설비에서는 process start와 process complete. Link message는 1101에 report 하나, 1102에 둘을 붙인다. Enable message는 둘 다 지정하고, 이번에는 25 01 01, BOOLEAN TRUE다. 맨 위 disable frame과 한 바이트씩 대 보면 된다. Stream 같고 Function 같고 payload 한 바이트가 다르다.
다섯 개 중 어느 것도 응답을 못 받았다. 이건 이 listener의 특성이다. HSMS control message만 구현했고 SECS-II 응답기가 없다. 설비 동작으로 읽으면 안 된다. 대신 이 글이 처음부터 말하던 그 실패 모양이 그대로 나온다. 응답 없는 setup message 다섯 개 바로 뒤에 보낸 Linktest.req는 0.75 ms 만에 답이 왔다.
TX 00 00 00 0A 00 0B 00 00 00 05 00 00 00 1A Linktest.req
RX 00 00 00 0A 00 0B 00 00 00 06 00 00 00 1A Linktest.rsp, 0.75 ms
링크는 초록불, 승인은 0건. Linktest로 interface 상태를 판단하는 host는 event가 하나도 설정 안 된 설비를 한 shift 내내 정상으로 표시한다. 여기서 돌아야 할 시계는 E37의 T3 reply timeout, typical 45초다. 내 client는 message마다 2초만 기다리고 넘어갔으니 capture의 대기 시간은 T-timer가 아니라 client 것이다.
Timing 문제는 가장 늦게 잡힌다
내용이 틀린 건 일주일이면 잡힌다. Timing이 틀린 건 양산까지 살아남는다. Report가 깨끗하게 parse되고 값도 전부 그럴듯하기 때문이다.
반복해서 나오는 세 가지.
- Metrology latch가 갱신되기 전에 process complete가 먼저 나간다. Report에는 직전 wafer 측정값이 실린다. 값이 다 정상 범위라 alarm도 안 뜬다. 같은 시각의 설비 log와 capture한 S6F11을 나란히 놓고 봐야 보인다.
- Recipe name을 event 시점이 아니라 report를 만드는 시점에 읽는다. Unload 중에 작업자가 다음 lot recipe를 미리 걸어 두면, 끝난 run이 쓰지도 않은 recipe로 기록된다. SPC 데이터가 몇 달 동안 조용히 오염된다.
- Carrier ID를 event 시점 상태가 아니라 현재 load port 상태에서 읽는다. Port 두 개를 번갈아 쓰면 스무 건에 한 건꼴로 엉뚱한 carrier에 붙는다.
고치는 쪽은 설비다. S6F11의 값은 전송 시점이 아니라 event 시점에 latch되어야 한다. 다만 그걸 증명해야 하고, 증명 방법은 늘 같다. Lot, carrier, recipe, slot, module 값을 아는 controlled run을 돌리고 timestamp와 함께 capture해서 설비 화면과 한 줄씩 대조한다.
Event 지점은 host가 내려야 하는 판단에서 고른다
설비 내부 상태 bit가 아니라 host의 판단에서 출발한다. MES, SPC, scheduler가 실제로 반응해야 하는 질문에 답하는 event만 남긴다.
- Carrier가 언제 process 가능한 상태가 되었는가.
- 물류가 움직인 시점에 실제로 어떤 recipe가 돌고 있었는가.
- 각 process module이 언제 시작하고 끝냈는가.
- Run이 정상 완료인가, abort인가, alarm stop인가.
- 이 측정값은 어느 chamber, slot, head, lane, station에서 나왔는가.
- 어떤 operator 조작이나 remote command가 상태를 바꿨는가.
PLC step마다 CEID를 만드는 건 전형적인 과잉 설계이고 비용을 두 번 낸다. 지금은 host 연동이 시끄러워지고, 나중에는 설비 software release마다 mapping이 깨진다. 안정적인 process milestone만 내보내고 내부 step 전환은 설비 진단 화면에 둔다.
GEM300 tool이면 이 부분은 이미 답이 나와 있다. E87이 carrier 상태 전환, E90이 substrate tracking, E40이 process job, E94가 control job, E157이 module process tracking을 state model과 transition event까지 정의한다. 준수한다고 하는 설비면 custom CEID를 만들기 전에 이쪽에 먼저 매핑한다. 번호가 바뀌는 건 항상 custom event 쪽이다.
Mapping table은 설비보다 빨리 썩는다
Host는 숫자를 저장하고 엔지니어는 이름으로 문제를 찾는다. CEID/RPTID/VID 표는 설비 software 밖에 둔다. 항목마다 숫자 ID, 사람이 읽는 이름, SECS item format, 단위, 변경을 승인하는 담당자까지 적는다.
Format은 생각보다 중요하다. Release가 바뀌면서 VID가 U4에서 A[16]으로 바뀌면, 고정폭 정수를 가정한 host parser가 조용히 깨진다. SECS-II에는 검증할 schema가 없고 item마다 붙은 format byte가 전부다. Host driver가 type 불일치를 log로 남기지 않으면 알아서 변환하거나 그냥 버린다.
여기서 문제는 문서가 없는 게 아니다. 문서와 실제 loaded configuration이 다른 것이다. 설비 software release에 맞춰 표를 version 관리하지 않을 거면 아예 안 쓰는 게 낫다.
S6F11 양은 용량 설계 문제다
Collection event는 의미 있는 순간을 찍는 것이지 데이터 수집 통로가 아니다. 이 구분이 무너지면 누가 장애라고 부르기 한참 전부터 host queue가 자란다.
잘라낼 만한 패턴.
- 몇 초 간격 heartbeat성 상태를 CEID로 발행한다.
- Lot당 한 번이면 될 recipe/parameter list를 모든 process step에 붙인다.
- 설비 쪽 debounce 없이 mechanical retry나 state bounce 때 같은 event가 반복된다.
- 모델명, serial, software version 같은 고정값을 매 report마다 다시 보낸다.
고주기 값은 trace data(S2F23)나 historian 경로를 쓰고 S6F11은 milestone에 남긴다. 설비가 event를 쏟아내는데 전부 필요하다면, 답은 host queue를 키우는 것보다 startup 때 CEID별 enable list를 좁히는 쪽인 경우가 많다.
Offline 동작: spooling은 buffer가 아니라 약속이다
GEM spooling은 "설비가 알아서 기억한다"가 아니라 정의된 동작이다. Host가 없는 동안 설비는 설정된 stream을 spool하고, 재접속하면 host가 S6F23에 RSDC = 0(전송) 또는 RSDC = 1(폐기)로 요청하고, 설비는 S6F24에 RSDA를 실어 답한다. 0이면 수락, 0이 아니면 거절이다. 전송하는 동안 새로 생기는 message도 함께 spool되므로 순서는 끝까지 유지된다. 관련해서 긴 단절 동안 spooling이 실제로 지켜주는 것도 같이 보면 좋다.
시운전에서 실제로 정해야 하는 것.
- 어떤 stream을 spool할 것인가(전부 spool하는 건 대개 틀린 답이다).
- Spool 깊이, 그리고 가득 찼을 때 오래된 것을 버리는지 spooling 자체를 멈추는지. GEM은 둘 다 허용하고, 이 차이가 장애 구간의 앞을 잃을지 뒤를 잃을지를 결정한다.
- 일정 시간 이상 끊겼으면 host가 purge할 것인가. 내 기본값은 한 shift보다 오래된 건 재생하지 않고 버리는 쪽이다.
- MES가 spool된 event와 실시간 event를 구분할 수 있는가. 못 하면 재접속 시 쏟아진 event가 현재 상태로 처리되고, lot 12개가 같은 초에 "시작"한다.
S6F23을 기다리지 않고 재접속하자마자 queue를 쏟아내는 비준수 설비도 현장에 있다. 시험해야 한다. 안전한 step에서 네트워크를 뽑고 뭐가 돌아오는지 본다.
의도를 갖고 정해야 하는 timeout
SEMI E37은 여기에 기본값을 강제하지 않는다. Timeout parameter 표에 typical 값으로 T3 = 45초 reply, T5 = 10초 connect separation, T6 = 5초 control transaction, T7 = 10초 not selected, T8 = 5초 network intercharacter를 적어 두었을 뿐이고, 전부 link별로 설정 가능한 값이다. Vendor가 typical 값을 그대로 실어 보내니 현장에서는 기본값처럼 동작한다. 다만 T3 = 30초로 들어온 설비에 표준을 들이밀 수는 없다. 그것도 규격을 지킨 것이다. 대부분 손대지 않고 대체로 그래도 된다. 다만 T3 45초는 죽지 않고 그냥 느린 host의 경우, MES가 DB write를 끝내기 전에 설비가 timeout을 내고 retry/spool 동작으로 넘어간다는 뜻이다. Host가 S6F12 경로 안에서 동기 commit을 한다면, 그 경로를 비동기로 바꾸거나 hookup 전에 설비 vendor와 T3 연장을 문서로 합의해 둔다.
Version upgrade는 CEID 번호 뒤에 숨어 가정을 깬다
설비 software update는 CEID 번호를 그대로 두고 event 동작만 바꾸는 경우가 많다. 새 release를 받기 전에 비교할 것.
- 추가, 삭제, 이름이 바뀐 CEID.
- 기존 report에 끼어든 새 VID — report 값을 RPTID 구조가 아니라 위치로 읽는 host면 전 field가 밀린다.
- Format 변경: 정수 폭, ASCII 길이, list 중첩.
- Event timing 변경(latch 갱신 후 보고에서 전 보고로).
- Status code 의미 변경.
- Software load 후 default report-link configuration. 이게 원복되는 일이 꽤 잦다.
생산에 중요한 event는 capture해 둔 실제 S6F11 sample을 parse하는 host 쪽 regression test를 작게라도 유지한다. TCP session이 붙는지 확인하는 건 아무것도 증명하지 않는다.
시운전 절차, 순서대로
생산에 중요한 sequence마다 이렇게 한다.
- Report 정의와 link를 전부 지운다(
S2F33에 빈 report list). - Report define, CEID link, event enable을 순서대로 하되 각 단계에서 DRACK, LRACK, ERACK을 확인한다.
- Lot, carrier, recipe, slot, module 값을 아는 controlled lot을 돌린다.
- S6F11/S6F12 traffic 전부를 timestamp와 함께 capture한다.
- 각 CEID를 눈으로 본 물리 sequence와 대조한다.
- Report 값은 interface manual이 아니라 설비 화면, PLC 상태, 설비 log와 대조한다.
- 설비 제어 software를 restart하고 link가 살아남았는지 본다. 그다음 host를 restart하고 반대쪽에서도 같은 것을 확인한다.
- 안전한 step에서 host 연결을 끊고 spooling, S6F23 교환, duplicate 처리를 확인한다.
- Capture한 message set을 해당 설비 software version의 baseline으로 보관한다.
다들 잊는 산출물이 이 baseline이다. 여덟 달 뒤 MES 불일치가 나오면, 정상 capture와 오늘 traffic을 diff해서 20분이면 정리된다. GEM manual을 놓고 다투면 며칠이 걸리고, 대개 양쪽 다 서로 다른 software version에 대해 맞는 말을 하고 있었다는 결론으로 끝난다.
Handover에 넣을 것
GEM manual은 handover 문서가 아니다. 운영에 필요한 건 실제로 동작하는 configuration이다. 이름·단위·format이 붙은 CEID/RPTID/VID mapping, host 쪽 setup sequence 원문, normal run·abort·alarm stop·reconnect 상황의 S6F11 capture, 합의한 spooling과 duplicate detection 규칙, production-critical event group별 담당자 이름.
승인 전에 하나만 더 해 본다. 중간에 delete 없이 enable sequence를 연속 두 번 돌려서 조용한 성공이 아니라 LRACK 3이 돌아오는지 확인한다. 이 코드를 삼키는 host driver는 첫날에는 멀쩡해 보이고 설비가 처음 recovery하는 날부터 어긋나기 시작한다.
설비 앞에 서기 전에 순서를 연습해 두기
출하일에 실제 설비를 붙잡고 DRACK, LRACK을 연습할 수는 없다. 대신 driver를 SECS/GEM 시뮬레이터에 붙이고, 위 capture처럼 내 socket에서 실제로 나간 게 뭔지 되읽어 보면 된다. Header, item format, list 개수, DATAID/RPTID/CEED 자리. Driver 버그는 대개 여기 있고 확인 비용도 싸다.
못 하는 것도 분명히 하자. 이 listener는 HSMS control message까지만 답하고 그 위는 아무것도 안 한다. DRACK도 LRACK도 ERACK도 돌아오지 않으니, 이 글의 절반인 acknowledgement 쪽은 여기서 연습이 안 된다. Recovery 중에 RPTID를 다시 매기는 설비도 재현 못 한다. 그건 설비만 한다. Byte 확인용이지 conformance test가 아니다.