知识门户

Back

SONiC × CMIS:光模块固件工程师的开源参考实现#


一、SONiC 是什么#

SONiC(Software for Open Networking in the Cloud)是一个开源的网络操作系统(NOS),由微软于 2016 年在 OCP(Open Compute Project)峰会上首次发布,最初为 Azure 数据中心的白盒交换机设计。

其核心设计理念可以概括为三个词:解耦、开源、社区驱动

  • 解耦:SONiC 运行在标准 x86 白盒交换机上,通过 SAI(Switch Abstraction Interface)抽象层屏蔽底层 ASIC 差异(支持 Broadcom、Marvell、Nvidia Spectrum、Intel Tofino 等),上层网络功能与硬件完全分离。
  • 开源:全部代码托管在 GitHub(sonic-net 组织下),采用 Apache 2.0 / BSD 许可证,任何人可以阅读、修改、分发。
  • 社区驱动:由 Linux 基金会托管,微软、Meta、Google、阿里、腾讯、快手、Cisco、Juniper、Nvidia 等公司共同维护,每月有社区例会和版本发布。

架构概览#

SONiC 采用 容器化微服务架构,每个功能域运行在独立的 Docker 容器中:

其中与光模块直接相关的,是 PMON 容器 中的 xcvrd(Transceiver Daemon)。后文将重点展开。


二、SONiC 用在哪里#

SONiC 的部署场景已经远超最初的”微软 Azure 内部工具”:

场景代表用户规模
超大规模云数据中心Microsoft Azure、Meta、阿里云、腾讯云、快手数十万端口
电信运营商AT&T、德国电信、中国电信(部分试点)城域网/骨干网
企业园区网部分金融、高校网络中小规模
AI/GPU 集群互联多家 AI 基础设施公司高密度 400G/800G

关键特征:SONiC 运行的交换机,几乎全部使用可插拔光模块(QSFP-DD、OSFP、QSFP56 等)作为端口介质。 这意味着每一台 SONiC 交换机,都需要与光模块进行完整的 CMIS 协议交互。

对于光模块厂商而言,“通过 SONiC 兼容性验证”正在成为进入云厂商供应链的基本门槛。


三、为什么光模块固件很重要#

一颗 400G/800G 光模块内部,除了光芯片(激光器、探测器、TIA/Driver)之外,还有一颗 MCU(通常是 Cortex-M0/M3/M4 级别),运行着模块固件。这颗固件的职责:

  1. 实现 CMIS 协议栈:响应主机的所有管理请求(读/写 EEPROM、状态机驱动、应用选择、告警上报);
  2. 控制光电器件:激光器偏置、APD/TIA 增益、CDR 锁定、均衡器调节;
  3. 执行 DSP 算法(针对 PAM4 模块):信号均衡、眼图优化;
  4. 实现固件升级(CDB):支持带内固件更新;
  5. 安全与认证:部分场景需要模块认证(Module Authentication)。

固件是光模块与主机之间唯一的”语言通道”。 一颗光模块,无论光学性能多好,如果固件的 CMIS 实现有 bug——状态机卡死、寄存器映射错误、中断标志未及时清除——在主机侧的表现就是”模块不识别”或”链路起不来”。

在封闭交换机时代,主机侧 CMIS 实现是黑盒,固件工程师只能被动适配。而 SONiC 的出现,彻底改变了这一局面。


四、SONiC 中 CMIS 相关代码的结构#

这是本文的核心部分。SONiC 中与光模块/CMIS 相关的代码分布在多个仓库中,以下逐一说明。

4.1 代码仓库地图#

4.2 核心组件:sonic_platform_base/sonic_xcvr/api/public/cmis.py#

这是 SONiC 中 CMIS 协议的主机侧实现,对应固件工程师最关心的内容:

(1)内存映射(mem_maps/cmis.py)

定义了 CMIS 5.x 的全部 Page/Bank 结构:

