竞争与冒险/亚稳态/跨时钟域
文章目录
- 竞争与冒险/亚稳态/跨时钟域
-
- 1.亚稳态
-
- [1.1 好文章](#1.1 好文章)
- [1.2 什么是亚稳态](#1.2 什么是亚稳态)
- 1.3亚稳态的解决办法
-
- [1.3.1 跨时钟域的亚稳态------采用同步机制](#1.3.1 跨时钟域的亚稳态——采用同步机制)
-
- [1.3.1.1 单比特(脉冲和单比特流)的跨时钟域同步](#1.3.1.1 单比特(脉冲和单比特流)的跨时钟域同步)
-
- [1.3.1.1.1 单比特流的跨时钟域同步](#1.3.1.1.1 单比特流的跨时钟域同步)
- [1.3.1.1.2 脉冲的跨时钟域同步](#1.3.1.1.2 脉冲的跨时钟域同步)
- [1.3.1.2 多比特的跨时钟域同步](#1.3.1.2 多比特的跨时钟域同步)
-
- [1.3.1.2 多比特在工程上直接用异步fifo(最好)](#1.3.1.2 多比特在工程上直接用异步fifo(最好))
- [1.3.1.2 多比特慢到快(MUX同步器)](#1.3.1.2 多比特慢到快(MUX同步器))
- [1.3.1.2 多比特快到慢](#1.3.1.2 多比特快到慢)
- [1.3.2 异步输入信号异步释放时的亚稳态](#1.3.2 异步输入信号异步释放时的亚稳态)
- [1.3.3 同时钟域的亚稳态------STA分析解决时序违例](#1.3.3 同时钟域的亚稳态——STA分析解决时序违例)
- 2.竞争与冒险
-
- [2.1 实际工程中分析和处理竞争冒险](#2.1 实际工程中分析和处理竞争冒险)
- [2.1 理论的分析和处理竞争冒险](#2.1 理论的分析和处理竞争冒险)
1.亚稳态
1.1 好文章
亚稳态和同步器 https://zhuanlan.zhihu.com/p/111549994
1.2 什么是亚稳态
寄存器的输入信号不满足寄存器的建立时间和保持时间,导致寄存器的输出信号在一定时间内不能达到稳定的0或者1的现象。(建立时间 Tsu:触发器在时钟上升沿到来之前,其数据输入端的数据必须保持不变的时间。保持时间 Th:触发器在时钟上升沿到来之后,其数据输入端的数据必须保持不变的时间 。)
一般来说产生亚稳态的输入信号一般是异步输入(异步复位信号),或者是其他时钟域的信号跨到本地时钟域作本地时钟域寄存器的输入。
1.3亚稳态的解决办法
人们不会想着怎么降低发生亚稳态的概率,而是想着怎么减小亚稳态的影响。
一般来说:
- 跨时钟域的亚稳态------采用同步机制
- 异步输入信号异步释放时的亚稳态------采用异步输入同步释放
- 同时钟域的亚稳态------STA分析解决时序违例(降低时钟频率、插入流水寄存器)
现在分别来分析这几种方式
1.3.1 跨时钟域的亚稳态------采用同步机制
采用同步机制的前提是本地时钟域的输入信号需要是源时钟域的寄存输出,中间不能有组合逻辑,这样的原因是:1. 在原时钟域里面调整时序,剔除组合逻辑的由竞争产生的冒险(毛刺) 2. 使得信号的变化频率不会超过源时钟的频率
这里所讲的同步机制就是CDC问题了
这里主要分成两种情况,分别是单比特的跨时钟域同步,和多比特的跨时钟域同步,以下是几篇好文章
FPGA跨时钟域处理方法 https://blog.csdn.net/qq_35396239/article/details/118861983
跨时钟信号处理方法 https://zhuanlan.zhihu.com/p/113832794
1.3.1.1 单比特(脉冲和单比特流)的跨时钟域同步
单比特信号主要分为单比特流和脉冲信号
1.3.1.1.1 单比特流的跨时钟域同步
单比特流主要分为:
-
就是频率过快的脉冲信号,两个脉冲信号之间的间隔小于了采样时钟的2周期长度,或者是两个脉冲信号之间的长度小于握手机制要求间隔
-
一段要求持续特定时钟周期(比如慢时钟域N个周期,快时钟域也要求N个周期)的电平信号
-
一段0101的单比特数据流
都采用异步fifo来跨时钟
对于单比特流数据而言,无论是快时钟域到慢时钟域,还是慢时钟域到快时钟域,如果不使用RAM或者FIFO这类存储空间,想直接将数据通过流的方式进行同步,是无法做到的。这是因为两个时钟域的时钟周期长度不一样,随着时间的积累,一定会发生数据的错位。因此若想同步跨时钟域的流数据,必须要借助存储器空间,否则是无法同步流数据的。需要注意的是,快时钟域到慢时钟域的流数据,是不能一直持续的,否则就需要无限大的存储空间。
1.3.1.1.2 脉冲的跨时钟域同步
这里的脉冲我觉得是采样时钟域下的持续一个周期的脉冲,源时钟域的的信号可以是持续一个周期的脉冲信号或者是不关心持续多长时钟周期的电平信号(只需要在采样时钟域下采集到即可------采集形成一个采样时钟周期长度的信号)
那么可以分成两种情况
1. 慢时钟域到快时钟域:
网上说直接打两拍,第一拍 相当于同步到采样时钟域,第一级DFF寄存器的q端输出可能存在亚稳态(因异时钟域信号的输入到达第一级DFF的D端可能不满足第一级DFF的建立时间和保持时间),打第2拍 ,如果将第二级DFF和第一级DFF布局挨得很近的话,亚稳态就很大概率不会传播到第二级DFF的D端,这样就阻止了亚稳态的传播,这时候第二级DFF的Q端输出的就是稳定的信号了,然后打第三拍,与打第2拍信号结合,检测上升沿形成采样时钟域下的脉冲信号。
说的这个过程是没问题的,但是怎么衡量快时钟域了,他该比慢时钟域快多少了?只快一点点行吗,答案是不行的
因为第一级DFF的Q端输出的亚稳态,**再稳定之后,并不能保证数据稳定后,是正确的值,而是随机的0或1。**但是由于慢时钟域的脉冲信号持续时间大于快时钟域的一个周期,因此在快时钟域的下一个上升沿到来时,慢时钟域的脉冲信号仍然持续,此时快时钟域可以采到正确的值。也就是说,出现亚稳态时,快时钟域实际上需要已经对慢时钟域的信号进行第二次采样了。显然地,第二次采样时,需要满足建立时间与保持时间,否则可能会再次出现亚稳态。比如下图
虽然时钟快,但是只快一点点,第一次采样他采集就不满足建立时间,第二次采样不满足保持时间,导致两次采样都是亚稳态,输出的结果很可能不是正确的值
因此可以看出快时钟域与慢时钟域的关系并不是任意的,两者并不能接近到无法满足第二次采样的建立时间和保持时间。快时钟域慢时钟域需要满足下面的条件:Tslow>Tfast+Thold+Tsetup(网上常说的3时钟沿(包括下降沿)原则),其中Tslow为慢时钟域的时钟周期,Tfast为快时钟域的时钟周期,Thold与Tsetup分别为快时钟域寄存器的保持时间和建立时间。
通过上面的讨论我们可以发现,当使用多级寄存器时,如果出现了亚稳态,快时钟域 能够 采到慢时钟域信号所需的时间 比没有出现亚稳态时 可能会多一个周期。如果有彼此关联的两个多比特信号,比如说地址信号,它们从慢时钟域同步至快时钟域时,可能到达快时钟域的时间时不一样的,那么得到的地址就是错误的,这就是多比特信号即使是从慢时钟域到快时钟域,也不能够使用多级寄存器同步的原因。但如果多比特信号彼此无关,从慢时钟域到快时钟域时,是可以使用多级寄存器同步的。另外若多比特数据每次只有一个比特发生变化,也可以采用多级寄存器同步的方法,例如格雷码。
慢时钟域到快时钟域的时候,我一般会观察慢时钟域下的信号的持续长度满不满足2倍快时钟的时钟周期(一般来说3时钟沿原则就行,但是为了保险起见,还是遵循奈奎斯特定理吧,采样频率大于两倍信号频率)。如果满足,那直接打3拍,采集慢时钟域信号形成快时钟域的脉冲;如果不满足,那我就用计数器展宽信号,满足理想持续长度,然后在打3拍(更好的是用握手机制)
展宽信号的写法就是在源时钟域里面写一个计数器,检测到源时钟域的脉冲后拉高展宽信号,计数到目标值(展宽信号长度是两倍采样时钟周期)之后才拉低展宽信号,然后再将展宽信号在采样时钟域里面打3拍,检测其上升沿即可
1. 快时钟域到慢时钟域:
这里直接信号展宽满足快时钟域下的信号的持续长度达到2倍慢时钟的时钟周期
但是更好的是握手,现在手撕握手代码。
verilog
`timescale 1ns / 1ps
//
// Company: BUPT
// Engineer: Haoyan Hong
//
// Create Date: 2023/07/08 20:36:12
// Design Name:
// Module Name:
// Project Name:
// Target Devices:xlinx vc709
// Tool Versions: vivado2021.2 + modelsim2020.4
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module onebit_sync_handshake
//parameter
// #(
// )
//port
(
input wire i_src_clk ,
input wire i_des_clk ,
input wire i_src_signal ,
input wire i_rst ,
output wire o_des_pulse
);
/*******************************function******************************/
/*******************************parameter*****************************/
/*******************************port**********************************/
/*******************************mechine*******************************/
/*******************************reg***********************************/
(* ASYNC_REG="true" *)reg rst_src1,rst_src2;
(* ASYNC_REG="true" *)reg rst_des1,rst_des2;
reg src_handshake_req;//源时钟域的握手请求信号
reg ro_des_pulse ;
reg src_handshake_req_detect ;//src_handshake_req在des时钟域下的打一拍
reg src_handshake_req_detect_1d ;//src_handshake_req在des时钟域下的打两拍
reg src_handshake_req_detect_2d ;//src_handshake_req在des时钟域下的打三拍
reg des_handshake_ack; //des域的握手反馈信号ack
reg des_handshake_ack_detect ;//des_handshake_ack_detect在src时钟域下的打一拍
reg des_handshake_ack_detect_1d ;//des_handshake_ack_detect在src时钟域下的打两拍
reg des_handshake_ack_detect_2d ;//des_handshake_ack_detect在src时钟域下的打三拍
/*******************************wire**********************************/
/*******************************assign********************************/
assign sync_rst_src = rst_src2;//异步复位同步释放
assign sync_rst_des = rst_des2;//异步复位同步释放
assign o_des_pulse = ro_des_pulse;
/*******************************component*****************************/
/*******************************always********************************/
//reset bridge
always @(posedge i_src_clk or posedge i_rst) begin
if (i_rst) begin
rst_src1 <= 1'b1;
end
else
rst_src1 <= 1'b0;
end
always @(posedge i_src_clk or posedge i_rst) begin
if (i_rst) begin
rst_src2 <= 1'b1;
end
else
rst_src2 <= rst_src1;
end
always @(posedge i_des_clk or posedge i_rst) begin
if (i_rst) begin
rst_des1 <= 1'b1;
end
else
rst_des1 <= 1'b0;
end
always @(posedge i_des_clk or posedge i_rst) begin
if (i_rst) begin
rst_des2 <= 1'b1;
end
else
rst_des2 <= rst_des1;
end
//**********************src*******************************//
// src_handshake_req
always @(posedge i_src_clk) begin
if (sync_rst_src) begin
src_handshake_req <= 1'b0;
end
else if(!des_handshake_ack_detect_2d && des_handshake_ack_detect_1d) begin//des_handshake_ack_detect的上升沿
src_handshake_req <= 1'b0;
end
else if(i_src_signal) begin
src_handshake_req <= 1'b1;
end
else
src_handshake_req <= src_handshake_req;
end
// des_handshake_ack_detect
always @(posedge i_src_clk) begin
if (sync_rst_src) begin
des_handshake_ack_detect <= 1'b0;
end
else
des_handshake_ack_detect <= des_handshake_ack;
end
// des_handshake_ack_detect_1d
always @(posedge i_src_clk) begin
if (sync_rst_src) begin
des_handshake_ack_detect_1d <= 1'b0;
end
else
des_handshake_ack_detect_1d <= des_handshake_ack_detect;
end
// des_handshake_ack_detect_2d
always @(posedge i_src_clk) begin
if (sync_rst_src) begin
des_handshake_ack_detect_2d <= 1'b0;
end
else
des_handshake_ack_detect_2d <= des_handshake_ack_detect_1d;
end
//**********************des*******************************//
//将src_handshake_req信号在des时钟域下打3拍,第一拍同步到des时钟域下(输出可能存在亚稳态),第二拍防止亚稳态的传播,第三拍是为了检测src_signal_req的边沿
// 不用第二拍的输出q和第一拍输出q检测是因为第一拍输出q可能会有亚稳态
// src_handshake_req_detect
always @(posedge i_des_clk) begin
if (sync_rst_des) begin
src_handshake_req_detect <= 1'b0;
end
else
src_handshake_req_detect <= src_handshake_req;
end
// src_handshake_req_detect_1d
always @(posedge i_des_clk) begin
if (sync_rst_des) begin
src_handshake_req_detect_1d <= 1'b0;
end
else
src_handshake_req_detect_1d <= src_handshake_req_detect;
end
// src_handshake_req_detect_2d
always @(posedge i_des_clk) begin
if (sync_rst_des) begin
src_handshake_req_detect_2d <= 1'b0;
end
else
src_handshake_req_detect_2d <= src_handshake_req_detect_1d;
end
// des_handshake_ack
always @(posedge i_des_clk) begin
if (sync_rst_des) begin
des_handshake_ack <= 1'b0;
end
else if(!src_handshake_req_detect_2d && src_handshake_req_detect_1d) begin //src_handshake_req的上升沿
des_handshake_ack <= 1'b1;
end
else if(src_handshake_req_detect_2d && !src_handshake_req_detect_1d) begin //src_handshake_req的下降沿
des_handshake_ack <= 1'b0;
end
else
des_handshake_ack <= des_handshake_ack;
end
// ro_des_pulse
always @(posedge i_des_clk) begin
if (des_handshake_ack) begin
ro_des_pulse <= 1'b0;
end
else if(!src_handshake_req_detect_2d && src_handshake_req_detect_1d) begin //src_handshake_req的上升沿
ro_des_pulse <= 1'b1;
end
else
ro_des_pulse <= 1'b0;
end
endmodule
这里ack可以拉高检测req的上升沿,拉低检测下降沿(拉低也可以用计数器来控制)
仿真文件:
verilog
`timescale 1ns / 1ps
//
// Company: BUPT
// Engineer: Haoyan Hong
//
// Create Date: 2023/07/08 20:36:12
// Design Name:
// Module Name:
// Project Name:
// Target Devices: xlinx vc709
// Tool Versions: vivado2021.2 + modelsim2020.4
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_onebit_sync_handshake();
/***************parameter*************/
parameter CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns;
parameter CYCLE_SRC = 10 ;//系统时钟周期,单位ns,默认10ns;
parameter CYCLE_DES = 20 ;//系统时钟周期,单位ns,默认10ns;
parameter P_RST_CYCLE = 3 ;//系统复位持续时钟周期;
parameter STOP_TIME = 200 ;//仿真运行时间,复位完成后运行100个系统时钟后停止;
/***************reg*******************/
reg i_src_clk;
reg i_des_clk;
reg i_src_signal;
reg i_clk;
reg sync_rst_src;
reg sync_rst_des;
/***************wire******************/
wire o_rst;
wire o_des_pulse;
/***************assign****************/
/***************initial***************/
//生成周期为CYCLE数值的系统时钟;
initial begin
i_clk = 0;
forever #(CYCLE/2) i_clk = ~i_clk;
end
initial begin
i_src_clk = 0;
forever #(CYCLE_SRC/2) i_src_clk = ~i_src_clk;
end
initial begin
i_des_clk = 0;
forever #(CYCLE_DES/2) i_des_clk = ~i_des_clk;
end
initial begin
force sync_rst_src = onebit_sync_handshake_inst.sync_rst_src;
force sync_rst_des = onebit_sync_handshake_inst.sync_rst_des;
repeat(STOP_TIME)begin//循环STOP_TIME次;
@(posedge i_clk);
end
$stop;//停止仿真;
end
//task任务的使用
initial begin
gen_data();
end
/***************component*************/
rst_gen_module # (
.P_RST_CYCLE(P_RST_CYCLE)
)
rst_gen_module_inst (
.i_clk(i_clk),
.o_rst(o_rst)
);
onebit_sync_handshake onebit_sync_handshake_inst (
.i_src_clk(i_src_clk),
.i_des_clk(i_des_clk),
.i_src_signal(i_src_signal),
.i_rst(o_rst),
.o_des_pulse(o_des_pulse)
);
/***************always****************/
/***************function**************/
task gen_data;
//整型变量的申明
integer i;
//函数的主体
begin
//变量的初始化
i_src_signal <= 1'b0;
//等待高复位
// @(posedge srst);
@(negedge sync_rst_src);
//再过几个时钟周期
repeat (5) @(posedge i_src_clk);
//数据的发送
for (i=0;i<256;i=i+1) begin
@(posedge i_src_clk);
i_src_signal <= 1'b1;
@(posedge i_src_clk);
i_src_signal <= 1'b0;
repeat (10) @(posedge i_src_clk);
end
end
endtask
endmodule
仿真结果如下:
可以看出必须要在req拉低之后,才能在src时钟域产生新的脉冲信号,如果在req为高的时候产生新的脉冲信号,那么脉冲信号就会采集失败。
在代码的时候,打拍和if两种写法综合成的电路是一样的
1.3.1.2 多比特的跨时钟域同步
1.3.1.2 多比特在工程上直接用异步fifo(最好)
为什么异步fifo可以进行跨时钟域处理?
链接:https://www.nowcoder.com/questionTerminal/1937ecf26c584e909b0566d8f7d188ab
1异步FIFO使用RAM将两个时钟域进行隔离 2、在异步FIFO的读写控制中,引入了格雷码(读写指针------读写的地址,将这种连续变化计数器风格的二进制代码转化成格雷码),格雷码相邻的两个数值之间只有一位不同,因此降低了亚稳态产生的概率。如果指针发生变化,并且在指针传递的过程中如果产了亚稳态,无非出现两种情况。第一种传递过来的指针与未变化之前的指针相同,这时不会引起empty或者full信号的变化,但这种情况不会使得fifo的功能出现错误。第二种情况就是传递过来的指针与变化后的指针相同,这种情况下是我们想要的,因此异步FIFO适合进行跨时钟域处理
为什么格雷码能打拍处理亚稳态?
- https://icer96.blog.csdn.net/article/details/123018083?spm=1001.2101.3001.6650.3\&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-123018083-blog-124333231.235^v43^pc_blog_bottom_relevance_base3\&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-123018083-blog-124333231.235^v43^pc_blog_bottom_relevance_base3\&utm_relevant_index=4
- 跨时钟域处理(二)------格雷码(连续二进制数) https://zhuanlan.zhihu.com/p/161681621
由于格雷码相邻数据只有一个bit不同,因此,在进行跨时钟域传输时,只有发生变化的那个bit有可能处于亚稳态,而其余bit由于保持不变,因此是稳定的,故多比特格雷码的跨时钟域传输相当于单比特信号的跨时钟域传输,我们采用打两拍的方法即可处理。
1.3.1.2 多比特慢到快(MUX同步器)
根据数据有效标志信号进行多比特数据的跨时钟域处理,
https://cloud.tencent.com/developer/article/1779892
为什么MUX能实现多bit同步
由于data enable A 的时序等同于 data bus,跨到data enable B 时也就保证了 data bus 穿过 DFF 的信号已经稳定,即可拿来锁入最后一级DFF
PDF可以私信我,嘿嘿嘿
1.3.1.2 多比特快到慢
https://zhuanlan.zhihu.com/p/496117926
在慢时钟域下对数据有效标志信号进行展宽.
1.3.2 异步输入信号异步释放时的亚稳态
这种亚稳态是因为异步输入异步释放的时候(0变到1或者1变到0),不满足当前时钟域下寄存器的建立时间和保持时间
所以解决的办法就是同步释放,具体看我的博文。
1.3.3 同时钟域的亚稳态------STA分析解决时序违例
这里得看时序分析,寄存器域寄存器之间的模型
2.竞争与冒险
竞争,指组合逻辑中,不同输入数据到达门器件的时间不一致
冒险,指因竞争而导致非预期的输出(毛刺)
2.1 实际工程中分析和处理竞争冒险
对于同步电路(比如DFF的clk是同一个信号),一般不关心竞争与冒险,因在做STA时序分析的时候已经滤除掉了
对于异步电路(DFF的clk是同一个信号不是同一个clk),就需要,但一般都从电路上解决这个问题,通过增加clock domain的dff,让clk0到clk1的数据交互是DFF输出就行