首页 > 其他分享 >【《硬件架构的艺术》读书笔记】03 处理多个时钟(3)

【《硬件架构的艺术》读书笔记】03 处理多个时钟(3)

时间:2022-12-06 23:00:32浏览次数:54  
标签:03 格雷 读书笔记 FIFO rd wr ptr 3.8 时钟

3.8 异步FIFO(双时钟FIFO)

 

 如上图,X通过xclk将数据写入FIFO,Y通过yclk将数据读出。注意这里写满标志信号在写时钟域,空信号在读时钟域。

对比握手信号,异步FIFO用于对性能要求较高的设计中,尤其是时钟延迟比系统资源更重要的环境中。

异步FIFO主要需要注意信号亚稳态的问题。

3.8.1 避免用二进制计数器实现指针

如果使用二进制计数,一次可能变换多位,这时就需要将多位数据同步到另一个时钟域,很容易造成错误。

3.8.2 使用格雷码取代二进制计数

格雷码优势是在一个数变成另一个数时,只有一位出现变化。所以其在转换中最多只会一位错误,读取时要么读到旧值,要么读到新值,但是不会读到其他值。

3.8.2.1 同步指针的影响

 

 

 

 

 

 需要注意的是在t6、t7的时候,由于同步电路需要两个时钟周期,所以fifo_full会多拉高两个周期,这样阻止了两个周期的数据写入,但是对数据的准确性是无害的。

同样,空也会多两个周期阻止FIFO读访问。

 

 

 空判断是将写指针同步到读时钟域,在读空后写入FIFO,需要两个周期读时钟域才能得到写指针增加的信息,所以空信号也会多延两个周期。同样这里组织了对FIFO的读操作,也是无害的。

3.8.3 用格雷码实现FIFO指针

 

 

 1、将格雷码转换成二进制码。

2、根据条件递增二进制值。

3、二进制转格雷码。

4、保存到寄存器。

3.8.3.1 格雷码到二进制转换器

 转换公式:

 

所以可以将格雷码右移再按位异或得到二进制码

3.8.3.2 二进制-格雷码转换器

 

 可以看出可以直接将二进制码右移后于自身异或操作,计算出格雷码。

3.8.3.3 格雷码计数器的实现

将四个步骤结合起来,就可以实现一个格雷码计数器。

 

 3.8.4 FIFO满和FIFO空的产生

为了判断空满,还需要额外一位对两种情况进行区分。

最高位不同,其他位相同,FIFO满。

所有位相同,FIFO为空。

由于存在XOR链路,最大操作频率取决于格雷码计数器速度。

 

 图里感觉又有点错误,满判断时,应该是最高位不同。

如图,一共用了四个转换器,如果直接使用格雷码实现空满操作就可以省掉这些转换器,但是这会增加复杂度并需要额外的逻辑。

3.8.4.1 产生空满的另一种方法

该方法创建两个格雷码计数器,一个n位,另一个n-1位。通过n位格雷码的最高两位异或,来产生一个n-1位格雷码的MSB。其余n-2位与n位格雷码的n-2位保持一致。

 

 使用这两个格雷码计数器,就可以进行FIFO空满判断。

3.8.5 双时钟FIFO设计

 

 3.8.5.1 FIFO空条件的产生

这里直接比较读写指针格雷码,相比先将格雷码转化成二进制码,直接对比格雷码可以节省4个转换器。

同步后的写指针wr_ptr_sync和rd_gtemp(下一个将要寄存进入rd_ptr的格雷码)。(这里没太搞懂为什么要用下一个rd_ptr的格雷码,是为了提前一个周期输出full和empty信号吗?)(调代码的时候发现确实应该使用下一个rd_ptr的格雷码,不然产生空满信号需要一个周期,空满信号被采样到用于控制FIFO读写又需要一个周期,会造成FIFO满了还向FIFO中写入的现象。)

3.8.5.2 FIFO满条件的产生

 

 如图,rd_ptr=7,wr_ptr=8时,会出现FIFO满的错误声明。使用二元n位格雷码计数器可以轻松避免该情况。

以下三个条件为真时,满标志拉高。

1)同步后的读指针rd_ptr_sync的MSB应该与下一个写指针的格雷码wr_gtemp的MSB应该不同。

