同步FIFO + 异步FIFO 【设计详解及代码分享】

正月放风筝 2022-06-05 18:49:33

FIFO表示先入先出,是一种存储结构。可满足一下需求:

1、当输入数据速率和输出速率不匹配时。可作为临时存储单元。

2、用于不同时钟域之间的同步。

3、输入数据路径和输出数据路径之间的数据宽度不匹配时,可用于数据宽度调整电路。

 

同步FIFO

同步FIFO主要是空满信号的产生,

一般情况下写使能并且非满的情况下,写地址加1;

 

 always @(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            waddr <= 'b0;
        end else begin
            if(winc & ~wfull)
                waddr <= waddr + 1'b1;
        end
    end


读使能并且非空的情况下,读地址加1。

    always @(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            raddr <= 'b0;
        end else begin
            if(rinc & ~rempty)
                raddr <= raddr + 1'b1;
        end
    end 


因此空和满的信号就要根据读写地址的比较来产生。

当我们只写不读的时候,FIFO总会被填满,因此就产生满信号。当我们一直读的时候,FIFO的数据总会被读完,因此就产生空信号。

通常我们会把写地址和读地址的位宽设置比FIFO深度多1bit,这一bit就是用来判断产生空满的。(假设我们定义FIFO可以存8个数据,那么要设置  :

    reg [3:0] waddr;
    reg [3:0] raddr;


当waddr[2:0]==raddr[2:0] && waddr[3]==raddr[3](也可以直接写成waddr==raddr)时,说明FIFO里没有能读出的数据,因此产生空信号:

assign empty = (waddr==raddr)? 1:0;


当waddr[2:0]==raddr[2:0] && waddr[3]!=raddr[3]时,说明写地址正好比读地址大一个FIFO的存储深度,因此FIFO已无空位置可写,产生满信号:

assign wfull = (waddr=={~raddr[3],raddr[2:0]})? 1:0;


同步FIFO一般注意这两点就行。

异步FIFO 

异步FIFO原理和同步FIFO一样,区别在于由于写时钟和读时钟的异步所带来的多bit数据同步问题。【这也是为什么要用格雷码,格雷码只有1bit数据变化,降低亚稳态概率】。

异步FIFO的读地址和写地址加1的操作和同步一样,只是注意always里时钟的区别:

always @(posedge wclk or negedge wrstn) begin
    if(~wrstn) begin
        waddr <= 'b0;
    end else begin
        if(winc && !wfull)
            waddr <= waddr + 1'b1;
    end
end
    
always @(posedge rclk or negedge rrstn) begin
    if(~rrstn) begin
        raddr <= 'b0;
    end else begin
        if(rinc && !rempty)
            raddr <= raddr + 1'b1;
    end
end  


在把 写地址 同步到 读时钟域 之前,需要将 写地址 转换成格雷码,减小亚稳态概率;同理,在把 读地址 同步到 写时钟域 之前,需要将 读地址 转换成格雷码,减小亚稳态概率。

assign raddr_gray = raddr ^ (raddr >> 1);
assign waddr_gray = waddr ^ (waddr >> 1);


转换完成后,剩下的就是同步问题,只需要打2拍即可。【用写时钟去采样读地址,用读时钟采样写地址。这些地址都是格雷码

   

 // 写时钟域,采样读地址
    always @(posedge wclk or negedge wrstn) begin
        if(~wrstn) begin
            raddr_wclk <= 'b0;
            raddr_wclk_1d <= 'b0;
        end else begin
            {raddr_wclk, raddr_wclk_1d} <= {raddr_wclk_1d, raddr_gray};
        end
    end 
    
    // 读时钟域,采样写地址
    always @(posedge rclk or negedge rrstn) begin
        if(~rrstn) begin
            waddr_rclk <= 'b0;
            waddr_rclk_1d <= 'b0;
        end else begin
            {waddr_rclk, waddr_rclk_1d} <= {waddr_rclk_1d, waddr_gray};
        end
    end 


至此:我们在写时钟域有了自己的原生的写地址的格雷码(waddr_gray )和同步过来的读地址(raddr_wclk);在读时钟域有了自己的原生的读地址的格雷码(raddr_gray )和同步过来的写地址(waddr_rclk)。

我们可以用其来判断满信号和空信号。由于格雷码和二进制码不同,因此比较方式稍有改变【最高位和次高位取反】:

assign rempty = (raddr_gray_r==waddr_rclk)? 1:0;
assign wfull  = (waddr_gray_r=={~raddr_wclk[3:2],raddr_wclk[1:0]})? 1:0;


异步FIFO一般多注意这两点【格雷码和打2拍同步】就行。



另外不知道大家是否还有一个疑问,就是为什么要在读时钟域产生空信号,在写时钟域产生满信号,反过来行不行??

我们想一下:读时钟域所同步的写地址是延时了两个读时钟周期的,也就是说当读时钟得到了用于判断空信号的写地址时,实际上的写地址 是要比 读时钟所同步的写地址 大。因此当判断FIFO为空时,实际上FIFO里可能还有数据,这个数据是在读时钟用打2拍来同步写地址时,写时钟域写进去的数据。同样的道理:写时钟域所同步的读地址是延时了两个写时钟周期的,也就是说当写时钟得到了用于判断满信号的读地址时,实际上的读地址 是要比 写时钟所同步的读地址 大。因此当判断FIFO为满时,实际上FIFO里可能还有空位,这个空位是在写时钟用打2拍来同步读地址时,读时钟域又读出了几个数据所造成的空位。

但是这个并不会造成异步FIFO的功能问题,大不了多写两个数据或者多读两个数据就行。但是如果空满信号在错误的时钟域产生,就会造成异步FIFO功能错误。
————————————————

原文链接:https://blog.csdn.net/GTTTONG/article/details/125126348

...全文
282 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

111,857

社区成员

发帖
与我相关
我的任务
社区描述
本社区给芯片工程师提供分享技术文章、讨论技术细节的场地。内容包括但不限于芯片设计、验证、封测。 数字IC精品文章收录(CSDN近500篇) http://t.csdn.cn/QbivO
社区管理员
  • MangoPapa
  • 张江打工人
  • IC_Novice
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 欢迎大家投稿、提问、回答问题 ~
  2. 有意愿服务本社区的请私信 MangoPapa 或 发邮件至 mangopapa@yeah.net
  3. 芯片相关友好社区,欢迎加入互通交流! “芯片爱好者聚集地社区” http://t.csdn.cn/sUFm

数字IC精品文章收录(CSDN近500篇)
       数字IC精品文章收录(500篇)

学习路线|基础知识|接口|总线|脚本语言|芯片求职|安全|EDA|工具|低功耗设计|Verilog|低功耗|STA|设计|验证|FPGA|架构|AMBA|书籍|

试试用AI创作助手写篇文章吧