sunsili 发表于 2023-8-2 09:16:11

【FPGA】跨时钟设计:异步FIFO设计

本帖最后由 sunsili 于 2023-8-2 10:02 编辑

【FPGA】跨时钟设计:异步FIFO设计文章来源于IC的世界,作者IC小鸽


1、异步FIFO
在ASIC设计或者FPGA设计中,我们常常使用异步fifo(first in first out)(下文简称为afifo)进行数据流的跨时钟,可以说没使用过afifo的Designer,其设计经历是不完整的。废话不多说,直接上接口信号说明。
2、afifo接口信号说明
如下表格为常见的afifo接口信号,非必须指的部分场景的afifo可能不存在此信号。不同公司对afifo接口的设计可能不一样,但是基本都包含了如下接口:wr表示write,写侧时钟域信号,rd表示read,读侧时钟域信号。
信号名称位宽必要性含义
almost_full1非必须将满信号,1表示afifo快满了,当afifo的有效数量大于配置时,置1
full       1非必须afifo满信号,1表示afifo已满
empty      1非必须1表示
rd_data    必须有效数据
ovf_int    1非必须1表示上溢出,即afifo满了还有数据写入
udf_int    1非必须1表示下溢出,即afifo空了外部逻辑还产生了读使能
data_count非必须afifo存储的数据量
cfg_almost_full_value 非必须将满配置信号,一般由配置寄存器模块提供驱动
wr_rst_n            1必须写侧复位
wr_clk            1必须写侧时钟
wr_en               1必须写使能,1表示有数据写入
wr_data             必须写数据
rd_rst_n            1必须读时钟与复位
rd_clk            1必须读时钟
rd_en               1必须读使能

3、设计原理
为了方便描述,本章节将以深度为8的afifo进行讲解,其中读写地址位宽为3,格雷码地址位宽为4。         图1 afifo结构图(来自eetop ThinkSpark)
图2:读写地址计算图
(1)存储模块:中间区域为memory存储模块,用于存储数据data,要么是1R1W的ram,要么是普通的寄存器。项目自研代码中,存储模块通常使用1R1W的ram,其需要memory生成器生成,需要与制造工艺匹配。而在soft IP中,针对小规格的afifo,为了方便,常常使用寄存器作为afifo的存储。
(2)写地址产生逻辑写地址waddr在wr_clk时钟域产生,有两个作用,作为存储模块的写地址并且产生格雷码waddr_gray。此种需要注意:waddr是递增的,且会翻转。如果afifo深度为8(n),则waddr位宽为3(log2(n) ),waddr计数到7后,再次写入则翻转为0。
(3)读地址产生逻辑读地址raddr在rd_clk时钟域产生,有两个作用,作为存储模块的读地址并且产生格雷码raddr_gray。此种需要注意:raddr是递增的,且会翻转。如果afifo深度为8(n),则raddr位宽为3(log2(n) ),raddr计数到7后,再次读出则翻转为0。
(4)读地址同步使用2级或者3级单bit同步器Synchronizer将读地址格雷码raddr_gray同步到wr_clk时钟域得到raddr_gray_sync,raddr_gray_sync进行格雷码逆转成二进制编码得到raddr_sync,用于产生将满信号和满信号。
在fpga设计中,2级单bit同步器Synchronizer就是2个串联的寄存器,在ASIC设计中,通常是定制的cell(会将两个/三个寄存器摆放靠得很近)。
(5)写地址同步器使用2级或者3级单bit同步器Synchronizer将写地址格雷码waddr_gray同步到rd_clk时钟域得到waddr_gray_sync,waddr_gray_sync进行格雷码逆转成二进制编码得到waddr_sync,用于产生将空信号和空信号。
(6)满信号产生逻辑此模块首先计算在wr_clk时钟域的剩余可写afifo深度,即wr_gap=raddr_sync+4’d8(FIFO深度)-waddr,然后根据wr_gap产生amost_full和full信号always @(posedge wr_clk or negedge wr_rst_n)    if(~wr_rst_n)      full <= 1'b0;    else      full <= (!(|wr_gap)) || ((wr_gap==1)&wr_en);
[*]
always @(posedge wr_clk or negedge wr_rst_n)    if(~wr_rst_n)       begin            almost_full <= 1'b0;      end    else      begin            if( wr_data_cnt>=cfg_almost_full_value )                  almost_full <= 1'b1;            else                  almost_full <= almost_full;      end
(7)空信号产生逻辑此模块首先计算在rd_clk时钟域的可读afifo深度,即assign{ovf_nc1,rd_gap} = waddr_sync - raddr,然后根据rd_gap产生empty信号。always @(posedge rd_clk or negedge rd_rst_n)    if(~rd_rst_n)      empty <= 1'b1;    else       empty <= (!(|rd_gap)) || ((rd_gap==1)&rd_en);         
4、重点说明(1)格雷码的优势格雷码的特点就是在递增,递减,或者翻转过程中,只会有1个bit位发生变化。因此单bit同步器Synchronizer同步后,只有存在变化的那一个bit可能会发生亚稳态。即使发生了亚稳态,体现的结果要么是0,要么是1,在格雷码上的同步效果就是当前clk周期没有同步(相当于delay了一个目的周期)到或者当前周期已采样到。同时它也有自己的局限性,那就是循环计数深度必须是2的n次幂(也可以不是2的n次幂哦),否则就失去了每次只变化一位的特性。深度为16的二进制及格雷码递变表如下:Binary               Gray0          0000                  00001          0001                  00012          0010                  00113          0011                  00104          0100                  01105          0101                  01116          0110                  01017          0111                  01008          1000                  11009          1001                  110110         1010                  111111         1011                  111012         1100                  101013         1101                  101114         1110                  100115         1111                  1000
0          0000                  0000
(2)空信号计算方法的妙处
在rd_clk时钟域计算可读数据量以及empty信号,看图2会发现,实际rd_gap永远小于等于真实可读数据量,能够保证empty为0时永远不会发生空读现象,即afifo没有数据,也进行了读操作。
(3)满信号计算方法的妙处
在wd_clk时钟域计算可写数据量以及full信号,看图2会发现,实际wr_gap永远小于等于真实可写数据量,能够保证full为0时永远不会发生写溢出现象,即full为0时,afifo可能存在空闲位置。
(4)格雷码转二进制代码function          gray2bin;    //to change the gray code to bin code
   input          gray_in;   //input gray code   reg            gray_code;   //reg gray    reg            bin_code;    //bin code result   integer i,j;                              //integer   reg tmp;                                  //tmp   begin       gray_code = gray_in;       for(i=0;i<=ADDR_WIDTH;i=i+1)         begin         tmp=1'b0;         for(j=i;j<=ADDR_WIDTH;j=j+1)            tmp=gray_code^tmp;         bin_code=tmp;         end       gray2bin= bin_code;   endendfunction

(5)二进制转格雷码always @(posedge rd_clk or negedge rd_rst_n)    if(~rd_rst_n)      raddr_gray<= {(ADDR_WIDTH + 1){1'b0}};    else      raddr_gray<= raddr ^ {1'b0,raddr};
页: [1]
查看完整版本: 【FPGA】跨时钟设计:异步FIFO设计