zoukankan      html  css  js  c++  java
  • Verilog-异步FIFO

    参考博文:https://blog.csdn.net/alangaixiaoxiao/article/details/81432144

    1、概述

      异步FIFO设计的关键是产生“写满”和“读空”信号,这两个信号的产生需要用到读指针rptr和写指针wptr构建组合逻辑进行判断,然而读指针属于读时钟域rclk,写指针属于写时钟域wclk,因此必须进行同步化处理以消除亚稳态。异步FIFO的设计一般采用2种手段进行同步化处理:

      (1)将读指针rptr打2拍到写时钟域,将写指针wptr打两拍到读时钟域,消除亚稳态;

      (2)由于读写指针都是多比特信号,直接对它们进行同步化容易产生亚稳态,且用组合逻辑进行判断容易产生毛刺,因此改用格雷码进行异步时钟域的传输。

    2、代码

      设计思路有以下几点:

      (1)在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~0111、1000~1111,MSB作为折回标志位,而低3位作为地址指针。如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,w_addr = 1000,为满。如果两个指针完全相同,为空。

      (2)使用gray码解决了一个问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。空的判断标准仍是完全相同,满的判断标准需要满足:

        ■ 格雷码指针的最高位不同,因为wptr必须比rptr多折回一次。

        ■ wptrrptr的次高位不相等,如下表的7(格雷码为0100)和15(格雷码为1000),转化为二进制对应的是01111111MSB不同说明多折回一次,111相同代表同一位置。

        

        ■ 其余位完全相同

      (3)对双口RAM的寻址采用二进制码,异步时钟域的交互采用格雷码。

        

    `timescale 1ns / 1ps
    
    module asynchronous_fifo
    #(parameter ASIZE=3,DSIZE=8)
    (
        //
        input rclk,
        input rrst_n,
        input rinc,
        output [DSIZE-1:0] rdata,
        output rempty,
        //
        input wclk,
        input wrst_n,
        input winc,
        input [DSIZE-1:0] wdata,
        output wfull
        );
    
    // 读写地址,由wbin、rbin的低位生成
    wire [ASIZE-1:0] waddr,raddr;
    reg [ASIZE:0] wbin,rbin;
    // 读写指针(比地址位宽大1)
    reg [ASIZE:0] wgray,rgray;
    wire [ASIZE:0] wbinnext,wgraynext,rbinnext,rgraynext;
    // 经打拍后的读写指针
    reg [ASIZE:0] rq1_wgray,rq2_wgray,wq1_rgray,wq2_rgray;
    
    reg rempty_val,wfull_val;
    
    // 双端口存储器
    reg [DSIZE-1:0] mem [0:(1<<ASIZE)-1];
    reg [DSIZE-1:0] rdata_reg;
    //assign rdata = mem[raddr];
    always @(posedge wclk) begin
        if(!wfull && winc) mem[waddr] <= wdata;
    end
    always @(posedge rclk or negedge rrst_n) begin
        if(!rrst_n) rdata_reg <= 0;
        else if(!rempty && rinc) rdata_reg <= mem[raddr];
        else rdata_reg <= {DSIZE{1'bz}};  // 设成z态方便看波形
    end
    assign rdata = rdata_reg;
    
    // 同步化处理
    always @(posedge rclk or negedge rrst_n)begin
        if(!rrst_n) {rq1_wgray,rq2_wgray} <= 0;
        else {rq1_wgray,rq2_wgray} <= {wgray,rq1_wgray};
    end
    
    always @(posedge wclk or negedge wrst_n) begin
        if(!wrst_n) {wq1_rgray,wq2_rgray} <= 0;
        else {wq1_rgray,wq2_rgray} <= {rgray,wq1_rgray};
    end
    
    // 写地址和写满信号产生
    always @(posedge wclk or negedge wrst_n) begin
        if(!wrst_n) {wbin,wgray} <= 0;
        else {wbin,wgray} <= {wbinnext,wgraynext};
    end
    assign wbinnext = (~wfull & winc)? (wbin+1'b1) : wbin;
    assign waddr = wbin[ASIZE-1:0];
    assign wgraynext = (wbinnext>>1)^wbinnext;
    
    always @(posedge wclk or negedge wrst_n) begin
        if(!wrst_n) wfull_val <= 0; // 用同步到写时钟域的读指针与写指针进行比较,高两位不同其他位相同则满
        else wfull_val <= (wgraynext=={~wq2_rgray[ASIZE:ASIZE-1],wq2_rgray[ASIZE-2:0]});
    end
    assign wfull = wfull_val;
    
    // 读地址和读空信号
    always @(posedge rclk or negedge rrst_n) begin
        if(!rrst_n) {rbin,rgray} <= 0;
        else {rbin,rgray} <= {rbinnext,rgraynext};
    end
    assign rbinnext = (~rempty & rinc)? (rbin+1'b1) : rbin;
    assign raddr = rbin[ASIZE-1:0];
    assign rgraynext = (rbinnext >> 1) ^ rbinnext;
    
    always @(posedge rclk or negedge rrst_n) begin
        if(!rrst_n) rempty_val <= 0; // 用同步到读时钟域的写指针与读指针进行比较,各位都相同则空
        else rempty_val <= (rgraynext==rq2_wgray);
    end
    assign rempty = rempty_val;
    
    endmodule

    3、验证

      (1)testbench

      写8个数据(0-7)——触发wfull信号——读8个数据(0-7)——触发rempty信号——写8个数据(8-15)——触发wfull信号——读4个数据(8-11)——写4个数据(16-19)——触发wfull信号——读8个数据(12-19)——触发rempty信号

    `timescale 1ns / 1ps
    
    module asynchronous_fifo_tb;
    
        // Inputs
        reg rclk;
        reg rrst_n;
        reg rinc;
        reg wclk;
        reg wrst_n;
        reg winc;
        reg [7:0] wdata;
    
        // Outputs
        wire [7:0] rdata;
        wire rempty;
        wire wfull;
        
        integer i;
    
        // Instantiate the Unit Under Test (UUT)
        asynchronous_fifo uut (
            .rclk(rclk), 
            .rrst_n(rrst_n), 
            .rinc(rinc), 
            .rdata(rdata), 
            .rempty(rempty), 
            .wclk(wclk), 
            .wrst_n(wrst_n), 
            .winc(winc), 
            .wdata(wdata), 
            .wfull(wfull)
        );
    
        initial begin
            // Initialize Inputs
            rclk = 0;
            rrst_n = 0;
            rinc = 0;
            wclk = 0;
            wrst_n = 0;
            winc = 0;
            wdata = 0;
    
            // Wait 100 ns for global reset to finish
            #70;
            rrst_n = 1;
            wrst_n = 1;
            @(negedge rclk);
            
            // Write
            for(i=0;i<8;i=i+1) begin
                wdata = i;
                winc = 1;
                @(negedge wclk);
            end
            winc = 0;   //写满
            wdata = 0;  
            
            //Read
            @(negedge rclk);
            for(i=0;i<8;i=i+1) begin
                rinc = 1;
                @(negedge rclk);
            end
            rinc = 0;  //读空
            
            // Write 
            @(negedge wclk);
            for(i=0;i<8;i=i+1) begin
                wdata = i+8;
                winc = 1;
                @(negedge wclk);
            end
            winc = 0;
            wdata = 0;    // 写满
    
            //Read
            @(negedge rclk);
            for(i=0;i<4;i=i+1) begin
                rinc = 1;
                @(negedge rclk);
            end
            rinc = 0;  // 读一半    
    
            // Write 
            @(negedge wclk);
            for(i=0;i<4;i=i+1) begin
                wdata = i+16;
                winc = 1;
                @(negedge wclk);
            end
            winc = 0;
            wdata = 0;    // 写一半        
    
            //Read
            @(negedge rclk);
            for(i=0;i<8;i=i+1) begin
                rinc = 1;
                @(negedge rclk);
            end
            rinc = 0;  //读空        
            
            
            // Add stimulus here
    
        end
        
        always #20 wclk = ~wclk;
        always #30 rclk = ~rclk;
          
    endmodule

      (2)读空信号(rempty)存在几个周期的置0延迟,即在空状态写,写入数据后rempty不会立即置0,而是会经过几个周期以后才会置0,这跟跨异步时钟域的打拍有关,因为写指针需要2个周期才能同步到读时钟域与读指针进行比较。rempty在读状态下的置1不存在延迟,因为当前同步到读时钟域的写指针已经是最新的了,读指针的变化立即就可以触发置1,置0延迟置1不延迟对于实际使用是没有太大影响的,反之则有。同样写满信号(wfull)的置0也存在延迟。总结:由于指针跨时钟域打拍造成的时延,rempty的置0(最新的wptr没有及时同步到读时钟域)和wfull的置0(最新的rptr没有及时同步到写时钟域)会存在时延,但是不影响功能。

      (3)异步FIFO设计时,即使读写地址采用格雷码,仍然会出现快时钟域的地址信号在慢时钟域漏采,但不会导致空满判断错误造成功能问题 。原因与(2)类似,(2)是因为打拍时延问题,而漏采导致的结果类似,也是没有及时捕捉到最新的ptr,导致写满置0和读空置0存在延时。


  • 相关阅读:
    Markdown 图片与图床使用
    gitignore
    设置或更改Mac文件的默认打开程序
    Hive时间处理
    csv大文件处理方案-数据量超表格最大容纳行数解决方案
    js中的闭包之我理解
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(73)-微信公众平台开发-消息管理
    关于23种设计模式的有趣见解
    一步一步写算法(之 算法总结)
    ajax跨域通信-博客园老牛大讲堂
  • 原文地址:https://www.cnblogs.com/wt-seu/p/12242867.html
Copyright © 2011-2022 走看看