← 전체 글
SECS/GEM/약 11분 읽기/— 조회

S7F6은 왔는데 레시피가 없다 — 응답은 function 번호가 아니라 body로 확인한다

S7F5를 보냈더니 S7F6이 왔다. 그런데 body는 21 01 00, PPBODY가 아니라 B 1바이트 ack였다. 응답을 function 번호로 판정하면 안 되는 이유.

SECS/GEMMESSCADA문제 해결체크리스트

설비에서 레시피 본문을 호스트로 받아오는 코드를 썼다. S7F5에 PPID를 담아 보냈고, S7F6이 돌아왔다. stream 7, function 6, SystemBytes도 보낸 값 그대로다. 여기까지 전부 맞다. 그런데 PPBODY를 꺼내려고 하면 터진다. body 전체가 세 바이트 — 21 01 00 — 이고, 저건 list가 아니라 B 1바이트 0이다. 레시피가 아니라 ack가 온 것이다.

와이어에 올라온 S7F6: 4바이트 length prefix, 10바이트 header, 그리고 PPBODY 대신 B 1바이트가 들어 있는 body 3바이트 S7F6 — 와이어에서 17바이트 length = 13 length prefix SessionID S7F6 SystemBytes body 0000000D 000B 0706 0000 00000203 210100 0123 45 6789 header byte, E37은 0부터 센다 format byte 21 — B, L 아님 PPBODY 자리에 ack

E5가 S7F6에 요구하는 모양

SEMI E5의 stream 7은 process program 전송 계열이고, 방향이 둘로 갈린다. 호스트가 설비로 밀어 넣는 쪽은 S7F3 Process Program Send이고, 그 body는 PPID와 PPBODY 두 항목의 list다. 받은 설비는 S7F4로 ACKC7 하나를 돌려준다. ACKC7 0은 받았다는 뜻이다.

호스트가 끌어오는 쪽이 S7F5 Process Program Request다. body는 PPID 하나. 그 응답인 S7F6 Process Program Data의 body는 PPID와 PPBODY 두 항목의 list여야 한다. 즉 S7F4의 body와 S7F6의 body는 모양이 아예 다르다. 하나는 코드 한 바이트, 하나는 레시피가 실린 list다. function 번호가 짝수라는 공통점 말고는 닮은 데가 없다.

그래서 S7F6을 B 1바이트로 받으면 "거부당했다"가 아니다. E5에 그런 형태의 S7F6은 없다. 파싱 실패로 처리할 상황이다.

한 가지는 분명히 해 둔다. 정상적인 S7F6, 즉 L,2 {PPID, PPBODY}가 실제로 와이어에서 어떤 바이트로 보이는지는 이번 런에서 확인하지 못했다. 아래 캡처에 있는 건 전부 비정상 쪽이고, 두 항목 list라는 요구는 E5를 근거로 쓴 것이다. 설비가 요청을 거절할 때 정확히 무엇을 보내는지도 확인하지 못했다 — 구현마다 다르다.

캡처

시뮬레이터의 passive listener를 띄우고, 밖에서 소켓으로 직접 프레임을 밀어 넣었다. SessionID 11, 포트 5501. 설비 쪽 로그에 RX로 남은 것만 아래에 옮긴다.

Select부터.

TX  00 00 00 0A 00 0B 00 00 00 01 00 00 02 01
RX  00 00 00 0A 00 0B 00 00 00 02 00 00 02 01

SType 1 Select.req에 SType 2 Select.rsp. byte 3이 00이니 받아들여졌다. SystemBytes 00 00 02 01이 그대로 돌아왔다. 여기부터 data message를 보낼 수 있다.

다음은 S1F13. E30의 communication 게이트를 통과하려면 이게 먼저다 — S1F13 전에 보낸 data message가 SxF0으로 돌아오는 이유에 그 게이트만 따로 정리해 뒀다.

TX  00 00 00 0C 00 0B 81 0D 00 00 00 00 02 02 01 00
RX  00 00 00 37 00 0B 01 0E 00 00 00 00 02 02
          01 02 21 01 00 01 02 41 15 "VX-9000 Plasma Etcher" 41 0D "SECSGEM-1.4.1"

