• Verilog实现同步FIFO和异步FIFO


    一、同步FIFO

    1、代码

      1 //**************************************************************************
      2 // *** 名称 : sFIFO.v
      3 // *** 作者 : xianyu_FPGA
      4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
      5 // *** 日期 : 2020年6月
      6 // *** 描述 : 同步FIFO,读写位宽相同,默认写先读后
      7 //**************************************************************************
      8 module sFIFO
      9 //========================< 参数 >==========================================
     10 #(
     11 parameter DATA_W            = 8                 ,   //数据位宽
     12 parameter ADDR_W            = 4                     //地址位宽
     13 )
     14 //========================< 端口 >==========================================
     15 (
     16 input                       clk                 ,   //时钟
     17 input                       rst_n               ,   //复位
     18 input                       wr_en               ,   //写使能
     19 input       [DATA_W-1:0]    wr_data             ,   //写数据
     20 input                       rd_en               ,   //读使能
     21 output  reg [DATA_W-1:0]    rd_data             ,   //读数据
     22 output                      full                ,   //写满
     23 output                      empty                   //读空
     24 );
     25 //========================< 信号 >==========================================
     26 reg     [ADDR_W-1:0]        wr_addr             ;   //写地址
     27 reg     [ADDR_W-1:0]        rd_addr             ;   //读地址
     28 reg     [DATA_W-1:0]        ram[2**ADDR_W-1:0]  ;   //ram,地址位宽4,共16个
     29 reg     [ADDR_W  :0]        cnt                 ;  //计数器
     30 //==========================================================================
     31 //==    读写地址
     32 //==========================================================================
     33 //-- 写地址
     34 //---------------------------------------------------
     35 always @(posedge clk or negedge rst_n) begin
     36     if(!rst_n) begin
     37         wr_addr <= 0;
     38     end
     39     else if(wr_en & (!full)) begin
     40         wr_addr <= wr_addr + 1;
     41     end
     42 end
     43 
     44 //-- 读地址
     45 //---------------------------------------------------
     46 always @(posedge clk or negedge rst_n) begin
     47      if (!rst_n) begin
     48           rd_addr <= 0;
     49      end
     50      else if(rd_en & (!empty)) begin
     51           rd_addr <= rd_addr + 1;
     52      end
     53 end
     54 //==========================================================================
     55 //==    读写数据
     56 //==========================================================================
     57 //-- 写数据
     58 //---------------------------------------------------
     59 integer i;
     60 always @(posedge clk or negedge rst_n) begin
     61     if(!rst_n) begin
     62         for(i=0; i<16; i=i+1)
     63             ram[i] <= 0;
     64     end
     65     else if(wr_en) begin
     66             ram[wr_addr] <= wr_data;
     67     end
     68 end
     69 
     70 //-- 读数据
     71 //---------------------------------------------------
     72 always @(posedge clk or negedge rst_n) begin
     73     if (!rst_n) begin
     74         rd_data<=0;
     75     end
     76     else if (rd_en) begin
     77         rd_data <= ram[rd_addr];  
     78     end
     79 end
     80 //==========================================================================
     81 //==    空满标志
     82 //==========================================================================
     83 //-- 辅助计数
     84 //---------------------------------------------------
     85 always @(posedge clk or negedge rst_n) begin
     86     if (!rst_n) begin
     87         cnt <= 0;
     88     end
     89     else if(wr_en && (!rd_en) && (cnt!=16)) begin
     90         cnt <= cnt + 1;
     91     end
     92     else if(rd_en && (!wr_en) && (cnt!=0)) begin
     93         cnt <= cnt - 1;
     94     end
     95 end
     96 
     97 //-- 写满、读空
     98 //---------------------------------------------------
     99 assign full  = (cnt==16);
    100 assign empty = (cnt==0 );
    101 
    102 
    103 endmodule

    2、仿真

     1 `timescale 1ns/1ns
     2 module sFIFO_tb;
     3 //========================< 参数 >==========================================
     4 parameter DATA_W            = 8                 ;   //数据位宽
     5 parameter ADDR_W            = 4                 ;   //地址位宽
     6 //========================< 信号 >==========================================
     7 reg                         clk                 ;
     8 reg                         rst_n               ;
     9 reg                         wr_en               ;
    10 reg    [DATA_W-1:0]         wr_data             ;
    11 reg                         rd_en               ;
    12 //==========================================================================
    13 //==    例化
    14 //==========================================================================
    15 sFIFO
    16 #(
    17     .DATA_W                 (DATA_W             ),
    18     .ADDR_W                 (ADDR_W             )
    19 )
    20 u_sFIFO
    21 (
    22     .clk                    (clk                ),
    23     .rst_n                  (rst_n              ),
    24     .wr_en                  (wr_en              ),
    25     .wr_data                (wr_data            ),
    26     .rd_en                  (rd_en              ),
    27     .rd_data                (                   ),
    28     .full                   (                   ),
    29     .empty                  (                   )
    30 );
    31 //==========================================================================
    32 //==    时钟
    33 //==========================================================================
    34 always #10 clk=~clk;
    35 //==========================================================================
    36 //==    设计
    37 //==========================================================================
    38 initial  begin
    39     clk     = 1;
    40     rst_n   = 0;
    41     wr_en   = 0;
    42     wr_data = 0;
    43     rd_en   = 0;
    44     
    45     #101;
    46     rst_n   = 1;
    47     
    48     #20;
    49     gen_data;
    50     
    51     @(posedge clk);
    52     rd_en=1;
    53     
    54     repeat(16)@(posedge clk);
    55     rd_en=0;
    56 end
    57 
    58 task gen_data;
    59     integer i;
    60     begin
    61         for(i=0; i<16; i=i+1) begin
    62             wr_en   = 1;
    63             wr_data = i;
    64             #20;
    65         end
    66         wr_en   = 0;
    67         wr_data = 0;
    68     end
    69 endtask
    70 
    71 
    72 endmodule

    二、异步FIFO

    1、分析

    (1)格雷码

      比较空满时,需要读写地址进行判断,二者属于跨时钟域,需要进行打拍的同步处理,未避免亚稳态,采用格雷码,因为格雷码相邻只有一位变化,这样同步多位时更不容易产生问题。

    格雷码公式:gray = (binary>>1) ^ binary;

    (2)读空判断

      默认是先写后读,读追上了写,之后就是读空了。因此写满标志为【读写地址相同】。

    (3)写满判断

      默认是先写后读,写在前面,超过了一轮地址后,又追上了读,之后就是写满了。因此读空标志为【读写地址相同】。

      显然,这样的思维会导致读空和写满的标志相同,无法确定【读写地址相同】时到底是读空还是写满,因此可以设置一个写指针 wr_addr_ptr 和 读指针 rd_addr_ptr,其位宽比读写地址多1位,整个指针的长度是地址的 2 倍。假设前半段为 A,后半段为 B。

    • 读在 A,最高位为0,剩余位为100;
    • 写在 B,最高位为1,剩余位为100;

       我们便可以判定,这时写越过了一轮,又到了读的位置,这便是真正的写满标志。提炼一下就是:“读写的最高位不同,其余位相同”时,处于写满状态。

       以上是当地址编码为普通二进制码时的分析,但是格雷码是不一样的,他的排列有些不同,前半段 A 和后半段 B 是镜像对称的,如下图所示:

      通过观察格雷码的特点,我们可以这样判断:“读写的最高2位不同,其余位相同”时,处于写满状态。

    2、代码

      1 //**************************************************************************
      2 // *** 名称 : asFIFO.v
      3 // *** 作者 : xianyu_FPGA
      4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
      5 // *** 日期 : 2020年6月
      6 // *** 描述 : 异步FIFO,读写位宽相同,默认写先读后
      7 //**************************************************************************
      8 module asFIFO
      9 //========================< 参数 >==========================================
     10 #(
     11 parameter DATA_W            = 8                 ,   //数据位宽
     12 parameter ADDR_W            = 4                     //地址位宽
     13 )
     14 //========================< 端口 >==========================================
     15 (
     16 input                       rst_n               ,   //复位
     17 //FIFO写 ----------------------------------------
     18 input                       wr_clk              ,   //写时钟
     19 input                       wr_en               ,   //写使能
     20 input       [DATA_W-1:0]    wr_data             ,   //写数据
     21 output                      wr_full             ,   //写满
     22 //FIFO读 ----------------------------------------
     23 input                       rd_clk              ,   //读时钟
     24 input                       rd_en               ,   //读使能
     25 output  reg [DATA_W-1:0]    rd_data             ,   //读数据
     26 output                      rd_empty                //读空
     27 );
     28 //========================< 信号 >==========================================
     29 reg     [ADDR_W  :0]        wr_addr_ptr         ;   //写指针,多1位
     30 reg     [ADDR_W  :0]        rd_addr_ptr         ;   //读指针,多1位
     31 wire    [ADDR_W  :0]        wr_addr_gray        ;   //写地址_格雷码
     32 reg     [ADDR_W  :0]        wr_addr_gray_r      ;   //写地址打拍
     33 reg     [ADDR_W  :0]        wr_addr_gray_rr     ;   //写地址打拍
     34 wire    [ADDR_W  :0]        rd_addr_gray        ;   //读地址_格雷码
     35 reg     [ADDR_W  :0]        rd_addr_gray_r      ;   //读地址打拍
     36 reg     [ADDR_W  :0]        rd_addr_gray_rr     ;   //读地址打拍
     37 //-----------------------------------------------
     38 wire    [ADDR_W-1:0]        wr_addr             ;   //写地址
     39 wire    [ADDR_W-1:0]        rd_addr             ;   //读地址
     40 reg     [DATA_W-1:0]        ram[2**ADDR_W-1:0]  ;   //ram,地址位宽4,共16个
     41 //==========================================================================
     42 //==    地址指针
     43 //==========================================================================
     44 always @(posedge wr_clk or negedge rst_n) begin
     45     if(!rst_n) begin
     46         wr_addr_ptr <= 0;
     47     end
     48     else if(wr_en & (!wr_full)) begin
     49         wr_addr_ptr <= wr_addr_ptr + 1;
     50     end
     51 end
     52 
     53 always @(posedge rd_clk or negedge rst_n) begin
     54     if(!rst_n) begin
     55         rd_addr_ptr <= 0;
     56     end
     57     else if(rd_en & (!rd_empty)) begin
     58         rd_addr_ptr <= rd_addr_ptr + 1;
     59     end
     60 end
     61 //==========================================================================
     62 //==    空满信号
     63 //==========================================================================
     64 //-- 格雷码转换
     65 //---------------------------------------------------
     66 assign wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr;
     67 assign rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr;
     68 
     69 //-- 跨时钟域,打两拍
     70 //---------------------------------------------------
     71 always @(posedge wr_clk) begin
     72     rd_addr_gray_r  <= rd_addr_gray;
     73     rd_addr_gray_rr <= rd_addr_gray_r;
     74 end
     75 
     76 always @(posedge rd_clk) begin
     77     wr_addr_gray_r  <= wr_addr_gray;
     78     wr_addr_gray_rr <= wr_addr_gray_r;
     79 end
     80 
     81 //-- 写满标志:高2位不同,其余位相同
     82 //---------------------------------------------------
     83 assign wr_full  = (wr_addr_gray == ({~rd_addr_gray_rr[ADDR_W-:2],rd_addr_gray_rr[ADDR_W-2:0]}));
     84 
     85 //-- 读空标志:读写地址相同
     86 //---------------------------------------------------
     87 assign rd_empty = (rd_addr_gray == wr_addr_gray_rr);
     88 //==========================================================================
     89 //==    ram读写
     90 //==========================================================================
     91 //-- 读写地址
     92 //---------------------------------------------------
     93 assign wr_addr = wr_addr_ptr[ADDR_W-1:0];
     94 assign rd_addr = rd_addr_ptr[ADDR_W-1:0];
     95 
     96 //-- 写数据
     97 //---------------------------------------------------
     98 integer i;
     99 always @(posedge wr_clk or negedge rst_n) begin
    100     if(!rst_n) begin
    101         for(i=0; i<2**ADDR_W; i=i+1)
    102             ram[i] <= 0;
    103     end
    104     else if(wr_en & (!wr_full)) begin
    105             ram[wr_addr] <= wr_data;
    106     end  
    107 end
    108 
    109 //-- 读数据
    110 //---------------------------------------------------
    111 always @(posedge rd_clk or negedge rst_n) begin
    112     if(!rst_n) begin
    113         rd_data <= 0;
    114     end
    115     else if(rd_en & (!rd_empty)) begin
    116         rd_data <= ram[rd_addr];
    117     end
    118 end
    119 
    120 
    121 endmodule

    3、仿真

     1 `timescale 1ns/1ns
     2 module asFIFO_tb;
     3 //========================< 参数 >==========================================
     4 parameter DATA_W            = 8                 ;   //数据位宽
     5 parameter ADDR_W            = 4                 ;   //地址位宽
     6 //========================< 信号 >==========================================
     7 reg                         wr_clk              ;
     8 reg                         rd_clk              ;
     9 reg                         rst_n               ;
    10 reg                         wr_en               ;
    11 reg      [DATA_W-1:0]       wr_data             ;
    12 reg                         rd_en               ;
    13 //==========================================================================
    14 //==    例化
    15 //==========================================================================
    16 asFIFO
    17 #(
    18     .DATA_W                 (DATA_W             ),
    19     .ADDR_W                 (ADDR_W             )
    20 )
    21 u_asFIFO
    22 (
    23     .wr_clk                 (wr_clk             ),
    24     .rd_clk                 (rd_clk             ),
    25     .rst_n                  (rst_n              ),
    26     .wr_en                  (wr_en              ),
    27     .wr_data                (wr_data            ),
    28     .rd_en                  (rd_en              ),
    29     .rd_data                (                   ),
    30     .wr_full                (                   ),
    31     .rd_empty               (                   )
    32 );
    33 //==========================================================================
    34 //==    时钟
    35 //==========================================================================
    36 always #10 wr_clk=~wr_clk;
    37 always #5  rd_clk=~rd_clk;
    38 //==========================================================================
    39 //==    数据
    40 //==========================================================================
    41 initial begin
    42     wr_clk  = 1;
    43     rd_clk  = 0;
    44     rst_n   = 0;
    45     wr_en   = 0;
    46     wr_data = 0;
    47     rd_en   = 0;
    48     
    49     #101;
    50     rst_n = 1;
    51     
    52     #20;
    53     gen_data;
    54     
    55     @(posedge rd_clk);
    56     rd_en = 1;
    57     
    58     repeat(16)@(posedge rd_clk);
    59     rd_en=0;
    60 end
    61 
    62 task gen_data;
    63     integer i;
    64     begin
    65         for(i=0; i<16; i=i+1) begin
    66             wr_en   = 1;
    67             wr_data = i;
    68             #20;
    69         end
    70         wr_en   = 0;
    71         wr_data = 0;
    72     end
    73 endtask
    74 
    75 
    76 endmodule

  • 相关阅读:
    任意进制间的转换
    判断线段相交 hdu 1086
    大数(高精度)加减乘除取模运算
    sqlserver2008透明书库加密
    数据库质疑
    sql2005 和sql2008 同时安装
    editrules
    sqlserver 表值函数
    sqlserver释放内存
    sql2008查看备份进度
  • 原文地址:https://www.cnblogs.com/xianyufpga/p/13513025.html
Copyright © 2020-2023  润新知