HyperFlex 架构系列(2):Retiming 设计

一、Hyper-Retiming

一条寄存器到寄存器的路径上串着几级组合逻辑,每级延迟不一样。第一级 3 ns,第二级 1 ns。clock 周期至少得是 3 ns,最慢的那级决定了上限,频率卡在 333 MHz。把第一级的寄存器往前推一点,两级各分担 2 ns,clock 周期缩到 2 ns,频率从 333 MHz 提到 500 MHz。寄存器总数没变,沿着路径重新分布了一下。

这个操作就是 retiming。在传统的 FPGA 架构里它能带来的优化有限,因为寄存器只能待在 ALM 里面,布线通道上没有寄存器可以落脚,移动范围很小。

HyperFlex 在 fabric 里埋了大量 Hyper-Register,遍布布线通道和功能块输入处。被往前推或往后拉的寄存器有了落脚的地方,移动范围大得多,工具可以跨更远的距离去平衡路径延迟。Fitter 的 Retime 阶段在布局布线过程中运行,把 ALM 寄存器推到 Hyper-Register 的位置上,做 backward 和 forward retiming。整个过程不需要改 RTL,能带来 1.1~1.3x 的性能提升。

图 6:跨 LUT 的寄存器移动,retiming 前后对比

上图左侧是 retiming 前:最差路径走两级 LUT。右侧是 retiming 后:每级只剩一级 LUT,最差延迟减半。

Retiming 不是总能发生的。Compiler 无法 retime 一个寄存器的时候,就构成 Retiming Restriction。下面列出常见的限制条件和应对方法:

  • 避免异步复位,除非确实必要
  • 避免同步清零,同步清零通常属于广播信号,不利于 retiming
  • 尽量减少时序约束和例外中的通配符使用,因为 Compiler 不会 retime 作为此类约束端点的寄存器
  • 避免单周期(起停式)流控,如时钟使能 和 FIFO full/empty 信号。改用 valid 信号和 almost full/empty
  • 避免使用 preserve register 属性
  • 减少初始化上电条件(initial power-up conditions)的定义,它们会阻止 retiming
  • 处理组合 feedback loop 等 RTL 层面的 retiming 限制

1.1 复位策略

要在配置完成前将设计保持在复位状态,需要实现 Reset Release IP,或将 INIT_DONE 信号通过引脚引回。

为了最佳性能,应尽可能避免使用 reset(无论是异步还是同步)。Hyper-Register 没有异步复位,因此 Compiler 无法将带异步复位的寄存器 retime 到 Hyper-Register 位置。

使用同步复位替代异步复位可以使寄存器被 retime。设计中确实有一些寄存器需要同步或异步复位,但为了最佳性能应尽量减少数量。

1.1.1 移除异步复位

如果一个电路在复位信号保持足够长时间后能达到与完全复位等效的稳态,就可以移除该异步复位。

下表展示了异步复位将流水线中所有寄存器复位,阻止寄存器进入 Hyper-Register 位置的情况。代码中 aclr 是异步清零信号(asynchronous clear)。

// Verilog
always @(posedge clk, aclr)
    if (aclr) begin
        reset_synch <= 1'b0;
        aclr_int <= 1'b0;
    end else begin
        reset_synch <= 1'b1;
        aclr_int <= reset_synch;
    end

always @(posedge clk, aclr_int)  // Asynchronous reset
    if (!aclr_int) begin
        a <= 1'b0; b <= 1'b0; c <= 1'b0; d <= 1'b0; out <= 1'b0;
    end else begin
        a <= in; b <= a; c <= b; d <= c; out <= d;
    end

下图是上述代码对应的电路。当 aclr 拉低时,所有寄存器输出为 0。释放 aclr 并施加两个时钟脉冲后,全部寄存器进入正常工作模式。

图 7:完全异步复位的电路

