Verilog实现---特殊应用的异步FIFO

1.概述

网上有很多有关FIFO原理及其概述,这里给出三个比较好文章的提供参考:
https://blog.youkuaiyun.com/huigeyu/article/details/107331221
https://blog.youkuaiyun.com/alangaixiaoxiao/article/details/81432144
https://blog.youkuaiyun.com/weixin_46022434/article/details/105348433
以上的文章都是讲述通用的异步FIFO,但本文主要讲的是根据需求改写的异步FIFO

2.端口信号说明

名字类型说明
data_widthparameterFIFO的宽度
data_depthparameterFIFO的深度
addr_widthparameter地址位宽
read_widthparameter数据包位宽
rst_ninput复位
wr_clkinput写时钟,256Mhz
wr_eninput写使能,信号是随机的,持续时间也是随机的,但至少5个写时钟周期
data_ininput数据输入,32bit
rd_clkinput读时钟,128Mhz,是wr_clk二分频得到的
rd_eninput读使能,信号是随机的,持续时间也是随机的,但至少4个读时钟周期
out_emptyoutput空标志
out_fulloutput满标志
fifo_outputoutput数据输出,8bit,把32bit数据拆成4x8bit输出,每一个读时钟周期输出一个8bit的数据包
clk_32output将wr_clk八分频的时钟,用于其他模块使用

3.文件说明

名字说明
TOP顶层连接文件
div_2二分频module,将wr_clk二分频给rd_clk
div_8八分频module,将wr_clk八分频给外面模块使用
a_FIFO异步FIFO模块

4.代码

a_FIFO代码:

//-----------------------------------------------------------------------------------------------
//   Copyright         :        
//   Author            :        Luk.wj
//   File Name         :        a_FIFO
//   Module Name       :        a_FIFO
//   Create            :        2021.07.15
//   Revise            :        2021.07.30
//   Fuction           :        
//-----------------------------------------------------------------------------------------------

module a_FIFO #(
    parameter   data_width = 32, 
    parameter   data_depth = 4,
    parameter   addr_width = 2,
    parameter   read_width = 8
) //FIFO数据宽度,深度;地址位宽;数据包位宽
(
    input                           rst_n,
    input                           wr_clk,
    input                           wr_en,
    input      [data_width-1:0]     data_in,         
    input                           rd_clk,
    input                           rd_en,
    output reg                      out_empty,
    output reg                      out_full,
    output reg [read_width-1:0]     fifo_output
);

reg    [1:0]             count;     //计数

reg                      rst_nw1;   //异步复位,同步释放(write)
reg                      rst_nw2;
reg                      rst_nr1;   //异步复位,同步释放(read)
reg                      rst_nr2;

reg    [data_width-1:0]  data_out;       //32位输出
reg    [addr_width:0]    wr_addr_ptr;    //地址指针,比地址多一位,MSB检测
reg    [addr_width:0]    rd_addr_ptr;
wire   [addr_width-1:0]  wr_addr;        //地址
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];  //定义FIFO_RAM

reg  flag_wr;        //写标志
reg  flag_rd;        //读标志
reg  flag_wr_ptr;    //写指针标志
reg  flag_rd_ptr;    //读指针标志

reg  rd_en_delay1;  //读书使能打一拍,用于32bit转4x8bit输出

wire full;
wire empty;

//========================================================= 打一拍,用于32bit转4x8bit输出
always @(posedge rd_clk or negedge rst_nr2) begin
   if (!rst_nr2)  rd_en_delay1 <= 1'b0;
   else           rd_en_delay1 <= rd_en;
end
//========================================================= write Synchronized Asynchronous Reset
always @(posedge wr_clk or negedge rst_n) begin
   if (!rst_n) begin
      rst_nw1 <= 1'b0;
      rst_nw2 <= 1'b0;
   end
   else begin
      rst_nw1 <= 1'b1;
      rst_nw2 <= rst_nw1;
   end
end
//========================================================= read Synchronized Asynchronous Reset
always @(posedge rd_clk or negedge rst_n) begin
   if (!rst_n) begin
      rst_nr1 <= 1'b0;
      rst_nr2 <= 1'b0;
   end
   else begin
      rst_nr1 <= 1'b1;
      rst_nr2 <= rst_nr1;
   end
end
//========================================================= write fifo 
always @(posedge wr_clk or negedge rst_nw2) begin
   if (!rst_nw2)   
      flag_wr <= 1'b0;
   else if(wr_en && (!full) && (!flag_wr)) begin
      fifo_ram[wr_addr] <= data_in;
      flag_wr <= 1'b1;
   end
   else if(!wr_en) 
      flag_wr <= 1'b0;
end 
//======================================================== read_fifo
always@(posedge rd_clk or negedge rst_nr2) begin
   if(!rst_nr2) begin
      data_out <= 32'b0;
      flag_rd  <= 1'b0;
   end
   else if(rd_en && (!empty) && (!flag_rd)) begin
      data_out <= fifo_ram[rd_addr];
      flag_rd  <= 1'b1;
   end
   else if(!rd_en) 
      flag_rd  <= 1'b0;
