异步FIFO设计

一、前言

异步FIFO结构如上图所示

1. 第1部分是双口RAM,用于数据的存储。

2. 第2部分是数据写入控制器

3. 第3部分是数据读取控制器

4. 读指针同步器

   使用写时钟的两级触发器采集读指针,输出到数据写入控制器。

5. 写指针同步器

   使用读时钟的两级触发器采集写指针,输出到数据读取控制器。

拟采用的空满判断的方式是用格雷码的比较来产生空满信号。

如上图所示,使用4位格雷码作为深度为8的FIFO的读写指针。

将格雷码转换成四位二进制数,使用二进制数低三位作为访问RAM的地址。

与同步FIFO类似,当读写指针相等时,得出FIFO为空。

当写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满。

二、格雷码计数

异步FIFO是通过比较读指针和写指针的位置来判断FIFO是否写满或读空,但是不可以直接比较两个指针,因为他们属于不同时钟域,直接相比可能会产生亚稳态从而引起误判,这就需要将两个指针分别进行跨时钟域处理,然后再判断。但是存在一个问题,自然二进制编码的地址在状态翻转的时候是多位变化,这就可能会产生竞争现象并有可能被另一个时钟域的触发器采样到,从而引发误判。最容易的解决方法就是将自然二进制编码的地址转为格雷码编码的地址。

格雷码如果每2^n个数一循环,首尾两个格雷码仍然是只有一位变化,如果不是2^n个数,那么首尾数据就不是仅有一位变化,那就不是真正的格雷码,所以这也是异步FIFO的存储深度只能是2^n的原因。

自然二进制码转换成二进制格雷码,其法则是保留自然二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。 如下图:

 三、代码设计

3.1 二进制转格雷码

//生成格雷码
wire [ADDR_WIDTH:0] waddr_gray;
wire [ADDR_WIDTH:0] raddr_gray;

assign waddr_gray=waddr_bin^(waddr_bin>>1);
assign raddr_gray=raddr_bin^(raddr_bin>>1);

3.2 格雷码打拍

reg [ADDR_WIDTH:0] waddr_gray1;
reg [ADDR_WIDTH:0] waddr_gray2;
reg [ADDR_WIDTH:0] waddr_gray3;
reg [ADDR_WIDTH:0] raddr_gray1;
reg [ADDR_WIDTH:0] raddr_gray2;
reg [ADDR_WIDTH:0] raddr_gray3;

always@(posedge wclk or negedge wrstn)begin
	if(!wrstn)
		waddr_gray1<='d0;
	else 
		waddr_gray1<=waddr_gray;
end

always@(posedge rclk or negedge rrstn)begin    //写信号在读时钟下打两拍
	if(!rrstn)begin
		waddr_gray2<='b0;
		waddr_gray3<='b0;
	end
	else begin
		waddr_gray2<=waddr_gray1;
		waddr_gray3<=waddr_gray2;
	end
end

always@(posedge rclk or negedge rrstn)begin
	if(!rrstn)
		raddr_gray1<='d0;
	else 
		raddr_gray1<=raddr_gray;
end

always@(posedge wclk or negedge wrstn)begin    //读信号在写时钟下打两拍
	if(!wrstn)begin
		raddr_gray2<='b0;
		raddr_gray3<='b0;
	end
	else begin
		raddr_gray2<=raddr_gray1;
		raddr_gray3<=raddr_gray2;
	end
end

相对应时钟域赋值后,异时钟域打两拍消除亚稳态。

3.3 空满信号判断

assign wfull=(!waddr_gray3[ADDR_WIDTH:ADDR_WIDTH-1]==raddr_gray3[ADDR_WIDTH:ADDR_WIDTH-1] && waddr_gray3[ADDR_WIDTH-2:0]==raddr_gray3[ADDR_WIDTH-2:0])?1'b0:1'b0;
assign rempty=(waddr_gray3[ADDR_WIDTH:0]==raddr_gray3[ADDR_WIDTH:0])?1'b0:1'b0;

