此页面属于关于时序(timing)的一系列页面。前几页解释了时序计算背后的理论,讨论了时钟周期约束(clock period constraint),并介绍了时序收敛。但是,如果您已尝试将其正确处理,但出现时序问题怎么办?此页面试图回答该问题。
介绍
在上一页中,我试图说服您,没有单一的方法可以解决时序收敛问题。有时专注于关键路径(critical path)是正确的,有时则不然。有时只需更改工具设置即可轻松解决问题,有时则要困难得多。没有什么可以替代利用您拥有的所有经验和智慧来找到问题的根本原因。没有办法用清单来总结时序收敛。
然而,列出可能的策略通常会有所帮助。因此,在此页面上,我收集了一些在遇到时序问题时值得思考的主题。如果您阅读本文是因为您有一个特定的时序问题需要解决,那么这些想法之一可能会引导您找到解决方案。但不要指望您的解决方案会在此处详细说明。
另请记住,这一系列的页面并没有在这里结束。为了激发动力,我选择在许多其他主题之前讨论时序收敛。但是,后面几页中的信息也是相关的。
同样的原因,关于 I/O 时序约束的讨论推迟到后面。目前,我专注于在 FPGA内部开始和结束的路径(paths)。
因此,这里有一些与时序收敛相关的想法需要考虑。
想法#1: 修复逻辑设计
这始终是最没有吸引力的解决方案。如果已知设计可以正常工作,则尤其如此。你不想改变有用的东西。然而,问题的根本原因通常是 Verilog 代码编写得不够好,无法满足所需的性能。逻辑设计的改变一劳永逸地解决了这个问题,而不是面临持续的困难。
在上一页中有一些编写快速逻辑的建议。值得重复这一点: 在开发过程中始终牢记时序。修复时序问题比从头开始正确编写 Verilog 代码要难得多。
想法#2: 缩小扇出(fan-out)
当网络(net)具有较高的扇出时,传播延迟(propagation delay)增加的原因主要有两个:
- 电容的实体线材更大,所以换逻辑状态需要更多的电量。
- 对于网络的所有目的地,工具更难找到具有低时延(delay)的布线(routing): 这些目的地是分散在逻辑阵列(logic fabric)上的逻辑单元(logic elements)。所以目的地越多,就越难优化时序,使所有网络都具有较低的时延。
如果在设计里面用了一个同步复位(synchronous reset),那么这个信号很可能就拥有了一个高扇出。该主题将在单独的页面中讨论。
但是,由于扇出较高,每个达到大量逻辑单元的信号都可能导致时序问题。有时这个高扇出很明显(例如时钟使能信号(clock enable signals)),有时很难预料到高扇出。 FPGA 工具通常可以通过列出具有最高扇出的网络来提供帮助。
有两种方法可以使扇出保持低电平:
- FPGA 工具对扇出有限制。达到此限制时,工具会复制寄存器(register),即网络的来源。可以通过综合约束(synthesis constraints)更改每个寄存器(register)的限制值。也可以通过更改综合工具(synthesizer)的参数来更改全局限制。
- 编辑 Verilog 代码: 明明把高扇出的寄存器复制成几个寄存器。
显然,这两种方法都达到了相同的结果: 高扇出的寄存器复制成几台寄存器。因此,如果这可以通过工具自动完成(使用第一种方法),为什么还要手动执行此操作(如第二种方法)?
第二种方法需要更多的努力,但有一个显着的优势: 可以以合理的方式复制寄存器。请记住,目标不仅仅是减少扇出。同样重要的是,每个寄存器的输出(output)被分配到放置在逻辑阵列上的一个小区域中的逻辑单元。否则,由于物理距离,结果是大布线时延(routing delays)。因此,如果在编写 Verilog 代码时考虑到扇出,则可以确保逻辑单元之间的短连接。这在不同页面上针对同步复位信号(synchronous reset signal)进行了演示。
相反,如果 FPGA 工具负责复制寄存器,结果可能不那么高效。布线时延的改进取决于决定如何使用每个复制的寄存器的算法。结果的质量取决于使用哪个 FPGA 工具。
请注意,默认情况下,当综合工具检测到两台行为完全相同的寄存器时,这些寄存器会自动合并为一台寄存器。因此,如果 Verilog 代码中复制了寄存器,则综合工具将用一个寄存器替换所有副本。即使这些等效的寄存器是在不同的模块中定义的,情况通常也是如此。为了避免这种合并,有必要显式禁用此功能。实现此目的的常见方法是使用综合属性(synthesis attributes),例如“dont_touch”、“dont_merge”或“keep”。
想法#3: 检查 floorplanning
默认情况下,逻辑单元在逻辑阵列上的位置由 FPGA 工具(更具体地说是 placer)自动确定。但是,可以请求将某些逻辑单元放置在 FPGA上的特定区域。也可以请求将特定的逻辑单元放置在特定位置。此类请求称为floorplanning 。这些请求是通过 placement 约束发出的, placement 约束通常是语法类似于时序约束(timing constraints)的 Tcl 命令。
在大多数情况下, placement 约束使实现时序约束变得更加困难。第一个原因很明显: 当 placer的选择有限时,结果只能比没有限制的差。但是,还有更具体的原因:
- Floorplanning 可以将许多逻辑强制到 FPGA上的一个小区域。这可能导致布线 congestion(routing congestion): 该区域内的逻辑单元需要比平时更多的布线。结果, router 被迫使用次优资源。这导致布线时延不是最优的,并且可能无法满足要求。
- Placement 约束可能会迫使逻辑单元远离,即使将它们彼此靠近放置会更好。因此,为了减少布线时延, placer 无法移动逻辑单元。
- Floorplanning 可以创建对布线有障碍的区域。例如,假设 floorplanning 创建了一个由逻辑单元密集填充的区域。其他逻辑的布线可能被迫避免进入这个拥挤区域。因此,布线需要走更远的距离,因此布线时延变得更大。
在大多数设计中,最好避免使用 floorplanning ,从而让 placer 可以自由优化逻辑单元的放置。然而,在某些情况下 placement 约束是常用的,例如:
- IP block 可以包含它生成的逻辑单元的 placement 约束。例如,实现 PCIe 接口的 IP 通常会创建 placement 约束来确定其最重要组件的位置: transceivers、锁相环(PLLs)和专用的 PCIe hard IP。这种 placement 约束通常是必要且正确的。
- FPGA 可以划分区域,每个区域只包含特定的模块。这就是 floorplanning的本意。像这样划分 FPGA 的动机可能是为了让一个项目的不同团队能够独立工作。
- Partial Reconfiguration是一种允许在工作时将比特流(bitstream)加载到 FPGA 中的功能,因此只有一部分 FPGA 受到影响。为此,需要 floorplanning : FPGA 分为新比特流到货时保持不变的区域,以及此比特流更新的区域。
对于时序收敛,重要的是要意识到 placement 约束是问题的潜在原因。特别是如果布线时延大于预期,根本原因可能是 placer无法优化逻辑单元的位置。请记住, floorplanning 可能会对与放置受限的逻辑单元无关的路径产生负面影响。
想法#4: 检查时序约束
时序约束对于 FPGA的可靠运行至关重要。因此,应在项目的实现(implementation)之前对其进行验证。但是,有时时序约束无论如何都会出错。在时序收敛过程中,错误会变得很明显。这不应该发生,但是当它发生时,解决问题当然更好。
有一整页关于检查时序约束。在这里我只讨论两个可能导致时序收敛出现问题的常见错误:
- 在 unrelated 时钟之间的路径上不必要地执行时序约束。
- 在异步复位(asynchronous resets)上不必要地执行时序约束。
首先,关于 unrelated 时钟: 跨时钟域(clock domain crossings)的话题前面已经讨论过了: 时序约束反映哪些时钟是 related 时钟哪些不是 related 时钟很重要的原因有很多。最重要的原因是为了保证逻辑的正常运行,但是时序收敛也受到了影响: 如果一对时钟被工具不必要地视为 related 时钟,这将导致在这两个时钟之间的路径上不必要地执行时序要求。结果,这些工具以真正需要这些努力的路径为代价,浪费了这些路径的努力。
修复此问题的时序约束将在本系列页面的后面进行说明。
关于异步复位: 在大多数情况下,有必要在以异步复位结尾的路径上强制执行时序约束。但有时,没有必要这样做。例如,如果保证当复位(reset)变为不活动时,时钟不会处于活动状态。另一种可能性是接收异步复位的触发器(flip-flop)具有针对时序违反(timing violations)的保护机制,就像跨时钟域一样。在这些情况下,工具为满足时序要求所做的努力毫无意义。
很难意识到这类问题使得实现时序收敛变得困难: 有时关键路径与不必要地被视为 related 时钟的两个时钟无关。如果异步复位转移了工具的注意力,那就更难识别了。在这种情况下,试图专注于关键路径以改进其时序可能是徒劳的。
时序约束当然在其他方面也可能出错。这里描述的情况只是一种可能性。因此,时序出现问题可能是总体审查时序约束的好机会。
想法#5: 再试一次
回想一下,布局和布线(place and route)过程开始于在逻辑阵列上相当任意地散布逻辑单元。这些工具然后通过反复尝试尝试改进时序。因此,这个过程的成功依赖于一定的运气。也有可能布局和布线算法稍微不同的行为会获得更好的结果,即使没有逻辑解释原因。
因此,如果时序约束失败,并且负 slack 相对较小(大约是总时延的 10-20% ),重试可能就足够了。但只是重新运行实现可能不会有任何好处: 大多数 FPGA 软件的设计目的是在使用相同的输入运行时准确重复其结果。因此,在重新运行之前有必要进行一些更改。此更改与关键路径无关。重点只是避免准确重复之前的实现。
例如,在 Vivado 中,每 run 都有一个称为“strategy”的属性(attribute)。顾名思义,这个属性(attribute)控制工具在实现期间应用的策略。更改这个属性可确保下一个实现(implementation)不会与前一个相同。对于特定的逻辑设计,不同的策略也可能更有意义。
所有 FPGA 工具都提供了类似的可能性来修改实现流程的参数。通常可以要求付出更大的努力来实现设计的目标。有时确实需要付出更大的努力,但通常要求付出更大的努力只是因为工具可以做其他事情。
避免重复的另一种方法是更改 Verilog 代码。再次说明,更改不一定与关键路径相关。有时更改寄存器的名称就足以获得与之前的实现足够不同的实现。
这个想法可以发挥到极致: 可以在多台计算机上并行运行实现,因此每台实现的参数略有不同。当 FPGA 的价格很重要时,这是有道理的。在这种情况下,值得让计算机努力工作,以便在更便宜的 FPGA上实现时序约束。
总结一下,这个方法主要靠运气。相应的期望应该是: 仅当工具偶尔无法实现时序约束时,再试一次才有帮助。但如果可能的话,以其他方式改进时序总是更好。
想法#6: FPGA 满了吗?
当 FPGA的液位达到大约 70%时,时序约束出现问题是很常见的。出现这种情况的主要原因有以下三个:
- 逻辑单元被更密集地封装到 FPGA的逻辑阵列中。因此, placer改进时序的自由更加有限,因为将逻辑单元从一个地方移动到另一个地方更加困难。这导致更大的布线时延。
- FPGA 中的接线数量 (布线资源(routing resources)) 是有限的。只要 FPGA 相对空, router 就可以在逻辑单元之间选择最合适的通路。当添加更多逻辑时,次优选择会导致更大的布线时延。
- FPGA 中的专用逻辑单元可能会用完。例如,大多数 FPGA 都有 block RAMs 和用于算术乘法的专用组件。当这些资源用完时,这些工具会使用简单的逻辑单元(slices) 实现所需的功能。这通常会导致大量的逻辑级(logic levels),从而增加逻辑时延(logic delay)。 FPGA的填充水平也开始比预期更快地增加,因为使用 slices 而不是专用的逻辑单元。
在上面给出的三个原因中,只有第三个有某种解决方案: 例如,它可能有助于手动决定哪个逻辑使用 FPGA的 block RAMs 和其他类似资源。除此之外,完整的 FPGA 的唯一解决方案是选择更大的。然而,这并不总是一种选择。因此,随着更多逻辑的加入,预计时序收敛会变得更硬,这一点很重要。
想法#7: 也许您需要不同的 FPGA?
有时,别无选择,只能承认 FPGA 无法胜任这项工作。如果相同的 FPGA 可用于更高的 speed grade,解决方案可以是决定升级到更快的 FPGA。这个决定当然会增加购买成本,但它也会带来另一个意想不到的后果: speed grade更高的 FPGAs 可能会短缺。即使这些更快的 FPGAs 在特定时间充裕,但当供不应求时,更快的 FPGAs 通常第一个从市场上消失。
这是很自然的: 较快的 FPGAs 是那些通过测试较好的,它们总是可以用来代替较慢的 FPGAs。有时制造商无法生产速度更快的 FPGAs,有时会有大量消费者购买它可以使用的所有产品。
因此,如果您长期从事用于生产的产品,请始终选择设计可以使用的最低 speed grade 。即使钱不是问题也是如此。
一种完全不同的升级类型是从更新的 FPGA 系列中选择 FPGA 。或者选择其他供应商的 FPGA 。这种变化更为剧烈,但如果项目处于早期阶段,则应予以考虑。我们都倾向于爱上我们熟悉的工具和组件。但是,当一切都感觉太熟悉时,就是四处寻找替代方案的好时机。
话虽如此,经验丰富的 FPGA 工程师知道选择最新的、全新的 FPGA 及其工具是一场冒险的赌博。但通常有一个相当成熟的替代方案比当前的 FPGA选择要好得多。在这种情况下,最好离开舒适区并尝试一些新的东西。
想法#8: 降低温度范围
我几乎把这种可能性放在最后是有原因的: 这是所有解决方案中最丑陋的解决方案。但有时别无选择。
默认情况下,工具对时序约束的强制执行可确保 FPGA 在数据手册(datasheet)中定义的整个温度范围内可靠地工作。某些 FPGA 工具允许为项目选择另一个温度范围(例如, Quartus 有一个名为“MAX_CORE_JUNCTION_TEMP”的属性用于此目的)。这可用于通知工具无需支持整个温度范围。
一般来说, FPGAs 中逻辑单元的时延随温度升高而升高。如果最大温度降低,则时延的值在时序计算中会更小。这使得工具更容易实现 tsetup 要求。有时,这是使工具实现时序约束的唯一方法。
了解使用此方法的风险很重要。特别要注意的是,我们谈论的是 junction temperature。换句话说,这是 FPGA的 silicon 上的温度。这不是环境温度。
因此,当最高温度为 85°C时,并不意味着 FPGA 可以在该温度的烤箱中工作。该温度也可以在室温 (25°C) 下达到,特别是在 FPGA上没有 heatsink 的情况下。 junction 的温度与环境温度之间始终存在差异。这种差异的大小取决于 FPGA 的功耗和冷却解决方案。
如果您使用商业产品,请注意 FPGA 的环境温度可能比室温高得多。特别是,如果 FPGA 放在通风不好的箱子里,箱内的温度会比箱外的温度高很多。更糟糕的是,大多数电子产品的工作温度预计在 0°C 和 40°C 之间(大约,这些数字因产品而异)。那么当最终产品在最高温度下进行测试时, FPGA 在产品外壳内, junction的温度是多少?这就是要问的问题。时序计算必须基于该温度(或更高)。
换句话说,如果为了实现时序约束而降低最高温度,并且在实验室中一切正常,那没有任何意义。回想一下,关于时序约束, FPGA 设计在实验室中工作始终毫无意义。但降低最高温度在这方面更糟。不加考虑地缩小温度范围可能会招来真正的麻烦: 生产前的产品最终测试,在最高温度下进行可能会失败,如果温度范围被修正,则不可能达到时序约束。解决此问题的唯一方法是从头重写 FPGA 设计。
因此,在为时序收敛更改温度范围之前,请确保这样做是安全的: 在所有可能的工作条件下,对 junction 的温度范围进行严格评估。
请注意,无法通过更改实现的参数将温度范围扩展到默认值之外。该工具的默认温度范围始终与数据手册中规定的相同。因此,无法保证 FPGA 在超出此默认温度范围的情况下可靠运行。
想法#9: 未对齐的 related 时钟
这是一个比较深奥的情况,也有点难以理解。所以这就是为什么我把这个话题放在最后。
假设两个未对齐的 related 时钟之间有一个跨时钟域(clock domain crossing)。也就是说,这两个时钟是从同一个参考时钟(reference clock)派生出来的,但是这两个时钟之间并没有控制时钟偏移(clock skew)的机制。
结果,工具变得更难满足这两个时钟之间的路径的时序要求。有两种可能的困难: (回想一下之前解释过 tsetup 和 thold )
- 当时钟由于时钟偏移而晚于第一个触发器(flip-flop)到达时: 因此,在下一个时钟边沿(clock edge)到达第二个触发器之前的时间更短。因此更难满足 tsetup 要求。
- 当时钟由于时钟偏移而比第一个触发器更早到达时: 因此,第一个触发器在同一个时钟边沿到达第二个触发器之前更新其输出。因此,第二个触发器可能会违反 thold 要求。这些工具通过人为地延长路径的布线来防止这种情况发生。这可能导致无法满足 tsetup 要求。也是对布线资源的浪费。
需要注意的是,当工具需要比平时更努力地工作来克服这些困难时,可能会以优化其他路径为代价。
但是未对齐的 related 时钟并不是错误,有时这是不可避免的。这种情况只意味着工具需要更加努力地工作。如果实现了时序约束,那么设计就没有问题了。但是,应尽可能通过合理的努力避免这种情况。
请注意,上面“想法 #5”中提到的错误是不同的,尽管这两个错误都会对工具造成不必要的困难,并且都与跨时钟域相关。
解决 related 时钟未对齐情况的最佳解决方案是对齐这些时钟。这通常是通过在现有锁相环上添加锁相环或添加时钟输出(clock output)来完成的。目标是所讨论的两个时钟都是同一个锁相环(PLL)的输出。
另一种可能的解决方案是将时钟视为 unrelated 时钟。这需要对逻辑设计本身以及时序约束进行更改。如果这不是太困难,那么这可能是值得的。稍后将详细介绍此主题。
我们现在将查看 related 时钟之间未对齐的跨时钟域示例。
wire pll_clk;
reg [24:0] result;
reg [11:0] x, y, x1, y1;
clk_wiz_0 pll_i
(.clk_in1(clk),
.clk_out1(pll_clk));
always @(posedge clk)
begin
x1 <= x;
y1 <= y;
end
always @(posedge pll_clk)
result <= x1 * y1;
请注意,有一个锁相环(clk_wiz_0)。这个锁相环使用 @clk 作为它的参考时钟(reference clock),其频率为 250 MHz。它与上一页顶部示例中显示的锁相环相同。 @pll_clk 的频率是 125 MHz。
此示例的重要部分是两个 related 时钟(@clk 和 @pll_clk)之间的跨时钟域。因为锁相环只生成了 @pll_clk ,所以这两个时钟没有对齐。所以在路径到 @result (从 @x1 和 @y1)中有一个时钟偏移(clock skew)。尽管是时钟偏移,时钟还是 related 时钟,工具会尽量满足时序的要求。
如果使用 @clk 的唯一原因是需要 250 MHz 频率的时钟,则正确的解决方案是使用锁相环生成另一个时钟。生产一个和参考时钟同频的时钟不算浪费资源。相反,这样做可以为工具省去很多力气。如示例所示,直接使用 @clk 的理由只有一个: 当使用 @clk 的逻辑必须在锁相环生成可用的时钟之前工作时。
Vivado 生成的时序报告(timing report)如下。在这个特定案例中,只有 tsetup 要求存在问题。
Slack (VIOLATED) : -1.456ns (required time - arrival time) Source: x1_reg[7]/C (rising edge-triggered cell FDRE clocked by clk {rise@0.000ns fall@2.000ns period=4.000ns}) Destination: result_reg/DSP_OUTPUT_INST/ALU_OUT[10] (rising edge-triggered cell DSP_OUTPUT clocked by clk_out1_clk_wiz_0 {rise@0.000ns fall@4.000ns period=8.000ns}) Path Group: clk_out1_clk_wiz_0 Path Type: Setup (Max at Slow Process Corner) Requirement: 4.000ns (clk_out1_clk_wiz_0 rise@8.000ns - clk rise@4.000ns) Data Path Delay: 3.012ns (logic 2.677ns (88.878%) route 0.335ns (11.122%)) Logic Levels: 5 (DSP_A_B_DATA=1 DSP_ALU=1 DSP_M_DATA=1 DSP_MULTIPLIER=1 DSP_PREADD_DATA=1) Clock Path Skew: -2.192ns (DCD - SCD + CPR) Destination Clock Delay (DCD): 0.998ns = ( 8.998 - 8.000 ) Source Clock Delay (SCD): 3.202ns = ( 7.202 - 4.000 ) Clock Pessimism Removal (CPR): 0.012ns Clock Uncertainty: 0.148ns ((TSJ^2 + DJ^2)^1/2) / 2 + PE Total System Jitter (TSJ): 0.071ns Discrete Jitter (DJ): 0.103ns Phase Error (PE): 0.086ns Clock Net Delay (Source): 1.414ns (routing 0.002ns, distribution 1.412ns) Clock Net Delay (Destination): 1.184ns (routing 0.002ns, distribution 1.182ns) Clock Domain Crossing: Inter clock paths are considered valid unless explicitly excluded by timing constraints such as set_clock_groups or set_false_path. Location Delay type Incr(ns) Path(ns) Netlist Resource(s) ------------------------------------------------------------------- ------------------- (clock clk rise edge) 4.000 4.000 r AG12 0.000 4.000 r clk (IN) net (fo=0) 0.000 4.000 clk_IBUF_inst/I AG12 INBUF (Prop_INBUF_HRIO_PAD_O) 0.738 4.738 r clk_IBUF_inst/INBUF_INST/O net (fo=1, routed) 0.105 4.843 clk_IBUF_inst/OUT AG12 IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O) 0.049 4.892 r clk_IBUF_inst/IBUFCTRL_INST/O net (fo=1, routed) 0.795 5.687 clk_IBUF BUFGCE_X1Y2 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.101 5.788 r clk_IBUF_BUFG_inst/O X2Y0 (CLOCK_ROOT) net (fo=62, routed) 1.414 7.202 clk_IBUF_BUFGCE SLICE_X52Y45 FDRE r x1_reg[7]/C ------------------------------------------------------------------- ------------------- SLICE_X52Y45 FDRE (Prop_HFF_SLICEM_C_Q) 0.138 7.340 f x1_reg[7]/Q net (fo=1, routed) 0.335 7.675 result_reg/A[7] DSP48E2_X8Y18 DSP_A_B_DATA (Prop_DSP_A_B_DATA_DSP48E2_A[7]_A2_DATA[7]) 0.396 8.071 r result_reg/DSP_A_B_DATA_INST/A2_DATA[7] net (fo=1, routed) 0.000 8.071 result_reg/DSP_A_B_DATA.A2_DATA<7> DSP48E2_X8Y18 DSP_PREADD_DATA (Prop_DSP_PREADD_DATA_DSP48E2_A2_DATA[7]_A2A1[7]) 0.182 8.253 r result_reg/DSP_PREADD_DATA_INST/A2A1[7] net (fo=1, routed) 0.000 8.253 result_reg/DSP_PREADD_DATA.A2A1<7> DSP48E2_X8Y18 DSP_MULTIPLIER (Prop_DSP_MULTIPLIER_DSP48E2_A2A1[7]_U[10]) 0.994 9.247 f result_reg/DSP_MULTIPLIER_INST/U[10] net (fo=1, routed) 0.000 9.247 result_reg/DSP_MULTIPLIER.U<10> DSP48E2_X8Y18 DSP_M_DATA (Prop_DSP_M_DATA_DSP48E2_U[10]_U_DATA[10]) 0.164 9.411 r result_reg/DSP_M_DATA_INST/U_DATA[10] net (fo=1, routed) 0.000 9.411 result_reg/DSP_M_DATA.U_DATA<10> DSP48E2_X8Y18 DSP_ALU (Prop_DSP_ALU_DSP48E2_U_DATA[10]_ALU_OUT[10]) 0.803 10.214 r result_reg/DSP_ALU_INST/ALU_OUT[10] net (fo=1, routed) 0.000 10.214 result_reg/DSP_ALU.ALU_OUT<10> DSP48E2_X8Y18 DSP_OUTPUT r result_reg/DSP_OUTPUT_INST/ALU_OUT[10] ------------------------------------------------------------------- ------------------- (clock clk_out1_clk_wiz_0 rise edge) 8.000 8.000 r BUFGCE_X1Y2 BUFGCE 0.000 8.000 r clk_IBUF_BUFG_inst/O net (fo=62, routed) 1.078 9.078 pll_i/inst/clk_in1 MMCME3_ADV_X1Y0 MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0) -1.777 7.301 r pll_i/inst/mmcme3_adv_inst/CLKOUT0 net (fo=1, routed) 0.422 7.723 pll_i/inst/clk_out1_clk_wiz_0 BUFGCE_X1Y0 BUFGCE (Prop_BUFCE_BUFGCE_I_O) 0.091 7.814 r pll_i/inst/clkout1_buf/O X2Y0 (CLOCK_ROOT) net (fo=6, routed) 1.184 8.998 result_reg/CLK DSP48E2_X8Y18 DSP_OUTPUT r result_reg/DSP_OUTPUT_INST/CLK clock pessimism 0.012 9.010 clock uncertainty -0.148 8.862 DSP48E2_X8Y18 DSP_OUTPUT (Setup_DSP_OUTPUT_DSP48E2_CLK_ALU_OUT[10]) -0.104 8.758 result_reg/DSP_OUTPUT_INST ------------------------------------------------------------------- required time 8.758 arrival time -10.214 ------------------------------------------------------------------- slack -1.456
这个报告(report)显示工具无法到达时序约束。显示的路径从 @clk的上升沿(rising edge)开始,位于 4 ns,结束于 @pll_clk的上升沿位于 8 ns。问题在于输入引脚的时钟到达第一个触发器的时钟输入引脚(clock input pin)所需的时间: 3.2 ns。因此,此时钟边沿(clock edge)的到达时间为 7.2 ns。
但是 @pll_clk 是由锁相环生成的,所以这个时钟和 @clk的输入引脚是对齐的。因此时延只是 1.0 ns。 @pll_clk到达第二个触发器的时间因此是 9.0 ns。所以留给数据路径(data path)的时间是 9.0 – 7.2 = 1.8 ns (大约,因为时钟 uncertainty (clock uncertainty)等)。这对于算术乘法是不够的,即使使用了指定的算术单元。因此无法满足时序要求。
因此,在此示例中,由于时钟偏移,时钟晚于第一个触发器到达。这导致无法满足 tsetup 要求。
请注意,这可以通过操纵 @pll_clk的对齐方式来解决。例如,锁相环的参考时钟可以是全局时钟缓冲器(global clock buffer)的输出分配 @clk。也可以定义锁相环的相位 shift (phase shift)以实现更好的对齐。然而,这些解决方案只能作为最后的手段使用。
概括
再一次,这些只是可能有助于解决时序收敛问题的一些想法。不幸的是,解决这类问题可能需要的远不止这些。事实上, FPGAs 领域中没有一个主题与时序收敛不相关。
如前所述,最好的策略是从一开始就小心编写逻辑设计。解决时序收敛的最佳方法是避免它。