ARTICLE DETAIL

资讯详情

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

同源分频时钟相位不确定:原因、风险与可靠解决策略

同源分频时钟相位不确定:原因、风险与可靠解决策略 先说一个我踩过的坑早年做一款通信芯片的FPGA验证功能仿真全绿上板之后两个分频时钟偶尔出现反相直接把后面一组并行数据全打歪了。查了三天最后定位到问题就出在“从同一个clki分频出来的两个时钟”我当时默认它们必然同相结果被现实狠狠教育了一顿。今天把这个问题的原理、风险、解决办法一次性讲透给刚好在写分频时钟、跨时钟域代码的同行提个醒。1. 从同一个时钟源分频真的只是“搭两个触发器”这么简单吗1.1 二分频时钟的基本结构与常见的“想当然”二分频电路在数字设计里可以说是最基础的单元之一。一个D触发器把反相输出QN接回D输入或者用T触发器结构在时钟上升沿到来时输出翻转一次就得到了频率为源时钟一半的时钟信号。很多工程师对这个电路的第一反应是既然div2_clk1和div2_clk2都是对同一个clki做二分频得到的那它们一定跟clki同频同相至少彼此之间也是确定的同相或者反相关系。毕竟源时钟是同一个分频逻辑也一样结果理所当然应该一致。但这里藏着一个“想当然”每个触发器的输出翻转边沿到底是clki的第几个上升沿取决于触发器的当前状态。而触发器的当前状态从上电那一刻开始本身就是一个需要被约束的变量。换句话说分频时钟相对源时钟的“相位”不是由分频电路本身天然决定的而是由分频触发器的初始状态决定的。如果两个分频触发器在复位后的初始状态不同——一个初始为0一个初始为1——那么它们后续翻转边沿就会错开一个clki周期。这个错开就是180度相位差。更麻烦的是复位信号本身如果到达两个触发器的时间有偏差或者复位释放的时刻不干净两个触发器的初始状态就可能出现随机组合这才有了标题里说的“可能同相、也可能反相”。1.2 同相与反相问题的真正边界要理解这个问题先把波形图画出来。clki时钟周期记为T两个分频时钟的周期都是2T。clki的上升沿按时间顺序编号为E0、E1、E2、E3……如果div2_clk1在E0上升沿之后从0翻到1那么它下一次翻转在E2再下一次在E4。div2_clk2如果也是在E0之后翻转那两个分频时钟完全同相如果div2_clk2是在E1之后翻转那么它在E3、E5这些奇号边沿翻转两个输出之间就存在一个T的偏移。这就是核心边界两个分频信号都“跟clki同步”——它们都在clki的上升沿附近发生变化没有任何一个出现亚稳态或延迟累计但彼此之间却可能错开一个完整的时钟周期。说白了各自同步并不等于相互之间相位确定。这个问题在芯片和FPGA设计中都是真实存在的。芯片设计里后端布局布线之后两个分频触发器可能被放在完全不同的物理位置复位路径和时钟路径的延迟差都会影响最终相位。FPGA里虽然触发器资源排列规整但复位网络的设计、综合工具对寄存器的初始化处理同样会导致相位不确定。2. 为什么相位会不确定根因拆解2.1 初始状态触发器的上电与复位不确定性任何一个触发器在上电瞬间如果没有外部复位或者复位释放不规范输出逻辑值是不确定的。在仿真环境里未复位寄存器的初值默认为X在实际芯片中可能是0也可能是1甚至上电后要经过一个不确定时间段才稳定。两个分频触发器如果各自随机初始化为0或1那组合情况就有四种初值组合div2_clk1翻转边沿div2_clk2翻转边沿相位关系都为0偶号边沿偶号边沿同相都为1奇号边沿奇号边沿同相div2_clk10, div2_clk21偶号边沿奇号边沿反相div2_clk11, div2_clk20奇号边沿偶号边沿反相从这张表能看出来只要两个分频触发器没有被明确约束到同一个初始状态同相和反相的概率各占一半。这也是为什么很多人第一次遇到这个问题时非常困惑同样的代码这次上电是好的下次上电就反了完全不确定。2.2 复位释放时刻让两个分频器“抢跑”同一个边沿有些设计是加了复位引脚的但相位依然会随机。这是比较隐蔽的情况问题不在复位本身而在复位释放的时刻。分频器工作在没有复位的常态下输出一直翻转触发器的当前状态决定了它下一次响应的是哪一个clki边沿。如果复位信号释放时刻距离某个clki上升沿太近触发器可能在这个边沿响应也可能在下一个边沿响应这就是所谓的“抢跑”。两个分频器如果抢跑的边沿不同一个在E1翻转一个在E2翻转相位就错开了。用一个现实的场景来说明。假设div2_clk1在复位释放后的第一个clki上升沿变成0此时div2_clk2如果复位释放时序上有些微差异可能已经在这个上升沿之前恢复翻转了那它就会沿用复位前的相位继续走。复位释放边沿和clki上升沿之间只要存在几纳秒的竞争结果就是一个不确定数这在板级实测里往往表现为“偶发反相”“多次上电结果不同”。所以只给分频触发器接上复位信号还不够必须保证两个触发器在同一个时钟边沿被干净地拉回确定状态并且复位释放相对clki要有足够的建立时间裕量。这就引出了数字设计里常说的异步复位同步释放。2.3 物理路径复位与时钟的偏斜也会搅局即便你把复位信号设计得很干净物理实现层面仍然有变数。两个分频触发器物理位置不同clki到达它们的时钟偏斜可能不同复位信号到达它们的偏斜也可能不同这两者叠加之后可能出现一个触发器先脱离复位、另一个后脱离复位的情况两者脱离复位后看到的第一个有效上升沿不是同一个。这在ASIC后端布局布线之后尤其明显。标准单元库里的触发器setup/hold时间都是皮秒级别但时钟树的偏斜、复位树的偏斜、单元延迟差累计起来就可能超过触发器的时序余量。而你的设计在综合阶段看到的复位时序只是理想模型到了布线阶段可能就变了。另外还有一个容易被忽略的点分频输出本身也是对clki敏感的时序路径。从clki的上升沿到div2_clkX的翻转中间要经过分频触发器内部的C2Q延迟再经过可能的缓冲器/反相器组合。这个延迟在静态时序分析里是一段确定的路径但在实际硅片上会受到工艺、电压、温度的影响。两个分频时钟各自相对clki的延迟如果不一致即便初值相同相位也不会严格相同只不过这个偏差通常是纳秒级的小量不会导致180度的彻底反相但在高速场景下依然需要关注。3. 从工程角度看相位不确定会带来什么麻烦3.1 跨时钟域风险两个“同源”时钟可能互相伤害很多工程师觉得两个时钟来自同一个源时钟那它们之间传输数据应该天然安全。这个直觉在分频时钟场景下是错误的。当div2_clk1和div2_clk2之间相位关系不确定甚至可能反相时从一个分频时钟域产生的信号被另一个分频时钟采样本质上就是一次跨时钟域传输。如果你的RTL代码直接把div2_clk1驱动的寄存器的输出接到div2_clk2驱动的寄存器输入端综合工具在时序分析中会把这条路径按两个时钟之间的相位关系去约束。如果实际相位与约束不一致采样就可能发生在被采数据正在变化的窗口内触发亚稳态。亚稳态的结果是灾难性的寄存器的输出可能在一个较长的时间里维持在无效电平然后随机落到0或1下游逻辑可能捕获到错误数据甚至导致状态机跳转到非法状态。在异步FIFO和跨时钟域接口设计里双触发器同步器是最基本的防护手段但很多临时写的分频时钟逻辑里压根没有这层防护。我见过一个案例两块单板用同样的固件一块板子的数据链路稳定跑了好几天另一块板子一上电就跑数错误。最后抓波形发现两块单板的div2_clk1和div2_clk2相位关系不一样一块恰好同相一块恰好反相。这就是典型的设计对相位关系存在隐含假设而实际制造、环境又无法保证这个假设。3.2 时序约束怎么给create_generated_clock 与 clock groups如果你确实要用分频时钟SDC约束里就必须把这些时钟的真实关系写清楚否则静态时序分析的结果会出现偏差。先要明确一点分频输出必须用create_generated_clock声明为源时钟的生成时钟不能直接把它当独立时钟去约束。基础写法是create_clock -name clki -period 10.0 [get_ports clki] create_generated_clock -name div2_clk1 -source [get_ports clki] \ -divide_by 2 [get_pins u_div1/clk_out] create_generated_clock -name div2_clk2 -source [get_ports clki] \ -divide_by 2 [get_pins u_div2/clk_out]这两条约束告诉了工具div2_clk1和div2_clk2都是clki的二分频。但这里还隐含了一个关键信息——工具默认生成时钟之间是有确定的相位关系的它会自动计算div2_clk1的翻转沿和div2_clk2的翻转沿对齐在clki的哪几个沿上。如果实际硬件中两者相位不确定这个“默认确定”就是错的。这种情况下最稳妥的做法是把这两个时钟设置为异步关系让工具不要对它们之间的路径做过于乐观的时序分析set_clock_groups -asynchronous -group {div2_clk1} -group {div2_clk2}或者对两者之间的路径设置false pathset_false_path -from [get_clocks div2_clk1] -to [get_clocks div2_clk2] set_false_path -from [get_clocks div2_clk2] -to [get_clocks div2_clk1]注意set_false_path是告诉工具“不要分析这条路径”并不代表这条路径在硬件上真的安全。如果你设置了false path就必须在RTL层面做好跨时钟域保护否则等于是把风险从静态时序分析里藏了起来问题依然会在真实芯片上爆发。3.3 仿真和实测为什么会对不上这个问题是仿真和实测差异里最经典的一类。仿真环境下未复位寄存器的初值通常是X一旦你加了复位仿真器会在这个时刻把所有寄存器赋成你指定的初值所以两个分频器复位之后永远是确定的同相关系功能仿真百发百中。到了实际芯片或FPGA上上电初值是不确定的复位信号也不是理想瞬间完成的。于是日常验证里就经常出现这样的场景RTL前仿真没问题后仿真加了门级延迟也没问题板级测试却偶发异常或者实验室里十块板子八块正常两块反相。要复现这个问题仿真时可以把两个分频器的初值分别设成不同组合跑一遍验证代码是否对相位敏感。也可以在后仿真的SDF反标里加入复位路径的偏斜人为制造复位释放时间差看看设计在反相情况下是否依然正确。这些手段能帮你提前暴露问题而不是等到贴片回来后对着示波器发呆。4. 怎么让相位关系确定下来四种可靠做法4.1 统一复位 异步复位同步释放要让两个分频器相位确定最直接的办法是让它们从复位状态开始就完全一致。保证这一点有三个要素第一两个分频触发器必须使用同一条复位信号不能在中间又加组合逻辑、又分叉后各自缓冲要保证复位路径尽量对称。第二复位释放动作必须相对clki是同步的。标准做法是异步复位同步释放复位信号异步生效把系统拉回复位态释放时先经过两级触发器同步让整个设计在同一个clki边沿脱离复位状态。这个同步后的复位信号再去接分频器和其他逻辑。第三分频触发器的复位初值必须显式设置成相同值。比如都复到0那么脱离复位后两个分频器都在同一个clki边沿被置0后续翻转边沿自然对齐。代码大致是这样reg rst_n_sync1, rst_n_sync2; always (posedge clki or negedge rst_n) begin if (!rst_n) begin rst_n_sync1 1b0; rst_n_sync2 1b0; end else begin rst_n_sync1 1b1; rst_n_sync2 rst_n_sync1; end end wire rst_n_domain rst_n_sync2; reg div2_clk1, div2_clk2; always (posedge clki or negedge rst_n_domain) begin if (!rst_n_domain) begin div2_clk1 1b0; div2_clk2 1b0; end else begin div2_clk1 ~div2_clk1; div2_clk2 ~div2_clk2; end end这里div2_clk1和div2_clk2在复位同步释放后的第一个clki有效边沿被同时采样之后每次clki边沿都同时翻转两者永久同相。如果两根分频线只是同一个信号的复制后端的时钟树工具也会尽量平衡它们的偏斜相位关系基本是可控的。4.2 用同一个计数器派生两相二分频时钟如果你想要的并不是两个相同的同相时钟而是希望制造确定性的180度反相两相时钟那更合适的方案是用同一个状态机同时产生两路输出。用一个模2计数器作为统一状态源在cnt0时翻转div2_clk1在cnt1时翻转div2_clk2。这样两路输出天然错开一个clki周期相位差永远是180度而且不会有初值竞争问题reg cnt; reg div2_clk1, div2_clk2; always (posedge clki or negedge rst_n) begin if (!rst_n) begin cnt 1b0; div2_clk1 1b0; div2_clk2 1b0; end else begin cnt ~cnt; if (~cnt) begin div2_clk1 ~div2_clk1; end else begin div2_clk2 ~div2_clk2; end end end这段逻辑的要点是整个计数器只有一个状态变量两路输出都从这个状态推导而来。不会出现两个触发器各自翻转、相位由初值拼运气的事情。这种结构在很多芯片里实际是标准做法尤其是需要I/Q两相本振信号、DDR接口的半速率时钟、开关电源的互补驱动等场景。它用同一个源时钟沿更新所有状态输出的时序关系天然确定。4.3 改用时钟使能彻底消灭分频时钟这是我在实际项目里最推荐的做法如果分频时钟只是用来给某个逻辑模块提供“慢一半”的采样节奏那根本不需要真的生成一个时钟信号用时钟使能clock enable就足够了。思路很简单主时钟仍然是clki额外生成一个clk_en信号在每个clki上升沿交替为0和1。下游逻辑只有在clk_en有效时才更新寄存器等效于在clki的奇偶边沿工作功能上与二分频时钟完全一致但整个设计里只有一个时钟域。reg clk_en; always (posedge clki or negedge rst_n) begin if (!rst_n) clk_en 1b0; else clk_en ~clk_en; end // 下游逻辑示例 reg [7:0] data; always (posedge clki or negedge rst_n) begin if (!rst_n) data 8d0; else if (clk_en) data data_in; end这样做最大的好处是从根本上消灭了分频时钟也就不存在分频时钟之间的相位不确定问题。所有逻辑都在clki域内时钟树和静态时序分析只需要处理一个主时钟打时序的难度低得多。尤其现在是深亚微米工艺多时钟域的时钟树综合、时钟偏斜控制成本很高能用一个时钟域解决的设计绝不用两个。4.4 用PLL/MMCM的专用输出相位关系交给IP核如果设计里确实需要多个不同频率、且相位关系关键的时钟比如需要clki、clki/2、clki/4同时使用还要求相位对齐那就不要自己用寄存器搭分频器了。FPGA里的PLL/MMCM、ASIC里的DLL/PLL硬核本身就是干这个的。这些专用IP内部有反馈环路和相位对齐电路可以输出多路频率不同的时钟同时保证它们之间相位关系可配置、可预测。Xilinx的MMCM可以指定CLKOUT0到CLKOUT6各自的divide值还能做精细的相位偏移Altera/Intel的PLL也类似。这些IP核输出的时钟都是从同一个VCO分频出来的相位关系由硬件保证不会出现上电随机反相这种问题。使用PLL/MMCM的时候SDC约束也要配合好。时钟树综合会把这些输出当成生成时钟处理工具会基于PLL的配置自动计算相位关系。你可以用类似下面的语句声明create_generated_clock -name clk_half -source [get_pins pll/CLKIN] \ -divide_by 2 [get_pins pll/CLKOUT0]需要注意PLL输出也不是瞬间建立的从配置完成到输出稳定需要一段锁定时间。设计里要等LOCK信号有效之后再让下游逻辑开始工作否则时钟还没稳定就开始跑一样会出乱子。5. 实操中的典型问题与排查技巧5.1 上电后 div2_clk1 与 div2_clk2 随机反相怎么快速确认我在现场排查这类问题时一般先看波形不看代码。把clki、div2_clk1、div2_clk2三根信号拉到示波器上用clki做触发源观察div2_clk1和div2_clk2的相对位置。同相的表现是两个分频时钟上升沿在时间上重合反相的表现是一个上升沿正好落在另一个下降沿附近。多做几次上电复位看相对位置是否每次都一样。如果多次测量结果在两种状态之间跳变那基本能判定是分频触发器初始状态竞争问题。如果是芯片内部时钟没法直接拉示波器就找一个观测寄存器在clki的上升沿采样div2_clk1和div2_clk2值置入一个状态寄存器通过调试接口读出来。正常同相时这两个采样值相同反相时不同。多读几次如果结果变化问题就实锤了。5.2 加了复位还是偶发失步问题出在哪我遇到过不少项目复位信号明明接了上电依然偶发反相。排查方向通常集中在下面几个第一看复位信号是否经过同步释放。很多代码里直接用一个按键触发的异步复位信号去复位分频器释放时刻完全随机。这个释放时刻如果落在clki上升沿附近两个分频器就可能出现抢跑。第二看两个分频器的复位路径是否对称。某些综合工具会对不同的复位端做不同的优化加了buffer或者进行了逻辑复制导致复位偏斜。第三看复位信号在高电平有效还是低电平有效、初值赋值是否一致。如果你一个复位到0、另一个复位到1那再同步也是反相。解决上电失步最彻底的方法是复位释放之后先观察clki连续跑几个周期再用一个使能信号把两个分频器拉入正常工作。相当于把“复位使能”两段式启动保证两个分频器从同一个边沿开始翻转之后它们的相位就永远一致了。5.3 巡检清单分频时钟设计自查表这几条是这些年反复踩坑总结出来的检查项每次设计里用到分频时钟我都会对照过一遍检查项说明通过标准分频器初值所有分频寄存器复位初值一致全部复位为0或全部复位为1复位时序复位释放是否同步于clki采用异步复位同步释放或复位后加使能门复位路径分频器复位路径是否对称无额外缓冲复位偏斜在时序余量内时序约束分频时钟是否声明为生成时钟使用create_generated_clock正确声明时钟关系分隔时钟之间的相位是否可控不可控时设置clock groups并在RTL做CDC保护跨时钟域两个分频时钟域之间是否有数据交互有交互则需同步器或异步FIFO不能裸连线仿真验证是否覆盖初值不同带来的相位变化跑反相情况下的功能仿真确认逻辑不受影响这套清单不止适用于二分频任何由同源时钟派生多个时钟的场景都可以套用。多花十分钟自查省的是上板后排障的三天时间。5.4 老工程师的一点心里话分频时钟本身是个再基础不过的电路但越基础的东西越容易在细节上翻车。我自己后来写设计能不用分频时钟就不用实在要用优先选择PLL/MMCM然后是时钟使能再然后才考虑寄存器分频并且一定要把复位、使能、时序约束全部做齐。因为这些方案带来的不只是相位确定这一个好处整个时序分析的复杂度、后端布局布线的压力、跨时钟域验证的成本都会一起降下来。如果你手头正好有一个设计在用多个分频时钟且让它们直接驱动逻辑我强烈建议你回头看看这几个问题两个分频器的初值怎么处理的复位释放同步了吗两个时钟之间有没有裸奔的跨时钟路径你现在花半天改代码很可能省下未来一个月在实验室和产线之间来回跑的时间。
返回列表