• 怎么用Verilog语言描述同步FIFO和异步FIFO


    感谢

    知乎龚大佬

    打杂大佬

    网上几个nice的博客(忘了是哪个了。。。。)

    前言

    虽然FIFO都有IP可以使用,但理解原理还是自己写一个来得透彻。

    什么是FIFO?

    Fist in first out。先入先出的数据缓存器,没有外部读写地址线,可同时读写。

    规则:永远不要写一个已经写满了的fifo。

              永远不要读一个读空了的fifo。

    FIFO种类?

    同步FIFO和异步FIFO。

    同步FIFO只有一个时钟,也就是说写端和读端的时钟是一毛一样的。

    异步FIFO读端和写端两个时钟则是不一样的。包括同频异相,异频异相。

    FIFO用途?

    1. 数据缓冲器。比如你写端burst一个数据,没有fifo缓冲的话就炸了。Fifo会把写端的突发数据吃到肚子里,读端可以慢慢的一个个读出来。
    2. 跨时钟域。异步fifo主要使用在不同时钟域的边缘,用来同步数据到另一个时钟域。

    ALTERA FIFO IP 的缺点是什么?

    虽然altera贴心的提供了FIFO的IP块,但是对于可移植性与自定义位宽深度更好的话,还是自己写的更佳。

    FIFO深度如何计算?(避免溢出)

    对于异步fifo,如果读时钟大于写时钟且每个周期读写,那么一定是会读空的,反之一定会被写满。一般来说,不会设计这么无聊的东西。

    假设写端有突发的数据,而读端是均匀的读出,怎么保证fifo不溢出呢?

    异步FIFO快转慢的问题:可能采样踩不到某些值。

    同步FIFO:

    当缓冲器使用,可以用ram资源搭。

    原理图:

    信号定义:

    clk:时钟信号

    rst_n:异步复位信号

    wr:写请求

    rd:读请求

    data:数据输入

    q:   数据输出

    full:满信号,表示fifo吃饱了

    empty:空信号,表示fifo肚子已经空掉了

    usedw:表示fifo中已有的数据个数

    仿真:

    没有usedw款:

    有usedw款:

    资源使用量:

     

    如何设计一个异步FIFO?

    一般用作跨时钟域,可用ram搭。

    判断读空与写满,读写指针要跨时钟域,所以采用格雷码减少亚稳态。

    什么是格雷码?如何与二进制码转换?

    格雷码的基本特点是两个相邻的码只有一位二进制数不同。

    二进制转格雷码:

    简单来说就是把二进制码右移一位再与二进制异或。

    assign wr_poi_gray = wr_poi ^ (wr_poi>>1); //produce wr pointer gray code;

    格雷码转二进制:

    格雷码转二进制是从左边第二位起,将每位与左边一位二进制码的值异或,作为该位二进制码的值。

    比如四位的码:

    bin[3] = gray[3];

    bin[2] = gray[2]^bin[3];

    bin[1] = gray[1]^bin[2];

    bin[0] = gray[0]^bin[1];

    原理图:

    信号定义:

    wrclk:写时钟信号

    rdclk: 读时钟信号

    rst_n:异步复位信号

    wr:写请求

    rd:读请求

    data:数据输入

    q: 数据输出

    full:满信号,表示fifo吃饱了

    empty:空信号,表示fifo肚子已经空掉了

    仿真:写时钟是读时钟的两倍。

    写读测试:由于两级同步器的存在,判定空满滞后两个周期。

     

    同时读写测试:

    资源使用量:

    注意:

    同步FIFO的地址扩展一位作为判断空满的状态位,读写指针低位都相同的时候,最高位相同则表示读空,不同则表示写满。

    1 assign full = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 1);  //highest bit is not same but rests bit is same;
    2 assign empty = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 0); //every bit is same;

    异步FIFO的地址位也扩展一位作为状态位,通过格雷码跨过时钟域判断空满。

    在写时钟域判断full:如果本时钟域的写地址格雷码和同步过来的读地址格雷码最高位和次高位均不同,其余位相同,则表示写满了。

    在读时钟域判断empty:如果本时钟域的读地址格雷码和同步过来的写地址格雷码最高位和次高位都一样,其余位也一样,则表示读空。

    1 assign full =  (wr_poi_gray == {~rd_poi_gray2[clogb2(DEPTH):clogb2(DEPTH)-1],rd_poi_gray2[clogb2(DEPTH)-2:0]});
    2 assign empty = (wr_poi_gray2 == rd_poi_gray);

     贴一下同步FIFO的代码:

    已定制ramstyle,如果你的不同请删掉或者更改。深度必须为2^n,否则更改函数clogb2中的depth为depth>0.
    仅供学习交流,请勿用于商业用途,版权所有。异步代码就不贴了,想研究请联系我。

     1 //************************************************
     2 //  Filename      : fifo_syn.v                             
     3 //  Author        : kingstacker                  
     4 //  Company       : School                       
     5 //  Email         : kingstacker_work@163.com     
     6 //  Device        : Altera cyclone4 ep4ce6f17c8  
     7 //  Description   : synchronize fifo ;8*8 ;depth shuold be 2^n,otherwise change the clogb2 funtion;                           
     8 //************************************************
     9 module  fifo_syn #(parameter WIDTH = 8,DEPTH = 8)( 
    10     //input;
    11     input    wire    clk,                //only one clock;
    12     input    wire    rst_n,
    13     input    wire    wr,                 //wr request;
    14     input    wire    rd,                 //rd request;
    15     input    wire    [WIDTH-1:0]  data,  //data in;
    16     //output;
    17     output   wire    [WIDTH-1:0]  q,     //data out;       
    18     output   wire    full,               //fifo is full;
    19     output   wire    empty,              //fifo is empty;
    20     output   wire    [clogb2(DEPTH)-1:0] usedw //data number in fifo;
    21 );
    22 function integer clogb2 (input integer depth);
    23 begin
    24     for (clogb2=0; depth>1; clogb2=clogb2+1) //depth>1 when you choose depth 2^n;otherwise change it to depth>0;for example depth is 7;
    25         depth = depth >>1;                           
    26 end
    27 endfunction               
    28 (* ramstyle = "M9K" *) reg [WIDTH-1:0]      memory [0:DEPTH-1];
    29 reg [clogb2(DEPTH):0] wr_poi;    //wr pointer;
    30 reg [clogb2(DEPTH):0] rd_poi;    //rd pointer;
    31 reg [WIDTH-1:0] q_r;            //reg q;
    32 reg [clogb2(DEPTH)-1:0] usedw_r;   //reg usedw_r;
    33 wire wr_flag;                   //real wr request;
    34 wire rd_flag;                   //real rd request;
    35 assign q = q_r;
    36 assign usedw = usedw_r;
    37 assign full = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 1);  //highest bit is not same but rests bit is same;
    38 assign empty = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 0); //every bit is same;
    39 assign wr_flag = ((wr == 1'b1) && (full == 1'b0));          //wr enable;
    40 assign rd_flag = ((rd == 1'b1) && (empty == 1'b0));         //rd enable;
    41 always @(posedge clk or negedge rst_n) begin
    42     if (~rst_n) begin
    43         wr_poi <= 0;
    44     end //if
    45     else begin
    46         wr_poi <= wr_flag ? wr_poi + 1'b1 : wr_poi;
    47         memory[wr_poi[clogb2(DEPTH)-1:0]] <= wr_flag ? data : memory[wr_poi[clogb2(DEPTH)-1:0]];
    48     end //else
    49 end //always
    50 always @(posedge clk or negedge rst_n) begin
    51     if (~rst_n) begin
    52         rd_poi <= 0;
    53         q_r <= 0;
    54     end //if
    55     else begin
    56         rd_poi <= rd_flag ? rd_poi + 1'b1 : rd_poi;
    57         q_r <= rd_flag ? memory[rd_poi[clogb2(DEPTH)-1:0]] : q_r;
    58     end //else
    59 end //always
    60 always @(posedge clk or negedge rst_n) begin
    61     if (~rst_n) begin
    62         usedw_r <= 0;
    63     end //if
    64     else begin
    65         case ({wr_flag,rd_flag})
    66                2'b00: begin
    67                    usedw_r <= usedw_r;
    68                end 
    69                2'b10: begin
    70                    if (usedw_r == DEPTH-1) begin  // full;
    71                        usedw_r <= usedw_r;
    72                    end
    73                    else begin
    74                        usedw_r <= usedw_r + 1'b1;
    75                    end
    76                end
    77                2'b01: begin
    78                    if (usedw_r == 0) begin     //empty;
    79                        usedw_r <= usedw_r;
    80                    end
    81                    else begin
    82                        usedw_r <= usedw_r - 1'b1;
    83                    end
    84                end
    85                2'b11: begin
    86                    usedw_r <= usedw_r;
    87                end                                             
    88                default:  usedw_r <= 0;
    89            endcase //case   
    90     end //else
    91 end //always
    92 
    93 endmodule

     以上。

  • 相关阅读:
    python 笔记8
    python笔记6
    python笔记5
    python笔记4
    python笔记3
    python课程2
    cobbler 坑爹指南
    hadoop filesystem 删除文件 复制文件 重命名文件
    hadoop 文件 复制 移动 FileUtil.copy
    soinn
  • 原文地址:https://www.cnblogs.com/kingstacker/p/7666358.html
Copyright © 2020-2023  润新知