Verilog实现异步FIFO(重难点)

FIFO总概

图来自文章Simulation and Synthesis Techniques for Asynchronous FIFO Design

一个异步FIFO一共由五个基本模块组成,分别是

①RAM存储器模块

②FIFO写地址以及写满判断模块(写控制端口)

③FIFO写时钟同步到读时钟模块

④FIFO读地址以及读空判断模块(读控制端口)

⑤FIFO读时钟同步到写时钟模块

下面是FIFO顶层模块概图:

Wdata:写入数据

Wfull:写满信号

Winc:写请求信号(写使能信号)

Wclk:写时钟

Wrst_n:写复位信号(低电平有效)

Rdata:读出数据

Rempty:读空信号

Rinc:读请求信号(读使能信号)

Rclk:读时钟

Rrst_n:读复位信号(低电平有效)

一、RAM

随机存取存储器(Random Access Memory)随机访问内存(RAM)相当于PC机上的移动存储,用来存储和保存数据的。在任何时候都可以读写,RAM通常用作操作系统或其他正在运行的程序的临时存储介质(可称作系统内存)。不过,当电源关闭时时RAM不能保留数据,如果需要保存数据,就必须把它们写入到一个长期的存储器中(例如硬盘)。正因为如此,有时也将RAM称作"可变存储器"。

RAM的顶层模块图:

Wdata:写入的数据

Waddr:写地址

Wclken:写使能信号

Wfull:写满标志信号

Wclk:写时钟

Rdata:读数据

Raddr:读地址

Verilog代码如下:

根据上面RAM的总概图,定义的一个宽度为32位,深度为16的RAM(即定义了16个寄存器,每个寄存器的宽度为32)

module fifomem
#(
    parameter  DATASIZE = 32, // Memory data word width               
    parameter  ADDRSIZE = 6  // 指针地址位宽设置为6,因为32=2^5,应该加一判断写满或读空
) // Number of mem address bits
(
    output [DATASIZE-1:0] rdata, //read data
    input  [DATASIZE-1:0] wdata, //write data
    input  [ADDRSIZE-1:0] waddr, raddr, 
    input                 wclken, wfull, wclk
);

 // RTL Verilog memory model
localparam DEPTH = 1<<ADDRSIZE;   // leftshift is equal divide two
reg [DATASIZE-1:0] mem [0:DEPTH-1]; //即定义了16个寄存器,每个寄存器的位宽为32位

assign rdata = mem[raddr];

always @(posedge wclk)  //当使能信号有效且还未写满的时候将数据写入实体中,与wclk时钟信号同步
    if (wclken && !wfull)
        mem[waddr] <= wdata;
 endmodule

二、读控制端口

读控制端口主要用于是否可以读取数据,读指针与读空的顶层模块图如下图

Rinc:读请求信号

Rclk:读时钟信号

Rrst_n:读复位信号

Raddr:二进制形式的写地址

Rempty:读空信号

Rptr:格雷码形式的读指针

Rq2_wptr:同步后的写指针 

作用:当时钟信号来且读请求信号有效时,读出一组数据,并且同时读地址向下加一位。然后将读地址转换为格雷码,判断是否为读空。(基于顶层模块进行理解)

读空的判断:读地址指针追上写地址指针

Verilog代码如下:

module rptr_empty
#(
    parameter ADDRSIZE = 6
)
(
    output reg  rempty, 
    output         [ADDRSIZE-1:0] raddr,  //二进制形式的读地址
    output reg  [ADDRSIZE  :0] rptr,  //格雷码形式的读指针
    input             [ADDRSIZE  :0] rq2_wptr, //同步后的写指针
    input             rinc, rclk, rrst_n
);
  reg  [ADDRSIZE:0] rbin;//寄存地址,方便用于计算或者转换为格雷码
  wire [ADDRSIZE:0] rgraynext, rbinnext;
 // GRAYSTYLE2 pointer
 //将二进制的读指针与格雷码进制的读指针同步
  always @(posedge rclk or negedge rrst_n) 
      if (!rrst_n) begin
          rbin <= 0;
          rptr <= 0;
      end  
      else begin        
          rbin <= rbinnext; //直接作为存储实体的地址
          rptr <= rgraynext;//输出到 sync_r2w.v模块,被同步到 wrclk 时钟域
      end
  // Memory read-address pointer (okay to use binary to address memory)
  assign raddr     = rbin[ADDRSIZE-1:0]; //直接作为存储实体的地址,比如连接到RAM存储实体的读地址端。
  assign rbinnext  = rbin + (rinc & ~rempty); //不空且有读请求的时候读地址加1
  assign rgraynext = (rbinnext>>1) ^ rbinnext; //将二进制的读指针转为格雷码
  // FIFO empty when the next rptr == synchronized wptr or on reset 
  assign rempty_val = (rgraynext == rq2_wptr); //当读指针等于同步后的写指针,则为空,即读空。
  always @(posedge rclk or negedge rrst_n) 
      if (!rrst_n)
          rempty <= 1'b1; 
      else     
          rempty <= rempty_val;
 