对固件工程师的意义:这里定义了主机侧”认为”的寄存器地址。如果固件的 Page 偏移、Bank 选择与这里不一致,主机就读不到正确数据。

(2)CMIS 状态机驱动(api/public/cmis.py)

class CmisApi:
    def enter_datapath_init(self, ...):
        """驱动模块进入 DPInit 状态"""

    def get_datapath_state(self, ...):
        """读取数据通路状态"""

    def configure_datapath(self, lane, tx_enable, rx_enable, ...):
        """配置单个 lane 的 Tx/Rx"""

    def select_application(self, app_code):
        """选择应用码(400G-DR4, 800G-2xFR4 等)"""

    def get_module_state(self):
        """读取模块状态:ModuleReady / ModuleLowPwr / ..."""
python

对固件工程师的意义:这里就是”主机怎么操作模块”的完整逻辑。每一步的顺序、超时、重试策略,全部可读。

(3)CDB 实现(api/public/cmis_cdb.py)

class CmisCdbApi:
    def firmware_download(self, image):
        """通过 CDB 下载固件镜像到模块"""

    def firmware_commit(self):
        """触发模块固件切换"""

    def firmware_activate(self):
        """激活新固件"""

    def get_firmware_version(self):
        """查询模块当前固件版本"""
python

对固件工程师的意义:CDB 升级流程中主机侧的每一步(分块大小、校验方式、超时、重试)都在这里。固件端必须严格匹配。

(4)应用码编解码(codes/public/cmis.py)

class CmisCodes:
    APPLICATIONS = {
        0x01: "400GBASE-DR4",
        0x18: "800G-2xFR4",
        # ...
    }
    HOST_INTERFACE = { ... }
    MEDIA_INTERFACE = { ... }
python

对固件工程师的意义:模块通告(Application Advertisement)中写入的应用码编号,必须与主机侧的解码表一致,否则主机无法识别模块能力。

4.3 守护进程:sonic-xcvrd/xcvrd/xcvrd.py#

xcvrd 是实际运行时的守护进程,它调用上述 CMIS 库,完成:

功能说明
模块热插拔检测通过 GPIO 中断感知模块插入/拔出
模块初始化序列按 CMIS 规范驱动 Module State Machine 和 DP State Machine
DOM 轮询周期性读取温度、电压、Tx Power、Rx Power、Bias Current
中断处理读取模块中断标志,映射为系统告警
状态上报将模块状态写入 Redis StateDB
固件升级调用 CDB API 执行模块固件 OTA

关键参数(对固件工程师至关重要):

# xcvrd 中的典型配置(具体值因版本而异)
TRANSCEIVER_INIT_TIMEOUT = 60      # 模块初始化超时(秒)
TRANSCEIVER_RETRY_COUNT = 3        # I²C 读取重试次数
DOM_POLL_INTERVAL = 1              # DOM 轮询周期(秒)
DP_INIT_POLL_INTERVAL = 1          # DP 状态轮询周期(秒)
python

固件工程师可以从源码中确认:主机不会在 1 秒内连续发起多次 I²C 读取,不会在模块初始化完成前强制 Reset,超时窗口足够宽。 这些是设计固件状态机时序的直接依据。

4.4 CLI 与调试工具#

SONiC 提供了一组命令行工具,方便在交换机上直接观察模块状态:

对固件工程师而言,sfputil show eeprom 可以直接 dump 模块任意 Page 的原始字节,是验证固件 EEPROM 写入是否正确的利器。


五、如何借助 SONiC 辅助光模块固件开发#

将上述信息整合,以下是光模块固件工程师利用 SONiC 的具体方法:

5.1 以 SONiC 代码作为”主机侧规范解读”#

CMIS 规范是英文文档,许多描述存在歧义(SHOULD / MAY / RECOMMENDED)。当遇到不确定的条款时:

  1. 打开 sonic-platform-common/sonic_xcvr/api/public/cmis.py
  2. 搜索对应的寄存器/状态机节点
  3. 观察 SONiC 的实际读写顺序、位域操作、超时处理
  4. 以此为基准实现固件端

