• 正文
  • 相关推荐
申请入驻 产业图谱

芯片设计:同步预取FIFO代码

16小时前
174
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

芯片设计中,FIFO(先进先出队列)是一个非常经典且重要的模块,主要用来存储数据、跨时钟域同步、打拍等。FIFO内部的数据存储结构可以使用寄存器(ff触发器)或者SRAM来搭建。

根据写时钟和读时钟是否同步,可以将FIFO分为同步FIFO和异步FIFO。根据读写端口数量可以分为单端口FIFO(只有一个写端口和一个读端口)和多端口FIFO(有多个写端口或多个读端口)。根据读数据是否可预取分为可预取FIFO和不可预取FIFO。可预取表示数据在读之前就是有效的(队首数据),可以在读有效当拍直接使用。

本文介绍同步预取FIFO的verilog代码实现。同步FIFO的核心在于准确判断空和满的状态。这里使用一种常用的指针扩展法(也叫高位异或判断法),通过增加一位读写指针位宽,解决了普通地址比较时“读指针等于写指针”既可能是空也可能是满的问题。经评论区指正,将空满判断由时序逻辑改为组合逻辑,避免溢出。

module sync_prefetch_fifo #(    parameter DATA_WIDTH = 8,       // 数据位宽    parameter DEPTH      = 16,      // FIFO深度    parameter ADDR_WIDTH = $clog2(DEPTH) // 地址位宽)(    input                       clk,        // 时钟    input                       rst_n,      // 异步复位    input                       wr_en,      // 写使能    input                       rd_en,      // 读使能    input      [DATA_WIDTH-1:0] wr_data,    // 写入数据    output     [DATA_WIDTH-1:0] rd_data,    // 读出数据    output reg                  full,       // 满标志    output reg                  empty       // 空标志);    // 定义FIFO存储阵列    reg [DATA_WIDTH-1:0] mem [DEPTH-1:0];    // 扩展一位的读写指针(例如深度16需要4位地址,指针设为5位)    reg [ADDR_WIDTH:0] wr_ptr;    reg [ADDR_WIDTH:0] rd_ptr;    // 实际的写/读允许信号(防止在满时写、空时读)    wire wr_allow = wr_en & !full;    wire rd_allow = rd_en & !empty;    // ---------------- 读写指针更新逻辑 ----------------    // 写指针:当允许写入时,指针加1    always @(posedge clk or negedge rst_n) begin        if (!rst_n)            wr_ptr <= 'd0;        else if (wr_allow)            wr_ptr <= wr_ptr + 1'b1;    end    // 读指针:当允许读取时,指针加1    always @(posedge clk or negedge rst_n) begin        if (!rst_n)            rd_ptr <= 'd0;        else if (rd_allow)            rd_ptr <= rd_ptr + 1'b1;    end    // ---------------- 数据存储与读出逻辑 ----------------    // 写入数据到RAM    integer i;    always @(posedge clk or negedge rst_n) begin        if (!rst_n) begin            // 复位时清空存储器(可选,视需求而定)            for (i = 0; i < DEPTH; i = i + 1)                mem[i] <= 'd0;        end        else if (wr_allow) begin            //写入时使用指针的低ADDR_WIDTH位作为地址            mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data;        end    end    // 读出数据,预取数据    assign  rd_data = mem[rd_ptr[ADDR_WIDTH-1:0]];          // ---------------- 空满状态判断逻辑 ----------------    //always @(posedge clk or negedge rst_n) begin    //  if (!rst_n) begin    //      full  <= 1'b0;    //      empty <= 1'b1;    //  end    //  else begin    //      // 满条件:最高位不同,其余位相同            assign full =  (wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) &                     (wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0]);    //          //      // 空条件:所有位完全相同            assign empty =  (wr_ptr == rd_ptr);    //  end   //endendmodule

FIFO的空满判断除了使用指针扩展法(也叫高位异或判断法),还可以使用计数器来统计FIFO深度的占用,具体如下:

每写入FIFO一次时,计数器加1,每读出一次FIFO时,加速器减1。当计数器等于FIFO深度时,表示FIFO满;当计数器为0时,表示FIFO空。Verilog代码如下:

    reg [ADDR_WIDTH:0] count;    always @(posedge clk or negedge rst_n) begin        if (!rst_n)            count  <= 'd0;        else if (wr_allow & !rd_allow)            count <= count + 1'b1;            else if (!wr_allow & rd_allow)            count <= count - 1'b1;    end    assign full  = (count == DEPTH);    assign empty = (count == 0);

 

相关推荐