SECS/GEM协议实战:基于secsgem源码的半导体EAP通信开发指南
2026/9/23 17:09:03 网站建设 项目流程

简介:这份资源是面向半导体设备自动化领域开发者的 SECS/GEM 协议源码包,适合从事 EAP 系统开发、设备通信对接的工程师以及希望深入理解 SEMI 标准的进阶学习者。它解决了半导体设备与上层系统之间通信协议实现的问题,涵盖 GEM、SECS 与 HSMS 三大核心模块,并配有完整的测试用例与文档参考。压缩包共 97 个文件,约 158KB,以 46 个 Python 源码文件为主体,辅以 38 个 rst 文档、若干 yml 与 sh 脚本,以及 Makefile、setup 配置和 LICENSE 等工程文件,目录结构清晰,便于按模块检索与二次开发。目前已有 791 人学习下载,说明该源码在半导体 EAP 圈内具备一定参考价值。读者可从中获取协议栈的完整实现思路、设备端与主机端示例代码、通信日志处理方式及单元测试组织方法,适合作为协议学习与项目迭代的起点。

1. 从一份 secsgem-master.zip 说起:半导体 EAP 通信为什么绕不开 SECS/GEM

半导体产线上,一台刻蚀机、一台量测机台、一台 CMP 设备,背后都挂着一个 EAP(Equipment Automation Program)。EAP 要做的核心事情只有一件:让 MES 和设备之间能对话。而设备侧说的“语言”,就是 SEMI 组织定义的 SECS/GEM 协议族。你手上这份secsgem-master.zip,正是一套用 Python 实现的 SECS/GEM 通信库源码,覆盖了 SECS-II 消息编解码、HSMS 传输层、GEM 状态机等关键模块。

很多人第一次接触半导体协议,会以为它和普通 TCP 上位机差不多,写个 socket 收发字符串就完事。真正上手才发现:SECS-II 是二进制流格式,消息体按 item 树递归编码;HSMS 有独立的会话状态机;GEM 又规定了设备必须响应哪些标准消息。这份源码的价值,就在于把这三层都拆开摆在你面前,让你能直接读实现、改参数、跑联调。适合谁?做设备自动化、MES 对接、机台数据采集的工程师,以及需要自研 EAP 或做协议网关的团队。

2. SECS-II 消息编解码:item 树、格式码与 secsgem 的解析入口

2.1 为什么 SECS-II 不能当字符串处理

SECS-II 消息体是一棵 item 树。每个 item 由三部分组成:格式码(Format Code)、长度字节数(Length Bytes)、实际数据。格式码占 1 字节,高 6 位表示数据类型,低 2 位表示长度占几个字节。比如0x21表示 2 字节长度的 Binary,0xA1表示 2 字节长度的 ASCII。如果你用普通字符串去解析,遇到长度字节数变化、列表嵌套、有符号整数补码,立刻就会错位。

secsgem里对应的模块是secsgem/secs/variables.pysecsgem/secs/functions.py。前者定义了各种 item 类型(ArrayBinaryBooleanU1~U8I1~I8F4/F8AJIS8等),后者负责把消息函数号(Stream/Function)和 item 树组装成完整报文。读源码时建议从functions.pyencode方法入手,它会把L(List)递归展开。

2.2 用源码跑一次消息编码

下面这段代码演示如何构造一条 S1F1(Are You There)并编码成字节流,直接调用库里的类:

from secsgem.secs.functions import SecsS01F01 from secsgem.secs.variables import Array, U1 # S1F1 通常无消息体,这里演示带一个 U1 列表的构造方式 msg = SecsS01F01() # 手动塞一个 Array 进去,观察编码结果 msg.data = Array(U1, [1, 2, 3]) encoded = msg.encode() print(encoded.hex()) # 输出形如:010100000003010203 # 01 01 是 S1F1,00 00 是后续长度占位,03 是列表长度,01 02 03 是三个 U1

逻辑说明:SecsS01F01继承自SecsStreamFunctionencode()会先写 Stream/Function 字节,再写消息体长度,最后递归写 item。参数说明:Array(U1, [...])的第一个参数是元素类型,第二个是 Python 列表;换成U4时每个元素占 4 字节,长度字段也会随之变化。常见坑是长度字节数选错——当数据超过 255 字节时,必须用 2 字节长度,否则接收端解析直接崩。

