赞
踩
以下内容摘自:《正点原子逻辑设计指南》
异步 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 << ADDR_SIZE; //RAM 深度 = 2^ADDR_WIDTH reg [DATA_SIZE-1:0] Mem[RAM_DEPTH-1:0]; always@(posedge wclk) begin if(wclken) Mem[waddr] <= wdata; end assign rdata = Mem[raddr]; endmodule module sync_r2w #(parameter ADDRSIZE = 4) ( output reg [ADDRSIZE:0] wq2_rptr, input [ADDRSIZE:0] rptr, input wclk, wrst_n ); reg [ADDRSIZE:0] wq1_rptr; always @(posedge wclk or negedge wrst_n) begin if (!wrst_n) {wq2_rptr,wq1_rptr} <= 0; else {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr}; end endmodule module sync_w2r #(parameter ADDRSIZE = 4) ( output reg [ADDRSIZE:0] rq2_wptr, input [ADDRSIZE:0] wptr, input rclk, rrst_n ); reg [ADDRSIZE:0] rq1_wptr; always @(posedge rclk or negedge rrst_n) if (!rrst_n) {rq2_wptr,rq1_wptr} <= 0; else {rq2_wptr,rq1_wptr} <= {rq1_wptr,wptr}; endmodule module rptr_empty #(parameter ADDRSIZE = 4) ( output reg rempty, output [ADDRSIZE-1:0] raddr, output reg [ADDRSIZE :0] rptr, input [ADDRSIZE :0] rq2_wptr, input rinc, rclk, rrst_n); reg [ADDRSIZE:0] rbin; wire [ADDRSIZE:0] rgraynext, rbinnext; wire rempty_val; //------------------- // GRAYSTYLE2 pointer: gray 码读地址指针 //------------------- always @(posedge rclk or negedge rrst_n) begin if (!rrst_n) begin rbin <= 0; rptr <= 0; end else begin rbin <= rbinnext ; rptr <= rgraynext; end end // gray 码计数逻辑 assign rbinnext = !rempty ? (rbin + rinc) : rbin; assign rgraynext = (rbinnext>>1) ^ rbinnext; //二进制到 gray 码的转换 assign raddr = rbin[ADDRSIZE-1:0]; //--------------------------------------------------------------- // FIFO empty when the next rptr == synchronized wptr or on reset //--------------------------------------------------------------- /* * 读指针是一个 n 位的 gray 码计数器,比 FIFO 寻址所需的位宽大一位 * 当读指针和同步过来的写指针完全相等时(包括 MSB),说明二者折回次数一致,FIFO 为空 */ assign rempty_val = (rgraynext == rq2_wptr); always @(posedge rclk or negedge rrst_n) if (!rrst_n) rempty <= 1'b1; else rempty <= rempty_val; endmodule module wptr_full #( parameter ADDRSIZE = 4 ) ( output reg wfull, output [ADDRSIZE-1:0] waddr, output reg [ADDRSIZE :0] wptr, input [ADDRSIZE :0] wq2_rptr, input winc, wclk, wrst_n); reg [ADDRSIZE:0] wbin; wire [ADDRSIZE:0] wgraynext, wbinnext; wire wfull_val; // GRAYSTYLE2 pointer always @(posedge wclk or negedge wrst_n) begin if (!wrst_n) begin wbin <= 0; wptr <= 0; end else begin wbin <= wbinnext; wptr <= wgraynext; end end //gray 码计数逻辑 assign wbinnext = !wfull ? wbin + winc : wbin; assign wgraynext = (wbinnext>>1) ^ wbinnext; assign waddr = wbin[ADDRSIZE-1:0]; /*由于满标志在写时钟域产生,因此比较安全的做法是将读指针同步到写时钟域*/ /**/ //------------------------------------------------------------------ // Simplified version of the three necessary full-tests: // assign wfull_val=((wgnext[ADDRSIZE] !=wq2_rptr[ADDRSIZE] ) && // (wgnext[ADDRSIZE-1] !=wq2_rptr[ADDRSIZE-1]) && // (wgnext[ADDRSIZE-2:0]==wq2_rptr[ADDRSIZE-2:0])); //------------------------------------------------------------------ assign wfull_val = (wgraynext== {~wq2_rptr[ADDRSIZE:ADDRSIZE- 1],wq2_rptr[ADDRSIZE-2:0]}); always @(posedge wclk or negedge wrst_n) begin if (!wrst_n) wfull <= 1'b0; else wfull <= wfull_val; end endmodule
2、测试代码
`timescale 1 ns/ 1 ps module TB(); reg rclk; reg rinc; reg rrst_n; reg wclk; reg [7:0] wdata; reg winc; reg wrst_n; wire [7:0] rdata; wire rempty; wire wfull; initial begin rrst_n = 0; wrst_n = 0; rclk = 0; wclk = 0; winc = 0; rinc = 0; #20 rrst_n = 1; wrst_n =1 ; end initial begin rclk = 'b0 ; wclk = 'b0 ; end always #10 rclk = ~ rclk; always #10 wclk = ~ wclk; always @(posedge rclk) rinc <= {$random} % 2; always @(posedge wclk) winc <= {$random} % 2; always @(negedge wclk) wdata <= {$random} % 256; AsyncFIFO U0_AsyncFIFO ( .rclk(rclk), .rdata(rdata), .rempty(rempty), .rinc(rinc), .rrst_n(rrst_n), .wclk(wclk), .wdata(wdata), .wfull(wfull), .winc(winc), .wrst_n(wrst_n) ); endmodule
3、仿真结果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。