module fifo_async#(
parameter data_width = 32,
parameter data_depth = 8,
parameter addr_width = 4
)
(
input rst_n,
input wr_clk,
input wr_en,
input [data_width-1:0] din,
input rd_clk,
input rd_en,
output reg valid,
output reg [data_width-1:0] dout,
output empty,
output full
);
reg [addr_width:0] wr_addr_ptr;//地址指针,比地址多一位,MSB用于检测在同一圈
reg [addr_width:0] rd_addr_ptr;
wire [addr_width-1:0] wr_addr;//RAM 地址
wire [addr_width-1:0] rd_addr;
wire [addr_width:0] wr_addr_gray;//地址指针对应的格雷码
reg [addr_width:0] wr_addr_gray_d1;
reg [addr_width:0] wr_addr_gray_d2;
wire [addr_width:0] rd_addr_gray;
reg [addr_width:0] rd_addr_gray_d1;
reg [addr_width:0] rd_addr_gray_d2;
reg [data_width-1:0] fifo_ram [data_depth-1:0];
//=========================================================write fifo
always@(posedge wr_clk or negedge rst_n)
begin:write_ram
integer i;
if(!rst_n)
for( i = 0; i < data_depth; i = i + 1 ) begin:write_ram_init
fifo_ram[i] <= 'h0;//fifo复位后输出总线上是0,并非ram中真的复位。可无
end//write_ram_init
else if(wr_en && (~full))
fifo_ram[wr_addr] <= din;
else
fifo_ram[wr_addr] <= fifo_ram[wr_addr];
end//write_ram
//========================================================read_fifo
always@(posedge rd_clk or negedge rst_n)
begin
if(!rst_n)
begin
dout <= 'h0;
valid <= 1'b0;
end
else if(rd_en && (~empty))
begin
dout <= fifo_ram[rd_addr];
valid <= 1'b1;
end
else
begin
dout <= dout;//fifo复位后输出总线上是0,并非ram中真的复位,只是让总线为0;
valid <= 1'b0;
end
end
assign wr_addr = wr_addr_ptr[addr_width-1-:addr_width];
assign rd_addr = rd_addr_ptr[addr_width-1-:addr_width];
//=============================================================格雷码同步化
always@(posedge wr_clk )
begin
rd_addr_gray_d1 <= rd_addr_gray;
rd_addr_gray_d2 <= rd_addr_gray_d1;
end
always@(posedge wr_clk or negedge rst_n)
begin
if(!rst_n)
wr_addr_ptr <= 'h0;
else if(wr_en && (~full))
wr_addr_ptr <= wr_addr_ptr + 1;
else
wr_addr_ptr <= wr_addr_ptr;
end
//=========================================================rd_clk
always@(posedge rd_clk )
begin
wr_addr_gray_d1 <= wr_addr_gray;
wr_addr_gray_d2 <= wr_addr_gray_d1;
end
always@(posedge rd_clk or negedge rst_n)
begin
if(!rst_n)
rd_addr_ptr <= 'h0;
else if(rd_en && (~empty))
rd_addr_ptr <= rd_addr_ptr + 1;
else
rd_addr_ptr <= rd_addr_ptr;
end
//========================================================== translation gary code
assign wr_addr_gray = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
assign rd_addr_gray = (rd_addr_ptr >> 1) ^ rd_addr_ptr;
assign full = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width-:2]),rd_addr_gray_d2[addr_width-2:0]}) ;//高两位不同
assign empty = ( rd_addr_gray == wr_addr_gray_d2 );
endmodule
module fifo_async_tb();
reg tb_rst_n ;
reg tb_wr_clk ;
reg tb_wr_en ;
reg [31:0] tb_din ;
reg tb_rd_clk ;
reg tb_rd_en ;
wire tb_valid ;
wire [31:0] tb_dout ;
wire tb_empty ;
wire tb_full ;
task delay;
input [31:0] num;
begin
repeat(num) @(posedge tb_wr_clk);
#1;
end
endtask
task delay1;
input [31:0] num;
begin
repeat(num) @(posedge tb_rd_clk);
#1;
end
endtask
initial begin
tb_wr_clk = 0;
end
always #40 tb_wr_clk = ~tb_wr_clk;
initial begin
tb_rd_clk = 0;
end
always #10 tb_rd_clk = ~tb_rd_clk;
initial begin
tb_rst_n = 1;
delay(1);
tb_rst_n = 0;
delay(1);
tb_rst_n = 1;
end
initial begin
$dumpfile(" async_fifo_tb.vcd ");
$dumpvars();
end
initial begin
tb_wr_en = 0;
tb_din = 0;
tb_rd_en = 0;
delay(3);
// write data
tb_wr_en = 1;
tb_din = 32'haaaa;
delay(1);
tb_wr_en = 0;
delay(5);
// read data, empty
tb_rd_en = 1;
delay1(1);
tb_rd_en = 0;
delay1(5);
// write data1
tb_wr_en = 1;
tb_din = 32'hbbbb;
delay(1);
tb_wr_en = 0;
delay(5);
// write data2
tb_wr_en = 1;
tb_din = 32'hcccc;
delay(1);
tb_wr_en = 0;
delay(5);
// write data3
tb_wr_en = 1;
tb_din = 32'hdddd;
delay(1);
tb_wr_en = 0;
delay(5);
// write data4, full
tb_wr_en = 1;
tb_din = 32'heeee;
delay(1);
tb_wr_en = 0;
delay(5);
// read data1
tb_rd_en = 1;
delay1(1);
tb_rd_en = 0;
delay1(5);
// read data2
tb_rd_en = 1;
delay1(1);
tb_rd_en = 0;
delay1(5);
// read data3
tb_rd_en = 1;
delay1(1);
tb_rd_en = 0;
delay1(5);
// read data4, empty
tb_rd_en = 1;
delay1(1);
tb_rd_en = 0;
delay1(5);
delay(5);
$finish;
end
fifo_async#(
.data_width(32) ,
.data_depth(4 ) ,
.addr_width(2 )
) dut1_fifo_async
(
.rst_n ( tb_rst_n ) ,
.wr_clk( tb_wr_clk ) ,
.wr_en ( tb_wr_en ) ,
.din ( tb_din ) ,
.rd_clk( tb_rd_clk ) ,
.rd_en ( tb_rd_en ) ,
.valid ( tb_valid ) ,
.dout ( tb_dout ) ,
.empty ( tb_empty ) ,
.full ( tb_full )
);
endmodule