异步FIFO设计(2)—— 简单代码实现
在阅读Clifford的相关文章后(详见上一篇: 异步FIFO设计(1))后,我根据自己的理解,简单编写了一下FIFO的FPGA实现代码。注意,此代码不能用于综合!!! 仅适用于学习以及验证实际FIFO的正确性。
具体代码
说明:本代码testbench抓取波形部分仅适用于在线仿真器。
1.Top view
`timescale 1ns/1ps
module top_module ();
parameter D_WIDTH = 16;
parameter A_WIDTH = 4;
parameter D_Depth = 8;
wire w_en,r_en;
reg f_flag, e_flag;
reg [A_WIDTH-1:0] w_addr,r_addr;
reg [D_WIDTH-1:0] BUF [D_Depth-1:0];
wire [D_WIDTH-1:0] buf0,buf1,buf7;
reg w_clk=0;
reg r_clk=0;
always #5 w_clk = ~w_clk; // Create clock with period=10
always #2.5 r_clk = ~r_clk;
initial `probe_start; // Start the timing diagram
// A testbench
reg [D_WIDTH-1:0] din=0;
reg [D_WIDTH-1:0] dout;
reg w_nrst=1, r_nrst=1;
reg winc=0, rinc=0;
initial begin
#7 w_nrst <= 1'b0; r_nrst <= 1'b0;
#3 w_nrst <= 1'b1; r_nrst <= 1'b1;winc=1;
#5 din <= 'h01;
#10 din <= 'h02;
#10 din <= 'h03;
#10 din <= 'h04;
#10 din <= 'h05;
#10 din <= 'h06;
#10 din <= 'h07;
#10 din <= 'h08;
#10 din <= 'h09;
#10 din <= 'h0A;
#10 din <= 'h0B;
#10 din <= 'h0C;
#10 din <= 'h0D;
#10 din <= 'h0E;
#10 din <= 'h0F;
#10 din <= 'h10;
#10 din <= 'h11;
#10 din <= 'h12;
#10 din <= 'h13;rinc=1;
#10 din <= 'h14;winc=0;
#10 din <= 'h15;
#10 din <= 'h16;
#10 din <= 'h17;
#10 din <= 'h18;
#10 din <= 'h19;
#10 din <= 'h1A;
#10 din <= 'h1B;
#10 din <= 'h1C;
#10 din <= 'h1D;
#10 din <= 'h1E;
#10 din <= 'h1F;
#5000 $finish; // Quit the simulation
end
//module instantiation
FIFO #(
.D_WIDTH(D_WIDTH),
.A_WIDTH(A_WIDTH),
.D_Depth(D_Depth)
) FIFO1 (
.din(din),
.w_clk(w_clk),
.r_clk(r_clk),
.w_nrst(w_nrst),
.r_nrst(r_nrst),
.winc(winc),
.rinc(rinc),
.dout(dout),
.w_en(w_en),
.r_en(r_en),
.f_flag(f_flag),
.e_flag(e_flag),
.w_addr(w_addr),
.r_addr(r_addr),
.buf0(buf0),
.buf1(buf1),
.buf7(buf7)
); // Sub-modules work too.
`probe(w_clk); // Probe signal "clk"
`probe(r_clk);
`probe(w_nrst);
`probe(r_nrst);
`probe(winc);
`probe(rinc);
`probe(din);
`probe(dout);
`probe(w_en);
`probe(r_en);
`probe(f_flag);
`probe(e_flag);
`probe(w_addr);
`probe(r_addr);
`probe(buf0);
`probe(buf1);
`probe(buf7);
endmodule
2. FIFO模块
module FIFO # (
parameter D_WIDTH = 16,
parameter A_WIDTH = 4,
parameter D_Depth = 8 )(
input [D_WIDTH-1:0] din,
input w_clk,
input r_clk,
input w_nrst,
input r_nrst,
input winc,
input rinc,
output reg [D_WIDTH-1:0] dout,
output w_en,r_en,
output reg f_flag, e_flag,
output reg [A_WIDTH-1:0] w_addr,r_addr,
output [D_WIDTH-1:0] buf0,buf1,buf7
);
//reg [A_WIDTH-1:0] w_addr;
//reg [A_WIDTH-1:0] r_addr;
//reg f_flag, e_flag;
//wire w_en, r_en;
wire r_addr_i;
reg [D_WIDTH-1:0] BUF [D_Depth-1:0];
assign {buf0,buf1,buf7} = {BUF[0],BUF[1],BUF[7]};
assign r_addr_i = !r_addr[A_WIDTH-1];
//write addr
always @(posedge w_clk or negedge w_nrst)
begin
if(!w_nrst | w_addr == {(A_WIDTH){1'b1}})
w_addr <= 'b0;
else if(!w_en)
w_addr <= w_addr;
else
w_addr <= w_addr +1'b1;
end
//read addr
always @(posedge r_clk or negedge r_nrst)
begin
if(!r_nrst | r_addr == {(A_WIDTH){1'b1}})
r_addr <= 'b0;
else if(!r_en)
r_addr <= r_addr;
else
r_addr <= r_addr +1'b1;
end
//full flag
/*always @(posedge w_clk or negedge w_nrst)
begin
if(!w_nrst )
f_flag <= 1'b0;
else if (w_addr == {!r_addr[A_WIDTH-1], r_addr[A_WIDTH-2:0]})
f_flag <= 1'b1;
else
f_flag <= 1'b0;
end
//empty flag
always @(posedge r_clk or negedge r_nrst)
begin
if(!r_nrst )
e_flag <= 1'b0;
else if (w_addr == r_addr)
e_flag <= 1'b1;
else
e_flag <= 1'b0;
end
*/
assign f_flag = (w_addr == {!r_addr[A_WIDTH-1], r_addr[A_WIDTH-2:0]})?1'b1:1'b0;
assign e_flag = (w_addr == r_addr)?1'b1:1'b0;
assign w_en = winc & !f_flag;
assign r_en = rinc & !e_flag;
//write data
always @(posedge w_clk)
begin
if(w_en)
BUF[w_addr[A_WIDTH-2:0]] <= din;
end
//read data
always @(posedge r_clk)
begin
if(r_en)
dout <= BUF[r_addr[A_WIDTH-2:0]];//mention that only N-2 LSBits are addr
else
dout <= 'bx;
end
endmodule
仿真结果
仿真环境:在线仿真器
时钟频率:w_clk=100-MHz, r_clk=200-MHz
仿真思路:通过修改testbench中winc和rinc的激励值,进行不同功能的测试。