简介:这份资源围绕半导体制造领域通用的SECS/GEM通信协议展开,面向半导体设备开发工程师、MES系统集成人员及自动化方向的学习者,帮助理解设备与主机之间的通信机制与行为模型。SECS负责物理层、链路层及应用层消息规范,GEM则定义设备状态模型、数据报告与操作命令,是设备远程控制与数据采集的基础。压缩包共72个文件,约296KB,以C#源码(.cs)、项目配置(.csproj、.sln、props、targets)、编译产物(dll、pdb、exe、bin)及缓存索引文件为主,另含少量json、txt说明,整体构成一个可编译运行的通信Demo工程,便于对照协议分层结构阅读代码实现。目前已有604人学习下载,适合希望从代码层面理解SECS/GEM消息交互、快速搭建验证环境或为自研设备通信模块提供参考的读者。
1. 半导体设备通信的隐形基石:为什么 SECS/GEM 是晶圆厂里最不该被忽视的协议
在晶圆厂里,一台价值数千万的刻蚀机如果无法与上层 MES 系统对话,它就只是一台昂贵的孤岛设备。SECS/GEM(SEMI Equipment Communications Standard / Generic Equipment Model)正是打破这种孤岛状态的通用语言。它不控制工艺配方,也不决定刻蚀速率,但它决定了设备能否被自动化调度、能否上报状态、能否接收远程指令。很多工程师第一次接触 SECS/GEM 时,会觉得它像一套陈旧、晦涩的串口协议,但真正让产线跑起来的,恰恰是这套看似不起眼的通信标准。搞懂它,才能理解半导体制造为什么能实现全自动化,以及当设备“失联”时,问题到底出在协议栈的哪一层。
2. SECS/GEM 的协议分层:从物理串口到消息语义的完整映射
2.1 SECS-I 与 HSMS:两种底层承载的取舍
SECS/GEM 并不是一个单一协议,而是一组标准的集合。最底层是消息传输层,历史上分为 SECS-I 和 HSMS 两条路线。SECS-I 基于 RS-232 串行物理层,采用块传输机制,每个数据块最大 254 字节,通过握手信号保证可靠性。它的设计年代很早,带宽有限,但胜在实现简单、确定性高。HSMS(High-Speed SECS Message Services)则运行在 TCP/IP 之上,用 TCP 的流式传输替代串行块,支持更高的吞吐量和更灵活的组网方式。
常见的实现思路是:老设备或对时序要求极严的场景仍保留 SECS-I,而新建产线几乎全部采用 HSMS。HSMS 的核心在于它定义了一套会话状态机,包括 NOT-CONNECTED、CONNECTED、NOT-SELECTED 和 SELECTED 四个状态。只有进入 SELECTED 状态,双方才能交换 SECS-II 消息。这个状态机解决了 TCP 连接建立后“谁先说话”的问题,也提供了独立的链路检测机制。
# HSMS 会话状态机的简化模拟 class HSMSState: NOT_CONNECTED = 0 CONNECTED = 1 NOT_SELECTED = 2 SELECTED = 3 class HSMSSession: def __init__(self): self.state = HSMSState.NOT_CONNECTED self.session_id = 0 def on_tcp_connect(self): # TCP 三次握手完成后进入 CONNECTED self.state = HSMSState.CONNECTED def send_select_req(self, session_id): # 发送 Select.req 尝试进入 SELECTED self.session_id = session_id # 实际实现中会等待 Select.rsp self.state = HSMSState.SELECTED def send_data(self, payload): if self.state != HSMSState.SELECTED: raise RuntimeError("链路未选中,无法发送数据") # 只有 SELECTED 状态才允许传输 SECS-II 消息 return self._encode_hsms_header(payload)上面代码里的SELECTED状态是 HSMS 通信的门槛。参数session_id用于区分同一 TCP 连接上的多个会话,通常设备端和主机端会约定一个固定值。如果状态机卡在NOT_SELECTED,数据消息会被直接丢弃,这也是现场调试时“TCP 通了但设备没反应”的最常见原因。
2.2 SECS-II 消息结构:为什么用流和功能号来分类
SECS-II 是真正承载语义的一层。每条消息由 Stream(流)和 Function(功能号)唯一标识,例如 S1F1 表示“Are You There”请求,S1F2 是它的回复。Stream 按业务领域划分:S1 是设备状态,S2 是设备控制,S5 是报警,S6 是事件报告,S7 是配方管理。Function 则区分请求和回复,奇数通常为请求,偶数对应回复。
消息体采用一种自描述的数据项格式,支持 List、Binary、Boolean、ASCII、I2/I4/I8、F4/F8 等类型。每个数据项前有一个格式字节,标明长度和类型。这种设计让接收方无需预先知道消息结构就能解析出完整树形数据。常见的实现思路是:先读格式字节,再根据类型读取对应长度的数据,遇到 List 则递归解析。
def parse_secs_item(data, offset=0): """解析 SECS-II 数据项,返回 (值, 新偏移)""" fmt_byte = data[offset] offset += 1 # 高 6 位是长度字节数,低 2 位是格式码 length_bytes = (fmt_byte >> 2) & 0x03 format_code = fmt_byte & 0x03 if length_bytes == 0: length = 0 else: length = int.from_bytes(data[offset:offset+length_bytes], 'big') offset += length_bytes if format_code == 0: # List items = [] end = offset + length while offset < end: item, offset = parse_secs_item(data, offset) items.append(item) return items, offset elif format_code == 1: # Binary return data[offset:offset+length], offset + length elif format_code == 2: # Boolean return bool(data[offset]), offset + 1 elif format_code == 3: # ASCII return data[offset:offset+length].decode('ascii'), offset + length # 其他数值类型省略,逻辑类似这段解析逻辑的关键在于length_bytes和format_code的位运算。SECS-II 用这种紧凑编码节省带宽,但也要求解析器严格按规范处理边界。如果长度字节数解析错误,后续所有数据都会错位,表现为“消息内容乱码”或“解析到一半抛异常”。
2.3 GEM 模型:设备行为的标准化抽象
GEM 建立在 SECS-II 之上,它规定了设备必须实现哪些 Stream/Function、状态模型如何定义、事件和报警如何上报。GEM 的核心是状态机:设备被抽象为通信状态、控制状态和过程状态三个维度。通信状态由 HSMS/SECS-I 决定;控制状态分为 ONLINE LOCAL、ONLINE REMOTE、OFFLINE 等;过程状态则反映设备是否在处理晶圆。
GEM 还定义了事件报告机制。设备端维护一张事件表,每个事件有唯一 ID,触发时通过 S6F11 上报,附带一组数据变量(DVVAL)。主机端通过 S2F33 定义报告规则,把事件 ID 和需要采集的变量绑定。这种“订阅-发布”模式让主机不必轮询所有状态,只在关心的事件发生时收到通知。
注意:GEM 并不强制所有设备实现全部功能,而是通过能力集(Capability)声明支持哪些特性。集成时必须先读设备的 GEM 能力文档,否则会对着不存在的功能号空等。
3. 从消息到动作:SECS/GEM 在设备端的实现机制
3.1 消息分发与事务管理
设备端收到一条 SECS-II 消息后,需要根据 Stream/Function 找到对应的处理函数。常见的实现思路是维护一张分发表,键为(stream, function),值为回调函数。对于请求消息,处理完成后必须回复对应的偶数功能号;对于回复消息,则要匹配到之前发出的请求事务上。
事务管理是容易被忽视的一环。SECS-II 允许异步通信,设备可以在等待某个请求回复的同时处理其他消息。但每条请求都有一个事务 ID(在 SECS-I 中是块号,在 HSMS 中是消息头里的 System Bytes),回复必须携带相同的事务 ID。如果实现时用全局变量存事务 ID,并发场景下就会串号,导致主机收到错误的回复。
class SecsTransactionManager: def __init__(self): self.pending = {} # system_bytes -> callback self.next_id = 1 def send_request(self, stream, function, payload, callback): sys_bytes = self.next_id self.next_id += 1 self.pending[sys_bytes] = callback # 实际发送时把 sys_bytes 写入 HSMS 消息头 self._send_raw(stream, function, payload, sys_bytes) def on_reply(self, sys_bytes, stream, function, payload): cb = self.pending.pop(sys_bytes, None) if cb: cb(stream, function, payload) else: # 收到未知事务 ID 的回复,通常是超时后重复回复 passsystem_bytes是事务匹配的唯一依据。参数callback在请求发出时注册,收到回复时触发。如果设备端在超时后重发请求,而主机端对同一请求回复了两次,第二次就会因为pending中已无对应项而被丢弃。这种设计避免了重复处理,但也要求超时时间设置合理。
3.2 事件报告与变量采集的时序
事件上报的典型流程是:设备内部状态变化触发事件,GEM 层根据已定义的报告规则组装数据,通过 S6F11 发送。主机收到后回复 S6F12 确认。如果主机没有定义任何报告规则,设备即使触发了事件也不会发送消息,这是很多“设备明明报警了但主机没记录”问题的根源。
变量采集分为两种:事件触发的 DVVAL 和主机主动请求的 S1F3/S1F4。前者是推送模式,后者是拉取模式。GEM 标准建议对实时性要求高的状态用事件推送,对配置类数据用主动请求。实现时需要注意变量值的缓存一致性:如果设备在组装报告期间状态又变了,应该以触发时刻的快照为准,而不是发送时刻的实时值。
3.3 配方管理:S7 消息的原子性保证
配方管理通过 S7 系列消息实现。S7F1/S7F2 用于请求配方上传或下载的权限,S7F3/S7F4 传输配方主体,S7F5/S7F6 请求配方列表。配方数据通常较大,可能跨越多个 SECS-II 消息。GEM 要求设备在处理配方传输时保持原子性:要么完整接收并生效,要么完全丢弃,不能出现半份配方。
常见的实现思路是:设备端在收到 S7F1 后分配一个临时缓冲区,后续 S7F3 的数据追加到缓冲区,直到收到结束标志或达到预期长度,再一次性写入配方存储区。如果中途通信中断,缓冲区被丢弃,原配方不受影响。这种“先缓存后提交”的模式避免了配方损坏导致产线停机的风险。
4. 避坑与排查:SECS/GEM 集成中最容易翻车的几个点
4.1 现象:TCP 连接成功但设备无响应
原因:HSMS 状态机停留在 NOT-SELECTED,双方没有完成 Select 握手。很多调试工具只显示 TCP 连接状态,不显示 HSMS 会话状态,导致误判。
解决:用支持 HSMS 状态显示的抓包工具确认 Select.req/Select.rsp 是否交换。检查 session_id 是否匹配,以及设备端是否配置了允许的主机 IP。
4.2 现象:消息解析到一半报数组越界
原因:SECS-II 数据项的长度字节数解析错误,或者消息体被截断。常见于手动拼接消息时漏掉了 List 的嵌套长度。
解决:在解析器中加入边界检查,每读一个数据项都验证剩余长度是否足够。用已知正确的消息样本做单元测试,覆盖 List 嵌套和空值场景。
4.3 现象:事件上报丢失或重复
原因:报告规则未定义导致事件被静默丢弃;或者事务 ID 管理不当,超时重发后主机重复处理。
解决:在设备端日志中记录事件触发和报告发送的完整链路。检查 S2F33 定义的报告规则是否覆盖了所有需要的事件 ID。事务管理器要保证超时后清理 pending 项,避免内存泄漏和重复回调。
4.4 现象:配方下载后设备报配方校验失败
原因:配方数据在传输过程中被截断或编码不一致。SECS-II 的 ASCII 类型不处理字符集转换,如果主机端用 UTF-8 而设备端按 ASCII 解析,非 ASCII 字符会出错。
解决:配方内容尽量使用纯 ASCII。传输前计算校验和,通过 S7F3 的自定义数据项附带校验值,设备端接收后重新计算比对。
4.5 现象:高并发下消息串号
原因:事务 ID 用全局变量存储,多个线程同时发送请求时互相覆盖。
解决:事务 ID 必须与连接或会话绑定,每个会话独立维护计数器。发送和接收路径要保证线程安全,或者采用单线程事件循环模型。
5. 设计取舍:为什么 SECS/GEM 没有走向现代化,却依然不可替代
SECS/GEM 诞生于上世纪八十年代,它的很多设计在今天看来并不优雅:SECS-I 的 254 字节块限制、SECS-II 的紧凑二进制编码、GEM 状态机的复杂性,都让新手望而生畏。但半导体行业对确定性和兼容性的要求远高于对新技术的追求。一套已经验证了三十年的协议,意味着全球数千种设备、数百套 MES 系统之间的互操作性有保障。任何替代方案都必须先证明自己能无缝兼容现有生态,而这几乎是不可能的任务。
从实现角度看,SECS/GEM 的取舍也很明显。它把可靠性放在性能之前:每条消息都有事务 ID 和超时重发机制,事件上报需要主机确认,配方传输要求原子性。这些机制增加了实现复杂度,但换来了产线级别的稳定性。相比之下,现代 REST 或 MQTT 协议在吞吐量和开发效率上占优,却缺少半导体设备所需的严格状态机和事务语义。
如果要从零实现一个 SECS/GEM 栈,我一般会建议先吃透 HSMS 状态机和 SECS-II 解析器,这两块是地基。GEM 层可以按能力集逐步实现,不必一开始就追求全功能。测试时用模拟主机和模拟设备对打,覆盖超时、断线重连、并发请求等异常场景。验证方法上,抓包对比是最直接的:用 Wireshark 配合 HSMS 解析插件,逐条核对消息的 Stream/Function、事务 ID 和数据项结构。
从那以后我每次接手设备通信问题,都强制先确认 HSMS 会话状态,再看 SECS-II 消息日志,最后才怀疑业务逻辑。这个顺序帮我省下了大量在错误方向上折腾的时间。希望帮到你。
本文还有配套的精品资源,点击获取