
1. 从一个反直觉的时序报告说起如果你做过一段时间的静态时序分析大概率遇到过这样一种情况工具报出一条路径的slack是负的你打开报告一看launch clock和capture clock是同一个时钟沿路径上既没有setup check也没有hold check取而代之的是一行data check。更让人困惑的是当你试图用常规的set_max_delay或者set_false_path去处理它时工具要么不认要么报出来的结果和你的预期完全对不上。这就是Data to Data Checks中文一般叫数据到数据检查在STA领域里属于那种平时不太碰、一碰就懵的类型。它和setup/hold最大的区别在于setup和hold检查的是数据和时钟之间的关系而data to data检查的是两个数据信号之间的时序关系。换句话说时钟在这类检查里退居二线真正被比较的是两条数据路径的到达时间。而零周期这个词则是data to data checks里最容易让人踩坑的地方。所谓零周期指的是launch和capture发生在同一个时钟周期内没有周期性的时钟沿来给你做参考。这就导致了一个很尴尬的局面传统的setup/hold分析框架里工具会自动帮你算一个周期的时间窗口但在零周期的data check里这个窗口需要你自己定义。我见过不少工程师在这个问题上卡住要么是不知道set_data_check该怎么写要么是写了之后发现工具报的路径和实际电路对不上。这篇文章就围绕这个核心问题展开把data to data checks的零周期挑战从头到尾拆一遍。不管你是刚接触STA的新人还是已经做过几个项目但一直没搞明白data check的老手下面这些内容应该都能帮你把这块知识补上。2. Data to Data Checks到底在检查什么2.1 从setup/hold的思维定式里跳出来大部分人对STA的理解是从setup和hold建立起来的。setup检查的是数据在时钟捕获沿到来之前必须稳定一段时间hold检查的是数据在时钟捕获沿之后必须继续保持一段时间。这两个检查的共同点是它们都依赖一个时钟周期作为时间参考。工具知道时钟周期是10ns还是5ns然后根据这个周期去算数据路径的裕量。但data to data checks不一样。它检查的是两个数据信号之间的相对时序。举个典型的例子一个多路选择器的两个输入或者一个触发器的D端和另一个触发器的D端之间存在某种协议约束——比如信号A必须在信号B之前到达或者两个信号之间的偏斜不能超过某个值。这种约束和时钟周期没有直接关系它是由电路协议或者接口规范决定的。你可以把它理解成setup/hold是数据对时钟的约束data to data是数据对数据的约束。前者是同步接口的常规检查后者更多出现在异步接口、源同步接口、或者某些特殊协议比如DDR的DQS和DQ之间的场景里。2.2 零周期到底意味着什么零周期这个词听起来很玄其实拆开看很简单。在setup/hold分析里launch clock edge和capture clock edge之间通常隔着一个或多个时钟周期这个周期数就是工具计算slack时用的时间窗口。但在data to data checks里如果launch和capture用的是同一个时钟沿或者根本没有时钟参与那么周期数就是零。零周期带来的直接后果是工具没有默认的时间窗口可以用了。在setup检查里工具知道capture edge比launch edge晚一个周期所以它用这个周期减去数据路径延迟和建立时间来算slack。但在零周期的data check里工具不知道这两个数据信号之间应该差多少时间这个差值必须由你来告诉它。这就是set_data_check命令存在的意义。它的作用就是告诉工具信号A和信号B之间有一个时序关系这个关系的时间值是多少检查的方向是什么。没有这个约束工具根本不知道要检查什么。2.3 一个具体的电路场景光说概念容易飘来看一个实际会遇到的场景。假设你有一个源同步接口发送端有一个时钟信号和一个数据信号接收端用发送端传来的时钟去采样数据。在这个场景里数据信号和时钟信号之间的偏斜skew是关键的时序指标。但如果你把接收端的采样触发器换成另一种结构比如用数据信号本身去触发另一个数据信号的捕获那么检查的对象就变成了两个数据信号之间的时序关系。再比如某些协议里规定控制信号必须在数据信号有效之前至少提前2ns到达。这个2ns就是data to data check的核心参数。工具需要知道这个值才能判断控制信号和数据信号之间的实际延迟差是否满足要求。这类场景在纯同步设计里不常见但在混合信号接口、高速串行接口、以及一些定制化的协议实现里data to data checks是绕不开的。3. set_data_check命令的实战用法3.1 命令的基本语法和参数含义set_data_check的语法在不同工具里略有差异但核心参数是相通的。以常见的EDA工具为例基本形式是这样的set_data_check -from launch_signal -to capture_signal -setup value set_data_check -from launch_signal -to capture_signal -hold value这里的-from是发起信号-to是捕获信号-setup和-hold分别对应建立检查和保持检查的时间值。注意这里的setup和hold和时钟的setup/hold不是一回事它们描述的是两个数据信号之间的时间关系。还有一个常用的参数是-clock用来指定参考时钟。在某些工具里如果不指定时钟data check会默认使用同一个时钟域下的时钟沿作为参考。但在零周期场景下这个参考时钟往往需要显式指定否则工具可能报出意料之外的结果。另外-setup和-hold的值可以是正数也可以是负数。正数表示捕获信号需要比发起信号晚到或者早到取决于检查方向负数则相反。这个符号很容易搞混后面会专门讲怎么判断。3.2 零周期场景下参数该怎么定零周期场景下最核心的问题就是-setup和-hold的值到底填多少。这个值不是拍脑袋定的它来自电路协议或者接口规范。比如某个接口规定数据信号必须在控制信号之后至少1.5ns到达那么对应的data check值就是1.5ns。但这里有个容易踩的坑工具计算slack的方式和你的直觉可能不一样。在setup检查里slack required_time - arrival_time。在data check里工具同样会算一个required time和arrival time但required time的构成方式不同。它会把-setup的值作为约束的一部分然后结合时钟路径和数据路径的延迟来算。我建议的做法是先用一个简单的例子把工具的算法摸清楚。比如构造一个只有两条数据路径、没有时钟的简单电路手动算一遍slack然后和工具报的结果对比。如果对不上就去看工具的文档确认它到底是怎么定义required time的。这一步花的时间不多但能帮你避免后面大量的困惑。3.3 一个完整的约束示例假设有一个源同步接口发送端输出一个数据信号tx_data和一个控制信号tx_ctrl接收端用rx_clk采样。协议规定tx_ctrl必须在tx_data有效之前至少提前1ns到达接收端。对应的约束可以这样写set_data_check -from tx_ctrl -to tx_data -setup 1.0 -clock rx_clk这条约束的意思是以rx_clk为参考tx_ctrl到tx_data之间需要满足1ns的建立关系。工具会根据这个约束去检查tx_ctrl的到达时间和tx_data的到达时间之间的差值是否满足要求。如果协议还规定了保持关系比如tx_ctrl在tx_data有效之后还需要保持0.5ns那就再加一条set_data_check -from tx_ctrl -to tx_data -hold 0.5 -clock rx_clk注意这里的-from和-to的顺序很重要。-from是发起信号-to是捕获信号。如果你把顺序搞反了检查的方向就反了结果自然也是错的。4. 零周期检查里最容易踩的三个坑4.1 坑一把data check当成false path处理这是最常见的一个错误。有些工程师看到data check报出负slack第一反应是这条路径不重要然后直接加一条set_false_path把它屏蔽掉。这样做在短期内确实能让时序报告变干净但问题是如果这条路径真的有时序要求屏蔽掉之后你就在报告里看不到任何风险了。更麻烦的是有些data check是协议强制要求的比如DDR接口里DQS和DQ之间的偏斜要求。如果你把它当成false path处理芯片流片后可能会在高速传输时出现数据采样错误而且这种错误在低速测试时往往看不出来等到系统联调时才暴露排查成本极高。正确的做法是先确认这条data check是否真的有时序要求。如果协议里有明确规定那就老老实实加约束、做分析、修时序。如果确认是工具误报或者约束写错了再考虑用set_false_path或者set_clock_groups去处理但一定要在约束文件里写清楚原因。4.2 坑二setup和hold的符号搞反前面提到过-setup和-hold的值可以是正数也可以是负数符号搞反是高频错误。我见过一个案例工程师在约束里写了-setup -0.5本意是捕获信号可以比发起信号早到0.5ns结果工具报出来的slack和预期完全相反。要搞清楚符号的含义关键是理解工具怎么定义正方向。在大多数工具里-setup的正值表示捕获信号需要比发起信号晚到即发起信号先到捕获信号后到负值则表示捕获信号可以比发起信号早到。但这个定义不是绝对的不同工具可能有细微差异。我的建议是不要靠记忆去判断符号而是用一个小实验去验证。构造两条延迟已知的路径分别用正值和负值去约束看工具报出的slack变化趋势。确认清楚之后再把这个规则记到自己的约束模板里。4.3 坑三忽略了时钟路径的影响虽然data check检查的是两个数据信号之间的关系但时钟路径并不是完全无关的。在大多数工具里data check的required time计算会涉及到时钟路径的延迟。如果你用的是-clock参数指定了参考时钟工具会把时钟树的延迟也算进去。这就导致了一个问题如果你在时钟树综合之前做data check分析和时钟树综合之后再做结果可能不一样。因为时钟树的延迟变了required time也跟着变了。处理这个问题的方法有两个一是在时钟树综合之前先用理想时钟做分析确认约束本身没问题二是在时钟树综合之后重新跑一遍用实际时钟延迟去验证。两个阶段的结果如果有差异要确认差异是否在可接受范围内。5. 从报告里读出真正的时序信息5.1 怎么在时序报告里定位data check路径data check路径在时序报告里的标识和setup/hold路径不太一样。setup/hold路径通常会显示launch clock和capture clock的信息而data check路径显示的是launch signal和capture signal。在报告里你可能会看到类似这样的字段Startpoint: tx_ctrl Endpoint: tx_data Path Type: data check看到Path Type: data check这一行就说明这条路径是data check路径不是常规的setup/hold路径。接下来要看的是Required Time和Arrival Time这两栏它们的差值就是slack。需要注意的是data check路径的Required Time构成和setup路径不同。setup路径的required time通常是capture clock edge的时间减去setup时间而data check路径的required time是launch signal的到达时间加上或减去约束值。这个差异导致data check的slack计算逻辑和setup不一样不能直接用setup的经验去套。5.2 关键字段的解读方法在data check报告里有几个字段需要特别关注Launch Signal发起信号的名称和到达时间Capture Signal捕获信号的名称和到达时间Data Check Value你在约束里设置的setup或hold值Required Time工具根据约束和路径延迟算出的要求时间SlackRequired Time和Arrival Time的差值解读的时候先看Slack是正还是负。如果是负的再看是Required Time太大还是Arrival Time太小。如果是Required Time太大可能是约束值设得太紧或者时钟路径延迟太大如果是Arrival Time太小可能是数据路径延迟不够需要加buffer或者调整布局。5.3 一个真实的报告分析案例假设报告里显示Startpoint: tx_ctrl Endpoint: tx_data Path Type: data check Data Check Value: 1.0 Launch Signal Arrival: 2.3ns Capture Signal Arrival: 2.8ns Required Time: 3.3ns Slack: -0.5ns从这个报告里可以读出tx_ctrl在2.3ns到达tx_data在2.8ns到达约束要求tx_ctrl到tx_data之间满足1ns的建立关系。工具算出的Required Time是3.3ns2.3 1.0而tx_data的实际到达时间是2.8ns所以slack是-0.5ns。要修这个负slack有两个方向一是让tx_ctrl更早到达减小Launch Signal Arrival二是让tx_data更晚到达增大Capture Signal Arrival。具体选哪个方向取决于电路的实际需求和布局布线的可行性。6. 修data check负slack的几种思路6.1 调整数据路径延迟最直接的方法是调整数据路径的延迟。如果slack是负的说明捕获信号到得太早或者发起信号到得太晚。可以在发起信号的路径上加buffer来增加延迟或者在捕获信号的路径上减buffer来减少延迟。但这里有个需要注意的地方data check路径上的buffer调整可能会影响其他路径的时序。比如你在tx_ctrl的路径上加了一个buffertx_ctrl到其他触发器的setup/hold可能会变差。所以调整之前要先确认这条路径的fanout和影响范围。我的经验是优先调整那些fanout小、影响范围窄的路径。如果一条data check路径上的信号同时驱动了很多其他逻辑调整它的延迟可能会引发连锁反应这时候就要考虑其他方法了。6.2 用时钟约束间接影响结果前面提到过data check的required time计算会涉及时钟路径。所以调整时钟约束也可以间接影响data check的结果。比如你可以调整参考时钟的延迟或者改变时钟树的结构让required time发生变化。但这种方法比较间接而且影响范围更大。除非你确认时钟调整不会影响其他路径的时序否则不建议作为首选方案。6.3 重新审视约束本身是否合理有时候负slack的出现不是因为电路有问题而是因为约束设得太紧。比如协议里规定的是至少提前1ns但你在约束里写成了1.5ns那自然会有负slack。这时候需要做的是重新确认协议要求把约束值改到合理范围内。还有一种情况是约束的方向搞反了。比如应该是-from A -to B你写成了-from B -to A那检查的方向就反了结果自然也是错的。这种错误在复杂设计里不容易发现需要仔细核对约束文件和电路结构。7. 几个实操中总结出来的经验7.1 先用理想时钟验证约束在时钟树综合之前先用理想时钟跑一遍data check分析。这一步的目的是确认约束本身没问题而不是去修时序。如果理想时钟下slack就是负的那说明约束值可能设得太紧或者路径延迟确实不够。如果理想时钟下slack是正的但实际时钟下变成负的那说明时钟树延迟是主要因素需要从时钟树入手去解决。7.2 把data check路径单独列出来看在时序报告里data check路径和setup/hold路径是混在一起的。为了方便分析可以用工具的命令把data check路径单独过滤出来。比如在PrimeTime里可以用report_timing -path_type data_check来只报data check路径。这样能帮你快速定位问题而不是在一大堆setup/hold路径里翻找。7.3 约束文件里写清楚注释data check约束不像setup/hold那么直观过一段时间回头看可能就忘了当初为什么这么写。所以强烈建议在约束文件里写清楚注释说明这条约束的来源比如协议第几页、哪个接口规范、约束值的含义、以及为什么选这个值。这样后面接手的人包括未来的你自己能快速理解约束的意图。7.4 和前端设计确认协议要求data check的约束值通常来自协议规范但协议规范有时候写得比较模糊或者有多种解读方式。遇到不确定的情况一定要和前端设计工程师确认。不要自己拍脑袋定一个值否则后面出了问题很难追溯。8. 零周期data check的边界条件8.1 什么时候不需要data check不是所有的数据到数据路径都需要data check。如果两条数据路径之间没有时序关系或者时序关系已经被setup/hold覆盖了那就不需要额外的data check约束。比如两条路径都来自同一个时钟域且都满足各自的setup/hold要求那它们之间的相对关系通常不需要单独检查。判断的标准是这两条数据路径之间是否存在独立于时钟的时序要求。如果有就需要data check如果没有就不需要。8.2 多时钟域下的data check如果launch signal和capture signal属于不同的时钟域data check的处理会更复杂。因为工具需要知道用哪个时钟作为参考以及两个时钟域之间的关系。在这种情况下-clock参数的使用就变得很关键。如果指定错了参考时钟结果可能完全不对。我的建议是在多时钟域场景下先用set_clock_groups把时钟域之间的关系定义清楚然后再加data check约束。这样工具能正确理解时钟域之间的时序关系data check的结果也更可靠。8.3 data check和false path的配合使用有些data check路径确实不需要检查比如上电复位时的控制信号路径。这种情况下可以用set_false_path把路径屏蔽掉。但要注意set_false_path的优先级比set_data_check高如果两个约束同时存在false path会覆盖data check。所以如果你先加了data check后来又加了false path那data check就失效了。反过来如果你先加了false path后来又加了data checkdata check也会被false path覆盖。这个优先级关系在约束文件里要特别注意。9. 从工具报告反推约束是否正确9.1 用report_timing验证约束生效加完data check约束之后第一件事是用report_timing确认约束确实生效了。如果报告里没有出现Path Type: data check的路径那说明约束可能没写对或者被其他约束覆盖了。验证的时候可以先用一个简单的测试用例构造两条延迟已知的路径加一条data check约束然后看报告里的slack是否和手动计算的一致。如果一致说明约束生效了如果不一致就要去查约束的写法或者工具的算法。9.2 检查约束的覆盖关系在复杂的约束文件里多条约束可能会互相覆盖。比如你先加了一条set_data_check后来又加了一条set_false_path那data check就被覆盖了。要检查约束的覆盖关系可以用工具的命令列出所有生效的约束然后逐条确认。在PrimeTime里可以用report_exceptions来列出所有的时序例外。这个报告会显示哪些约束生效了哪些被覆盖了。定期检查这个报告能帮你避免约束冲突的问题。9.3 用slack的变化趋势判断约束是否合理如果加了data check约束之后slack的变化趋势和预期一致那说明约束大概率是对的。比如你把约束值从1ns改成1.5nsslack应该变小因为要求更严了。如果slack反而变大了那说明约束的方向或者符号可能搞反了。这种方法不需要精确计算只需要看趋势。对于快速验证约束是否正确非常实用。10. 我个人在实际操作中的几点体会data to data checks的零周期问题说到底是一个工具不知道你要检查什么你必须告诉它的问题。setup/hold有时钟周期作为默认参考data check没有所以约束的写法就更关键。我自己的习惯是每加一条data check约束都先用理想时钟跑一遍确认slack的计算逻辑和手动算的一致。这一步花不了多少时间但能避免后面大量的困惑。另外约束文件里的注释一定要写清楚尤其是约束值的来源和含义。过几个月回头看没有注释的约束文件基本等于天书。还有一点data check的负slack不一定都要修。有些路径的时序要求比较宽松负一点点可能在实际工作条件下也能正常工作。但这种判断需要结合具体的协议要求和系统测试结果不能凭感觉。如果确认可以放宽那就调整约束值如果不能放宽那就老老实实修时序。最后分享一个小技巧如果你不确定一条data check路径的约束值该填多少可以先不加约束让工具报出实际的到达时间差然后根据协议要求去反推约束值。比如工具报出tx_ctrl比tx_data早到0.8ns协议要求至少提前1ns那约束值就是1nsslack就是-0.2ns。这样能帮你快速定位问题而不是在约束值上反复试错。