当前位置:   article > 正文

Verilog实现异步FIFO(重难点)_异步fifo verilog

异步fifo verilog

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)

  1. module fifomem
  2. #(
  3. parameter DATASIZE = 32, // Memory data word width
  4. parameter ADDRSIZE = 6 // 指针地址位宽设置为6,因为32=2^5,应该加一判断写满或读空
  5. ) // Number of mem address bits
  6. (
  7. output [DATASIZE-1:0] rdata, //read data
  8. input [DATASIZE-1:0] wdata, //write data
  9. input [ADDRSIZE-1:0] waddr, raddr,
  10. input wclken, wfull, wclk
  11. );
  12. // RTL Verilog memory model
  13. localparam DEPTH = 1<<ADDRSIZE; // leftshift is equal divide two
  14. reg [DATASIZE-1:0] mem [0:DEPTH-1]; //即定义了16个寄存器,每个寄存器的位宽为32
  15. assign rdata = mem[raddr];
  16. always @(posedge wclk) //当使能信号有效且还未写满的时候将数据写入实体中,与wclk时钟信号同步
  17. if (wclken && !wfull)
  18. mem[waddr] <= wdata;
  19. endmodule

二、读控制端口

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

Rinc:读请求信号

Rclk:读时钟信号

Rrst_n:读复位信号

Raddr:二进制形式的写地址

Rempty:读空信号

Rptr:格雷码形式的读指针

Rq2_wptr:同步后的写指针 

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

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

Verilog代码如下:

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

代码理解:

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

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

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

三、写控制端口 

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

Winc:写请求信号

Wclk:写时钟信号

Wrst_n:写复位信号

Waddr:二进制形式的写地址

Wfull:写空信号

Wptr:格雷码形式的写指针

Wq2_rptr:同步后的读指针 

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

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

Verilog代码如下:

  1. module wptr_full
  2. #(
  3. parameter ADDRSIZE = 4
  4. )
  5. (
  6. output reg wfull,
  7. output [ADDRSIZE-1:0] waddr,//二进制形式的写地址
  8. output reg [ADDRSIZE :0] wptr, //格雷码形式的写指针
  9. input [ADDRSIZE :0] wq2_rptr,//同步后的读指针
  10. input winc, wclk, wrst_n
  11. );
  12. reg [ADDRSIZE:0] wbin;
  13. wire [ADDRSIZE:0] wgraynext, wbinnext;
  14. // GRAYSTYLE2 pointer
  15. always @(posedge wclk or negedge wrst_n)
  16. if (!wrst_n)
  17. {wbin, wptr} <= 0;
  18. else
  19. {wbin, wptr} <= {wbinnext, wgraynext};
  20. // Memory write-address pointer (okay to use binary to address memory)
  21. assign waddr = wbin[ADDRSIZE-1:0];
  22. assign wbinnext = wbin + (winc & ~wfull);//写请求且没写满时,地址加一
  23. assign wgraynext = (wbinnext>>1) ^ wbinnext; //将二进制码转换为格雷码
  24. //-----------------------------------------------------------------
  25. assign wfull_val = (wgraynext=={~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]});
  26. //
  27. always @(posedge wclk or negedge wrst_n)
  28. if (!wrst_n)
  29. wfull <= 1'b0;
  30. else
  31. wfull <= wfull_val;
  32. endmodule

代码理解:

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

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

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

 四、读时钟同步到写时钟

顶层模块图如下所示

 

 Verilog代码如下:

  1. module sync_r2w
  2. #(
  3. parameter ADDRSIZE = 6
  4. )
  5. (
  6. output reg [ADDRSIZE:0] wq2_rptr, //将读指针同步到写指针
  7. input [ADDRSIZE:0] rptr, // 输入读指针
  8. input wclk, wrst_n
  9. );
  10. reg [ADDRSIZE:0] wq1_rptr;
  11. always @(posedge wclk or negedge wrst_n)
  12. if (!wrst_n) begin
  13. wq1_rptr <= 0; //清零复位信号
  14. wq2_rptr <= 0;
  15. end
  16. else begin
  17. wq1_rptr<= rptr;//两级寄存器
  18. wq2_rptr<=wq1_rptr;
  19. end
  20. endmodule

 

 五、读时钟同步到写时钟 

 顶层模块图如下所示

 

Verilog代码如下

  1. module sync_w2r
  2. #(parameter ADDRSIZE = 6)
  3. (
  4. output reg [ADDRSIZE:0] rq2_wptr, //将写指针同步到读指针
  5. input [ADDRSIZE:0] wptr, //输入写指针
  6. input rclk, rrst_n
  7. );
  8. reg [ADDRSIZE:0] rq1_wptr;
  9. always @(posedge rclk or negedge rrst_n)
  10. if (!rrst_n)begin
  11. rq1_wptr <= 0;
  12. rq2_wptr <= 0;
  13. end
  14. else begin
  15. rq1_wptr <= wptr;
  16. rq2_wptr <= rq1_wptr;
  17. end
  18. endmodule

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/1009938
推荐阅读
相关标签
  

闽ICP备14008679号