同步FIFO和异步FIFO的Verilog实现
一、同步FIFO
1、同步FIFO的Verilog实现
module class_6_fifo#(
parameter Width = 16,
parameter Depth = 4
)(
input wire i_clk ,
input wire i_rst_n ,
input wire [Width-1:00] i_data ,
input wire i_wr ,
input wire i_rd ,
output reg [Width-1:00] o_data ,
output wire o_empty ,
output wire o_full
);
reg [Width-1:00] Memory [00:2**Depth-1];
reg [Depth-1:00] addr_wr ;
reg [Depth-1:00] addr_rd ;
reg [Depth:00] cnt ;
/*************写信号有效且未写满,数据输入RAM,写地址加1*******************/
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
addr_wr <= #1 'd0;
else if(i_wr && (!o_full))
begin
Memory[addr_wr] <= #1 i_data;
addr_wr <= #1 addr_wr + 1'b1;
end
end
/*************写信号有效且未写满,数据输入RAM,写地址加1*******************/
/*************读信号有效且未读空,RAM输出数据,读地址加1*******************/
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
begin
o_data <= #1 'd0;
addr_rd <= #1 'd0;
end
else if(i_rd && (!o_empty))
begin
o_data <= #1 Memory[addr_rd];
addr_rd <= #1 addr_rd + 1'b1;
end
end
/*************读信号有效且未读空,RAM输出数据,读地址加1*******************/
/*************读写信号有效,生成cnt计数*******************/
always@(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n)
cnt <= #1 'd0;
else begin
case({i_wr,i_rd})
2'b00 : begin //读写信号无效,cnt不变
cnt <= #1 cnt ;
end
2'b01 : begin //读信号有效且未读空,cnt加1
if(!o_empty)
cnt <= #1 cnt - 1'b1;
end
2'b10 : begin //写信号有效且未写满,cnt加1
if(!o_full)
cnt <= #1 cnt + 1'b1;
end
2'b11 : begin //读写信号有效,cnt不变
cnt <= #1 cnt ;
end
endcase
end
end
/*************读写信号有效,生成cnt计数*******************/
assign o_empty = (cnt == 0) ? 1'b1 : 1'b0; //读空标志位
assign o_full = (cnt == 2**Depth ) ? 1'b1 : 1'b0; //写满标志位
endmodule
2、同步FIFO的testbench
module tb_fifo#(
parameter Width = 16,
parameter Depth = 4
);
reg i_clk ;
reg i_rst_n ;
reg [Width-1:00] i_data ;
reg i_wr ;
reg i_rd ;
wire [Width-1:00] o_data ;
wire o_empty ;
wire o_full ;
class_6_fifo#(
.Width(Width),
.Depth(Depth)
)u_class_6_fifo(
.i_clk (i_clk ) ,
.i_rst_n (i_rst_n) ,
.i_data (i_data ) ,
.i_wr (i_wr ) ,
.i_rd (i_rd ) ,
.o_data (o_data ) ,
.o_empty (o_empty) ,
.o_full (o_full )
);
parameter clk_period = 20 ;
initial i_clk = 0;
always #(clk_period/2) i_clk = ~i_clk;
integer i;
integer j;
initial begin
i_rst_n=0;
i_rd=0;
i_wr=0;
i_data=5;
#(clk_period * 20 + 1);
i_rst_n = 1 ;
for(i=0;i<20;i=i+1)
begin
@(posedge i_clk);
i_wr = 1;
i_data = i + 1;
end
i_wr = 0;
#(clk_period * 20 + 1);
for(j=0;j<20;j=j+1)
begin
@(posedge i_clk);
i_rd = 1;
end
i_rd = 0;
#(clk_period * 20 + 1);
end
3、同步FIFO的仿真结果
写数据
读数据
二、异步FIFO
1、异步FIFO的Verilog实现
module class_8_dcfifo#(
parameter WIDTH = 16,
parameter DEPTH = 4
)(
input i_rst_n ,
input i_wr_clk ,
input i_wr ,
input i_rd_clk ,
input i_rd ,
input [WIDTH-1:00] i_data ,
output reg [WIDTH-1:00] o_data ,
output o_full ,
output o_empty
);
reg [WIDTH-1:00] Memory [00:2**DEPTH-1];
wire [DEPTH-1:00] wr_addr ;
wire [DEPTH-1:00] rd_addr ;
reg [DEPTH:00] wr_addr_ptr ;
reg [DEPTH:00] rd_addr_ptr ;
wire [DEPTH:00] wr_addr_ptr_grey ;
wire [DEPTH:00] rd_addr_ptr_grey ;
reg [DEPTH:00] wr_addr_ptr_grey1 ;
reg [DEPTH:00] wr_addr_ptr_grey2 ;
reg [DEPTH:00] rd_addr_ptr_grey1 ;
reg [DEPTH:00] rd_addr_ptr_grey2 ;
/**********写时钟域下数据输入RAM,写使能且未写满,写指针加1**********/
always@(posedge i_wr_clk or negedge i_rst_n)
begin
if(!i_rst_n)
wr_addr_ptr <= #1 'd0;
else if(i_wr & (!o_full))
begin
Memory[wr_addr] <= #1 i_data;
wr_addr_ptr <= wr_addr_ptr + 1'b1;
end
end
/**********写时钟域下数据输入RAM,写使能且未写满,写指针加1**********/
/**********读时钟域下RAM输出数据,读使能且未读空,读指针加1**********/
always@(posedge i_rd_clk or negedge i_rst_n)
begin
if(!i_rst_n)
begin
o_data <= #1 'd0;
rd_addr_ptr <= #1 'd0;
end
else if(i_rd & (!o_empty))
begin
o_data <= #1 Memory[rd_addr];
rd_addr_ptr <= rd_addr_ptr + 1'b1;
end
end
/**********读时钟域下RAM输出数据,读使能且未读空,读指针加1**********/
/**********读写地址位输出,读写指针除去最高位**********/
assign wr_addr = wr_addr_ptr[DEPTH-1:0];
assign rd_addr = rd_addr_ptr[DEPTH-1:0];
/**********读写地址位输出,读写指针除去最高位**********/
/**********读写指针二进制码转换为格雷码**********/
assign wr_addr_ptr_grey = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
assign rd_addr_ptr_grey = (rd_addr_ptr >> 1) ^ rd_addr_ptr;
/**********读写指针二进制码转换为格雷码**********/
/****读写指针跨时钟域处理,写时钟域下采集读指针,读时钟域下采集写指针******/
always@(posedge i_wr_clk)
begin
rd_addr_ptr_grey1 <= rd_addr_ptr_grey;
rd_addr_ptr_grey2 <= rd_addr_ptr_grey1;
end
always@(posedge i_rd_clk)
begin
wr_addr_ptr_grey1 <= wr_addr_ptr_grey;
wr_addr_ptr_grey2 <= wr_addr_ptr_grey1;
end
/*****读写指针跨时钟域处理,写时钟域下采集读指针,读时钟域下采集写指针*****/
/**********读空标志 即 读指针格雷码与读时钟域下采集写指针相等**********/
assign o_empty = (rd_addr_ptr_grey == wr_addr_ptr_grey2) ? 1'b1 : 1'b0;
/**********读空标志 即 读指针格雷码与读时钟域下采集写指针相等**********/
/***写满标志 即 写指针格雷码与写时钟域下采集读指针,高两位取反相等,其他位相等**/
assign o_full = (wr_addr_ptr_grey == {(~rd_addr_ptr_grey2[DEPTH:DEPTH-2]),rd_addr_ptr_grey2[DEPTH-3:0]}) ? 1'b1 : 1'b0;
/***写满标志 即 写指针格雷码与写时钟域下采集读指针,高两位取反相等,其他位相等**/
endmodule
2、异步FIFO的testbench
module tb_defifo#(
parameter WIDTH = 16,
parameter DEPTH = 4
);
reg i_rst_n ;
reg i_wr_clk ;
reg i_wr ;
reg i_rd_clk ;
reg i_rd ;
reg [WIDTH-1:00] i_data ;
wire [WIDTH-1:00] o_data ;
wire o_full ;
wire o_empty ;
class_8_dcfifo#(
.WIDTH (WIDTH),
.DEPTH (DEPTH)
)u_class_8_dcfifo(
.i_rst_n (i_rst_n ) ,
.i_wr_clk (i_wr_clk) ,
.i_wr (i_wr ) ,
.i_rd_clk (i_rd_clk) ,
.i_rd (i_rd ) ,
.i_data (i_data ) ,
.o_data (o_data ) ,
.o_full (o_full ) ,
.o_empty (o_empty )
);
parameter wr_clk_period = 20 ;
parameter rd_clk_period = 50 ;
initial i_wr_clk = 0;
always#(wr_clk_period/2) i_wr_clk = ~i_wr_clk;
initial i_rd_clk = 0;
always#(rd_clk_period/2) i_rd_clk = ~i_rd_clk;
integer i,j;
initial begin
i_rst_n = 0;
i_wr = 0;
i_rd = 0;
#100;
i_rst_n = 1;
i_data = 5;
for(i=0;i<16;i=i+1)
begin
@(posedge i_wr_clk);
i_wr = 1;
i_data = i_data + 1;
end
i_wr = 0;
#(rd_clk_period ** 10000 + 1);
for(j=0;j<16;j=j+1)
begin
@(posedge i_rd_clk);
i_rd = 1;
end
i_rd = 0;
#(rd_clk_period ** 100 + 1);
$finish;
end
endmodule
3、异步FIFO的仿真结果
FIFO未写满情况
FIFO写满的情况