跨时钟域信号处理方法&异步FIFO

本文介绍了在数字系统设计中,跨时钟域数据传输的问题及其解决方法,重点讨论了全同步、全局异步局部同步以及多时钟域设计。针对亚稳态和同步失败,提出了同步器、保持寄存器、握手信号和异步FIFO等解决方案。详细阐述了异步FIFO的工作原理,包括写满和读空标志的产生,以及在读写指针同步化中的格雷码应用,最后给出了一个256深度、16位宽度的Verilog HDL异步FIFO设计实例。
摘要由CSDN通过智能技术生成

跨时钟域

全同步设计:1个时钟
全异步设计:没有时钟(存在少)
全局异步,局部同步设计:不可避免,单一时钟不能满足设计需求

多时钟域设计不可避免问题:亚稳态,同步失败
同步化:同步器;保持寄存器和握手(效率不高);异步FIFO设计

两级寄存器
一级寄存概率的平方,虽然两级并不能完全消除亚稳态危害,但是提高了可靠性,减少其发生概率
分类:电平同步器,边沿检测同步器,脉冲同步器
电平同步器(打两拍)
在这里插入图片描述
从Clock1到Clock2的跨时钟,触发器之间无组合逻辑,电平同步一般只适用于频率一样,相位上没有同步,但是两者频率不一样,就不能使用。如果是慢时钟到快时钟域,会出现数据重复采样。

边沿检测同步器(慢到快)
在这里插入图片描述
将慢时钟域的脉冲同步到快时钟域,快时钟采集慢数据,会出现数据重复,将数据变化转为一个脉冲,并进行门运算,去除重复数据

脉冲同步器(快到慢)
在这里插入图片描述
慢到快只需要考虑亚稳态问题,快到慢要考虑采样速率,保证数据不丢失,这个问题称为信号宽度问题。一般安全的宽度是快时钟域的信号宽度必须是慢时钟域周期的1.5倍以上,也就是持续三个时钟沿以上。整个设计包括脉冲电平检测、双触发器同步、边沿检测三部分。

多bit信号跨时钟域传输
在这里插入图片描述
b_load和b_en各自经过同步器延时之后,产生不同步的问题,解决方案将两路合成一路,再经过同步器,分成两路信号控制置位和使能端

总线信号的跨时钟域
正确方法:保持寄存器加握手信号;异步FIFO设计在这里插入图片描述
异步FIFO:读时钟和写时钟为独立时钟,思想:隔离时钟域,匹配读写速度

异步FIFO介绍
FIFO(First In First out)先进先出,它是一种存储器结构,能读写数据,所以有双口RAM作为存储数据的单元,在不同时钟域传输数据,使用异步FIFO来作为传输的缓冲区,既可以保证异步时钟数据传输的时序要求变得宽松,也提高传输效率。

FIFO写满和读空标志的产生
读写指针比较产生,同步FIFO直接把读写指针比较或者运算产生,而异步FIFO由于读写指针不同时钟域,比较前指针必须同步化,由于二进制的指针会出现多位同时跳变,直接由同步器进行同步化会有问题
在这里插入图片描述
判断存储器(RAM)写入写出的状态,需要有写满判断(wfull信号),和读空判断(empty信号),如何得到写满和读空信号?通过比较读写地址(告诉我们数据数据位置)、指针(四个信号 waddr,wptr,raddr,rptr)来判断此刻FIFO的写满和读空。
判断中涉及到w2r和r2w时钟域问题(2级寄存器同步)

对于异步FIFO,读写为不同时钟,如果直接采样,就会有亚稳态问题,比如写地址从00111改变01000的时候,读时钟恰好采样,除了最高位,其他4为有可能出现亚稳态,也有可能同步到错误的地址,这就是需要引入格雷码的原因。

设定地址指针位数时需要+1位,如16个数组需要4位,地址指针位数为5,
当最高位和次高位相同,其余位相同则认为是读空;
当最高位和次高位不同,其余位相同则认为是写满;

在这里插入图片描述
下面是用Verilog HDL设计深度为256(存储器可以写入256个数据),位宽为16(每个数据的宽度为16)的FIFO

