FPGA 中亚稳态产生原因与避免

  

目录

1.触发器的Tsu,Th

2.消除亚稳态的方法

2.1对异步信号进行同步提取边沿

2.2FIFO进行异步跨频数据处理

2.3对复位电路进行异步复位,同步释放


  在FPGA系统中,如果数据传输中不满足触发器的Tsu和Th,或者复位过程中复位信号的释放相对于有效时钟沿的恢复时间(recovery time)不满足,就可能产生亚稳态,此时触发器输出端Q在有效时钟沿之后比较长的一段时间处于不确定的状态,在这段时间里Q端在0和1之间处于振荡状态,而不是等于数据输入端D的值。这段时间称为决断时间(resolution time)。经过resolution time之后Q端将稳定到0或1上,但是稳定到0或者1,是随机的,与输入没有必然的关系。

1.触发器的Tsu,Th

在数字电路中,触发器(Flip-Flop)是一种用于存储和稳定数据的元件。Tsu和Th是触发器的输入信号的时序参数,用于描述输入信号在时钟边沿前后的稳定要求。

1. Tsu(Setup Time):Tsu是指在时钟边沿到来之前,输入信号必须保持稳定的最小时间。在时钟边沿之前,输入信号必须达到并保持其稳定的逻辑电平,以确保正确的数据采样。如果输入信号在Tsu之内变化,可能会导致触发器的输出变得不确定。

2. Th(Hold Time):Th是指在时钟边沿到来之后,输入信号必须保持稳定的最小时间。在时钟边沿之后的一段时间内,输入信号必须保持在其稳定的逻辑电平,以确保正确的数据保持。如果输入信号在Th之内变化,也可能会导致触发器的输出变得不确定。

这两个时序参数的目的是确保输入信号在时钟边沿时被稳定采样,以避免亚稳态(Metastability)和不确定性。设计和时序约束的目标是满足Tsu和Th的要求,以确保正确的数据传输和存储操作。如果输入信号的变化在Tsu和Th之间,会增加触发器出现错误的风险。

Tsu和Th的值通常由触发器的制造商提供,并且会根据具体的触发器类型和工作条件而有所不同。在设计数字电路时,需要根据所选用的触发器和设计要求来选择合适的时序参数值,并确保输入信号满足这些要求,以确保可靠的操作。

2.消除亚稳态的方法

有亚稳态产生,我们就要对亚稳态进行消除,常用对亚稳态消除有三种方式:

(1)       对异步信号进行同步处理;

(2)       采用FIFO对跨时钟域数据通信进行缓冲设计;

(3)       对复位电路采用异步复位、同步释放方式处理。

2.1对异步信号进行同步提取边沿

input      signal;
 
reg signal_r1;
reg signal_r2;
 
wire signal_rising;
wire signal_down;
 
always@(posedge clk or negedge rst)
    if(!rst)
    signal_r1<= 1'b0;
    else
    signal_r1<= signal;
 
always@(posedge clk or negedge rst)
    if(!rst)
    signal_r2<= 1'b0;
    else
    signal_r2<= signal_r1;
 
assign signal_rising = ((signal_r1 == 1'b1)&&(signal_r2 == 1'b0))? 1:0; //检测上升延
assign signal_down   = ((signal_r1 == 1'b0)&&(signal_r2 == 1'b1))? 1:0; //检测下降延
 
 
//-----------便捷方法如下----------
 
input        signal;
reg  [3:0]   sig_nsyn_r;   //信号异步移位
wire         signal_rising;
wire         signal_down;
 
always@(posedge clk or negedge rst)
begin
       if(!rst) 
         sig_nsyn_r <= 2’d0;
       else         
         sig_nsyn_r <= { sig_nsyn_r [0], sig_nsyn };
end
 
assign     signal_rising = ~sig_nsyn_r[3]  & sig_nsyn_r[2];
assign     signal_down   =  sig_nsyn_r[3]  & ~sig_nsyn_r[2];

这种边沿提取方式对于一个稳定的系统是不合适的,例如:当第一级寄存器采集到亚稳态,那势必造成sig_nsyn_p输出亚稳态,这样就会对采用sig_nsyn_p的信号进行判断的电路造成影响,甚至判断出错误的值。根据亚稳态产生概率,如果在100M时种下那第一级寄存器产生亚稳态的概率约为10%,随着系统采集频率升高,那产生亚稳态的概率也会随之上升。因此,在进行异步信号跨频提取边沿时候,一般采用多进行一级寄存器消除亚稳态,可能在系统稳定性要求高的情况下,采用更多级寄存器来消除亚稳态,如程序清单 4.2所示,即为采用4级寄存器消除亚稳态,相应的边沿信号产生的时间就晚了两个时钟周期。