下图展示了从电路中间移除异步复位后的情况。如果部分复位后,修改后的电路能稳定到与原电路相同的稳态,那么这种修改在功能上就是等效的。

图 8:部分异步复位,从电路中间移除复位

下图展示了在寄存器链中包含反相器的情况。这类电路通常需要额外的同步复位来保持在流水线中。

1783072860821

如果从反相器所在的寄存器上移除异步复位并施加时钟,寄存器输出不会回到复位状态。这种情况下电路无法保持功能等效。

下图展示了包含反相器的电路中必须保留异步复位的场景。反相器后的寄存器不能移除复位,否则稳态不正确。

图 10:反相器电路,必须保留异步复位

为了避免因非自然反相功能导致的复位逻辑问题,可以校验输出并与复位释放同步。下图展示了这种方法:如果校验流水线能在计算流水线真正有效时使能输出,那么行为上与移除复位是等效的。即使计算部分的电路不能自然复位,这个方法也适用。

图 11:校验输出并与复位同步

下表展示了使用最少异步复位的示例。

// Verilog
always @(posedge clk, aclr)
    if (aclr) begin
        reset_synch_1 <= 1'b0;
        reset_synch_2 <= 1'b0;
        aclr_int <= 1'b0;
    end else begin
        reset_synch_1 <= 1'b1;
        reset_synch_2 <= reset_synch_1;
        aclr_int <= reset_synch_2;
    end

```verilog
// Asynchronous reset for output register
always @(posedge clk, posedge aclr_int)
    if (aclr_int) out <= 1'b0;
    else out <= d;

```verilog
// Synchronous reset for input register
always @(posedge clk)
    if (reset_synch_2) a <= 1'b0;
    else a <= in;

