//异步FIFO
//深度16 宽度16
module top
#(
parameter depth = 16,
parameter width = 16
)
(
input wr_clk ,
input rst_n ,
input wr_en ,
input [width-1:0] data_in ,
input rd_clk ,
input rd_en ,
output [width-1:0] data_out ,
output full ,
output empty
);
reg [width-1:0] mem [depth-1:0];
reg [width-1:0] temp_rdata;
reg [4:0] waddr;
wire [4:0] waddr_gray;
reg [4:0] waddr_w2r1;
reg [4:0] waddr_w2r2;
reg [4:0] raddr;
wire [4:0] raddr_gray;
reg [4:0] raddr_r2w1;
reg [4:0] raddr_r2w2;
//写指针
always@(posedge wr_clk or negedge rst_n)
begin
if (!rst_n)
waddr <= 5'd000;
else if (wr_en)
waddr <= waddr + 1'b1;
else
waddr <= waddr;
end
//写指针转格雷码
assign waddr_gray = (waddr >> 1) ^ waddr;
//写指针同步到读时钟域
always@(posedge wr_clk or negedge rst_n)
begin
if (!rst_n)
{waddr_w2r2,waddr_w2r1} <= {2{5'b0_0000}};
else
{waddr_w2r2,waddr_w2r1} <= {waddr_w2r1,waddr_gray};
end
//读指针
always@(posedge rd_clk or negedge rst_n)
begin
if (!rst_n)
raddr <= 5'd000;
else if (rd_en)
raddr <= raddr + 1'b1;
else
raddr <= raddr;
end
//读指针转换为格雷码
assign raddr_gray = (raddr >> 1) ^ raddr;
//读指针同步到写时钟域
always@(posedge rd_clk or rst_n)
begin
if (!rst_n)
{raddr_r2w2,raddr_r2w1} <= {2{5'b0_0000}};
else
{raddr_r2w2,raddr_r2w1} <= {raddr_r2w1,raddr_gray};
end
//写数据
always@(posedge wr_clk)
begin
mem[waddr] <= data_in;
end
//读数据 1 latency
assign data_out = temp_rdata;
always@(posedge rd_clk or negedge rst_n)
begin
if (!rst_n)
temp_rdata <= {width{1'b0}};
else if (rd_en == 1'b1)
temp_rdata <= mem[raddr];
else
temp_rdata <= temp_rdata;
end
//空信号检测(写指针同步到rd_clk后与读指针进行比较)
assign empty = (waddr_w2r2 == raddr_gray) ? 1'b1 : 1'b0;
//满信号检测(读指针同步到wr_clk后与写指针进行比较)
assign full = ({~(raddr_r2w2[4:3]),raddr_r2w2[2:0]} == waddr_gray) ? 1'b1 : 1'b0;
endmodule
//testbench
`timescale 1ns/1ns
module top_test();
reg wr_clk ;
reg rst_n ;
reg wr_en ;
reg [15:0] data_in ;
reg rd_clk ;
reg rd_en ;
wire [15:0] data_out ;
wire full ;
wire empty ;
initial
begin
wr_clk = 1'b0;
rd_clk = 1'b0;
rst_n = 1'b0;
wr_en = 1'b0;
rd_en = 1'b0;
data_in = 16'h0000;
repeat(5)@(posedge wr_clk)
rst_n = 1'b1;
repeat(5)@(posedge wr_clk)
drive_w();
repeat(5)@(posedge wr_clk)
drive_r();
end
task drive_w;
integer i;
begin
for (i=0;i<16;i=i+1)
begin
wr_en <= 1'b1;
data_in <= i[15:0];
@(posedge wr_clk);
wr_en <= 0;
end
end
endtask
task drive_r;
integer i;
begin
for (i=0;i<16;i=i+1)
begin
rd_en <= 1'b1;
@(posedge rd_clk);
rd_en <= 1'b0;
end
end
endtask
always #10 wr_clk = ~wr_clk;
always #5 rd_clk = ~rd_clk;
top top_inst1(
.wr_clk (wr_clk ),
.rst_n (rst_n ),
.wr_en (wr_en ),
.data_in (data_in ),
.rd_clk (rd_clk ),
.rd_en (rd_en ),
.data_out (data_out ),
.full (full ),
.empty (empty)
);
endmodule