endmodule

代码理解:

1.当复位信号有用时,读地址和读指针都为空,否则将读地址和读指针都储存起来

2.当读请求信号有用且没有读空时,读地址加一,同时利用前后的读地址计算出格雷码形式的读指针

3. 当读指针等于同步后的写指针时,即为读空。

三、写控制端口 

写控制端口主要用于是否可以写入数据,写指针与写满的顶层模块图如下图

Winc:写请求信号

Wclk:写时钟信号

Wrst_n:写复位信号

Waddr:二进制形式的写地址

Wfull:写空信号

Wptr:格雷码形式的写指针

Wq2_rptr:同步后的读指针 

 作用:当时钟信号来且写请求信号有效时,写出一组数据,并且同时写地址向下加一位。然后将写地址转换为格雷码,判断是否为写满。(基于顶层模块进行理解)

写满的判断:写地址指针再次追上读地址指针

Verilog代码如下:

module wptr_full
#(
    parameter ADDRSIZE = 4
) 
(
    output reg wfull,   
    output        [ADDRSIZE-1:0] waddr,//二进制形式的写地址
    output reg [ADDRSIZE  :0] wptr, //格雷码形式的写指针
    input           [ADDRSIZE  :0] wq2_rptr,//同步后的读指针
    input           winc, wclk, wrst_n
);
  reg  [ADDRSIZE:0] wbin;
  wire [ADDRSIZE:0] wgraynext, wbinnext;
  // GRAYSTYLE2 pointer
  always @(posedge wclk or negedge wrst_n)   
      if (!wrst_n)
          {wbin, wptr} <= 0;   
      else         
          {wbin, wptr} <= {wbinnext, wgraynext};
  // Memory write-address pointer (okay to use binary to address memory) 
  assign waddr        = wbin[ADDRSIZE-1:0]; 
  assign wbinnext   = wbin + (winc & ~wfull);//写请求且没写满时,地址加一
  assign wgraynext = (wbinnext>>1) ^ wbinnext; //将二进制码转换为格雷码
  //-----------------------------------------------------------------
  assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); 
  //
  always @(posedge wclk or negedge wrst_n)
      if (!wrst_n)
          wfull  <= 1'b0;   
      else     
          wfull  <= wfull_val;
 
  endmodule

代码理解:

1.当复位信号有用时,写地址和写指针都为空,否则将写地址和写指针都储存起来

2.当写请求信号有用且没有写满时,写地址加一,同时利用前后的写地址计算出格雷码形式的写指针

3. 当写指针再次追上同步后的读指针时,即为写满。

 四、读时钟同步到写时钟

顶层模块图如下所示

 

 Verilog代码如下:

module sync_r2w
#(
    parameter ADDRSIZE = 6
)
(
    output reg [ADDRSIZE:0] wq2_rptr,   //将读指针同步到写指针
    input            [ADDRSIZE:0] rptr,       // 输入读指针
    input            wclk, wrst_n
);
 
reg [ADDRSIZE:0] wq1_rptr;
 
  always @(posedge wclk or negedge wrst_n)   
      if (!wrst_n) begin
          wq1_rptr <= 0;          //清零复位信号
          wq2_rptr <= 0;
      end           
      else begin        
          wq1_rptr<= rptr;//两级寄存器
          wq2_rptr<=wq1_rptr;
      end          
  endmodule

 

 五、读时钟同步到写时钟 

 顶层模块图如下所示

 

Verilog代码如下

module sync_w2r
#(parameter ADDRSIZE = 6)
(
    output reg [ADDRSIZE:0] rq2_wptr, //将写指针同步到读指针
    input           [ADDRSIZE:0] wptr, //输入写指针    
    input            rclk, rrst_n
);
 
reg [ADDRSIZE:0] rq1_wptr;
 
  always @(posedge rclk or negedge rrst_n)   
      if (!rrst_n)begin
          rq1_wptr <= 0;
          rq2_wptr <= 0;
      end 
      else begin
          rq1_wptr <= wptr;
          rq2_wptr <= rq1_wptr;
      end
        
endmodule

 

  • 8
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
异步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 ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值