end
always @(posedge rd_clk or negedge rst_nr2) begin
   if (!rst_nr2) begin
      count <= 2'b00;
      fifo_output <= 8'b0;
   end
   else if(rd_en_delay1) begin
      case (count)
         2'b00 : begin
            fifo_output <= data_out[7:0];
            count       <= count + 1;
         end
         2'b01 : begin
            fifo_output <= data_out[15:8];
            count       <= count + 1;
         end
         2'b10 : begin
            fifo_output <= data_out[23:16];
            count       <= count + 1;
         end
         2'b11 : begin
            fifo_output <= data_out[31:24];
            count       <= 2'b00;
         end
         default: begin
            count       <= 2'b00;
            fifo_output <= 8'b0;
         end
      endcase
   end
   else count <= 2'b00;
end
//地址为地址指针去掉第一位
assign wr_addr = wr_addr_ptr[addr_width-1:0];
assign rd_addr = rd_addr_ptr[addr_width-1:0];
//============================================================= wr_clk
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_nw2) begin //写指针+1
   if(!rst_nw2) begin
      wr_addr_ptr <= 'b0;
      flag_wr_ptr <= 1'b0;
   end
   else if(wr_en && (!full) && (!flag_wr_ptr)) begin
      wr_addr_ptr <= wr_addr_ptr + 1;
      flag_wr_ptr <= 1'b1;
   end
   else if(!wr_en) 
      flag_wr_ptr <= 1'b0;
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_nr2) begin //读指针+1
   if(!rst_nr2) begin
      rd_addr_ptr <= 'b0;
      flag_rd_ptr <= 1'b0;
   end
   else if(rd_en && (!empty) && (!flag_rd_ptr)) begin
      rd_addr_ptr <= rd_addr_ptr + 1;
      flag_rd_ptr <= 1'b1;
   end
   else if(!rd_en) 
      flag_rd_ptr <= 1'b0;
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;

//========================================================== judge full or empty
assign full  = (wr_addr_gray == {~(rd_addr_gray_d2[addr_width:1]),rd_addr_gray_d2[addr_width-2:0]}) ;  //高两位不同,其余位相同
assign empty = (rd_addr_gray == wr_addr_gray_d2);

always @(posedge rd_clk or negedge rst_nr2) begin
   if (!rst_nr2)  out_empty <= 1'b0;
   else           out_empty <= empty;
end
always @(posedge wr_clk or negedge rst_nw2) begin
   if (!rst_nw2)  out_full  <= 1'b0;
   else           out_full  <= full; 
end

endmodule

div_2代码:

//-----------------------------------------------------------------------------------------------
//   Copyright         :        
//   Author            :        Luk.wj
//   File Name         :        div_2
//   Module Name       :        div_2
//   Create            :        2021.08.17
//   Revise            :        2021.08.17
//   Fuction           :        时钟二分频
//-----------------------------------------------------------------------------------------------

module div_2 (
    input      wr_clk,
    input      rst_n,
    output reg rd_clk
);
    
always @(posedge wr_clk or negedge rst_n) begin
   if (!rst_n)   rd_clk <= 1'b0;
   else          rd_clk <= ~rd_clk;
end

endmodule

div_8代码:

//-----------------------------------------------------------------------------------------------
//   Copyright         :        
//   Author            :        Luk.wj
//   File Name         :        div_8
//   Module Name       :        div_8
//   Create            :        2021.08.17
//   Revise            :        2021.08.17
//   Fuction           :        时钟八分频
//-----------------------------------------------------------------------------------------------

module div_8 (
    input           wr_clk,
    input           rst_n,
    output reg      clk_32
);

reg [1:0]  cnt;
always @(posedge wr_clk or negedge rst_n) begin
   if (!rst_n) begin
       cnt    <= 2'b0;
       clk_32 <= 1'b0;
   end
   else if (cnt == 3) begin
       cnt    <= 2'b0;
       clk_32 <= ~clk_32;
   end
   else  cnt  <= cnt + 1;
end   
 
endmodule

TOP代码:

//-----------------------------------------------------------------------------------------------
//   Copyright         :        
//   Author            :        Luk.wj
//   File Name         :        TOP
//   Module Name       :        TOP
//   Create            :        2021.08.17
//   Revise            :        2021.08.17
//   Fuction           :        顶层连接文件
//-----------------------------------------------------------------------------------------------

module TOP (
    input      [31:0]     data_in,
    input                 rst_n,
    input                 wr_en,
    input                 rd_en,
    input                 wr_clk,
    output                out_empty,
    output                out_full,
    output     [7:0]      fifo_output,
    output                clk_32
);

wire rd_clk;

