Verilog实现FIFO设计(二)之异步可变位宽,可变深度

请查看:异步FIFO—Verilog实现
写的非常好,此处不再赘述。


补充一下采用博主代码实现时候的一些问题(可能是我个人的问题):

  1. 在fifo_async这个模块中的下列代码:
//=========================================================write fifo 
genvar i;
generate 
for(i = 0; i < data_depth; i = i + 1 )
begin:fifo_init
always@(posedge wr_clk or posedge rst)
    begin
       if(rst)
          fifo_ram[i] <= 'h0;//fifo复位后输出总线上是0,并非ram中真的复位。可无
       else if(wr_en && (~full))
          fifo_ram[wr_addr] <= din;
       else
          fifo_ram[wr_addr] <= fifo_ram[wr_addr];
    end   
end    
endgenerate    

此处代码编译是没问题的,但是我后期通过Vivado进行综合的时候会报错,具体错误我忘了,大概意思就是:fifo_ram这个东西多次赋值是不允许的。应该是generate的问题,解决方法就是不使用generate for,老老实实的把所有的赋值为0都写上,或者在always块里面使用for循环进行初始化,具体参考以下代码(和参考文章中的代码基本相同,就是改了一下我说到的那个问题,这个代码仿真,综合都通过了):

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 <=   'h0;//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

2.一个简单的testbench,先写入一个数据再读出来,然后依次写入四个数据,使full信号有效,再依次读出来。

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

仿真波形图:
在这里插入图片描述
读出来的结果:
在这里插入图片描述
3.如果你想使用一个深度的异步fifo(data_depth=1),即用来缓冲数据的话,通过这个代码是无法实现的,会报错,这个代码只能是深度为4,如果资源不那么紧张的话,就将就用一下吧。如果想要写一个数据立马读出来,你可以在例化的时候使用.re_en(~empty),即只要fifo非空,就开始读数据。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
异步FIFO是一种用于在两个异步时钟域之间传输数据的解决方案。在Verilog实现异步FIFO可以采用以下步骤: 1. 定义FIFO的输入和输出接口:包括读写时钟、读写使能信号、读写数据和读写指针等。 2. 使用两个时钟域的寄存器同步输入信号:由于读写时钟不同,需要使用两级寄存器级联来同步输入信号,以消除亚稳态。 3. 实现FIFO的读写逻辑:根据读写使能信号和读写指针,确定读写操作的时机和数据。 4. 实现FIFO的存储器:可以使用RAM或者其他存储结构来存储数据。 5. 实现FIFO的读写指针逻辑:根据读写操作的完成情况,更新读写指针的值。 6. 添加互斥逻辑:为了避免读写冲突,可以使用互斥逻辑来控制读写操作的互斥性。 需要注意的是,在实现异步FIFO时,需要考虑跨时钟域的问题。可以使用两级寄存器同步和格雷码等方法来解决跨时钟域的问题,确保读写指针的比较正确。 总之,通过定义接口、同步输入信号、实现读写逻辑和存储器、更新读写指针以及添加互斥逻辑等步骤,可以在Verilog实现异步FIFO。 #### 引用[.reference_title] - *1* *3* [异步FIFO---Verilog实现](https://blog.csdn.net/alangaixiaoxiao/article/details/81432144)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于Verilog实现异步FIFO](https://blog.csdn.net/ZHOUJIAN1997/article/details/121597269)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值