您当前的位置: 首页 > 

耐心的小黑

暂无认证

  • 1浏览

    0关注

    323博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

手写异步FIFO

耐心的小黑 发布时间:2021-03-09 17:52:35 ,浏览量:1

以下内容摘自:《正点原子逻辑设计指南》

一、异步FIFO简介

异步 FIFO 有两个时钟信号,读和写接口分别采用不同时钟,这两个时钟可能时钟频率不同,也可能时钟相位不同,可能是同源时钟,也可能是不同源时钟。

在现代逻辑设计中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简便、快捷的解决方案,使用异步 FIFO 可以在两个不同时钟系统之间快速而方便地传输实时数据。

异步 FIFO 指针的考虑:

为什么异步 FIFO 的指针需要特殊处理呢?因为异步 FIFO 的空满指示需要使用指针进行判断,如果空满判断直接用两个时钟域的信号做逻辑判断,会导致逻辑判断错误,如下图所示:读逻辑的时钟采样写地址的时候,由于路径延迟和两个时钟相位都不同,就会导致读逻辑采样到的写地址完全错误(如果采样的时候刚好写地址信号跳变,还可能会导致亚稳态产生),导致空标记错误,导致 FIFO 不能使用。 在这里插入图片描述 一般异步 FIFO 的地址传递需要使用格雷码,每次地址变化格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到 gray 码的转换电路,将地址值转换为相应的 gray 码,然后将该 gray 码同步到另一个时钟域进行对比,作为空满状态的检测。

使用 gray 码进行对比,如何判断“空”与“满”?

使用 gray 码解决了指针采样错误的问题,但同时也带来另一个问题,即在格雷码域如何判断空与满。

  • 对于“空”的判断依然依据二者完全相等(包括 MSB);

  • 而对于“满”的判断,首先需要说明的是,下图中虽然有16个格雷码,但其实是一个深度为8的FIFO,这是因为在判满时,写指针必须绕一圈追上读指针才行,所以读写指针就必须扩展1位变成4位。例如,当写指针是15,读指针是7时,才能认为是写指针比读指针多了一圈,但此时读写的实际地址仍然是相同的,也即都是7。 由于 gray 码除了 MSB 外,具有镜像对称的特点,当读指针指向 7,写指针指向 8 时,除了 MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在 gray 码上判断为满必须同时满足以下 3 条: 1、wptr 和同步过来的 rptr 的 MSB 不相等,因为 wptr 必须比 rptr 多折回一次。 2、wptr 与 rptr 的次高位不相等,如下图位置 7 和位置 15,转化为二进制对应的是 0111 和 1111,MSB不同说明多折回一次,111 相同代表同一位置。 3、 剩下的其余位完全相等。 在这里插入图片描述 下图即为异步FIFO的系统框图: 在这里插入图片描述

二、程序设计

1、RTL代码

module AsyncFIFO
#(parameter ASIZE = 4, //地址位宽
  parameter DSIZE = 8) //数据位宽
( 
    input [DSIZE-1:0] wdata, 
    input winc, wclk, wrst_n, //写请求信号,写时钟,写复位
    input rinc, rclk, rrst_n, //读请求信号,读时钟,读复位
    output [DSIZE-1:0] rdata, 
    output wfull,
    output rempty
 );
    wire [ASIZE-1:0] waddr ;
    wire [ASIZE-1:0] raddr ;
    wire [ASIZE:0] wptr ;
    wire [ASIZE:0] rptr ;
    wire [ASIZE:0] wq2_rptr ;
    wire [ASIZE:0] rq2_wptr ;
 /************************************************************
 * In order to perform FIFO full and FIFO empty tests using 
 * this FIFO style, the read and write pointers must be
 * passed to the opposite clock domain for pointer comparison
 *************************************************************/
 //在检测“满”或“空”状态之前,需要将指针同步到其它时钟域时,
 //使用格雷码,可以降低同步过程中亚稳态出现的概率
sync_r2w I1_sync_r2w(
    .wq2_rptr(wq2_rptr),
    .rptr(rptr),
    .wclk(wclk),
    .wrst_n(wrst_n));

sync_w2r I2_sync_w2r (
    .rq2_wptr(rq2_wptr),
    .wptr(wptr),
    .rclk(rclk),
    .rrst_n(rrst_n));

/*
* DualRAM 
*/
DualRAM #(DSIZE, ASIZE) I3_DualRAM(
    .rdata(rdata),
    .wdata(wdata),
    .waddr(waddr),
    .raddr(raddr),
    .wclken(winc),
    .wclk(wclk));
/*
* 空、满比较逻辑
*/
rptr_empty #(ASIZE) I4_rptr_empty(
    .rempty(rempty),
    .raddr(raddr),
    .rptr(rptr),
    .rq2_wptr(rq2_wptr),
    .rinc(rinc),
    .rclk(rclk),
    .rrst_n(rrst_n));
wptr_full #(ASIZE) I5_wptr_full(
    .wfull(wfull),
    .waddr(waddr),
    .wptr(wptr),
    .wq2_rptr(wq2_rptr),
    .winc(winc),
    .wclk(wclk),
    .wrst_n(wrst_n));

endmodule

module DualRAM
#(
parameter DATA_SIZE = 8, // 数据位宽
parameter ADDR_SIZE = 4 // 地址位宽
)
(
    input wclken,wclk,
    input [ADDR_SIZE-1:0] raddr, //RAM read address
    input [ADDR_SIZE-1:0] waddr, //RAM write address
    input [DATA_SIZE-1:0] wdata, //data input
    output [DATA_SIZE-1:0] rdata //data output
); 

localparam RAM_DEPTH = 1             
关注
打赏
1640088279
查看更多评论
0.0410s