异步fifo的用途:1.跨时钟域,异步FIFO读写分别采用相互异步的不同时钟。2.位宽变换,对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。
同步fifo可看同步FIFO设计及验证_矜持小梦的博客-CSDN博客
异步FIFO的设计难点在于空满标志符的产生,由于异步FIFO的读写是用不同的时钟来控制的,所以不能采用计数器的方法来产生空满标志符
因为不同的时钟信号,还需要解决跨时钟域的问题:此笔记中有写IC设计验证基础笔试题总结_矜持小梦的博客-CSDN博客_ic验证笔试
解决方法就是加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
空满标志:当读写指针相等时,可能是空也可能是满2种情况,将指针宽度多增加一位,即可用来做空满判断。最高位不同,低位都相同,则满。最高位相同,低位也都相同,则空。
寄存器:
引入格雷码:如果是满状态的话,我们以二进制为例,应该满足读指针=3’b111,写指针=3’b011,相对应的格雷码应该满足读指针=3’b100,写指针=3’b010,通俗来讲,满状态要满足读指针和写指针的高位和次高位相反,其余各位相等。读状态则完全相同。同时由于格雷码的引入,使得FIFO的深度只能是2的幂次方。格雷码相邻数据只有一位跳变,这样就大大降低了数据出错的概率
二进制转为格雷码:assign gray_code = (bin_code>>1) ^ bin_code;
对于深度为2n的FIFO,需要的读/写指针位宽为(n+1)位
设计图
async_fifo.v
module async_fifo #(parameter FIFO_WIDTH = 8,
FIFO_DEPTH = 16,
ADDR_WIDTH = 4)
(
input wire rclk,
input wire wclk,
input wire rst_n,
input wire wr_en,
input wire rd_en,
input wire[FIFO_WIDTH-1:0] wr_data,
output reg [FIFO_WIDTH-1:0] rd_data,
output reg empty,
output reg full
);
//memory
reg [FIFO_WIDTH-1:0] mem[FIFO_DEPTH-1:0];
//address
reg [ADDR_WIDTH-1:0] wr_addr,rd_addr;
//binary adderss
reg [ADDR_WIDTH:0] wr_addr_b,rd_addr_b;
//gray address
reg [ADDR_WIDTH:0] wr_addr_g,rd_addr_g;
//sample gray address
reg [ADDR_WIDTH:0] wr_addr_g_dly1,wr_addr_g_dly2,rd_addr_g_dly1,rd_addr_g_dly2;
//binary code to gray code
initial begin
assign wr_addr_g = (wr_addr_b>>1) ^ wr_addr_b;
assign rd_addr_g = (rd_addr_b>>1) ^ rd_addr_b;
//addr
assign wr_addr = wr_addr_b[ADDR_WIDTH-1:0];
assign rd_addr = rd_addr_b[ADDR_WIDTH-1:0];
end
//read & write allow
wire rd_allow = rd_en && !empty;
wire wr_allow = wr_en && !full;
//write data
always @(posedge wclk or negedge rst_n)
if(rst_n == 1'b0)
wr_addr_b <= 'b0;
else if(wr_allow) begin
mem[wr_addr] <= wr_data;
wr_addr_b <= wr_addr_b + 1'b1;
end
//read data
always @(posedge rclk or negedge rst_n)
if(rst_n == 1'b0)
rd_addr_b <= 'b0;
else if(rd_allow) begin
rd_data <= mem[rd_addr];
rd_addr_b <= rd_addr_b + 1'b1;
end
//wr_addr_g sample twice
always @(posedge rclk or negedge rst_n)
if(rst_n == 1'b0)
{wr_addr_g_dly2,wr_addr_g_dly1} <= 'b0;
else
{wr_addr_g_dly2,wr_addr_g_dly1} <= {wr_addr_g_dly1,wr_addr_g};
//rd_addr_g sample twice
always @(posedge wclk or negedge rst_n)
if(rst_n == 1'b0)
{rd_addr_g_dly2,rd_addr_g_dly1} <= 'b0;
else
{rd_addr_g_dly2,rd_addr_g_dly1} <= {rd_addr_g_dly1,rd_addr_g};
initial begin
assign empty = (rd_addr_g == wr_addr_g_dly2);
assign full = (wr_addr_g == {~rd_addr_g_dly2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_g_dly2[ADDR_WIDTH-2:0]});
end
endmodule
async_fifo_tb.v
`timescale 1ns/1ps
module async_fifo_tb();
parameter FIFO_WIDTH = 8,
FIFO_DEPTH = 16,
ADDR_WIDTH = 4;
reg rclk,wclk,rst_n,wr_en,rd_en;
reg [FIFO_WIDTH-1:0] wr_data;
wire[FIFO_WIDTH-1:0] rd_data;
wire empty,full;
async_fifo u1
/*#( .FIFO_WIDTH (FIFO_WIDTH),
.FIFO_DEPTH (FIFO_DEPTH),
.ADDR_WIDTH (ADDR_WIDTH)
)*/
(
.rclk (rclk ),
.wclk (wclk ),
.rst_n (rst_n ),
.wr_en (wr_en ),
.rd_en (rd_en ),
.wr_data (wr_data),
.rd_data (rd_data),
.empty (empty ),
.full (full )
);
initial begin
rst_n=0;
rclk=0;
wclk=0;
#5
rst_n=1;
end
always #30 rclk=~rclk;
always #20 wclk=~wclk;
initial begin
wr_en=0;
rd_en=0;
#50
wr_en=1;
wr_data=8'h0;
#40 wr_data=8'h1;
#40 wr_data=8'h2;
#40 wr_data=8'h3;
rd_en=1;
#40 wr_data=8'h4;
#40 wr_data=8'h5;
#40 wr_data=8'h6;
#40 wr_data=8'h7;
#40 wr_data=8'h8;
#40 wr_data=8'h9;
#40 wr_data=8'hA;
#40 wr_data=8'hB;
#40 wr_data=8'hC;
#40 wr_data=8'hD;
#40 wr_data=8'hE;
#40 wr_data=8'hF;
#60
wr_en=0;
//rd_en=1;
#1000
$finish;
end
endmodule
仿真波形