• 同步FIFO的verilog实现(1)——计数法


    一、FIFO概述

    1、FIFO的定义

            FIFO是英文First-In-First-Out的缩写,是一种先入先出的数据缓冲器,与一般的存储器的区别在于没有地址线, 使用起来简单,缺点是只能顺序读写数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

    2、FIFO的作用

    (1)跨时钟域的数据传输

    (2)对不同宽度的数据进行位宽转换

    (3)数据缓存

    3、FIFO的分类

            根据FIFO工作的时钟域的不同,可以将FIFO分为以下两类

            (1)同步FIFO:读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。常用于对不同宽度的数据进行位宽转换

            (2)异步FIFO:读写时钟不一致,读写操作是互相独立的。常用于跨时钟域的数据传输

    4、FIFO的一些重要参数

    (1)FIFO宽度:指的是FIFO读写的数据位,就像MCU有8位和16位,ARM 32位等等,FIFO的宽度在单片成品IC中是固定的,也有可选择的,如果用FPGA自己实现一个FIFO,其数据位,也就是宽度是可以自己定义的。

    (2)FIFO深度:指的是FIFO可以存储多少个N位的数据(假设FIFO宽度为N)。

    (3)满标志(full):FIFO已满或将要满时由FIFO送出的状态信号,以阻止FIFO的写操作,避免数据溢出。

    (4)空标志(empty):FIFO已空或将要空时由FIFO送出的状态信号,以阻止FIFO的读操作,避免数据读空。

    (5)读时钟:读操作的时钟,在每个时钟沿来临时读数据。

    (6)写时钟:写操作的时钟,在每个时钟沿来临时写数据。

    (7)读指针:指向下一个要读出的地址,读完后自动加1。

    (8)写指针:指向下一个要写入的地址,写完后自动加1。

    读写指针其实就是读写的地址,只不过这个地址不能任意选择,而是连续的。

    二、同步FIFO

            FIFO设计的关键在于读写指针的设计和生成可靠的空满信号

    1、读写指针

    • 读指针:总是指向下一个将要读取的单元,复位时指向第一个单元(编号为0)。
    • 写指针:总是指向当前要被读出的数据,复位时指向第一个单元(编号为0)。

    2、空/满信号

            FIFO设计中最重要的是空(Empty)、满(Full)信号的判断。

            当第一次读写指针相等时,表明FIFO为空,这种情况发生在复位操作时或者当读指针读出FIFO中最后一个字后,追赶上写指针时,此时读空信号有效:

            当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈折回来(wrapped around)又追上了读指针:

            两种方式都是以读写指针相等作为判断标志,所以我们需要寻找其它的方法进行判断。

    三、计数法实现同步FIFO

            在同步FIFO中,我们可以很容易的使用计数来判断FIFO中还剩下多少可读的数据,从而可以判断空、满

    1、verilog实现

    1. //------------------------<计数法设计同步FIFO>----------------------------
    2. module sync_fifo#(
    3. //-----------------------------<参数定义>---------------------------------
    4. parameter FIFO_WIDTH = 16, //FIFO宽度
    5. parameter FIFO_DEPTH = 16 //FIFO深度
    6. )(
    7. //-----------------------------<接口定义>---------------------------------
    8. input clk, //时钟信号
    9. input rst, //复位信号
    10. input [FIFO_WIDTH-1:0] din, //FIFO输入数据(写数据)
    11. input rd_en, //读使能信号
    12. input wr_en, //写使能信号
    13. output reg [FIFO_WIDTH-1:0] dout, //FIFO输出数据(读数据)
    14. output empty, //FIFO空标志
    15. output full, //FIFO满标志
    16. output reg [$clog2(FIFO_DEPTH):0] fifo_cnt //$clog2是以2为底取对数
    17. );
    18. //-----------------------------<reg定义>---------------------------------
    19. reg [FIFO_WIDTH-1:0] fifo_buffer[FIFO_DEPTH-1:0]; //用二维数组实现RAM
    20. reg [$clog2(FIFO_DEPTH)-1:0] wr_addr; //写地址(写指针)
    21. reg [$clog2(FIFO_DEPTH)-1:0] rd_addr; //读地址(读指针)
    22. //-----------------------------<读操作>-----------------------------------
    23. always@(posedge clk or posedge rst)begin
    24. if(rst)
    25. rd_addr <= 0;
    26. else if(rd_en && !empty)begin //读使能有效且FIFO非空
    27. rd_addr <= rd_addr + 1'd1; //读指针递增
    28. dout <= fifo_buffer[rd_addr]; //fifo读出数据
    29. end
    30. else begin
    31. rd_addr <= rd_addr;
    32. dout <= dout;
    33. end
    34. end
    35. //-----------------------------<写操作>-----------------------------------
    36. always@(posedge clk or posedge rst)begin
    37. if(rst)
    38. wr_addr <= 0;
    39. else if(wr_en && !full)begin //写使能有效且FIFO非满
    40. wr_addr <= wr_addr + 1'd1; //读指针递增
    41. fifo_buffer[wr_addr] <= din; //数据写入fifo
    42. end
    43. else begin
    44. wr_addr <= wr_addr;
    45. end
    46. end
    47. //-----------------------------<fifo_cnt>-----------------------------------
    48. always@(posedge clk or posedge rst)begin
    49. if(rst)
    50. fifo_cnt <= 0;
    51. else if(wr_en && !full && !rd_en) //只写不读且FIFO没有满,fifo_cnt递增
    52. fifo_cnt <= fifo_cnt + 1'd1;
    53. else if(rd_en && !empty && !wr_en) //只读不写且FIFO没有空,fifo_cnt递减
    54. fifo_cnt <= fifo_cnt - 1'd1;
    55. else //同时读写或者不读不写,fifo_cnt不变
    56. fifo_cnt <= fifo_cnt;
    57. end
    58. //-----------------------------</满信号>-----------------------------------
    59. assign full = (fifo_cnt == FIFO_DEPTH) ? 1'b1 : 1'b0; //满信号
    60. assign empty = (fifo_cnt == 0) ? 1'b1 : 1'b0; //空信号
    61. endmodule

    2、功能测试

    1. `timescale 1ns/1ns
    2. //-----------------------------<计数法同步FIFO测试>---------------------------------
    3. module tb_sync_fifo();
    4. parameter WIDTH = 8;
    5. parameter DEPTH = 8;
    6. reg clk ;
    7. reg rst ;
    8. reg [WIDTH-1:0] din ;
    9. reg wr_en ;
    10. reg rd_en ;
    11. wire [WIDTH-1:0] dout ;
    12. wire full ;
    13. wire empty ;
    14. wire [$clog2(DEPTH):0] fifo_cnt;
    15. //-----------------------------<测试模块例化>---------------------------------
    16. sync_fifo #(
    17. .FIFO_WIDTH (WIDTH), //FIFO宽度
    18. .FIFO_DEPTH (DEPTH) //FIFO深度
    19. )
    20. sync_fifo_u1(
    21. .clk (clk ),
    22. .rst (rst ),
    23. .din (din ),
    24. .rd_en (rd_en ),
    25. .wr_en (wr_en ),
    26. .dout (dout ),
    27. .empty (empty ),
    28. .full (full ),
    29. .fifo_cnt (fifo_cnt )
    30. );
    31. //-----------------------------<模块测试>---------------------------------
    32. initial begin
    33. clk <= 1'b0; //初始时钟为0
    34. rst <= 1'b0; //初始复位
    35. din <= 'd0;
    36. wr_en <= 1'b0;
    37. rd_en <= 1'b0;
    38. #10
    39. rst <= 1'b1;
    40. #10
    41. rst <= 1'b0;
    42. repeat(10)
    43. #10 begin
    44. wr_en <= 1'b1;
    45. rd_en <= 1'b0;
    46. din <= $random; //生成8位的随机数
    47. end
    48. repeat(10)
    49. #10 begin
    50. wr_en <= 1'b0;
    51. rd_en <= 1'b1;
    52. end
    53. $finish;
    54. end
    55. //------------------------------<设置时钟>----------------------------------------
    56. always #5 clk = ~clk;
    57. endmodule

    3、测试结果

  • 相关阅读:
    STC 51单片机42——汇编 定时器 舵机
    美国洛杉矶站群服务器如何提高网站排名?
    GO 抽象工厂模式设计
    分析 diff 上下游调用的工具/想法
    阿里云突然玩起了性价比,网友:嘎嘎香!
    Vue监视数据的原理
    如何开发一个扩展性高、维护性好的软件系统?(一个程序员最基本的修养)
    【C++】简述STL——string类的使用
    元宇宙,小荷才露尖尖角
    Windows 安装 OBS 采集Ubuntu设备图像配置指南
  • 原文地址:https://blog.csdn.net/apple_53311083/article/details/132381065