2)写时钟域两位MSB异或应与读指针一样。

3)剩下的LSB都应该一样。

(其实总结一下就是高两位要正好相反,剩下的低位应该完全相等)

 

异步FIFO代码

module Dual_Clock_FIFO#(
    parameter WIDTH=16,
    parameter DEPTH=8
)
(
    input wclk,
    input rclk,
    input reset_n,
    input [WIDTH-1:0]wr_data,
    input wr_en,
    input rd_en,
    output reg[WIDTH-1:0]rd_data,
    output  fifo_full,
    output  fifo_empty
);
    localparam  RAM_SIZE=(1<<DEPTH);
    reg [WIDTH-1:0]DPRAM[RAM_SIZE-1:0];
    wire [DEPTH:0]wr_ptr,rd_ptr;
    reg [DEPTH:0]wr_ptr_bin,rd_ptr_bin;
    reg [DEPTH:0]wr_ptr_r,rd_ptr_r;
    reg [DEPTH:0]wr_ptr_sync,rd_ptr_sync;


    assign  wr_ptr = (wr_ptr_bin >> 1) ^ wr_ptr_bin;
    assign  rd_ptr = (rd_ptr_bin >> 1) ^ rd_ptr_bin;      

    always @(posedge rclk or negedge reset_n) begin
        if(~reset_n)
            rd_data <= 'd0;
        else begin
            if(rd_en && ~fifo_empty)
                rd_data <= DPRAM[rd_ptr_bin];
        end
    end

    always @(posedge wclk) begin
        if(wr_en && ~fifo_full)
            DPRAM[wr_ptr_bin] <= wr_data;
    end

    always @(posedge rclk or negedge reset_n) //two stages synchronizer
    begin: wr_ptr_sync_gen
        if(~reset_n)begin
            wr_ptr_r <= 'b0;
            wr_ptr_sync <= 'b0;
        end
        else begin
            wr_ptr_r <= wr_ptr;
            wr_ptr_sync <= wr_ptr_r;
        end
    end

//    always @ (posedge rclk or negedge reset_n)
//    begin: fifo_empty_gen
//        if (~reset_n)
//            fifo_empty <= 1'b1;
//        else
//            fifo_empty <= (rd_ptr == wr_ptr_sync);
//    end
    assign fifo_empty = (rd_ptr == wr_ptr_sync);

    always @(posedge wclk or negedge reset_n) //two stages synchronizer
    begin: rd_ptr_sync_gen
        if(~reset_n)begin
            rd_ptr_r <= 'b0;
            rd_ptr_sync <= 'b0;
        end
        else begin
            rd_ptr_r <= rd_ptr;
            rd_ptr_sync <= rd_ptr_r;
        end
    end

    wire rd_2nd_msb = rd_ptr_sync [DEPTH] ^ rd_ptr_sync [DEPTH - 1];
    wire wr_2nd_msb = wr_ptr [DEPTH] ^ wr_ptr [DEPTH-1];

//    always @ (posedge wclk or negedge reset_n)
//    begin: fifo_full_gen
//        if (~reset_n)
//            fifo_full <= 1'b0;
//        else
//            fifo_full <= ((wr_ptr [DEPTH] != rd_ptr_sync[DEPTH]) &&
//            (rd_2nd_msb == wr_2nd_msb) &&
//            (wr_ptr[DEPTH -2:0] == rd_ptr_sync[DEPTH-2:0]));
//    end
    assign  fifo_full = ((wr_ptr [DEPTH] != rd_ptr_sync[DEPTH]) &&
            (rd_2nd_msb == wr_2nd_msb) &&
            (wr_ptr[DEPTH -2:0] == rd_ptr_sync[DEPTH-2:0]));

    always @(posedge rclk or negedge reset_n)
    begin:rd_ptr_gen
        if(~reset_n)
            rd_ptr_bin <= 'd0;
        else if(rd_en && ~fifo_empty)
            rd_ptr_bin <= rd_ptr_bin +1'b1;
    end

    always @(posedge wclk or negedge reset_n)
    begin:wr_ptr_gen
        if(~reset_n)
            wr_ptr_bin <= 'd0;
        else if(wr_en && ~fifo_full)
            wr_ptr_bin <= wr_ptr_bin +1'b1;
    end  

    