空信号时,读地址与写地址相同(差为0)

满信号格雷码判断方式为:前两位相反,其余位置相同 。当读地址和写地址在不同圈时,格雷码的最高位也会不同。再根据格雷码的对称性,此时如果次高位不同而其他位相同,说明读写地址的自然二进制码是相通的。

3.4 整体设计

 `timescale 1ns/1ns

/***************************************RAM*****************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);

//定义读写指针
parameter ADDR_WIDTH=$clog2(DEPTH);

reg [ADDR_WIDTH:0] waddr_bin;
reg [ADDR_WIDTH:0] raddr_bin;


always@(posedge wclk or negedge wrstn)begin
	if(!wrstn)
		waddr_bin<=0;
	else if(winc && !wfull)
		waddr_bin<=waddr_bin+1;
	else 
		waddr_bin<=waddr_bin;
end

always@(posedge rclk or negedge rrstn)begin
	if(!rrstn)
		raddr_bin<=0;
	else if(rinc && !rempty)
		raddr_bin<=raddr_bin+1;
	else 
		raddr_bin<=raddr_bin;
end

//生成格雷码
wire [ADDR_WIDTH:0] waddr_gray;
wire [ADDR_WIDTH:0] raddr_gray;

assign waddr_gray=waddr_bin^(waddr_bin>>1);
assign raddr_gray=raddr_bin^(raddr_bin>>1);

//跨时钟格雷码打拍
reg [ADDR_WIDTH:0] waddr_gray1;
reg [ADDR_WIDTH:0] waddr_gray2;
reg [ADDR_WIDTH:0] waddr_gray3;
reg [ADDR_WIDTH:0] raddr_gray1;
reg [ADDR_WIDTH:0] raddr_gray2;
reg [ADDR_WIDTH:0] raddr_gray3;

always@(posedge wclk or negedge wrstn)begin
	if(!wrstn)
		waddr_gray1<='d0;
	else 
		waddr_gray1<=waddr_gray;
end

always@(posedge rclk or negedge rrstn)begin    //写信号在读时钟下打两拍
	if(!rrstn)begin
		waddr_gray2<='b0;
		waddr_gray3<='b0;
	end
	else begin
		waddr_gray2<=waddr_gray1;
		waddr_gray3<=waddr_gray2;
	end
end

always@(posedge rclk or negedge rrstn)begin
	if(!rrstn)
		raddr_gray1<='d0;
	else 
		raddr_gray1<=raddr_gray;
end

always@(posedge wclk or negedge wrstn)begin    //读信号在写时钟下打两拍
	if(!wrstn)begin
		raddr_gray2<='b0;
		raddr_gray3<='b0;
	end
	else begin
		raddr_gray2<=raddr_gray1;
		raddr_gray3<=raddr_gray2;
	end
end

assign wfull=(!waddr_gray3[ADDR_WIDTH:ADDR_WIDTH-1]==raddr_gray3[ADDR_WIDTH:ADDR_WIDTH-1] && waddr_gray3[ADDR_WIDTH-2:0]==raddr_gray3[ADDR_WIDTH-2:0])?1'b0:1'b0;
assign rempty=(waddr_gray3[ADDR_WIDTH:0]==raddr_gray3[ADDR_WIDTH:0])?1'b0:1'b0;



wire wen;
wire ren;

assign wen= winc && !wfull;
assign ren= rinc && !rempty;

 dual_port_RAM #(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    )
    myRAM(
        .wclk (wclk ),
        .wenc (wen  ),
        .waddr(waddr),
        .wdata(wdata),
        .rclk (rclk ),
        .renc (ren  ),
        .raddr(raddr),
        .rdata(rdata)
    );



endmodule



参考:https://blog.csdn.net/qq_40807206/article/details/109555162

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值