2.3 解码时最容易踩的三个坑

坑点现象处理方式
长度字节数判断报文错位,后续 item 全乱读格式码低 2 位,决定读 1/2/3 字节长度
List 嵌套深度递归解析栈溢出或漏读用递归函数,遇到L格式码继续展开
有符号整数I1/I2/I4 出现负数异常按补码解释,不要当无符号读

secsgemvariables.py里每个类型都有decode类方法,直接复用比自己手写稳。如果你要对接非 Python 的 EAP,建议把这份解码逻辑当参考实现,逐字段对照 SEMI E5 标准文档验证。

3. HSMS 传输层:连接状态机、Select 流程与 secsgem 的会话管理

3.1 HSMS 和 SECS-I 的区别,以及为什么现在都用 HSMS

SECS-I 走串口,速率低、布线麻烦,一条线只能挂一台设备。HSMS(High-Speed SECS Message Services)走 TCP,支持多会话、高吞吐,是现在 EAP 对接的主流。HSMS 有自己的控制消息:Select.req、Select.rsp、Deselect.req、Linktest.req 等。设备侧和主机侧必须先完成 Select 握手,才能发数据消息。

secsgem里 HSMS 相关代码在secsgem/hsms/目录,核心是connection.pypackets.pypackets.py定义了 HSMS 报文头(10 字节:2 字节会话 ID、1 字节流、1 字节功能、1 字节 PType、1 字节 SType、4 字节系统字节),connection.py维护状态机:NOT_CONNECTEDCONNECTEDSELECTED

3.2 用源码起一个 HSMS 被动端

下面演示如何用库里的HsmsPassiveConnection监听端口,等待主机 Select:

from secsgem.hsms import HsmsPassiveConnection # 被动端:设备侧,监听 5000 端口 conn = HsmsPassiveConnection(address="0.0.0.0", port=5000) # 注册 Select 请求回调,握手成功后打印 @conn.events.select def on_select(session_id): print(f"session {session_id} selected") conn.start() # 保持运行,等待主机连接 input("按回车退出\n") conn.stop()

逻辑说明:HsmsPassiveConnection内部会先 accept TCP,再等对端发 Select.req,收到后回 Select.rsp 并触发select事件。参数说明:address填本机监听地址,port默认 5000,实际项目里按设备手册改;session_id用于区分同一 TCP 上的多个会话,通常设备侧固定为 0 或 0xFFFF。常见坑是防火墙没放行端口,或者主机侧 T3(回复超时)设得太短,Select.rsp 还没发出去就判超时。

3.3 Linktest 与超时参数怎么设

HSMS 用 Linktest 保活,防止 TCP 假连接。secsgemHsmsConnection支持配置T3T5T6T7T8等定时器。T3 是等待回复的时间,T5 是连接分离后的重连间隔,T6 是 Linktest 间隔,T7 是未 Select 时的超时,T8 是网络字符间超时。半导体现场常见做法是 T3 设 45 秒、T5 设 10 秒、T6 设 30 秒、T7 设 10 秒、T8 设 5 秒,但具体要看设备手册,不能照搬。

提示:T3 设太小会导致正常但稍慢的设备频繁超时重发,设太大则故障发现慢。联调阶段建议先用默认值,稳定后再按现场网络质量微调。

4. GEM 状态机与标准消息:secsgem 里设备行为怎么落地

4.1 GEM 不是协议,是行为规范

GEM(Generic Equipment Model)规定了设备在什么状态下该响应什么消息。比如设备必须支持 S1F1(Are You There)、S1F13(Establish Communications)、S2F17(Date and Time Request)、S5F1(Alarm Report)等。EAP 侧要做的,是维护设备状态模型:Control State(设备控制权在谁手里)、Processing State(是否在跑片)、Communication State(通信是否正常)。

secsgemsecsgem/gem/目录下有EquipmentHost两个类,分别对应设备侧和主机侧。Equipment类里内置了标准消息的默认处理,你只需要覆盖需要自定义的部分。比如 S1F13 默认回 S1F14,S2F17 默认回当前时间。

