先贴张图
异步FIFO要注意的几点:
- 一般来说都是写的快读的慢,这样就会涉及到最小fifo深度计算的问题
- 没有写地址和读地址,仅仅使用写使能和读使能来控制
- 跨时钟域使用格雷码和两步D触发器进行同步
- 读空写满信号设置
module asyn_fifo(
input wire clk_w,//50mhz
input wire clk_r,//25mhz
input wire rstn,
input wire r_en,
input wire w_en,
input wire [7:0] cin,
output wire w_full,
output wire r_empty,
output reg [7:0] q
);
reg [7:0] mem [255:0];
wire wr,rd;
reg [8:0] wr_addr;
reg [8:0] rd_addr;
reg [8:0] rd_gray_r1,rd_gray_r2;
reg [8:0] wr_gray_r1,wr_gray_r2;
wire [8:0] wr_gray;
wire [8:0] rd_gray;
wire [8:0] w_rgray;
wire [8:0] r_wgray;
//格雷码转换
assign wr_gray = (wr_addr>>1) ^ wr_addr;
assign rd_gray = (rd_addr>>1) ^ rd_addr;
//跨时钟域,打两拍
always @(posedge clk_w or negedge rstn)
if(!rstn)
{rd_gray_r1,rd_gray_r2}<=2'd0;
else
{rd_gray_r1,rd_gray_r2}<={rd_gray,rd_gray_r1};
always @(posedge clk_r or negedge rstn)
if(!rstn)
{wr_gray_r1,wr_gray_r2}<=2'd0;
else
{wr_gray_r1,wr_gray_r2}<={wr_gray,wr_gray_r1};
assign w_rgray = wr_gray_r2;
assign r_wgray = rd_gray_r2;
//空满标志
assign w_full = ((r_wgray[8:7]==(~wr_gray[8:7]))&&(r_wgray[6:0]==wr_gray[6:0]))?1'd1:1'd0;
assign r_empty = (w_rgray == rd_gray) ? 1'd1:1'd0;
//读写标志
assign rd = r_en && (~r_empty);
assign wr = w_en && (~w_full);
//写地址及写数据
always @(posedge clk_w or negedge rstn)
if(!rstn)
wr_addr<=1'd0;
else if(wr)begin
wr_addr<=wr_addr+1'd1;
mem[wr_addr[7:0]]<=cin;
end
else begin
wr_addr<=wr_addr;
mem[wr_addr[7:0]]<=mem[wr_addr[7:0]];
end
//读地址及读数据
always @(posedge clk_r or negedge rstn)
if(!rstn)
rd_addr<=1'd0;
else if(rd)begin
rd_addr<=rd_addr+1'd1;
q<=mem[rd_addr[7:0]];
end
else begin
rd_addr<=rd_addr;
q<=1'd0;
end
endmodule
`timescale 1ns/1ns
module asyn_fifo_tb;
reg clk_w;
reg clk_r;
reg rstn;
reg r_en;
reg w_en;
reg [7:0] cin;
wire [7:0] q;
wire w_full;
wire r_empty;
initial begin
clk_w=1'd0;
clk_r=1'd0;
r_en=1'd0;
w_en=1'd0;
cin=1'd0;
rstn=1'd0;
#20;
rstn=1'd1;
write;
read;
#10000;
$stop;
end
task write;
integer i;
begin
for(i=0;i<257;i=i+1)begin
w_en=1'd1;
@(posedge clk_w);
cin = {$random}%257;
end
w_en=1'd0;
end
endtask
task read;
integer i;
begin
for(i=0;i<257;i=i+1)begin
@(posedge clk_r);
r_en=1'd1;
end
r_en=1'd0;
end
endtask
always #10 clk_w = ~clk_w;
always #20 clk_r = ~clk_r;
asyn_fifo asyn_fifo(
.clk_w ( clk_w ),
.clk_r ( clk_r ),
.rstn ( rstn ),
.r_en ( r_en ),
.w_en ( w_en ),
.cin ( cin ),
.w_full ( w_full ),
.r_empty ( r_empty ),
.q ( q )
);
endmodule