赞
踩
通常情况下,设计的异步FIFO的深度是2的N次方,但事实上,选择这个2^N的原因也是因为格雷码这么取的时候,最大值+1回到最小值时,跳变还是只有1bit。
事实上格雷码可以取2N个进行环回,例如取格雷码个数为12,在2^4=16的基础上,掐头去尾,取中间的12个格雷码,这样从0011到最大值1011中间任意两个相邻数之间只有1bit位的跳变。
任意偶数个数格雷码,都可以实现相邻数字跳变仅有1bit发生变化。
异步FIFO的关键就在于读写指针跨时钟域传输的问题,保证每次跳变仅 有1bit变化,这样无论是变化前还是变化后的读写指针被同步到另一个时钟域,都不会发生“FIFO空时继续读”、“FIFO满时继续写”这样的逻辑错误。
对于偶数深度的FIFO,如FIFO深度为4。那么无论FIFO深度是多少,我们都会对地址进行扩展1bit,来作为标志位,用于产生空满信号。因此FIFO深度为4时,我们的读写指针取值范围为:
{0_110,0_111,0_101,0_100,1_100,1_101,1_111,1_110}
可以看到,任意两个相邻的数字仅有1bit发生跳变,取格雷码回环就按照:在2^4=16的基础上,掐头去尾,取中间的8个格雷码。
对于奇数深度的FIFO,如FIFO深度为3。那么无论FIFO深度是多少,我们都会对地址进行扩展1bit,来作为标志位,用于产生空满信号。因此FIFO深度为3时,我们的读写指针取值范围为:
{0_111,0_101,0_100,1_100,1_101,1_111}
可以看到,任意两个相邻的数字仅有1bit发生跳变,取格雷码回环就按照:在2^4=16的基础上,掐头去尾,取中间的6个格雷码。
也就是说,无论FIFO深度是奇数还是偶数,我们都会对读写指针扩1bit标志位,使得扩充后他们的深度一定是偶数。这样我们就可以用第二节提到的偶数格雷码环回特性:任意偶数个数格雷码,都可以实现相邻数字跳变仅有1bit发生变化。让读写指针跨时钟域传输时,相邻数字仅有1bit发生变化,就不会出现FIFO读写错误,所以FIFO深度可以是任意值。
- module async_fifo
- #(
- parameter DATA_WIDTH = 16, //FIFO 位宽(数据位宽)
- FIFO_DEPTH = 8, //FIFO 深度
- ADDR_DEPTH = $clog2(FIFO_DEPTH) //根据FIFO深度计算地址位宽,此处为3。
- )
- (
- //reset signals
- input wire wr_rst_n_i ,
- input wire rd_rst_n_i ,
-
- //write interface
- input wire wr_clk_i ,
- input wire wr_en_i ,
- input wire [DATA_WIDTH-1:0] wr_data_i ,
-
- //read interface
- input wire rd_clk_i ,
- input wire rd_en_i ,
- output reg [DATA_WIDTH-1:0] rd_data_o ,
-
- //flag signals
- output wire fifo_full_o ,
- output wire fifo_empty_o
- );
-
- //memary
- reg [DATA_WIDTH-1:0] fifo_buffer [FIFO_DEPTH-1:0];
-
- //memary addr
- wire [ADDR_DEPTH-1:0] wr_addr; //真实写地址,作为写ram地址
- wire [ADDR_DEPTH-1:0] rd_addr; //真实读地址,作为读ram地址
-
- //write poiter and write poiter of gray and sync
- reg [ADDR_DEPTH:0] wr_ptr ; //写指针,二进制
- wire [ADDR_DEPTH:0] gray_wr_ptr ; //写指针,格雷码
- reg [ADDR_DEPTH:0] gray_wr_ptr_d0 ; //写指针在读时钟域下同步一拍
- reg [ADDR_DEPTH:0] gray_wr_ptr_d1 ; //写指针在读时钟域下同步二拍
-
- //read poiter and read poiter of gray and sync
- reg [ADDR_DEPTH:0] rd_ptr ; //读指针,二进制
- wire [ADDR_DEPTH:0] gray_rd_ptr ; //读指针,格雷码
- reg [ADDR_DEPTH:0] gray_rd_ptr_d0 ; //读指针在写时钟域下同步一拍
- reg [ADDR_DEPTH:0] gray_rd_ptr_d1 ; //读指针在写时钟域下同步二拍
-
- //---write poiter and bin2gray---//
- always@(posedge wr_clk_i or negedge wr_rst_n_i)begin
- if(~wr_rst_n_i)begin
- wr_ptr <= 'b0;
- end else if(wr_en_i && !fifo_full_o)begin //写使能有效,且非满
- wr_ptr <= wr_ptr + 1'b1;
- end
- end
-
- assign gray_wr_ptr = wr_ptr ^ (wr_ptr>>1);
- //---gray_wr_ptr sync to read clk domain---//
- always@(posedge rd_clk_i or negedge rd_rst_n_i)begin
- if(~rd_rst_n_i)begin
- gray_wr_ptr_d0 <= 'b0; //寄存1拍
- gray_wr_ptr_d1 <= 'b0; //寄存2拍
- end else begin
- gray_wr_ptr_d0 <= gray_wr_ptr ; //寄存1拍
- gray_wr_ptr_d1 <= gray_wr_ptr_d0; //寄存2拍
- end
- end
-
- //---read poiter and bin2gray---//
- always@(posedge rd_clk_i or negedge rd_rst_n_i)begin
- if(~rd_rst_n_i)begin
- rd_ptr <= 'b0;
- end else if(rd_en_i && !fifo_empty_o)begin //读使能有效,且非空
- rd_ptr <= rd_ptr + 1'b1;
- end
- end
-
- assign gray_rd_ptr = rd_ptr ^ (rd_ptr>>1);
- //---gray_rd_ptr sync to write clk domain---//
- always@(posedge wr_clk_i or negedge wr_rst_n_i)begin
- if(~wr_rst_n_i)begin
- gray_rd_ptr_d0 <= 'b0; //寄存1拍
- gray_rd_ptr_d1 <= 'b0; //寄存2拍
- end else begin
- gray_rd_ptr_d0 <= gray_rd_ptr ; //寄存1拍
- gray_rd_ptr_d1 <= gray_rd_ptr_d0; //寄存2拍
- end
- end
-
- //---full flag and empty flag---//
- //当高位相反且其他位相等时,写指针超过读指针一圈,FIFO被写满
- //同步后的读指针格雷码高两位取反,再拼接上余下位
- assign fifo_full_o = (gray_wr_ptr == {~gray_rd_ptr_d1[ADDR_DEPTH], ~gray_rd_ptr_d1[ADDR_DEPTH-1], gray_rd_ptr_d1[ADDR_DEPTH-2:0]})
- ? 1'b1 : 1'b0;
-
- //当所有位相等时,读指针追到到了写指针,FIFO被读空
- assign fifo_empty_o= (gray_rd_ptr == gray_wr_ptr_d1) ? 1'b1 : 1'b0;
-
- //---write addr and read addr---//
- assign wr_addr = wr_ptr[ADDR_DEPTH-1:0]; //写RAM地址等于写指针的低 ADDR_DEPTH 位(去除最高位)
- assign rd_addr = rd_ptr[ADDR_DEPTH-1:0]; //读RAM地址等于读指针的低 ADDR_DEPTH 位(去除最高位)
-
- //---write operation---//
- integer i;
-
- always@(posedge wr_clk_i or negedge wr_rst_n_i)begin
- if(~wr_rst_n_i)begin
- for(i=0; i<FIFO_DEPTH; i=i+1)begin
- fifo_buffer[i] <= 'd0;
- end
- end else if(wr_en_i && !fifo_full_o)begin //写使能有效,且非满
- fifo_buffer[wr_addr] <= wr_data_i;
- end
- end
- //---read operation---//
- always@(posedge rd_clk_i or negedge rd_rst_n_i)begin
- if(~rd_rst_n_i)begin
- rd_data_o <= {(DATA_WIDTH){1'b0}};
- end else if(rd_en_i && !fifo_empty_o)begin //读使能有效,且非空
- rd_data_o <= fifo_buffer[rd_addr];
- end
- end
-
- endmodule
-
-

- `timescale 1ns/1ns //时间单位/精度
-
- //----------<模块及端口声明>-------//
- module tb_async_fifo();
-
- parameter DATA_WIDTH = 8 ; //FIFO位宽
- parameter FIFO_DEPTH = 8 ; //FIFO深度
-
- parameter TEST_TIME = 300; //随机测试最大次数
-
-
- reg wr_rst_n_i ;
- reg rd_rst_n_i ;
-
- reg wr_clk_i ;
- reg wr_en_i ;
- reg [DATA_WIDTH-1:0] wr_data_i ;
-
- reg rd_clk_i ;
- reg rd_en_i ;
- wire [DATA_WIDTH-1:0] rd_data_o ;
-
- wire fifo_full_o ;
- wire fifo_empty_o;
-
- integer i;
-
-
- //------------<设置时钟>-------------------//
- always #10 rd_clk_i = ~rd_clk_i; //读时钟周期20ns
- always #20 wr_clk_i = ~wr_clk_i; //写时钟周期40ns
-
- //------------<设置初始测试条件>----------//
- initial begin
- rd_clk_i = 1'b0; //初始时钟为0
- wr_clk_i = 1'b0; //初始时钟为0
- wr_rst_n_i <= 1'b0; //初始复位
- rd_rst_n_i <= 1'b0; //初始复位
- wr_en_i <= 1'b0;
- rd_en_i <= 1'b0;
- wr_data_i <= 'd0;
- #5
- wr_rst_n_i <= 1'b1;
- rd_rst_n_i <= 1'b1;
-
- //-----------固定测试-------------//
- //重复8次写操作,让FIFO写满
- repeat(8) begin
- @(negedge wr_clk_i)begin
- wr_en_i <= 1'b1;
- wr_data_i <= $random; //生成8位随机数
- end
- end
- //拉低写使能
- @(negedge wr_clk_i) wr_en_i <= 1'b0;
-
- //重复8次读操作,让FIFO读空
- repeat(8) begin
- @(negedge rd_clk_i)
- rd_en_i <= 1'd1;
- end
- //拉低读使能
- @(negedge rd_clk_i)rd_en_i <= 1'd0;
-
- //重复4次写操作,写入4个随机数据
- repeat(4) begin
- @(negedge wr_clk_i)begin
- wr_en_i <= 1'b1;
- wr_data_i <= $random; //生成8位随机数
- end
- end
- //持续同时对FIFO读
- @(negedge rd_clk_i)rd_en_i <= 1'b1;
- //持续同时对FIFO写,写入数据为随机数据
- repeat(100) begin
- @(negedge wr_clk_i)begin
- wr_en_i <= 1'b1;
- wr_data_i <= $random; //生成8位随机数
- end
- end
- // 拉低读写使能
- @(negedge wr_clk_i)wr_en_i <= 1'b0;
- @(negedge rd_clk_i)rd_en_i <= 1'b0;
-
- //等待5个写时钟周期
- repeat(5) @(posedge wr_clk_i);
-
- //--------------随机测试---------------//
- for(i=0; i<TEST_TIME; i=i+1)begin
- if(i<100) begin
- @(negedge wr_clk_i) ;
- wr_en_i <= $random() % 2;
- wr_data_i <= $random();
- @(negedge rd_clk_i);
- rd_en_i <= 1;
- end
- if(i<200) begin
- @(negedge wr_clk_i) ;
- wr_en_i <= 1;
- wr_data_i <= $random();
- @(negedge rd_clk_i);
- rd_en_i <= $random() % 2;
-
- end
- else if(i<TEST_TIME)begin
- @(negedge wr_clk_i) ;
- wr_en_i <= $random() % 2;
- wr_data_i <= $random();
- @(negedge rd_clk_i);
- rd_en_i <= $random() % 2;
- end
- end
-
- repeat(5) @(posedge wr_clk_i);
- $finish();
- end
-
- //------------<例化被测试模块>-----------//
- async_fifo
- #(
- .DATA_WIDTH (DATA_WIDTH), //FIFO位宽
- .FIFO_DEPTH (FIFO_DEPTH) //FIFO深度
- )
- async_fifo_inst(
- .wr_rst_n_i (wr_rst_n_i ),
- .rd_rst_n_i (rd_rst_n_i ),
-
- .wr_clk_i (wr_clk_i ),
- .wr_en_i (wr_en_i ),
- .wr_data_i (wr_data_i ),
-
- .rd_clk_i (rd_clk_i ),
- .rd_en_i (rd_en_i ),
- .rd_data_o (rd_data_o ),
-
- .fifo_full_o (fifo_full_o ),
- .fifo_empty_o (fifo_empty_o)
- );
-
- endmodule

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。