module asyn_fifo
	#(
		parameter data_width = 16,
		parameter data_depth = 8,
		parameter ram_depth  = 256
	)
	(
	input							rst_n,
	
	input               			wr_clk,
	input               			wr_en,
	input      [data_width-1:0]     data_in,
	output              			full,
	
	input               			rd_clk,
	input               			rd_en,
	input  reg [data_width-1:0]     data_out,
	output              			empty
	);
	
	reg  [data_depth-1:0] wr_adr;
	reg  [data_depth-1:0] rd_adr;
	
	reg  [data_depth:0] wr_adr_ptr;
	reg  [data_depth:0] rd_adr_ptr;
	
	wire  [data_depth:0] wr_adr_gray;
	reg  [data_depth:0] wr_adr_gray1;
	reg  [data_depth:0] wr_adr_gray2;
	wire  [data_depth:0] rd_adr_gray;
	reg  [data_depth:0] rd_adr_gray1;
	reg  [data_depth:0] rd_adr_gray2;
	
	//dual port ram -write and read
	assign wr_adr = wr_adr_ptr[data_depth-1:0];
	assign rd_adr = rd_adr_ptr[data_depth-1:0];
	
	integer i;
	reg [data_width-1:0] ram_fifo [ram_depth-1:0];
	
	always@(posedge wr_clk or negedge rst_n)begin
		if(!rst_n)begin
			for(i=0;i<ram_depth;i++)
				ram_fifo[i] <= 'd0;//全部清零
		end
		else if(wr_en && (~full))
			ram_fifo[wr_adr] <= data_in;
		else
			ram_fifo[wr_adr] <= ram_fifo[wr_adr];
	end
	
	always@(posedge rd_clk or negedge rst_n)begin
		if(!rst_n)
			data_out <= 'd0;
		end
		else if(rd_en && (~empty))
			data_out <= ram_fifo[rd_adr];
		else
			data_out <= 'd0;
	end
	
	//wr_adr_ptr ++ and rd_adr_ptr ++
	always@(posedge wr_clk or negedge rst_n)begin
		if(!rst_n)
			wr_adr_ptr <= 'd0;
		else if(wr_en && (~full))
			wr_adr_ptr <= wr_adr_ptr + 1'b1;
		else
			wr_adr_ptr <= wr_adr_ptr;
	end
	
	always@(posedge rd_clk or negedge rst_n)begin
		if(!rst_n)
			rd_adr_ptr <= 'd0;
		end
		else if(rd_en && (~empty))
			rd_adr_ptr <= rd_adr_ptr + 1'b1 ;
		else
			rd_adr_ptr <= rd_adr_ptr;
	end
	
	//binary to gray
	assign wr_adr_gray = (wr_adr_ptr >> 1) ^ wr_adr_ptr;
	assign rd_adr_gray = (rd_adr_ptr >> 1) ^ rd_adr_ptr;
	
	//gray cdc compare
	always@(posedge wr_clk or negedge rst_n)begin
		if(!rst_n)begin
			wr_adr_gray1 <= 'd0;
			wr_adr_gray2 <= 'd0;
		end
		else begin 
			wr_adr_gray1 <= wr_adr_gray;
			wr_adr_gray2 <= wr_adr_gray1;
		end
	end
	
	always@(posedge rd_clk or negedge rst_n)begin
		if(!rst_n)begin
			rd_adr_gray1 <= 'd0;
			rd_adr_gray2 <= 'd0;
		end
		else begin 
			rd_adr_gray1 <= rd_adr_gray;
			rd_adr_gray2 <= rd_adr_gray1;
		end
	end
	
	assign empty = (rd_adr_gray == wr_adr_gray2)?1'b1:1'b0;
	assign full=(wr_adr_gray[data_depth:data_depth-1] !=rd_adr_gray2[data_depth:data_depth-1])&&(wr_adr_gray[data_depth:data_depth-2:0] ==rd_adr_gray2[data_depth-2:0]);

endmodule	

测试代码如下

'timescale 1us/1us

module asyn_fifo_tb;
	reg						rst_n;
	
	reg                     wr_clk;
	reg						wr_en;
	reg        [15:0]       data_in;
	wire					full;
	
	reg						rd_clk;
	reg						rd_en;
	wire       [15:0]       data_out;
	wire					empty;
	
	asyn_fifo asyn_fifo_inst
	(
		.rst_n    (rst_n),
		
		.wr_clk   (wr_clk),
		.wr_en    (wr_en),
		.data_in  (data_in),
		.full     (full),
		
		.rd_clk   (rd_clk),
		.rd_en    (rd_en),
		data_out  (data_out),
		.empty    (empty)
	);

	initial wr_clk = 0;
	always#10 wr_clk=~wr_clk;
	
	initial rd_clk = 0;
	always#30 rd_clk=~rd_clk;
	
	always@(posedge wr_clk or negedge rst_n)begin
		if(!rst_n)
			data_in <= '0d0;
		else if(wr_en)
			data_in <=data_in + 1'b1;
		else 
			data_in <= data_in;
	end
	
	initial begin
		rst_n = 0;
		wr_en = 0;
		rd_en = 0;
		#200;
		rst_n = 1;
		wr_en = 1;
		#20000;
		wr_en = 0;
		rd_en = 1;
		#20000;
		rd_en = 0;
		$stop;
	end
	
endmodule
		

仿真测试结果如下图所示
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值