FPGA系列(5)
时序约束和CDC
跨时钟域同步
这部分还没有实践过,只是将过程中涉及到觉得有用的一些资料整理在这里。
跨时钟域(CDC)直接赋值会导致亚稳态问题,不能保证每次赋值都是正确的。处理方式有:电平同步器、脉冲同步器、FIFO/RAM。设计的模块包括复位处理模块(比如异步复位同步释放)、慢到快同步器、快到慢同步器、双沿检测器。同步器用打两拍的方式实现,这是概率上的处理方式,根据乘法原理,打两拍会使得亚稳态的概率会大大降低。(_ ASYNC_REG = “TRUE” _)用于声明寄存器能够接收相对于时钟源的异步数据,或者说寄存器是一个同步链路上正在同步的寄存器。
慢到快的跨时钟域传输
相对简单,一般采用延迟打拍法,或延迟采样法。
最常用的同步方法是双级触发器缓存法,俗称延迟打拍法。异步信号从一个时钟域进入另一个时钟域之前,将该信号用两级触发器连续缓存两次,可有效降低因为时序不满足而导致的亚稳态问题。一般设计中使用两级触发器进行缓存即可满足设计时序需求。大量实验表明,三级触发器缓存可解决 99% 以上的此类异步时序问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
module delay_clap(
input clk1, //异步慢时钟
input sig1, //异步信号
input rstn, //复位信号
input clk2, //目的快时钟域时钟
output sig2); //快时钟域同步后的信号
reg [2:0] sig2_r ; //3级缓存,前两级用于同步,后两节用于边沿检测
always @(posedge clk2 or negedge rstn) begin
if (!rstn) sig2_r <= 3'b0 ;
else sig2_r <= {sig2_r[1:0], sig1} ; //缓存
end
assign sig2 = sig2_r[1] && !sig2_r[2] ; //上升沿检测
延迟采样法主要针对多位宽的数据传输。例如当两个异步时钟频率比为 5 时,可以先用延迟打拍的方法对数据使能信号进行 2 级打拍缓存,然后再在快时钟域对慢时钟域的数据信号进行采集。该方法的基本思想是保证信号被安全采集的时刻,而不用同步多位宽的数据信号,可节省部分硬件资源。利用打拍的方法进行延迟采样的 Verilog 描述如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//同步模块工作时钟为 100MHz 的模块
//异步数据对来自工作时钟为 20MHz 的模块
module delay_sample(
input rstn,
input clk1,
input [31:0] din,
input din_en,
input clk2,
output [31:0] dout,
output dout_en);
//sync din_en
reg [2:0] din_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn) din_en_r <= 3'b0 ;
else din_en_r <= {din_en_r[1:0], din_en} ;
end
wire din_en_pos = din_en_r[1] && !din_en_r[2] ;
//sync data
reg [31:0] dout_r ;
reg dout_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
dout_r <= 'b0 ;
else if (din_en_pos)
dout_r <= din ;
end
//dout_en delay
always @(posedge clk2 or negedge rstn) begin
if (!rstn) dout_en_r <= 1'b0 ;
else dout_en_r <= din_en_pos ;
end
assign dout = dout_r ;
assign dout_en = dout_en_r ;
endmodule
快到慢的跨时钟域同步
需要根据信号的特点来进行同步处理。对于单 bit 信号,一般可按电平信号和脉冲信号来区分。
电平信号:同步逻辑设计中,电平信号是指长时间保持不变的信号。保持不变的时间限定是相对于慢时钟而言的。只要快时钟的信号保持高电平或低电平的时间足够长,以至于能被慢时钟在满足时序约束的条件下采集到,就可以认为该信号为电平信号。既然电平信号能够被安全的采集到,所以从快时钟域到慢时钟域的电平信号也采用延迟打拍的方法做同步。
脉冲信号:同步逻辑设计中,脉冲信号是指从快时钟域输出的有效宽度小于慢时钟周期的信号。如果慢时钟域直接去采集这种窄脉冲信号,有可能会漏掉。
假如这种脉冲信号脉宽都是一致的,在知道两个时钟频率比的情况下,可以采用 “快时钟域脉宽扩展+慢时钟域延迟打拍” 的方法进行同步。
如果有时窄脉冲信号又表现出电平信号的特点,即有时信号的有效宽度大于慢时钟周期而能被慢时钟采集到,那么对此类信号再进行脉冲扩展显然是不经济的。此时,可通过 “握手传输” 的方法进行同步。
假设脉冲信号的高电平期间为有效信号期间,其基本原理如下。(1) 快时钟域对脉冲信号进行检测,检测为高电平时输出高电平信号 pulse_fast_r。或者快时钟域输出高电平信号时,不要急于将信号拉低,先保持输出信号为高电平状态。(2) 慢时钟域对快时钟域的信号 pulse_fast_r 进行延迟打拍采样。因为此时的脉冲信号被快时钟域保持拉高状态,延迟打拍肯定会采集到该信号。(3) 慢时钟域确认采样得到高电平信号 pulse_fast2s_r 后,再反馈给快时钟域。(4) 快时钟域对反馈信号 pulse_fast2s_r 进行延迟打拍采样。如果检测到反馈信号为高电平,证明慢时钟域已经接收到有效的高电平信号。如果此时快时钟域自身逻辑不再要求脉冲信号为高电平状态,拉低快时钟域的脉冲信号即可。此方法实质是通过相互握手的方式对窄脉冲信号进行脉宽扩展。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//同步模块工作时钟大约为 25MHz 的模块
//异步数据对来自工作时钟为 100MHz 的模块
module pulse_syn_fast2s
#( parameter PULSE_INIT = 1'b0
)
(
input rstn,
input clk_fast,
input pulse_fast,
input clk_slow,
output pulse_slow);
wire clear_n ;
reg pulse_fast_r ;
/**************** fast clk ***************/
//(1) 快时钟域检测到脉冲信号时,不急于将脉冲信号拉低
always@(posedge clk_fast or negedge rstn) begin
if (!rstn)
pulse_fast_r <= PULSE_INIT ;
else if (!clear_n)
pulse_fast_r <= 1'b0 ;
else if (pulse_fast)
pulse_fast_r <= 1'b1 ;
end
reg [1:0] pulse_fast2s_r ;
/************ slow clk *************/
//(2) 慢时钟域对信号进行延迟打拍采样
always@(posedge clk_slow or negedge rstn) begin
if (!rstn)
pulse_fast2s_r <= 3'b0 ;
else
pulse_fast2s_r <= {pulse_fast2s_r[0], pulse_fast_r} ;
end
assign pulse_slow = pulse_fast2s_r[1] ;
reg [1:0] pulse_slow2f_r ;
/********* feedback for slow clk to fast clk *******/
//(3) 对反馈信号进行延迟打拍采样
always@(posedge clk_fast or negedge rstn) begin
if (!rstn)
pulse_slow2f_r <= 1'b0 ;
else
pulse_slow2f_r <= {pulse_slow2f_r[0], pulse_slow} ;
end
//控制快时钟域脉冲信号拉低
assign clear_n = ~(!pulse_fast && pulse_slow2f_r[1]) ;
endmodule
当多位宽数据进行同步时,如果该数据各 bit 位都可以看作电平信号,即相对一段时间内各 bit 位数据均可以保持不变以至于能被慢时钟采集到,可以消耗一些触发器资源对多位宽数据进行简单的延迟打拍同步。但如果数据变化速率过快,就不能再使用延迟打拍采样的方法。因为此时数据各 bit 位不再是电平信号,变化的时间也参差不齐,用异步时钟进行打拍采样,可能会采集到因路径延迟不同而导致的错误数据。解决此类异步问题的常用方法是采用异步 FIFO (First In First Out)。
FIFO(First In First Out)是异步数据传输时经常使用的存储器。该存储器的特点是数据先进先出(后进后出)。其实,多位宽数据的异步传输问题,无论是从快时钟到慢时钟域,还是从慢时钟到快时钟域,都可以使用 FIFO 处理。FIFO 使用现成的 IP 即可,没有到大后期都不建议自己实现。
时序约束
这部分还没有实践过,只是将过程中涉及到觉得有用的一些资料整理在这里。
时序约束的基本步骤为:创建主时钟约束、确定芯片内部时序约束、确定芯片与外部连接的约束、特殊路径的约束。
时钟约束必须最早创建,端口进来时钟以及 GT 输出 RXCLK/TXCLK 都需要使用 create clock 自主创建。如果是差分输入时钟,使用 create_clock 创建约束 P 端即可。
PGA/ASIC 设计中的 IO 时序约束,核心是告诉综合 / 布局布线工具:外部芯片和 FPGA 之间的信号延迟有多少,工具要按这个来算建立 / 保持时间。
- 建立时间(Setup):数据要在时钟沿到来前稳定多久,用 max 约束(最坏情况)。
- 保持时间(Hold):数据要在时钟沿到来后稳定多久,用 min 约束(最好情况)。
Vivado 工具会自动推导 MMCM/PLL/BUFR 输出作为衍生时钟。create clock 定义的主时钟的起点即时序的“零起点”,在这之前的上游路径延时都被工具自动忽略。
同步时钟的时序需要 STA 分析,异步时钟域的信号切换需要特殊处理,可以通过 set false path 消除异步时钟域的时序检查。
输入约束举例:
1
2
3
4
5
# 保持时间(min):外部到FPGA的最小延迟
set_input_delay -clock [get_clocks {Clk}] -min -add_delay 1.21 [get_ports {Din[*]}]
# 建立时间(max):外部到FPGA的最大延迟
set_input_delay -clock [get_clocks {Clk}] -max -add_delay 2.25 [get_ports {Din[*]}]
输出约束举例:
1
2
3
4
5
# 保持时间(min):FPGA到外部的最小延迟(这里是负的,代表提前)
set_output_delay -clock [get_clocks {Clk}] -min -add_delay -0.59 [get_ports {Dout[*]}]
# 建立时间(max):FPGA到外部的最大延迟
set_output_delay -clock [get_clocks {Clk}] -max -add_delay 2.25 [get_ports {Dout[*]}]
set_input_delay:约束外部到 FPGA 的信号延迟,用于 FPGA 内部的建立 / 保持检查。 set_output_delay:约束 FPGA 到外部的信号延迟,用于外部器件的建立 / 保持检查。 -min 给 hold,-max 给 setup,是 IO 时序约束的固定套路。
利用最大 / 最小延时约束组合路径延时
利用虚拟时钟:假设输入或者输出端口接在某一时序器件上,定义该时序器件时钟为虚拟时钟;利用 set_input_delay/set_output_delay 将相应端口归到虚拟时钟时序上
时序约束举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 差分时钟 sys_clk_p/n,指定IO标准为DIFF_SSTL12
set_property IOSTANDARD DIFF_SSTL12 [get_ports "sys_clk_p"]
set_property IOSTANDARD DIFF_SSTL12 [get_ports "sys_clk_n"]
# 2. 绑定差分时钟的物理管脚
set_property PACKAGE_PIN AT49 [get_ports "sys_clk_p"]
set_property PACKAGE_PIN AU49 [get_ports "sys_clk_n"]
# 3. 绑定片选信号 mem_cs_n[1] 的物理管脚
set_property PACKAGE_PIN AU51 [get_ports "mem_cs_n[1]"]
# 4. 片选信号用SSTL12_DCI电平标准(带片上终端)
set_property IOSTANDARD SSTL12_DCI [get_ports "mem_cs_n[1]"]
# 5. 片选信号输出阻抗40Ω,做阻抗匹配
set_property OUTPUT_IMPEDANCE RDRV_40_40 [get_ports "mem_cs_n[1]"]
静态时序分析 STA:采用穷尽的分析方法检查信号的建立和保持时间是否满足时序要求,通过对最大路径延时和最小路径延时的分析,找出违背时序约束的错误并报告。
数字电路时序分析中,将路径分为时钟路径、数据路径和异步路径三种。时钟路径是从时钟源到触发器时钟端的路径,决定了时钟到达各触发器的偏斜(skew)和延时(delay);数据路径是从一个触发器的 Q 端,到下一个触发器 D 端的路径,包含了组合逻辑+连线,决定了数据从源触发器到目标触发器的传输延时,是同步时序分析中建立和保持时间检查的核心;异步路径是直接连接到触发器异步控制端的路径,不受时钟控制,容易产生亚稳态、毛刺、复位/置位冲突,需要专门的异步时序分析。同步时序分析需要考虑时钟路径和数据路径,核心是检查建立时间和保持时间关系,确保数据在时钟沿到来前稳定且到来后不提前变;异步时序分析需要考虑时钟路径和异步路径,核心是检察异步控制信号与时钟的时序关系,避免异步信号在时钟沿附近变化导致亚稳态、异步信号宽度不够导致触发器来不及响应、多个异步信号冲突等异常情况。
常见的时序优化方法有重定时 retiming 和流水线 pipeling 两种,目标都是锁定关键路径延时,提高电路工作频率。重定时指在不改变电路逻辑的前提下,移动寄存器的位置,将长路径延时分摊到相邻路径,让各段延时更均衡。流水线指在长组合逻辑中插入新的寄存器,将长路径拆分成多条短路径,每条短路径延时更小,提高整体频率。
另外还有两点注意:
(1)一般来讲,IOB 中只有少量的逻辑资源,它会有寄存器,但它几乎没有任何组合逻辑资源,因此需被约束到管脚上的寄存器不能有任何组合逻辑;如果输入一进来就是一驱多,输出是经过组合逻辑后直接输出到管脚上的寄存器,则会出现布线不通的问题;但事实上,如果不是资源或功耗要求很高,我们一般都会采用同步设计方式,而对外接口部分在同步设计中一般被视作异步接口,设计中会对接口做同步处理。
(2)被约束的寄存器除了受组合逻辑影响外,还会受到时钟驱动区域的影响;在设计中为了保证时钟的 Skew 等性能,一般会将时钟加入全局时钟网络或区域时钟网络,如果是全局时钟网络自不必管,它可驱动整个芯片,但如果是区域时钟约束就要注意了。区域时钟顾名思义,即每个时钟分管芯片的部分区域,每个 PLL 都对应几个专用外部输入管脚,如果使用了别的 PLL 则无法保证时序最优。
SDC 中的 clock 是指设计中施加于一个点上的已定义的、重复的信号特征: (1)内部的(internal):在设计中作为时钟指定到特定节点;(2)虚拟的(virtual):没有实际的源或没有直接连接到设计,比如驱动 FPGA 或被 FPGA 驱动的外部器件的时钟。即,如果一个时钟不是设计中的时钟,而仅作为外部器件的时钟源,且外部器件和该设计有输入 / 输出管脚的连接,那么就认为此时钟是虚拟时钟。 使用 create_clock 命令创建一个虚拟时钟时对源选项没有指定。虚拟时钟用于作为输出最大 / 最小延时约束的时钟源。即使用虚拟时钟来约束 set_input_delay 和 set_output delay,即在创建虚拟时钟之后可以执行 FPGA 器件和外部器件的寄存器之间的,register-to-register 的分析报告。
在不做任何时序约束的情况下,无论是 Xilinx 还是 Altrea 的 EDA,都会默认所有时钟均为同步时钟,会对所有路径进行分析,因此存在异步时钟的情况下,需要对其进行时钟约束。
多周期路径是一种时序例外的情况。在同步逻辑设计中,通常都是按照单周期关系考虑数据路径的。但是往往存在这样的情况一些数据不需要在下一个时钟周期就稳定下来,可能在数据发送后几个时钟周期之后才起作用;一些数据经过的路径太复杂,延时太大,不可能在下一个时钟周期稳定下来,必须要在数据发送后数个时钟周期之后才能被采用。针对这两种情况,设计者的设计意图都是:数据的有效期在以 launch edge 为起始到数个时钟周之后的 latch edge。这一设计意图不能被时序分析工具猜度出来,必须由设计者在时序约束中指定;否则,时序约束工具会按照单周期路径检查的方式执行,往往会误报出时序违规。不设置多周期路径约束的后果有两种:一是按照单周期路径检查的结果,虚报时序违规;二是导致布局布线工具按照单周期路径的方式执行,虽然满足了时序规范,但是过分优化了本应该多个周期完成的操作,造成过约束。过约束会侵占本应该让位于其他逻辑的布局布线资源,有可能造成其他关键路径的时序违规或时序余量变小。
在多周期路径的建立时间(Setup Time)检查中,Timequest 会按照用户指定的周期数延长 Data Required Time, 放松对相应数据路径的时序约束,从而得到正确的时序余量计算结果;在保持时间(Hold Time)检查中,Timequest 也会相应地延长 Data Required Time, 不再按照单周期路径的分析方式执行(不再采用 launch edgei 最近的时钟沿,而是采用 latch edge 最近的时钟沿),这就需要用户指定保持时间对应的多周期个数。 Timequest 缺省的 Hold Time 检查公式是需要用户修改的一一针对 Setup Time 多周期路径的设置也会影响到 Hold Time 的检查。究其原因,多周期路径是为了解决信号传播太慢的问题,慢到一个周期都不够,所以要把 Setup Time 的检查往后推几个周期一扩大 Setup Time 检查的时间窗口。而 Hold Time 检查信号是否传播的太快,如果把检查时刻往后推,就缩小了 Hold Time 检查的时间窗口。
时序约束的目的,为了满足建立时间和保持时间的要求,由于 RTL 代码中是不包含时序信息的,因此需要时序约束来告诉 EDA 软件这些时序上的前提条件。时钟过约束或欠约束都可能导致时序难以收敛,因此需要设定合理的时序约束。
FPGA 中的时序路径有四种:一是从输入端口到 FPGA 内部第一级触发器的路径,用 set_input_delay 指令约束;二是 FPGA 内部触发器之间的路径,用 create_clock 指令约束;三是 FPGA 内部末级触发器到输出端口的路径,用 set_output_delay 约束;四是 FPGA 输入端口到输出端口的路径,用 set_max_delay 约束。四种路径中,一般最关心的是第二种,即 FPGA 内部的时序逻辑。
典型的时序模型如下。完整的时序路径包括源时钟路径、数据路径和目的时钟路径,也可以表示为触发器+组合逻辑+触发器的模型。
最常用的时序约束是时钟约束,时钟约束又有许多分类。
- 源时钟/主时钟约束用来设置主时钟是多少兆 Hz,用 create clock 指令设置。主时钟通常有两种情况,一种是由外部时钟沿提供,通过时钟管脚进入 FPGA,该时钟引脚绑定的时钟为主时钟;另一种是高速收发器 GT 的时钟 RXOUTTCLK 或 TXOUTTCLK。
- 衍生时钟约束包括自定义时钟和自动生成的时钟,自定义时钟如自己写的分频器、倍频器,需要设置约束;自动生成的时钟如 MMCM、PLL、BUFR、debug_hub 生成的时钟,不需要自己约束。衍生时钟用 create generated clock 指令约束。
- 异步时钟约束和互斥时钟约束用 set clock group 指令设置,其中互斥时钟又分物理互斥和逻辑互斥,物理互斥指引脚不同的时钟,逻辑互斥则是用 mux 组织的时钟。物理互斥可用于验证同一个时钟端口在不同时钟频率下是否获得时序收敛。逻辑互斥是在使用 NUFGMUX 时,会有两个输入时钟,但只会有一个时钟被使用。
设计约束最基本的是核心频率约束,然后是时序例外约束,包括 FalsePath、MulticyclePath、MaxDelay、MinDelay。以上是芯片内部的时序约束。再之后是 IO 约束,IO 约束包括引脚分配位置、空闲引脚驱动方式、外部走线延时(nputDelay、 OutputDelay)、上下拉电阻、驱动电流强度等。加入 I/O 约束后的时序约束,才是完整的时序约束。
FPGA 是整个 PCB 系统时序收敛的一部分,在设计 PCB 时理论上应该阅读并分析其 I/O 时序图。FPGA 的 I/O Timing 可能在设计期间发生变化,可能存在修改设计重新编译后,FPGA 对外部器件的操作出现不稳定的问题,有可能是 IO 时序导致的问题。
输入延迟约束和输出延迟约束分别用 set_input_delay 和 set_output_delay 指令实现,其时钟源可以是时钟输入管脚或虚拟时钟。另外,输入延迟约束和输出延迟约束并不起到延迟的作用,而是对周期的约束。比如输入延迟约束用于用于告诉 vivado 输入数据和输入时钟之间的延迟关系;而想要调节输入信号延迟,则需要使用 IDELAY 硬件资源。最大最小延迟约束用 set_max_delay 和 set_min_delay 实现,最大最小延迟的主要应用场景有二:一是输入管脚的信号经过组合逻辑之后直接输出到管脚;二是异步电路之间的最大最小延迟。下图以输入延迟为例进行说明。
虚拟时钟没有与之绑定的物理管脚,通常用于设定输入和输出的延迟约束,主要用于以下三个场景:一是外部 IO 的参考时钟并不是设计中的时钟;二是 FPGA/IO 路径参考时钟来源于内部衍生时钟,但与主时钟的频率关系并不是整数倍;三是针对 IO 指定不同的 jitter 和 latency。总结来说,之所以要创建虚拟时钟,对于输入来说,是因为输入到 FPGA 数据的捕获时钟是 FPGA 内部产生的,与主时钟频率不同,或者 PCB 上有 Clock Buffer 导致时钟延迟不同。对于输出来说,下游器件只接收到 FPGA 发送过去的数据,并没有随路时钟,要用自己内部的时钟去捕获数据。另外,虚拟时钟必须在约束 IO 之前被定义。
两种时序例外,一种是多周期路径。保持时间的检查,默认是在建立时间的前一个周期进行的。但若出现路径过长或逻辑延长过长需要经过多个时钟周期才能到达目标寄存器;又或者在数据发起的几个周期之后后续逻辑才能使用,此时如果按照单周期路径进行时序检查,就会报时序违规,因此需要使用多周期约束的指令来设置。另一种是伪路径,伪路径指路径存在但该路径的功能不会发生或无需时序约束。创建伪路径可以减少工具运行优化的时间,增强实现结果。伪路径一般用于跨时钟域、一上电就被写入数据的寄存器、异步复位或测试逻辑、异步双端口 RAM,即主要用在异步时钟的处理上。












