FPGA数字IC笔试面试013—同步FIFO详解及代码分享

同步FIFO是笔试面试经常遇到的问题,常见于面试的手撕代码过程中。掌握同步FIFO有利于对异步FIFO的理解。
本文目录如下:

1.  FIFO 简介及用途;

2.  同步 FIFO 接口;

3.  双口 RAM 接口;

4.  基于双口 RAM 的同步 FIFO 结构;

5.  读写地址产生逻辑;

6.  空满信号产生逻辑;

7.  全部代码


1. FIFO 简介

FIFO (先入先出, First In First Out )存储器,在 FPGA 和数字 IC 设计中非常常用。 根据接入的时钟信号,可以分为同步 FIFO 和异步 FIFO 。

FIFO 底层基于双口 RAM ,同步 FIFO 的读写时钟一致,异步 FIFO 读时钟和写时钟不同。 同步时钟主要应用于速率匹配(数据缓冲),类似于乒乓存储提高性能的思想,可以让后级不必等待前级过多时间; 异步 FIFO 主要用于多 bit 信号的跨时钟域处理。

本文讨论同步 FIFO 的结构及控制逻辑设计,并给出代码。

2. 同步 FIFO 接口

对于同步 FIFO ,包含必要的接口如下图所示:

(1) clk : 时钟信号,读写共用;

(2) rst_n : 复位信号,视具体设计和芯片采用同步复位还是异步复位,此处默认使用异步低电平复位;

(3) wdata : 写数据信号,信号后带“ \ ”表示是多 bit 信号;

(4) rdata : 读数据信号,信号后带“ \ ”表示是多 bit 信号;

(5) wfull : 满信号,指示 FIFO 写满了,不能再写了,如果再写会覆盖掉还没读出的写入数据,造成数据丢失;

(6) rempty : 空信号,指示 FIFO 读空了,不能在读了,如果再读相当于有的数据重复读了第二遍,造成数据错误;

(7) winc : 写使能信号,写使能有效时表示希望能写入数据;

(8) rinc : 读使能信号,读使能有效时表示希望能读出数据;


3. 双口 RAM 接口

在实现 FIFO 时,无论是同步 FIFO 还是异步 FIFO ,通常会通过双口 RAM ( Dual Port RAM )并添加一些必要的逻辑来实现。双口 RAM 的接口如下图所示。

左侧全部是写时钟域的,包括写时钟、写数据、写地址和写使能信号;

右侧全部是读时钟域的,包括读时钟、读数据、读地址和读使能信号;


4. 基于双口 RAM 的同步 FIFO 结构

根据同步 FIFO 的接口和双口 RAM 的接口,在借助双口 RAM 实现同步 FIFO 时,如下图所示结构,只需要加入读、写控制逻辑即可。在写逻辑中,用于产生写地址和写满信号; 在读逻辑中,用于产生读地址和读空信号。 读写控制逻辑还需要受到读写使能信号的控制。


5. 读写地址产生逻辑

读写地址什么时候能够递增?

显然,对于写地址必须满足:

(1) 写使能有效(要写入);

(2) 没写满(能写入);

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

对于读地址必须满足:

(1) 读使能有效(要读出);

(2) 没读空(能读出);

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

6. 空满信号产生逻辑

搞定了读写地址的控制逻辑,还差最后一步也是最关键的信号:空满信号如何产生。

空: 读空,读地址追上写地址;

满: 写满,写地址追上读地址。

问题来了: 怎么判地址断追上了呢? 如果地址相等那应该是追上了,即 raadr == waddr 或者 wddr == raddr 。 如果按照这种判断,显然这两个地址追上对方的判断是等效的,无法区分出来到底是写追上读还是读追上写。

可以考虑: 使用 1 个标志位 flag 来额外指示写追上读还是读追上写。

参考前人的文献,判断空满的方式有多种,非常常用的一种是 Clifford E. Cummings 文章中提到的扩展 1 bit 的读写地址方法,也就是说,将前面提到的 flag 指示信号和原本 N 位的读写地址结合,使用 N+1 位的读写地址,其中最高位用于判断空满信号,其余低位还是正常用于读写地址索引。

以一个 4 深度的 FIFO 实例来说明, 4 深度原本需要 2 bit 的读写地址,现在扩展成 3 bit 。

使用低 2 位来进行双口 RAM 的地址索引,高位用于判断空满。 对于空信号,可以知道当 FIFO 里没有待读出的数据时产生。 也就是说,此时读追上了写,把之前写的数据刚刚全部都出,读地址和写地址此时指向相同的位置,读地址 - 写地址 =0 ,即

raddr == waddr 

对于写满信号,当写入后还没被读出的数据恰好是 FIFO 深度的时候,产生满信号,即写地址 - 读地址 = FIFO 深度 = 4 。 对照下图可以发现,此时对于双口 RAM 的 2 bit 的地址来说,读写地址一致; 对于最高位来所,写是 1 而读是 0 。

