十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

I2C门级仿真SDA毛刺误触发START的定位与根治

I2C门级仿真SDA毛刺误触发START的定位与根治 做数字IC验证这些年让我印象最深的一类问题不是功能逻辑写错而是“仿真里出现、上板也可能出现”的时序毛刺。这次要聊的是I2C门级仿真中一个非常典型的坑SDA线上的Delta Cycle毛刺把从机误触发成START导致整条通信链路错乱。我在项目里花了大概两天定位最后从设计、仿真、验证三个层面同时做了整改总算把这个“幽灵”彻底按死。这篇报告就把整个排查思路、根因和根治方案完整拆给你尤其适合做数字IC验证、FPGA开发以及写I2C从机协议逻辑的工程师。问题的表象很简单门级仿真跑回归偶发出现从机应答异常波形上看主机正常发起START但从机状态机就是跑飞有时候还会多回一个ACK。刚开始以为是地址匹配问题后来才意识到是SDA上出现了一个极为短暂的毛刺被从机的START检测逻辑误认为协议起始条件。这个毛刺究竟怎么产生的为什么RTL仿真里看不见门级仿真里却被放大成“事故”下面一步步讲。1. 问题现象SDA毛刺误触发START从机状态无故跳变1.1 I2C协议里SCL高电平期间的SDA变化是“禁地”I2C是同步串行总线靠SCL时钟沿采样SDA数据。协议里有一条铁律除了START和STOPSDA只能在SCL为低电平时变化。START条件的定义是SCL保持高电平期间SDA由高变低STOP则是SCL保持高电平期间SDA由低变高。从机和主机的协议引擎本质都在检测这个沿。主机发出一个合法的START后总线上所有从机都知道“新的一帧开始了”然后开始接收地址。如果SDA在SCL高电平期间出现一个毛刺哪怕只有几个纳秒的低脉冲只要毛刺沿足够陡、幅度足够低从机的前端检测电路就会把它当成START。后续地址、ACK、数据全都错位状态机当然也就乱了。所以I2C从机设计里SDA输入路径的稳定性和毛刺免疫能力比一般信号要求高得多。很多从机IP在SDA输入后都会加同步器和数字滤波就是为了挡这一类问题。但这次遇到的是输出侧的SDA三态驱动路径出问题而且只在门级仿真里暴露。1.2 门级仿真特有的Delta Cycle毛刺数字仿真里有一个概念叫delta cycle也叫delta步。在同一时刻内仿真器会通过多轮事件调度让所有组合逻辑迭代到最终稳定值。每一次迭代产生的中间值就是一个delta。组合逻辑越深、涉及的门越多中间翻转可能就越多。RTL仿真里我们用的是行为级描述assign语句在没有延迟的情况下所有信号理论上在同一个delta内完成传播波形上看不到中间状态。但到了门级仿真GLS综合后的网表由标准单元组成SDF文件反标了每一条路径的延迟。两个输入同时变化时如果它们到达输出门的时刻不同输出就可能短暂地出现错误电平这就是门级仿真中常见的竞争毛刺。更麻烦的是如果两个事件交错发生在同一个时间戳波形放大到最高分辨率你会看到一条“竖线”——SDA在这个时刻先拉低又拉高宽度可能是0个仿真时间单位也可能是几个皮秒。这种毛刺肉眼几乎看不到但被逻辑采样时就会变成一个真实的跳变沿。这就是Delta毛刺的威力。1.3 我当时看到的故障表现项目里我们的I2C从机跑在门级仿真环境里用VCS出波形模拟主机向从机连续写入多个字节。回归脚本每天早上跑500轮大概7到10轮会挂。挂的轮次里从机状态机跳到非法状态或者应答位错乱日志里能明显看到从机返回的ACK时序不满足协议。第一次看到波形时根本无法理解主机端的START都好好的从机为什么像发现新大陆一样重新开始后来在从机内部的start_detect信号上打了标记才发现在合法的START之后一两拍从机又内部产生了一个START标志导致状态机被二次重置。于是问题从“状态机跑飞”收敛到“从机为什么多检测到一个START”。再往后把SDA、SCL、内部start_detect三条信号放大到最高精度终于看到在SCL高电平期间SDA出现了一个极其微小的向下跌落宽度大约0.2ns刚好被start_detect的组合逻辑捕捉到。毛刺如果不展开delta默认波形窗口里根本看不出来。这一下定位方向就明确了。2. 排查过程把“看不见”的0延迟毛刺抓出来2.1 先用监视代码记录误START出现的精确时刻定位这种偶发问题不能靠肉眼盯波形。我第一时间在仿真环境里加了一段监视逻辑专门盯着SDA在SCL高电平期间的下降沿always (negedge sda) begin if (scl) begin $display(SDA fell while SCL high at %t, $time); end end这段代码很简单但非常有效。它会在每次SDA下降且SCL为高时打印时间戳不需要在波形里翻来翻去。跑完回归后日志里把所有报警时刻汇总再回到Verdi里加载波形把时间指针定位到报警点。同时我在从机内部拉了一条start_detect的中级信号出来这样能区分“合法START”和“内部误触发的START”合法START发生时从机处于空闲状态误触发发生时从机正忙着传输数据。日志里告警时刻对应的状态直接指向了后者。这一步帮我把问题范围缩小到了从机协议前端。2.2 展开Delta Cycle毛刺原形毕露在Verdi波形窗口里如果只按默认显示SDA信号看起来一直为高完全没有低脉冲。这时候就要用到“展开delta”的能力。Verdi的Signal Waveform窗口有展开Delta项的选项Questasim的波形也有类似功能。把SDA信号下方的delta事件全部展开后你能看到同一时间戳里SDA经历了1、0、1的翻转序列中间那个0状态的保持时间为0就是典型的delta毛刺。有的工程师看到宽度为0会下意识说“这是仿真假象芯片上不会存在”。这话只说对了一半。delta毛刺可以来自仿真调度竞争也可以来自门级延迟差。前者确实是仿真特有的后者在硅片上表现为极窄的真实脉冲可能被IO的寄生RC滤波掉也可能没有。所以我们不能直接忽略必须先搞清楚毛刺是从哪一级逻辑出来的。2.3 追到源头三态门与组合逻辑的竞争我在网表里沿着SDA输出路径往回追。SDA是三态总线输出侧有一个OE控制的驱动管数据端来自协议状态机。问题网表的核心逻辑大概长这样assign sda_oe (state I2C_ACK) scl_high; assign sda_dout ack_value; // 0: ACK, 1: NACK assign sda sda_oe ? ~sda_dout : 1bz;其中scl_high是SCL经过同步和展宽后的高电平指示state是状态机输出。看起来很简单但两个输入都是组合逻辑产生。在门级仿真中state和scl_high到达三态门的时间不同。如果OE和数据端在同一条物理路径上竞争就可能出现极短的“先开后关”或“先关后开”让SDA被拉到低电平后再释放形成低毛刺。再看时序关系这个毛刺发生时恰好SCL处于高电平内部start_detect组合逻辑直接把SDA下降沿当成START。从机进入START响应流程原本的次态逻辑被覆盖状态机就乱了。2.4 为什么这么窄的毛刺能触发从机START检测有人会问从机START检测逻辑难道没有同步器吗问题在于从机协议引擎通常为了性能会用异步边沿检测start_detect scl (~sda_prev sda);其中sda_prev是上一拍锁存的SDA。如果SDA毛刺发生在时钟有效沿附近采样结果可能恰好采到低电平下一拍再采回高电平内部就产生了一个单周期的高脉冲start_detect。这个0.2ns的毛刺在协议规范面前微不足道但仿真器里的寄存器和组合门都是理想开关模型只要脉冲到达触发沿附近照样被当作有效跳变。真实芯片里如果IO PAD没有滤波同样可能触发。根因不是“毛刺太窄不太可能”而是“为什么SDA输出路径会在SCL高期间产生非协议规定的跳变”。3. 根治方案设计、仿真、验证三管齐下3.1 设计层修复把SDA输出寄存器化与SCL规范对齐第一刀也是最根本的一刀改设计。I2C协议明确要求SDA只能在SCL低电平时变化那么SDA输出就不应该由state和scl_high这类组合条件直接驱动。改成在SCL下降沿统一更新OE和数据always (negedge scl or negedge rst_n) begin if (!rst_n) begin sda_oe_q 1b0; sda_out_q 1b1; end else begin sda_oe_q sda_oe_pre; sda_out_q sda_out_pre; end end assign sda sda_oe_q ? ~sda_out_q : 1bz;因为SDA只在SCL下降沿之后改变SCL高电平期间它天然保持稳定从协议上就杜绝了在SCL高跳变的可能。综合后即使OE和数据的寄存器到PAD之间有组合路径只要STA约束好它们的传播延迟不会在SCL高电平窗口内产生毛刺。这一刀改完后门级仿真里SDA不再出现高电平期间的低脉冲。注意设计上还要保证sda_oe_pre和sda_out_pre在SCL下降沿前足够的setup时间否则反而会在下降沿附近引入新的竞争。对于I2C应用这一般不难做到因为SCL下降沿附近的协议变化窗口本来就宽。3.2 仿真层控制脉冲过滤与非零延迟激励设计修复的同时也需要在仿真环境里加一层保护防止这类毛刺在门级回归里继续“捣乱”。Verilog的标准单元库通常会有specify块里面可以对路径脉冲做控制。举一个库单元的示例specify (A Y) (0.5, 0.5); $width(posedge A, 1.0); PATHPULSE$ (0.2, 0.2); endspecifyPATHPULSE$表示小于0.2ns的路径脉冲不会在输出端传播这样门级仿真中那些远小于正常路径延迟的毛刺会被库模型过滤掉。实际项目中标准单元的库文件里这些参数默认可能没有开或者被工具选项覆盖。跑GLS之前最好让库/后端工程师确认一下而不是自己在仿真命令里胡乱关时序检查。还有一个非常常见的坑testbench用#0同时翻转SDA和SCL会产生纯粹由调度竞争导致的delta毛刺。门级仿真里这种毛刺会被误认为是真实毛刺干扰定位。我给TB里的I2C激励模型做了改造所有驱动都带非零延迟例如#0.1、#0.2避免0时刻多信号竞争的伪delta事件。仿真层控制只能让“仿真现象”消失并不能修芯片设计。所以仿真层是辅助设计层才是根治。3.3 验证层加固START检测增加毛刺滤波窗口为了纵深防御我又在从机的START检测前端加了一个数字毛刺滤波器。思路很简单对SDA输入做两级同步再检测下降沿只有低电平持续超过一定时间才认为有效。伪代码如下reg sda_sync1, sda_sync2; always (posedge sys_clk or negedge rst_n) begin if (!rst_n) begin sda_sync1 1b1; sda_sync2 1b1; end else begin sda_sync1 sda; sda_sync2 sda_sync1; end end wire start_pulse sda_sync1 ~sda_sync2;两级同步能消除多数亚稳态和小于一个系统时钟周期的毛刺。如果系统时钟是64MHz单周期约15.6ns远小于I2C快速模式START建立时间0.6us所以不会误杀合法START但能滤掉0.2ns的毛刺。如果你的系统时钟太快还可以用计数器做一个可配置的最小低电平脉宽窗口。这一步相当于给从机加了“免疫系统”即便未来某个版本又把SDA路径改成组合逻辑毛刺也不会直接打穿协议引擎。成本不大收益明显。3.4 三种方案的取舍和实施优先级我把三种方案列了个表方便不同角色参考方案改动位置对设计的影响仿真回归影响上板有效性SDA输出寄存器化RTL/网表中等需重新综合与STA低功能逻辑不变高从源头消除非协议跳变库脉冲过滤/激励延迟仿真环境无低可能掩盖真实问题无START检测毛刺滤波从机接收逻辑小增加几个寄存器低需重新验证中等对内部毛刺免疫我的实施优先级是先做设计层修复再做验证层滤波仿真层设置只作为防护网。千万不要本末倒置用仿真选项把问题盖过去。仿真环境里毛刺消失了上板时同一个设计重新出现那才是真正的灾难。4. 回归验证从波形到上板4.1 修复后波形对比整改完成后的第一件事不是立刻跑500轮回归而是先回到之前必挂的那个用例把SDA、SCL、内部start_detect和状态机信号拉出来。修复后波形非常干净SCL高电平区间内SDA一路保持稳定直到STOP时出现SDA由低变高。之前那个0.2ns的毛刺在展开delta后也彻底消失了。我特意保留了修复前后的波形做比对方便给组里其他同事看。这个对比图后来被写进了项目的设计审查文档作为“组合逻辑不可直接驱动三态总线”的反面教材。4.2 大规模门级回归结果设计层修复后重新综合出新的网表再把门级回归跑起来。我安排了两轮验证第一轮把之前频繁失败的写序列用例重复跑1000轮结果通过率100%。第二轮跑完整的I2C从机门级回归集包括随机时序扰动、多个从机挂总线、时钟占空比变化、温度电压角点下的SDF重反标总计约3000轮只出现了一个和本问题无关的已知配置告警误START和状态机跳变全部消除。验证组还加了一条新的断言覆盖率项SCL高电平期间SDA不能翻转。跑完整回归这条断言0次违例。这个指标被固化到后续所有的I2C相关验证计划里。4.3 上板实测情况芯片回片后我在两批评估板上分别做了压力测试。一批用标准I2C主机反复读写另一批用逻辑分析仪模拟主机做随机时序扰动。测试条件覆盖常温、高温、低压连续跑了三天没有再出现过一次误START。到这里这个从“门级仿真的Delta毛刺”引发的问题才算是真正画上句号。5. 避坑清单这些教训值几个通宵5.1 为什么RTL仿真抓不到这个BugRTL综合前组合逻辑都是理想零延迟assign sda sda_oe ? ~sda_dout : 1bz;在RTL里不会产生中间毛刺。门级仿真引入了单元延迟和路径延迟所有组合逻辑的到达时间差才暴露出来。所以很多I2C协议问题只在GLS阶段出现不要惊讶。如果你在项目里只跑RTL仿真就准备流片至少要把I2C这类异步接口做门级仿真并且跑足够多轮。毛刺类问题属于概率性事件一轮两轮很难碰到500轮起步才有统计意义。5.2 为什么加个RC滤波并不一定能根治有人建议在SDA上并联一个RC低通滤波器把0.2ns毛刺吃掉。这在某些低速板级I2C场景下有效但在芯片内部或小数控系统里不一定可靠RC会拉慢SDA的上升下降沿影响I2C高速模式的信号完整性而且RC参数要随总线负载调整很难一劳永逸。更关键的是RC滤波治标不治本SDA输出路径的竞争问题还在换个角点或频率可能又炸一次。根治的思路应该是让SDA在协议不允许变化的窗口内根本不变而不是在输出端“擦屁股”。设计层消除竞争协议层保证时序才是长期稳定方案。5.3 处理仿真毛刺时千万别乱关时序检查有些同学在门级仿真遇到毛刺第一反应是加notimingcheck或者直接关掉$setup/$hold检查让时序违例不再报错毛刺可能也不“想象”了。这非常危险。时序检查正是门级仿真存在的意义之一关了它SDF反标就是一个摆设其他时序问题会被一起掩盖。正确做法是先把毛刺到底是“仿真竞争”还是“真实路径延迟竞争”判断清楚再用库脉冲过滤或设计修复。千万别用关掉整条安全绳的方式解决局部问题。5.4 以后设计I2C这些规则要刻进DNA经过这次排查我的个人checklist里多了几条硬规则I2C的SDA输出OE和数据必须来自同步时序电路不能在SCL高电平窗口内被组合逻辑更新SDA/SCL输入必须经过同步和毛刺滤波再进协议引擎门级仿真环境必须保留SDA stability断言任何#0驱动在TB里都要清理。这些规则不只是在这次项目里有效几乎适用于所有异步串行接口设计。与其每次踩坑后再花两天排查不如一开始就把协议时序当作约束写进RTL。最后再分享一个小技巧调试这类毛刺时先把波形窗口的时间分辨率调到最小再看信号属性里的delta计数。很多EDA工具默认不展示delta但你把光标放到那个“竖线”上它会提示当前delta序号。养成这个习惯后你会发现自己对仿真调度的理解会上一个台阶。这次I2C Gate仿真的SDA毛刺问题就是靠这个习惯撬开了突破口。
返回列表