2.2FIFO进行异步跨频数据处理

高位扩展法

  • 当最高位不同,且其他位相同,则表示读指针或者写指针多跑了一圈,而显然不会让读指针多跑一圈(多跑一圈读啥?),所以可能出现的情况只能是写指针多跑了一圈,与就意味着FIFO被写满了
  • 当最高位相同,且其他位相同,则表示读指针追到了写指针或者写指针追到了读指针,而显然不会让写指针追读指针(这种情况只能是写指针超过读指针一圈),所以可能出现的情况只能是读指针追到了写指针,也就意味着FIFO被读空了
     
  • “写满”的判断:需要将读指针同步到写时钟域,再与写指针判断
  • “读空”的判断:需要将写指针同步到读时钟域,再与读指针判断

假读空

假写满

   跨时钟域进行同步之前,可以先讲二进制转换为格雷码表示,相对于二进制,格雷码在进行跨时钟域转换时,产生亚稳态的概率更小,格雷码是一种非权重码,每次变化位数只有一位,这就有效的避免了在跨时钟域情况下亚稳态问题发生的概率。举个例子,二进制的7(0111)跳转到8(1000),4位都会发生变化,所以发生亚稳态的概率就比较大。而格雷码的跳转就只有一位(从0100--1100,仅第四位发生变化)会发生变化,有效地减小亚稳态发生的可能性。

  1. 分别构造读、写时钟域下的读、写指针,指针位数需拓展一位。举例,设计的FIFO深度为16,16个地址需要4位二进制数表示,同时扩宽一位作为指示位,所以指针的位宽共需要5位。
  2. 分别将读、写指针从二进制码转换成格雷码
  3. 将格雷码形式的读指针同步到写时钟域;将格雷码形式的写指针同步到读时钟域
  4. 在写时钟域判断“写满”:格雷码形式的读写指针高2位相反,其余位相等
  5. 在读时钟域判断“读空”:格雷码形式的读写指针高2位相等,其余位也相等--即全部相等
//异步FIFO
module	async_fifo
#(
	parameter   DATA_WIDTH = 'd8  ,								//FIFO位宽
    parameter   DATA_DEPTH = 'd16 								//FIFO深度
)		
(		
//写数据		
	input							wr_clk		,				//写时钟
	input							wr_rst_n	,       		//低电平有效的写复位信号
	input							wr_en		,       		//写使能信号,高电平有效	
	input	[DATA_WIDTH-1:0]		data_in		,       		//写入的数据
//读数据			
	input							rd_clk		,				//读时钟
	input							rd_rst_n	,       		//低电平有效的读复位信号
	input							rd_en		,				//读使能信号,高电平有效						                                        
	output	reg	[DATA_WIDTH-1:0]	data_out	,				//输出的数据
//状态标志					
	output							empty		,				//空标志,高电平表示当前FIFO已被写满
	output							full		    			//满标志,高电平表示当前FIFO已被读空
);                                                              
 
//reg define
//用二维数组实现RAM
reg [DATA_WIDTH - 1 : 0]			fifo_buffer[DATA_DEPTH - 1 : 0];
	
reg [$clog2(DATA_DEPTH) : 0]		wr_ptr;						//写地址指针,二进制
reg [$clog2(DATA_DEPTH) : 0]		rd_ptr;						//读地址指针,二进制
reg	[$clog2(DATA_DEPTH) : 0]		rd_ptr_g_d1;				//读指针格雷码在写时钟域下同步1拍
reg	[$clog2(DATA_DEPTH) : 0]		rd_ptr_g_d2;				//读指针格雷码在写时钟域下同步2拍
reg	[$clog2(DATA_DEPTH) : 0]		wr_ptr_g_d1;				//写指针格雷码在读时钟域下同步1拍
reg	[$clog2(DATA_DEPTH) : 0]		wr_ptr_g_d2;				//写指针格雷码在读时钟域下同步2拍

