(二)异步FIFO

本文详细探讨了异步FIFO在跨时钟域数据存储读写中的应用,重点讨论了亚稳态现象、单bit和多bit信号的同步策略。还介绍了使用格雷码来降低亚稳态风险和实现FIFO的空满标志检测。最后,概述了异步FIFO的RTL代码设计,包括dual_port_sram和async_fifo模块。

一、异步FIFO

数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous), 异步是指相位不同,频率可能不同,不同时钟上升沿之间关系不确定,相位关系不确定,常用于跨时钟域数据的存储读写。

1、亚稳态

        以单bit信号为例:

说明:

  • 亚稳态:上图Din是DFF1的输入由上一个DFF0在时钟域clka下输出的单bit信号,当DFF0输出到clkb时钟域时,由于两个时钟域是异步的,所以无法保证采样到的DFF1的输出Ds是稳定的,因为信号跨时钟域,它要在clkb时钟域稳定下来需要一定的时间(这段时间包括触发器内部电路的时间、导线,导线中间组合逻辑电路的时间),这段稳定时间中的输出Ds是不稳定的,叫这个不稳定的状态为亚稳态但经过一段时间最终输出Ds会达到稳定状态0或1

2、单bit信号Din慢时钟a同步到快时钟b

若单bit信号Din慢时钟a到快时钟b采用打两拍的方法,打的拍数越多亚稳态消除的越好(信号抖动时间不是确定的,随时钟变化

  1. 第一拍:b时钟下同步Din,减小采到亚稳态的概率,虽采到随机值,但最终会达到一个正确的稳定值(随机值最终不会到一个错误的值), 所以输出信号的Ds基本稳定了,但不能用这个信号,因为在时钟b下,仍然有可能会采样到亚稳态,采样到的亚稳态可能为1或0,是不确定的。
  2. 第二拍:第二次b时钟下同步Din,此时采到Ds信号基本稳定已消除了亚稳态,再输出Dout信号

3、单bit信号Din快时钟b同步到慢时钟a

若单bit信号Din快时钟b同步到慢时钟a,一定可以采到稳定的信号,但可能会出现重复采样问题即出现了多个快时钟周期内同一个数据的信号。

4、多bit信号为什么不能采用打两拍的方法进行跨时钟域同步

  1. 假设从时钟域A垮到时钟域B,是域A从00变到11,在时域B还能采样到从00变到11吗?在时域B中,时钟的上升沿来的时间是肯定一致的,但是各个bit从时钟域A到达时钟域B的时间是不一样的,亚稳态恢复到稳态的时间也是不一样的。即00中的两个0并不是同时变成1的,因此时钟域B中可能采到中间态01或10。
  2. 单比特发送亚稳态的概率变成多bit后概率会大很多,不直接打两拍是可以降低亚稳态发生的概率的。

二、异步FIFO设计:用于跨时钟域数据的存储读写

说明:

  • 写指针(wptr)指向的位置表示还未写,将要写,系统复位后(FIFO为空),写指针指向0,每写入一笔数据,写指针加1;
  • 读指针(rptr)指向的位置表示还未读,将要读,系统复位后(FIFO为空),读指针指向0,每读入一笔数据,读指针加1;
  • 空标志(empty):当读指针和写指针相等时,空标志=1,复位时,两指针都为0,或者当读指针追赶上了写指针
  • 满标志(full):当写指针和读指针相等时,满标志=1,写指针写了一圈后,又追上了读指针

1、为什么多bit要使用格雷码?

以深度为8的FIFO为例:

  •  在异步FIFO中,读写指针多出一个高bit信号来表示读写指针是否已经跑完一圈,自然二进制跑完一圈,例如rptr是1,wptr是9,从高位可以看出FIFO为满,若wptr也是1,说明FIFO为空,但使用自然二进制不好,因为按自然二进制读写指针的变化从3跳到4,变化了3个bit,多bit很容易采到亚稳态,所以使指针转换到格雷码来判断FIFO空满更方便,相邻格雷码只有一个bit不同,可以采取打两拍的方式同步
  • 例如rptr是1,wptr是9,对应格雷码是0001,1101,即wptr格雷码的高两位取反后与rptr格雷码相同,表示FIFO满,若对应格雷码直接相同,则表示FIFO为空。
  • full信号产生:与写时钟域有关,需要把转换为格雷码的读指针同步到写时钟域
  • empty信号产生:与读时钟域有关,需要把转换为格雷码的写指针同步到读时钟域

2、自然二进制转格雷码

//法一
module bin2gry(Gry,Bin);
	parameter length = 8;
	output 	[length-1:0] Gry;
	input 	[length-1:0] Bin;
	reg 	[length-1:0] Gry;
	
integer i;
always @(Bin)
begin
	for(i=0;i<length-1;i=i+1)
		Gry[i]=Bin[i]^Bin[i+1];
	Gry[i]=Bin[i];
end
//法二
assign Gray = (Bin >>1)^Bin;

endmodule

3、格雷码转自然二进制

module gry2bin(Gry,Bin);
	parameter length = 8;
	input 	[length-1:0] Gry;
	output  [length-1:0] Bin;
	reg 	[length-1:0] Bin;
integer i;
always @(Gry)
begin
	Bin[length-1]=Gry[length-1];
	for(i=length-2;i>=O;i=i-1)
		Bin[i]=Bin[i+1]^Gry[i];
end
endmodule

 说明:

  • 格雷码转二进制可以用来判断几乎空几乎满。

三、RTL代码设计:

1、dual_port_sram:

模拟双端口RAM的行为

`timescale 1ns/1ps
module dual_port_sram #(
	parameter 	SRAM_DEPTH = 16,
	parameter 	DATA_WIDTH = 8)(
	input 	  					   	 wclk ,
	input 	  					   	 rclk ,
	input 	  					   	 rst_n,
	input 	  					   	 wr_en,
	input 	  					     rd_en,
	input 	[$clog2(SRAM_DEPTH)-1:0] waddr,
	input 	[$clog2(SRAM_DEPTH)-1:0] raddr,		
	input 	[DATA_WIDTH-1:0]		 wdata,	
	output reg	[DATA_WIDTH-1:0]	 rdata  	
);
	reg [DATA_WIDTH-1:0]  mem[0:SRAM_DEPTH-1];
	integer i;
	//write mem
	always @(posedge wclk or rst_n)
	begin
		if(!rst_n)
			for(i=0;i<=SRAM_DEPTH-1;i=i+1)
				mem[i] <= {(DATA_WIDTH){1'b0}};
		else if(wr_en)
			mem[waddr] <= wdata;
		else
			mem[waddr] <= mem[waddr];
	end
	
	//read mem
	always @(posedge rclk or rst_n)
	begin
		if(!rst_n)
			rdata <= {(DATA_WIDTH){1'b0}};
		else if(rd_en)
			rdata <= mem[raddr];
		else
			rdata <= {(DATA_WIDTH){1'b0}};
	end
	
endmodule

2、async_fifo:

包括空满信号与几乎空几乎满信号的产生

`timescale 1ns/1ps
/***************************************AFIFO*****************************************/
module async_fifo#(
	parameter	DATA_WIDTH    = 8 ,
	parameter 	FIFO_DEPTH    = 16,
	parameter   ALMOST_WFULL  = 12,
	parameter   ALMOST_REMPTY = 4 
)(
	input 					wclk	 , 
	input 					rclk	 ,   
	input 					rst_n	 ,
	input 					wr_en	 ,
	input 			 		rd_en	 ,
	input  [DATA_WIDTH-1:0] wdata	 ,	
	output [DATA_WIDTH-1:0] rdata	 ,
	output 				    wfull	 ,
	output 				    al_wfull ,
	output 				    rempty	 ,
	output 				    al_rempty
);
    
    localparam ADDR_WIDTH = $clog2(FIFO_DEPTH);
    
    reg  [ADDR_WIDTH:0]   wptr;
    reg  [ADDR_WIDTH:0]   rptr;
    
    wire [ADDR_WIDTH-1:0] wptr_addr;
    wire [ADDR_WIDTH-1:0] rptr_addr;
    
    wire [ADDR_WIDTH:0]   wptr_gray;
    wire [ADDR_WIDTH:0]   rptr_gray;
	
	reg  [ADDR_WIDTH:0]   wptr_gray1;
    reg  [ADDR_WIDTH:0]   rptr_gray1;
    
    reg  [ADDR_WIDTH:0]   wptr_gray2;
    reg  [ADDR_WIDTH:0]   rptr_gray2;
    
    reg  [ADDR_WIDTH:0]   wptr_gray3;
    reg  [ADDR_WIDTH:0]   rptr_gray3;
	
	wire [ADDR_WIDTH:0]   wptr_gray_bin;
    wire [ADDR_WIDTH:0]   rptr_gray_bin;
	
	reg  [ADDR_WIDTH-1:0] alfull_cnt;
	reg  [ADDR_WIDTH-1:0] alempty_cnt;
	
    wire wenc;
    wire renc;

       
    dual_port_sram #(
        .SRAM_DEPTH(FIFO_DEPTH),
        .DATA_WIDTH(DATA_WIDTH))
    u_dual_port_sram(
        .wclk  (wclk) ,
        .rclk  (rclk) ,
        .rst_n (rst_n),   
        .wr_en (wenc) ,     
        .rd_en (renc) ,
        .waddr (wptr_addr) ,
        .raddr (rptr_addr) ,   
        .wdata (wdata),	
		.rdata (rdata)
    );   
	              
	assign wptr_addr = wptr[ADDR_WIDTH-1:0];
    assign rptr_addr = rptr[ADDR_WIDTH-1:0];
    
    assign wenc      = wr_en && (~wfull) ;
    assign renc      = rd_en && (~rempty);
	
    //wptr & rptr
    always @(posedge wclk or negedge rst_n)
    begin
        if(!rst_n)
            wptr <= {(ADDR_WIDTH+1){1'b0}};
        else if(wenc)
            wptr <= wptr + 1'b1;
    end
    
    always @(posedge rclk or negedge rst_n)
    begin
        if(!rst_n)
            rptr <= {(ADDR_WIDTH+1){1'b0}};
        else if(renc)
            rptr <= rptr + 1'b1;
    end
    
    //bin to gray sync
    assign wptr_gray = wptr ^ (wptr >> 1);
    assign rptr_gray = rptr ^ (rptr >> 1);
	
	//timing logic outputs gray  prevent burrs
	always @(posedge wclk or negedge rst_n)
	begin
		if(!rst_n)
			wptr_gray1 <= {(ADDR_WIDTH+1){1'b0}};
		else
			wptr_gray1 <= wptr_gray;		
	end
	
	always @(posedge rclk or negedge rst_n)
	begin
		if(!rst_n)
			rptr_gray1 <= {(ADDR_WIDTH+1){1'b0}};
		else
			rptr_gray1 <= rptr_gray;		
	end
	
    //use write clock to sync read addr
    always @(posedge rclk or negedge rst_n)
    begin
        if(!rst_n)begin
            wptr_gray2 <= {(ADDR_WIDTH+1){1'b0}};
			wptr_gray3 <= {(ADDR_WIDTH+1){1'b0}};
		end	
        else begin
            wptr_gray2 <= wptr_gray1;
			wptr_gray3 <= wptr_gray2;
		end
    end
    //use read clock to sync write addr
    always @(posedge wclk or negedge rst_n)
    begin
        if(!rst_n)begin
            rptr_gray2 <= {(ADDR_WIDTH+1){1'b0}};
			rptr_gray3 <= {(ADDR_WIDTH+1){1'b0}};
		end	
        else begin
            rptr_gray2 <= rptr_gray1;
			rptr_gray3 <= rptr_gray2;
		end
    end
    
    //wfull & rempty
    assign wfull  = wptr_gray == {~rptr_gray3[ADDR_WIDTH:ADDR_WIDTH-1],rptr_gray3[ADDR_WIDTH-2:0]};
    assign rempty = rptr_gray == wptr_gray3;

	//gray to bin
	generate
		genvar i;
		for(i=ADDR_WIDTH;i>=0;i=i-1)
		begin:gray_bin
			if(i==ADDR_WIDTH)begin
				assign wptr_gray_bin[i] = wptr_gray3[i];
				assign rptr_gray_bin[i] = rptr_gray3[i];
			end
			else begin
				assign wptr_gray_bin[i] = wptr_gray_bin[i+1] ^ wptr_gray3[i];
				assign rptr_gray_bin[i] = rptr_gray_bin[i+1] ^ rptr_gray3[i];
			end
		end
	endgenerate
    
	//almost wfull & almost rempty
	always@(*)
	begin
		if(wptr[ADDR_WIDTH] == rptr_gray_bin[ADDR_WIDTH])
			alfull_cnt = wptr[ADDR_WIDTH-1:0] - rptr_gray_bin[ADDR_WIDTH-1:0];
		else
			alfull_cnt = FIFO_DEPTH - (rptr_gray_bin[ADDR_WIDTH-1:0] - wptr[ADDR_WIDTH-1:0]);
	end
	
	assign al_wfull    = alfull_cnt >= ALMOST_WFULL ;
	
	always@(*)
	begin
		if(rptr[ADDR_WIDTH] == wptr_gray_bin[ADDR_WIDTH])
			alempty_cnt = wptr_gray_bin[ADDR_WIDTH-1:0] - rptr[ADDR_WIDTH-1:0];
		else
			alempty_cnt = FIFO_DEPTH - (rptr[ADDR_WIDTH-1:0] - wptr[ADDR_WIDTH-1:0]);
	end
	
	assign al_rempty   = alempty_cnt <= ALMOST_REMPTY;
	
endmodule

 3、tb_async_fifo

测试代码:

`timescale 1ns/1ps
module tb_async_fifo;

	parameter	DATA_WIDTH    = 8;
	parameter 	FIFO_DEPTH    = 8; 
	parameter   ALMOST_WFULL  = 6; 
	parameter   ALMOST_REMPTY = 2;
	
	reg 				  wclk	   ;
	reg 				  rclk	   ;
	reg 				  rst_n	   ;
	reg 				  wr_en	   ;
	reg 			 	  rd_en	   ;
	reg  [DATA_WIDTH-1:0] wdata	   ;	
	wire [DATA_WIDTH-1:0] rdata	   ;
	wire 				  wfull	   ;
	wire 				  al_wfull ;
	wire 				  rempty   ;
	wire 				  al_rempty;
	
async_fifo #(
	.DATA_WIDTH   (DATA_WIDTH   ),
	.FIFO_DEPTH   (FIFO_DEPTH   ),
	.ALMOST_WFULL (ALMOST_WFULL ),
	.ALMOST_REMPTY(ALMOST_REMPTY)
	)u_async_fifo(
	.wclk	     (wclk	   ), 
	.rclk	     (rclk	   ),
	.rst_n	     (rst_n	   ),
	.wr_en	     (wr_en	   ),
	.rd_en	     (rd_en	   ),
	.wdata	     (wdata	   ),
	.rdata	     (rdata	   ),
	.wfull	     (wfull	   ),
	.al_wfull 	 (al_wfull ),
	.rempty      (rempty   ),
	.al_rempty   (al_rempty)
);
	always #5 rclk = ~rclk;
	always #8 wclk = ~wclk;
	
	reg [7:0] tempdata=0;
	
	initial begin
		wclk   = 0;
		rclk   = 0; 
		rst_n  = 0;
		wr_en  = 0;
		rd_en  = 0;
		wdata  = 0;
		#15
		rst_n  = 1;
		push(1);
	
	fork
		push(2);
		pop(tempdata);//出现了假空现象
	join
	
		push(10);
		push(20);
		push(30);
		push(40);
		push(50);
		push(60);
		push(70);
		push(80);
		push(90);
		push(100);
		push(110);
		push(120);
		push(130);
		pop(tempdata);
		push(tempdata);//出现了假满现象
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		push(140);     //假几乎满
		pop(tempdata);
		push(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		pop(tempdata);
		push(5);
		pop(tempdata);
		#300
		$finish;
	end
	
initial begin
	$vcdpluson();
	$vcdplusmemon();
	$fsdbDumpfile("test.fsdb");
	$fsdbDumpvars(0,tb_async_fifo,"+parameter");
	$fsdbDumpMDA;
end

	task push(input [7:0] data);
		if(wfull)
			$display("---cannot push %d:Buffer Full---",data);
		else if(al_wfull)
		begin
			$display(" pushed %d:Almost Full---",data);
			wdata = data;
			wr_en = 1;
			@(posedge wclk);
			#5 wr_en  = 0;	
		end
		else begin
			$display("pushed:",data);
			wdata = data;
			wr_en = 1;
			@(posedge wclk);
			#5 wr_en  = 0;	
		end
	endtask
	
	task pop(output [7:0] data);
		if(rempty)
			$display("---cannot pop :Buffer Empty---");
		else if(al_rempty)
		begin
			rd_en = 1;
			@(posedge rclk);
			#3 rd_en = 0;
			data = rdata;
			$display("poped %d:Almost Empty---",data);
		end
		else begin
			rd_en = 1;
			@(posedge rclk);
			#3 rd_en = 0;
			data = rdata;
			$display("poped:",data);
		end
	endtask

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值