이 응답은 모양이 제대로다. 01 02로 열리니 L,2이고, 첫 항목이 21 01 00 — COMMACK 0 — 이고, 둘째 항목이 또 L,2로 MDLN과 SOFTREV를 담고 있다. 41은 ASCII의 format byte, 15는 길이 21바이트. 같은 설비가 S1F14에서는 list를 제대로 돌려준다는 점을 기억해 두자. 뒤에서 비교할 대상이다.

이제 S7F5.

TX  00 00 00 15 00 0B 87 05 00 00 00 00 02 03 41 09 45 54 43 48 5F 42 41 53 45
RX  00 00 00 0D 00 0B 07 06 00 00 00 00 02 03 21 01 00

요청 body는 41 09 다음에 ETCH_BASE 9바이트, 설비에 실제로 올라가 있는 PPID다. 응답 길이는 13 — header 10에 body 3. 그 body가 21 01 00이다. format byte 21의 상위 6비트가 format code 8, 즉 binary이고 하위 2비트가 length byte 개수 1이다. 01은 데이터 1바이트, 00은 값 0. list의 format code는 0이고 그 format byte는 01로 나타난다. 첫 바이트 하나만 봐도 list가 아니라는 게 끝난다. format byte를 읽는 규칙 자체는 SECS-II item header와 length byte에 따로 써 뒀다.

마지막으로 S7F3을 보냈다. 비교용이다.

TX  00 00 00 1B 00 0B 87 03 00 00 00 00 02 04 01 02 41 09 45 54 43 48 5F 46 41 53 54 21 02 00 01
RX  00 00 00 0D 00 0B 07 04 00 00 00 00 02 04 21 01 00

body가 S7F6과 바이트 단위로 똑같다. 그런데 이쪽은 맞는 응답이다. S7F4의 body는 ACKC7 하나이고, 21 01 00은 ACKC7 0이다. 같은 세 바이트가 S7F4에서는 정답이고 S7F6에서는 오류다. 그래서 "ack가 왔으면 성공"이라는 판정은 stream 7에서 통하지 않는다.

여기서 쓴 것은 공개 시뮬레이터의 listener다. 설비가 아니다. 실제 설비 중에 S7F5에 ack를 돌려주는 놈이 얼마나 있는지는 모른다. 중요한 건 호스트 코드가 이런 응답을 만났을 때 어떻게 되느냐다.

호스트에서 넣을 체크

내가 S7 계열 파서에 꼭 넣는 순서가 이렇다.

  1. SType 0인지. data message가 아니면 거기서 끝.
  2. SystemBytes가 내가 보낸 값인지. 응답 짝은 여기서 결정된다 — pipelined 요청은 SystemBytes로 맞춘다.
  3. function 번호가 기대값인지. 대개 여기까지만 보고 다음으로 넘어간다. 그게 문제다.
  4. top-level item이 L,2인지. 첫 body 바이트가 01이 아니면 PPBODY는 없다. 인덱싱하기 전에 걸러야 한다.
  5. 그 list의 둘째 항목 타입이 내 PPBODY 디코더가 받을 수 있는 것인지.

4번을 assert로 박아 두는 쪽이 맞다고 본다. 조용히 빈 레시피를 돌려주는 파서가 제일 고약하다 — MES 화면에는 "다운로드 완료"가 뜨고, 레시피 테이블에는 빈 행이 하나 생긴다. 그걸 몇 주 뒤에 발견한다.

E5에는 이런 상황에 쓸 stream 9가 있다. 받은 message를 해석할 수 없을 때 보내는 S9F7 Illegal Data다. 방향 규칙과 각 S9 message가 무엇을 지적하는지는 SECS-II stream 9 오류 message에 정리해 뒀다. 보낼지 말지는 호스트 정책이지만, 최소한 로그에는 "S7F6 body가 L이 아니었다"가 바이트째로 남아 있어야 한다. 다음에 설비 업체와 통화할 때 그 세 바이트가 유일한 근거다.

위 프레임은 전부 직접 재현할 수 있다. listener를 띄우고 소켓으로 같은 바이트를 보내면 된다: SECS/GEM 시뮬레이터.