//概率问题。2拍已经基本能防止出现亚稳态了。3、4拍提升不大
//wire define
wire [$clog2(DATA_DEPTH) : 0]		wr_ptr_g;					//写地址指针,格雷码
wire [$clog2(DATA_DEPTH) : 0]		rd_ptr_g;					//读地址指针,格雷码
wire [$clog2(DATA_DEPTH) - 1 : 0]	wr_ptr_true;				//真实写地址指针,作为写ram的地址
wire [$clog2(DATA_DEPTH) - 1 : 0]	rd_ptr_true;				//真实读地址指针,作为读ram的地址
 
//地址指针从二进制转换成格雷码
assign 	wr_ptr_g = wr_ptr ^ (wr_ptr >> 1);					
assign 	rd_ptr_g = rd_ptr ^ (rd_ptr >> 1);
//读写RAM地址赋值
assign	wr_ptr_true = wr_ptr [$clog2(DATA_DEPTH) - 1 : 0];		//写RAM地址等于写指针的低DATA_DEPTH位(去除最高位)
assign	rd_ptr_true = rd_ptr [$clog2(DATA_DEPTH) - 1 : 0];		//读RAM地址等于读指针的低DATA_DEPTH位(去除最高位)
 
 
//写操作,更新写地址
always @ (posedge wr_clk or negedge wr_rst_n) begin
	if (!wr_rst_n)
		wr_ptr <= 0;
	else if (!full && wr_en)begin								//写使能有效且非满
		wr_ptr <= wr_ptr + 1'd1;
		fifo_buffer[wr_ptr_true] <= data_in;
	end	
end
//将读指针的格雷码同步到写时钟域,来判断是否写满
always @ (posedge wr_clk or negedge wr_rst_n) begin
	if (!wr_rst_n)begin
		rd_ptr_g_d1 <= 0;										//寄存1拍
		rd_ptr_g_d2 <= 0;										//寄存2拍
	end				
	else begin												
		rd_ptr_g_d1 <= rd_ptr_g;								//寄存1拍
		rd_ptr_g_d2 <= rd_ptr_g_d1;								//寄存2拍
	end	
end
//读操作,更新读地址
always @ (posedge rd_clk or negedge rd_rst_n) begin
	if (!rd_rst_n)
		rd_ptr <= 'd0;
	else if (rd_en && !empty)begin								//读使能有效且非空
		data_out <= fifo_buffer[rd_ptr_true];
		rd_ptr <= rd_ptr + 1'd1;
	end
end
//将写指针的格雷码同步到读时钟域,来判断是否读空
always @ (posedge rd_clk or negedge rd_rst_n) begin
	if (!rd_rst_n)begin
		wr_ptr_g_d1 <= 0;										//寄存1拍
		wr_ptr_g_d2 <= 0;										//寄存2拍
	end				
	else begin												
		wr_ptr_g_d1 <= wr_ptr_g;								//寄存1拍
		wr_ptr_g_d2 <= wr_ptr_g_d1;								//寄存2拍		
	end	
end
//更新指示信号
//当所有位相等时,读指针追到到了写指针,FIFO被读空
assign	empty = ( wr_ptr_g_d2 == rd_ptr_g ) ? 1'b1 : 1'b0;
//当高位相反且其他位相等时,写指针超过读指针一圈,FIFO被写满
//同步后的读指针格雷码高两位取反,再拼接上余下位
assign	full  = ( wr_ptr_g == { ~(rd_ptr_g_d2[$clog2(DATA_DEPTH) : $clog2(DATA_DEPTH) - 1])
				,rd_ptr_g_d2[$clog2(DATA_DEPTH) - 2 : 0]})? 1'b1 : 1'b0;
endmodule

2.3对复位电路进行异步复位,同步释放

//*******************同步复位模块**************************
 
//-----------端口定义-------------------------------
module	rst_test(
	input		clk		,		//工作时钟
	input		rst_n	,		//复位,低电平有效
	input		in		,		//输入信号
	output	reg	out				//输出信号
);
//-----------reg定义-------------------------------
reg arst_n_r;
reg arst_n;
//-----------复位信号同步模块-------------------------------
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		arst_n_r <= 1'b0	;	//复位将输出置零
		arst_n <= 1'b0		;	//复位将输出置零	
	end
	else begin
		arst_n_r <= 1'b1	;	//跟接rst_n是一样的,都是逻辑1
		arst_n <= arst_n_r	;		
	end
end
//-----------输出模块-------------------------------
always@(posedge clk or negedge arst_n)begin
	if(!arst_n)
		out <= 1'b0;		    //复位将输出置零
	else
		out <= in;			    //其他时候将输入赋值给输出
end	
	
endmodule
 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值