/* Define Relationship */
div_2 div2(
    .wr_clk ( wr_clk ),
    .rst_n  ( rst_n  ),
    .rd_clk ( rd_clk )
);

div_8 div8(
    .wr_clk ( wr_clk ),
    .rst_n  ( rst_n  ),
    .clk_32 ( clk_32 )
);

a_FIFO#(
    .data_width ( 32 ),
    .data_depth ( 4 ),
    .addr_width ( 2 ),
    .read_width ( 8 )
)FIFO(
    .rst_n        ( rst_n        ),
    .wr_clk       ( wr_clk       ),
    .wr_en        ( wr_en        ),
    .data_in      ( data_in      ),
    .rd_clk       ( rd_clk       ),
    .rd_en        ( rd_en        ),
    .out_empty    ( out_empty    ),
    .out_full     ( out_full     ),
    .fifo_output  ( fifo_output  )
);
   
endmodule

5.测试&波形

测试代码:

//-----------------------------------------------------------------------------------------------
//   Copyright         :        
//   Author            :        Luk.wj
//   File Name         :        a_FIFO_tb
//   Module Name       :        a_FIFO_tb
//   Create            :        2021.07.20
//   Revise            :        
//   Fuction           :        
//-----------------------------------------------------------------------------------------------
`timescale 1 ns/ 1 ps
module TOP_tb (

);

// inputs
reg [31:0] data_in;
reg rd_en;
reg rst_n;
reg wr_clk;
reg wr_en;
// outputs                                              
wire [7:0]  fifo_output;
wire out_empty;
wire out_full;
wire clk_32;

localparam w_CYCLE = 4;  //256Mhz    

integer l_delay, h_delay, r_l_delay, i, j, k; // i,j,k分别是写使能个数,数据个数,读使能个数
integer flag_tb_f;  // 用于标志判断的

TOP i1(
    .data_in     ( data_in     ),
    .rst_n       ( rst_n       ),
    .wr_en       ( wr_en       ),
    .rd_en       ( rd_en       ),
    .wr_clk      ( wr_clk      ),
    .out_empty   ( out_empty   ),
    .out_full    ( out_full    ),
    .fifo_output ( fifo_output ),
    .clk_32      ( clk_32      )
);
 
// generate write clock
initial begin
  wr_clk = 1'b0;
  forever
    #(w_CYCLE/2) wr_clk = ~wr_clk;
end  

// reset
initial begin
  rst_n = 1'b1;
  #2
  rst_n = 1'b0;
  #10
  rst_n = 1'b1;
end

// generate random data
initial begin
  #1
	data_in = 32'h0;
	for (j = 0; j < 800; j = j + 1) begin
		#(w_CYCLE) data_in = {$random} % 16 + 16 * ({$random} % 16) + 16**2 * ({$random} % 16) + 16**3 * ({$random} % 16) 
		                     + 16**4 * ({$random} % 16) + 16**5 * ({$random} % 16) + 16**6 * ({$random} % 16) + 16**7 * ({$random} % 16);
	end
end

// 写满标志显示
initial begin // 写满标志flag初始化
  flag_tb_f = 0;
  // flag_tb_r = 0;
end

always @(posedge wr_clk) begin
	if (wr_en && (!flag_tb_f) && out_full) begin
    $display("full happen time is %t", $time);
    $display("full is %h, Full!!!", out_full);
    flag_tb_f <= 1;
  end
  else if (!wr_en) begin
    flag_tb_f <= 0;
  end
end

// 写空标志显示
// always @(posedge rd_clk) begin
// 	if (rd_en && (!flag_tb_r) && out_empty) begin
//     $display("empty happen time is %t", $time);
//     $display("empty is %h, Empty!!!", out_empty);
//     flag_tb_r <= 1;
//   end
//   else if (!rd_en) begin
//     flag_tb_r <= 0;
//   end
// end

// generate random wr_en
initial begin
	wr_en = 1'b0;
	#40
	for (i = 0; i < 6; i = i + 1) begin
		l_delay = 20 * (1 + {$random} % 4) + ({$random} % 10);  // 低电平持续时间
	  h_delay = 20 * (2 + {$random} % 5) + ({$random} % 10); // 高电平持续时间
		wr_en = 1'b1;
		#h_delay
		wr_en = 1'b0;
		#l_delay;
	end
  #200
  wr_en = 1'b1;
  #103
  wr_en = 1'b0;
  #200
  wr_en = 1'b1;
  #89
  wr_en = 1'b0;
end


// read data
initial begin                                                                        
  rd_en = 1'b0;
  #900
  for (k = 0; k < 7; k = k + 1) begin
		r_l_delay = 30 * (3 + {$random} % 4) + ({$random} % 10); // 读使能低电平持续20 * (3 + 0-3) + 0-9
		rd_en = 1'b1;
		#(8*w_CYCLE)
		rd_en = 1'b0;
		#r_l_delay;
	end                                                                                      
  #400 $stop;                  
end    
            
endmodule

波形:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值