SONiC 的实现,代表了业界主流主机的行为。 因为 SONiC 被多家云厂商采用,兼容 SONiC 就等于兼容了最大的客户群。

5.2 搭建本地验证环境#

无需购买商用交换机,即可在实验室完成完整的 CMIS 交互验证:

方案 A:SONiC-VS(虚拟交换机)

  • 在 x86 服务器或虚拟机上安装 SONiC-VS
  • 通过 I²C-to-USB 转接板连接真实光模块
  • 修改 platform 驱动,将物理 I²C 总线映射到 SONiC 的 port 抽象
  • 运行 xcvrd,观察完整的模块初始化、DOM 读取、中断处理流程

方案 B:仅运行 xcvrd

  • 不安装完整 SONiC,仅提取 sonic-platform-daemonssonic-platform-common
  • 编写一个最小化的 I²C 驱动后端(对接转接板)
  • 直接调用 CmisApi 的方法,逐步验证固件的每个寄存器

方案 C:单元测试级验证

  • 参考 sonic-xcvrd/tests/ 中的 mock 测试
  • 用 Python 模拟 I²C 读写,验证固件输出的 EEPROM 内容是否符合 SONiC 的解析预期

5.3 定位兼容性问题的标准流程#

当模块在某台 SONiC 交换机上出现”不识别""链路不起来""DOM 数据异常”等问题时:

整个过程不再依赖交换机厂商的技术支持,完全自主可控。

5.4 跟踪 CMIS 新版本的实现#

CMIS 协议持续演进(5.0 → 5.1 → 5.2 → 5.3),新版本可能增加:

  • 新的应用码(如 800G-2xLR4、1.6T 相关)
  • 新的状态机节点(如 CPO 场景下的特殊低功耗模式)
  • CDB 扩展命令
  • 模块认证(Module Authentication)

SONiC 社区通常会在规范发布后数月到一年内完成主机侧实现。固件工程师可以:

  • 关注 sonic-net/sonic-platform-common 的 PR 和 Issue
  • 在 CMIS 新版本发布后,第一时间查看 SONiC 的适配进度
  • 提前根据 SONiC 的实现方向调整固件开发计划

5.5 参与社区,影响主机行为#

如果发现 SONiC 的 xcvrd 对某个 CMIS 条款的实现不合理(例如:超时太短导致某些模块初始化失败),可以:

  1. sonic-net/sonic-platform-daemons 提交 Issue,附上模块侧的日志和时序分析
  2. 提交 PR 修复(例如增加超时、调整重试策略)
  3. 在 OCP 的 SONiC 社区会议上展示问题

光模块固件工程师不再是生态的被动接受者,而是可以主动影响主机行为的参与者。


六、总结#

问题答案
SONiC 是什么?开源网络操作系统,容器化微服务架构,运行在白盒交换机上
用在哪里?全球头部云厂商数据中心、部分电信和企业网络
为什么光模块固件重要?固件是模块与主机之间唯一的协议通道,决定互操作性
SONiC 中 CMIS 库在哪?sonic-platform-common/sonic_xcvr/(协议实现)+ sonic-xcvrd(运行时守护进程)
怎么用 SONiC 帮助写固件?阅读主机侧代码、本地搭建验证环境、用 CLI 工具调试、跟踪新版本、参与社区

对于光模块固件工程师而言,SONiC 的最大价值在于:将曾经不可见的主机侧 CMIS 实现,变成了一份可阅读、可运行、可调试的开源代码。 它不是替代 CMIS 规范,而是为规范提供了最广泛采用的”参考实现”。

读懂 SONiC 的 CMIS 代码,就是读懂你的客户。


参考资源:

SONiC × CMIS:光模块固件工程师的开源参考实现
https://glinfei.space/blog/%E5%85%89%E9%80%9A%E8%AE%AF/sonic
Author 甘霖飞
Published at 2026年8月3日
Comment seems to stuck. Try to refresh?✨