```verilog
// Naturally resetting registers
always @(posedge clk) begin
    b <= a; c <= b; d <= c;
end
1.1.2 全局时钟树上的同步复位

使用全局时钟树来分布同步复位可能会限制 Compiler 的 retiming 优化。全局时钟树上没有 Hyper-Register,因此通过全局时钟树扇出的寄存器,其 retiming 灵活度低于通过布线 fabric 扇出的情况。

1.1.3 I/O 端口上的同步复位

Compiler 不会 retime 驱动输出端口的寄存器,也不会 retime 被输入端口驱动的寄存器。如果这样的 I/O 寄存器带有同步清零,就无法 retime 该寄存器。在实际设计中,reset 通常由内部逻辑产生,不是从 I/O 端口直接引入,所以这个限制在实际中不太常见。但在对小型逻辑做 benchmark 时可能出现,如果 reset 来自 I/O 端口,它所驱动的全部寄存器都无法 retime。在同步复位路径上添加一些寄存器可以解决这个问题。

1.1.4 复制和流水线化同步复位

如果同步清零信号导致时序问题,可以在源寄存器和目标寄存器之间复制该信号来解决。向前推送的寄存器不需要与向后推送的寄存器争抢 Hyper-Register 位置。对于设计中的小型逻辑块,这个方法是改善时序的有效策略。

1.2 时钟使能策略

高扇出的 时钟使能信号可能限制 retiming 能达到的性能。

1.2.1 本地化时钟使能

本地化 时钟使能的扇出较小,通常出现在 clocked process 或 always block 中。当信号在 case 或 if 语句的某个分支下未定义时,其保持前一个值,这就构成了时钟使能。

要在设计中检查是否存在时钟使能,可以查看 Fitter Report > Plan Stage > Control Signals 报告中的 Usage 列。本地化时钟使能 扇出小,retiming 容易,通常不会导致时序问题。

1.2.2 高扇出时钟使能

尽可能避免高扇出信号。高扇出 时钟使能驱动大量逻辑,数量之多使得 retiming 时寄存器在 时钟使能路径上推拉其他寄存器,可能在 时钟使能线上产生冲突。这种情况与同步复位小结中提到的激进 retiming 类似。该节讨论的一些方法,如复制 enable 逻辑,同样有助于解决 时钟使能路径上的冲突。

这类高扇出信号通常用于关闭大量逻辑的运行,例如 FIFO full 标志拉高时。通常可以绕过这类信号来设计,例如将 FIFO 设计为提前若干个时钟周期发出 almost full,让时钟使能 有足够时间传播回需要关闭的逻辑。这些额外的寄存器可以按需被 retime 到逻辑中。

1.2.3 带 Timing Exception 的时钟使能

Compiler 不会 retime 作为 multicycle 或 false path 时序例外端点的寄存器。时钟使能有时用于创建以主时钟一半或四分之一速率运行的子时钟域。有时这些时钟使能 控制单条路径,逻辑每隔一个时钟周期变化一次。由于通常用时序例外来放松时序要求,这种情况问题不大。如果一个 时钟使能校验了一条长而慢的数据路径,且该路径仍然难以满足时序,就在数据路径上添加一级寄存器,移除该路径上的 multicycle 约束。Hyper-Aware CAD 流程允许 Retimer 对该路径做 retiming 来改善时序。

1.3 在综合过程中保留寄存器

可以指定实体级分配(entity-level assignments)和综合属性,在综合过程中保留特定的寄存器。

Preserve Registers in Synthesis 分配可以在综合过程中保留指定的寄存器,而不限制后续的 Hyper-Retiming 优化。类似地,dont_merge 或 preserve_syn_only 综合属性也可以在保留寄存器的同时不限制 retiming:

logic hip_data; /* synthesis preserve_syn_only */
(*preserve_syn_only*) logic hip_data;

Preserve Registers 分配也可以保留寄存器,但不允许 Hyper-Retimer 对这些寄存器做优化。这个分配可用于调试场景下需要保持寄存器可见性的情况。

下表列出了综合保留相关的选项。

分配说明允许 Fitter 优化?
Preserve Registers in Synthesis阻止寄存器在综合过程中被移除,不限制综合后的优化
Preserve Fan-Out Free Register Node阻止无扇出的寄存器在综合过程中被移除
Preserve Registers阻止指定寄存器在综合过程中被移除和时序优化

对于无扇出寄存器,如果定义了它的模块内部没有扇出,PRESERVE_FANOUT_FREE_NODE 分配无法保留该寄存器。此时需要在源文件中实现 noprune pragma:

(*noprune*) reg r;

如果有多个该模块的实例,只有部分需要保留,可以在 HDL 中为寄存器设置一个 dummy pragma,同时设置 PRESERVE_FANOUT_FREE_NODE 分配。

1.4 时序约束注意事项

时序约束的使用方式影响编译结果。时序约束会影响 Fitter 放置逻辑的方式。

1.4.1 优化 Multicycle 路径

Compiler 不会 retime 作为 SDC 时序约束(包括 multicycle 和 false path)端点的寄存器。因此,应尽可能精确地指定时序约束和例外,避免产生 retiming restriction。

相比使用 multicycle 约束,使用实际的寄存器级给 Compiler 提供了最大的性能优化空间。例如,与其为组合逻辑指定 multicycle = 3,不如移除 multicycle 例外,在组合逻辑前后插入两额外的寄存器级。这样 Compiler 可以通过逻辑来最优地平衡这些额外的寄存器级。

1.4.2 过约束

过约束指示 Fitter 花费更多时间来优化设计的特定部分。在某些情况下过约束适合用来改善性能。但传统的过约束方法会限制 retiming 优化。HyperFlex 架构 FPGA 支持新的 is_post_route 函数,可以允许 retiming:

if { ! [is_post_route] } {
    # 在这里放置过约束
}

传统的过约束写法(注释中的 quartus_fit 判断块)会阻止 Hyper-Retiming。

1.5 时钟同步策略

在 HyperFlex 架构 FPGA 中,使用简单的同步策略可以达到最高速度。在简单的同步器跨时钟域路径上增加延迟很直接,但在其他跨域路径上增加延迟更复杂。

下图展示了一个简单的时钟域同步方案:一个域(蓝色)的寄存器直接连接到下一个域(红色)的寄存器。

图 12:简单的时钟域交叉

在红色域中添加延迟以便 retiming 的方式如下图所示。

图 13:为简单时钟域交叉增加延迟

下图展示了一种在 HyperFlex 架构 FPGA 中不够优化的跨域结构,但在针对其他器件族的设计中存在。蓝色时钟域和红色时钟域之间存在组合逻辑,没有正确做同步处理,也无法灵活地添加寄存器。

图 14:多处时钟域交叉

下图展示了在红色时钟域边界添加延迟的做法。不能在 red-to-red 路径上添加寄存器,否则路径会变得不平衡,可能改变设计功能。虽然可以在这个场景下添加延迟,但风险较高,需要在添加延迟前仔细分析各条路径。

图 15:在多处时钟域交叉位置添加延迟

对于 HyperFlex 架构 FPGA,应在进入组合逻辑之前完成跨时钟域路径的同步。这样添加延迟比前面那种场景要简单。

下图展示了蓝色域寄存器在进入组合逻辑前先同步到红色域。这种方法允许在同步寄存器前安全地添加流水线寄存器,而不会意外接触到 red-to-red 路径。在 HyperFlex 架构 FPGA 中应使用这种同步方法来获得最高性能。

1783072894584

1.5.1 时钟域交叉约束指南

对于多比特时钟域交叉,必须施加适当的时序约束。set_false_path 约束的优先级高于所有其他基于路径的约束。因此,当时钟域交叉上存在 set_false_path 约束时,时序分析会忽略其他低优先级约束(如 skew)。

正确约束时钟域交叉的要点:

  • 检查 SDC 约束,确保两个时钟域之间不存在 set_false_path 约束
  • 若要从 setup 和 hold 分析中移除两个时钟域之间的路径,使用 set_clock_groups 而非 set_false_path。set_clock_groups 的优先级低于 set_false_path
  • 使用 set_net_delay 约束两个时钟域之间的路径,使走线尽可能短
  • 使用 set_max_skew 约束两个时钟域之间的走线

以下是一个时钟域交叉的约束示例(数据从 clk_a 域到 clk_b 域):

create_clock -name clk_a -period 4.000 [get_ports {clk_a}]
create_clock -name clk_b -period 4.500 [get_ports {clk_b}]
set_clock_groups -asynchronous -group [get_clocks {clk_a}] -group \
    [get_clocks {clk_b}]
set_net_delay -from [get_registers {data_a[*]}] -to [get_registers \
    {data_b[*]}] -max -get_value_from_clock_period dst_clock_period \
    -value_multiplier 0.8
set_max_skew -from [get_keepers {data_a[*]}] -to [get_keepers {data_b[*]}] \
    -get_skew_value_from_clock_period src_clock_period \
    -skew_value_multiplier 0.8

1.6 亚稳态同步器

Compiler 会检测同步器链中的寄存器。Compiler 不会 retime 同步器链中的寄存器。为了允许对同步器链中的寄存器做 retiming,应在时钟域边界添加更多的流水线寄存器。

HyperFlex 架构 FPGA 的默认亚稳态同步器链长度为 3。Critical Chain 报告中会将亚稳态要求的寄存器标记为 REG (亚稳态 required)。

如果设计中使用了 2 级寄存器链作为同步器,可以通过以下设置修改默认链长:Assignments > Settings > Compiler Settings > Advanced Settings (Synthesis) > Synchronization Register Chain Length,设置为 2。或在 QSF 文件中指定:

set_instance_assignment -name SYNCHRONIZATION_REGISTER_CHAIN_LENGTH 2 \
    -to * -entity <top_module_name>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值