endmodule

testbench

`timescale 1ns / 1ps


module tb_dual_clock_fifo();
reg wclk,rclk,rst_n,wr_en,rd_en;
wire full,empty;
reg [15:0]wr_data;
wire [15:0]rd_data;

initial begin
    wclk = 0;
    rclk = 0;
    rst_n = 0;
    wr_en = 0;
    rd_en = 0;
    wr_data = 0;
    #100 rst_n = 1;
    while(~full) begin
        #20
        wr_en <= 1'b1;
        wr_data <= wr_data + 1'b1;
    end
    repeat(100)
    begin
        @(posedge wclk)begin
            wr_en = {$random} %2;
        end
        @(posedge rclk)begin
            rd_en = {$random} %2;
        end        
    end
    while(~empty) begin
        #24
        wr_en <= 1'b0;
        rd_en <= 1'b1;
    end
    $finish;
end


always #10 wclk = ~wclk;
always #12 rclk = ~rclk;

Dual_Clock_FIFO my_fifo(
    .wclk(wclk),
    .rclk(rclk),
    .reset_n(rst_n),
    .wr_data(wr_data),
    .wr_en(wr_en),
    .rd_en(rd_en),
    .rd_data(rd_data),
    .fifo_full(full),
    .fifo_empty(empty)
    );

endmodule

波形

 

标签:03,格雷,读书笔记,FIFO,rd,wr,ptr,3.8,时钟
From: https://www.cnblogs.com/magnolia666/p/16920604.html

相关文章

  • hdu4739 Zhuge Liang's Mines --状压dp
    原题链接:​​http://acm.hdu.edu.cn/showproblem.php?pid=4739​​题意:n个点,求出可以组成的最多的正方形的点数,要求每个点只能用一次,且正方形边平行坐标轴。分析:把所有点组......
  • hdu5135 Little Zu Chongzhi's Triangles --状压dp
    原题链接:​​http://acm.hdu.edu.cn/showproblem.php?pid=5135​​题意:n根木棒,组成若干三角形,求最大面积和。分析:把所有木棒升序排序,可以组成三角形所有的的组合利用位运算......
  • poj2411 Mondriaan's Dream--状压dp
    原题链接:​​http://poj.org/problem?id=2411​​.题意:一个n*m的方格,给定一个1*2的方块,要求用这个方块填充方格,填满,一共多少种填充方法。分析:对于一行的某一列来说,该列有三......
  • UVALive 2038 Strategic game--树形dp
    原题链接:​​http://vjudge.net/problem/UVALive-2038​​题意:一棵树,n个点,0为根,求最少的点可以覆盖所有边。分析:dp[u][0]表示u点不选;dp[u][1]表示该点选择。#defin......
  • hdu1003 Max Sum--DP
    原题链接:​​http://acm.hdu.edu.cn/showproblem.php?pid=1003​​一:原题内容ProblemDescriptionGivenasequencea[1],a[2],a[3]......a[n],yourjobistocalc......
  • hdu1824 Let's go home--2-sat
    原题链接:​​http://acm.hdu.edu.cn/showproblem.php?pid=1824​​题意:n个队伍,每一个队(三人一队),或者队长留下或者其余两名队员同时留下;接下来m对编号,每一对队员,如果队员A留......
  • sed: -e 表达式 #1, 字符 1: 未知的命令:“'”
    https://blog.csdn.net/linmingan/article/details/80007727  加双引号!!利用sed更改文件test.txt的第一行为abc:sed-i'1cabc'test.txt但是当将'1cabc'以变量形......
  • 力扣刷题03
    344.反转字符串编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组s的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)......
  • 03.Nodejs中的路由与接口
    路由与接口目录目录路由与接口express基本使用托管静态资源nodemonExpress中的路由路由的匹配路由的使用模块化路由Express中间件中间件的格式全局生效的中间件局部生效......
  • 操作系统03_2
    虚拟内存的基本概念知识总览.传统存储管理方式的特征、缺点.局部性原理.虚拟内存的定义和特征.如何实现虚拟内存技术.review.请求分页管理方式知识总览.页......