再考虑下图所示的一种情况,写入待读出的数据仍然是 4 个,此时也是 4 深度的 FIFO 已经满了。 读写地址的低位相同,高位是写 0 读 1 。

对于写满的 2 种情况,总结下来,都是低位相同,最高位相反。

即:

raddr[N] = = ~waddr[N] raddr[N-1:0] = = waddr[N-1:0]

也就是:

raddr == {~waddr[N], waddr[N-1:0]}


所以,空满逻辑产生的代码为:
always @ (posedge clk&nbs***bsp;negedge rst_n) 
begin
  if(~rst_n) begin
    wfull <= 'b0;
    rempty <= 'b0;
  end 
  else begin
    wfull <= (raddr == {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH-1:0]});
    rempty <= (raddr == waddr);
  end 
end 

7. 全部代码

`timescale 1ns/1ns  

/****************************/
// 作者:FPGA探索者
/****************************/
module sfifo#(
  parameter  WIDTH = 8,
  parameter   DEPTH = 16
)(
  input           clk    , 
  input           rst_n  ,
  input           winc  ,
  input            rinc  ,
  input     [WIDTH-1:0]  wdata  ,

  output reg        wfull  ,
  output reg        rempty  ,
  output wire [WIDTH-1:0]  rdata
);
  // 用localparam定义一个参数,可以在文件内使用
    localparam ADDR_WIDTH = $clog2(DEPTH);

    reg [ADDR_WIDTH:0] waddr;
    reg [ADDR_WIDTH:0] raddr;
    always @ (posedge clk&nbs***bsp;negedge rst_n) begin
        if(~rst_n) begin
            waddr <= 'b0;
        end 
        else begin
            if( winc && ~wfull ) begin
                waddr <= waddr + 1'b1;
            end 
            else begin
                waddr <= waddr;    
            end 
        end 
    end 

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

    always @ (posedge clk&nbs***bsp;negedge rst_n) begin
        if(~rst_n) begin
            wfull <= 'b0;
            rempty <= 'b0;
        end 
        else begin
            wfull <= (raddr == {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH-1:0]});
            rempty <= (raddr == waddr);
        end 
    end 

// 带有 parameter 参数的例化格式    
dual_port_RAM  
    #(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    )
    dual_port_RAM_U0 
    (
        .wclk(clk),
      .wenc(winc),
        .waddr(waddr[ADDR_WIDTH-1:0]), 
      .wdata(wdata),        
      .rclk(clk),
        .renc(rinc),
        .raddr(raddr[ADDR_WIDTH-1:0]), 
      .rdata(rdata)     
);       
endmodule

/**************RAM 子模块*************/
module dual_port_RAM #(parameter DEPTH = 16,
             parameter WIDTH = 8)(
   input wclk
  ,input wenc
  ,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
  ,input [WIDTH-1:0] wdata        //数据写入
  ,input rclk
  ,input renc
  ,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
  ,output reg [WIDTH-1:0] rdata     //数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
  if(wenc)
    RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
  if(renc)
    rdata <= RAM_MEM[raddr];
end 

endmodule

欢迎留言讨论、咨询。

FPGA探索者:FPGA和数字IC求职必备向导~ ~

祝大家都能找到满意的实习和秋招offer!

【收藏!】FPGA数字IC求职必备知识点目录——持续更新
https://www.nowcoder.com/discuss/959891?source_id=profile_create_nctrack&channel=-1
#秋招##芯片设计工程师##FPGA工程师##芯片IC验证工程师##数字IC设计工程师#
FPGA数字IC笔试100道题 文章被收录于专栏

笔试刷题及解析,FPGA和数字IC类的笔试题汇总、解析,助力实习、提前批、秋招

全部评论
FIFO用的不是伪双口RAM吗
2 回复
分享
发布于 2022-07-24 18:21
为什么不牺牲一位数据,写指针-读指针=1的时候判空,写指针减读指针为负一判满
2 回复
分享
发布于 2023-06-01 22:17 陕西
联易融
校招火热招聘中
官网直投
哇哦,好帖
1 回复
分享
发布于 2022-05-13 16:37
好文自顶
点赞 回复
分享
发布于 2022-05-18 13:21
如何选择一份实习?——聊聊实习和对秋招的影响 https://www.nowcoder.com/discuss/953291
点赞 回复
分享
发布于 2022-05-20 21:00
请问有异步FIFO的详解吗
点赞 回复
分享
发布于 2022-06-16 00:11
见过最简单最清楚的写法!
点赞 回复
分享
发布于 2023-02-20 18:13 江苏
这代码仿真有问题,这样判断空满不对
点赞 回复
分享
发布于 04-20 11:37 湖南

相关推荐

42 150 评论
分享
牛客网
牛客企业服务