4.2 自定义一条 S2F17 时间响应

下面演示如何覆盖默认的时间响应,返回固定格式:

from secsgem.gem import Equipment from secsgem.secs.functions import SecsS02F17, SecsS02F18 from secsgem.secs.variables import A class MyEquipment(Equipment): def _on_s02f17(self, handler, message): # 自定义时间格式:YYYYMMDDhhmmss import datetime now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") return SecsS02F18(A(now)) eq = MyEquipment() eq.enable()

逻辑说明:_on_s02f17是库预留的钩子,收到 S2F17 时被调用,返回值会被自动编码成 S2F18 发回。参数说明:A(now)构造一个 ASCII item,长度自动计算;如果要返回多个字段,用Array包起来。常见坑是时间格式和设备侧期望不一致,导致 MES 解析失败,联调时务必和对方确认格式。

4.3 事件与报警上报的写法

GEM 要求设备主动上报事件(S6F11)和报警(S5F1)。secsgem里通过trigger_eventsend_alarm触发。事件需要预先定义 CEID(Collection Event ID)和关联的 VID(Variable ID),这些在Equipment初始化时注册。报警则要指定 ALCD(Alarm Code)和 ALID(Alarm ID)。实际项目里,CEID 和 ALID 由 MES 侧统一下发,EAP 不能自己乱编。

注意:S6F11 的消息体结构复杂,包含 Data ID、CEID、Report 列表,每个 Report 又含 VID 和值。用secsgemReports类组装比手写字节流可靠得多。

5. 联调排错与二次开发:从日志、抓包到自定义 Stream/Function

5.1 打开 secsgem 的日志看报文

联调时最有效的手段是看原始报文。secsgem用 Python 标准 logging,把secsgemlogger 级别调到 DEBUG,就能看到每条收发的十六进制和解析结果:

import logging logging.basicConfig(level=logging.DEBUG) logging.getLogger("secsgem").setLevel(logging.DEBUG)

日志里会打印Sent: 010100000000这类内容,对照 SEMI E5 文档逐字节核对。如果解析报错,先看格式码和长度字节,再看 item 嵌套层级。常见错误是设备侧发了非标准格式码,库不认识直接抛异常,这时需要扩展variables.py里的类型映射。

5.2 用 Wireshark 抓 HSMS 报文

HSMS 走 TCP,Wireshark 能直接抓。过滤条件用tcp.port == 5000,然后 Follow TCP Stream。HSMS 报文头固定 10 字节,前 4 字节是长度,接着是会话 ID 和 Stream/Function。如果 Wireshark 没解析成 HSMS,可以手动按偏移看:第 5 字节是 PType,第 6 字节是 SType,SType=1 是 Select.req,SType=2 是 Select.rsp,SType=9 是 Linktest.req。

5.3 自定义一条非标准 Stream/Function

有些设备厂商会扩展私有消息,比如 S64F1。secsgem支持自定义:

from secsgem.secs.functions import SecsStreamFunction from secsgem.secs.variables import U4 class SecsS64F01(SecsStreamFunction): _stream = 64 _function = 1 _data_format = [U4] # 消息体是一个 U4 # 注册到设备侧处理 class MyEquipment(Equipment): def _on_s64f01(self, handler, message): value = message.data[0].get() print(f"收到私有消息,值={value}") return None

逻辑说明:_stream_function定义消息号,_data_format定义消息体结构。参数说明:U4表示 4 字节无符号整数,多个字段用列表按顺序排列。注册后,收到 S64F1 会自动调用_on_s64f01。常见坑是私有消息的格式码和标准冲突,建议先和对方确认字节布局,再用variables.py里的类型拼装。

5.4 性能与稳定性上的几个经验值

高吞吐场景下,HSMS 单连接每秒几百条消息很常见。secsgem默认单线程处理,如果消息量大,建议把耗时逻辑(如数据库写入)放到独立线程,避免阻塞收发。另外,TCP 的 Nagle 算法会攒小包,联调时如果发现延迟高,可以在 socket 上设TCP_NODELAYsecsgemHsmsConnection里可以找到对应设置点,按需打开。最后,长时间运行要关注内存,item 树递归解析如果遇到畸形报文可能爆栈,建议在解码入口加深度限制。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询