可传参配置的同步异步fifo
可传参配置的同步异步fifo相比普通fifo有许多优势:
1、代码复用性强
写一个模块就能满足 同步数据缓存 和 跨时钟域缓冲 的需求。避免写两份 FIFO 模块,降低维护成本。
不同场景下只要改 parameter ASYNC 就能复用。
2、设计灵活,可快速切换架构
设计早期可能不知道模块最终是不是跨时钟域。
用可配置 FIFO,可以在不改 RTL 代码的情况下,快速切换同步/异步模式。
3、减少错误,统一接口
同步/异步 FIFO 的 接口信号形式是相同的。
用参数配置后,接口保持一致,不会因为 FIFO 类型不同而修改上层代码。
有利于 模块化设计 和 接口标准化。
4、便于综合优化
综合工具在识别到同步 FIFO 时,可以直接推导为 单口 RAM + 逻辑。
在异步模式下,可以推导为 双口 RAM + 跨时钟同步逻辑。
一份代码 → 工具会根据参数推导出最优实现。
5、支持不同应用场景
同步 FIFO:适合数据缓存 / 流量调节,延迟小,逻辑简单。
异步 FIFO:适合跨时钟域数据传输,安全可靠,适合大规模数据搬运。
参数化模块 = 两种场景都能覆盖。
一、RAM块缓存
指定使用块RAM进行数据缓存;读写操作独立运行。
/*** Module: reg_ram** Description:* 例化块ram进行数据读写** Created: ** Author:*/`timescale 1ns / 1psmodule reg_ram#(parameter WIDTH = 32, //数据位宽parameter SIZE = 3 //fifo深度
)
(input clk_wr,input wr_en,input [SIZE - 1 : 0] wr_addr,input [WIDTH - 1 : 0] wr_data,input clk_rd,input rd_en,input [SIZE - 1 : 0] rd_addr,output [WIDTH - 1 : 0] rd_data
);(* ram_style = "block" *) reg [WIDTH - 1 : 0] ram [0 : 2**SIZE-1];reg [WIDTH - 1 : 0] rd_data_d;// ram数据写always @(posedge clk_wr) beginif(wr_en) beginram[wr_addr] <= wr_data;endend// ram数据读always @(posedge clk_rd) beginif(rd_en) beginrd_data_d <= ram[rd_addr];endendassign rd_data = rd_data_d;endmodule
二、fifo调用顶层
/*** Module: fifo** Description:* fifo wrapper** Created: ** Author:*/`timescale 1ns / 1psmodule fifo#(parameter ASYNC = 1, //异步fifo开关parameter DATA_CNT_WR_EN = 1, //写数据计数parameter DATA_CNT_RD_EN = 1, //读数据计数parameter WIDTH = 32, //数据位宽parameter DEPTH = 3 //fifo深度
)
(input clk_wr,input rstn_wr,input wr_en,input [WIDTH - 1 : 0] wr_data,output [DEPTH - 1 : 0] wr_cnt,output alfull,output full,output overflow,input clk_rd,input rstn_rd,input rd_en,output [WIDTH - 1 : 0] rd_data,output [DEPTH - 1 : 0] rd_cnt,output alempty,output empty
);reg ram_wr_en, ram_wr_en;reg [DEPTH - 1 : 0] ram_rd_addr, ram_wr_addr;reg [WIDTH - 1 : 0] ram_wr_data, ram_rd_data;fifo_ctrl#(.ASYNC (1 ), //异步fifo开关.DATA_CNT_WR_EN (1 ), //写数据计数.DATA_CNT_RD_EN (1 ), //读数据计数.WIDTH (32 ), //数据位宽.DEPTH (3 ) //fifo深度)fifo_ctrl_inst(.clk_wr ( clk_wr ),.rstn_wr ( rstn_wr ), .wr_en ( wr_en ),.wr_data ( wr_data ), .ram_wr_en ( ram_wr_en ), .ram_wr_addr ( ram_wr_addr ), .ram_wr_data ( ram_wr_data ), .ram_data_wr_cnt ( wr_cnt ), .alfull ( alfull ),.full ( full ),.overflow ( overflow ), .clk_rd ( clk_rd ),.rstn_rd ( rstn_rd ), .rd_en ( rd_en ),.rd_data ( rd_data ), .ram_rd_en ( ram_rd_en ), .ram_rd_addr ( ram_rd_addr ), .ram_rd_data ( ram_rd_data ), .ram_data_rd_cnt ( rd_cnt ), .alempty ( alempty ), .empty ( empty ));reg_ram#(.WIDTH ( 32 ), //数据位宽.DEPTH ( 3 ) //fifo深度)reg_ram_inst(.clk_wr ( clk_wr ),.wr_en ( ram_wr_en ),.wr_addr ( ram_wr_addr ),.wr_data ( ram_wr_data ),.clk_rd ( clk_rd ),.rd_en ( ram_rd_en ),.rd_addr ( ram_rd_addr ),.rd_data ( ram_rd_data ));endmodule
三、fifo的控制模块
将读写操作转换为对块ram的地址读写,同时将读写地址进行gary码转换进行空满计算等。
/*** Module: fifo_ctrl** Description:* 根据ASYNC常量值来控制async或sync fifo读写;* 提供剩余可写入空间的计算和剩余可读出数据的计算;** Created: ** Author:*/`timescale 1ns / 1psmodule fifo_ctrl#(parameter ASYNC = 1, //异步fifo开关parameter DATA_CNT_WR_EN = 1, //写数据计数parameter DATA_CNT_RD_EN = 1, //读数据计数parameter WIDTH = 32, //数据位宽parameter DEPTH = 3 //fifo深度
)
(input clk_wr,input rstn_wr,input wr_en,input [WIDTH - 1 : 0] wr_data,output ram_wr_en,output [DEPTH - 1 : 0] ram_wr_addr,output [WIDTH - 1 : 0] ram_wr_data,output [DEPTH - 1 : 0] ram_data_wr_cnt,output alfull,output full,output overflow,input clk_rd,input rstn_rd,input rd_en,input [WIDTH - 1 : 0] rd_data,output ram_rd_en,output [DEPTH - 1 : 0] ram_rd_addr,output [WIDTH - 1 : 0] ram_rd_data,output [DEPTH - 1 : 0] ram_data_rd_cnt,output alempty,output empty
);reg [DEPTH - 1 : 0] fifo_wr_addr, fifo_wr_addr_inc1, fifo_wr_addr_inc2;reg [DEPTH - 1 : 0] fifo_wr_addr_gray, fifo_wr_addr_gray_d1, fifo_wr_addr_gray_d2;reg [DEPTH - 1 : 0] fifo_wr_addr_gray_inc1, fifo_wr_addr_gray_inc2;reg [DEPTH - 1 : 0] fifo_data_wr_cnt;reg [DEPTH - 1 : 0] fifo_rd_addr, fifo_rd_addr_inc1, fifo_rd_addr_inc2;reg [DEPTH - 1 : 0] fifo_rd_addr_gray, fifo_rd_addr_gray_d1, fifo_rd_addr_gray_d2;reg [DEPTH - 1 : 0] fifo_rd_addr_gray_inc1;reg [DEPTH - 1 : 0] fifo_data_rd_cnt;//写地址计算always @(posedge clk_wr) beginif(!rstn_wr) beginfifo_wr_addr <= 'h0;fifo_wr_addr_inc1 <= 'h0;fifo_wr_addr_inc2 <= 'h0;fifo_wr_addr_gray <= 'h0;overflow <= 0;endelse beginif(wr_en) beginif(!full) beginfifo_wr_addr <= fifo_wr_addr + 1; //当前写入地址fifo_wr_addr_inc1 <= fifo_wr_addr + 2;fifo_wr_addr_inc2 <= fifo_wr_addr + 3;fifo_wr_addr_gray <= bin2gray(fifo_wr_addr + 1); //当前写入地址的gray码endelse beginoverflow <= 1;endendif(!full) beginoverflow <= 0;endelse beginoverflow <= overflow;endendend// 读地址计算always @(posedge clk_rd) beginif(!rstn_rd) beginfifo_rd_addr <= 'h0;fifo_rd_addr_inc1 <= 'h0;fifo_rd_addr_inc2 <= 'h0;fifo_rd_addr_gray <= 'h0;endelse beginif(rd_en) beginif(!empty) beginfifo_rd_addr <= fifo_rd_addr + 1; //当前读出地址fifo_rd_addr_inc1 <= fifo_rd_addr + 2;fifo_rd_addr_gray <= bin2gray(fifo_rd_addr + 1); //当前读出地址的gray码endendendendgenerateif(ASYNC) begin : async_ctrl// 同步读地址always @(posedge clk_wr) beginif(!rstn_wr) beginfifo_rd_addr_gray_d1 <= 'h0;fifo_rd_addr_gray_d2 <= 'h0;endelse beginfifo_rd_addr_gray_d1 <= fifo_rd_addr_gray;fifo_rd_addr_gray_d2 <= fifo_rd_addr_gray_d1;endend// 同步写地址always @(posedge clk_rd) beginif(!rstn_wr) beginfifo_wr_addr_gray_d1 <= 'h0;fifo_wr_addr_gray_d2 <= 'h0;endelse beginfifo_wr_addr_gray_d1 <= fifo_wr_addr_gray;fifo_wr_addr_gray_d2 <= fifo_wr_addr_gray_d1;endendgenerate if(DATA_CNT_RD_EN) begin//写指针的格雷码减去读指针的格雷码即剩余可读出的数据数量assign fifo_data_rd_cnt = gray2bin(fifo_wr_addr_gray_d2) - gray2bin(fifo_rd_addr_gray);endelse beginassign fifo_data_rd_cnt = 0;endendgenerateendelse begin : sync_ctrlgenerate if(DATA_CNT_RD_EN) begin//写指针减去读指针即剩余可读出的数据数量assign fifo_data_rd_cnt = fifo_wr_addr - fifo_rd_addr;endelse beginassign fifo_data_rd_cnt = 0;endendgenerateendendgenerategenerate if(DATA_CNT_WR_EN) begin//总空间大小减去已使用的空间大小即剩余可写入的空间assign fifo_data_wr_cnt = 2**DEPTH - fifo_data_rd_cnt;endelse beginassign fifo_data_wr_cnt = 0;endendgenerate// I/O Connect //assign ram_wr_en = wr_en;assign ram_wr_data = wr_data;assign ram_wr_addr = fifo_wr_addr;assign ram_data_wr_cnt = fifo_data_wr_cnt;assign ram_rd_en = rd_en;assign ram_rd_data = rd_data;assign ram_rd_addr = fifo_rd_addr;assign ram_data_rd_cnt = fifo_data_rd_cnt;// 空满状态都可以用gray码计算// 满、将满通过wr地址判断assign alfull = (bin2gray(fifo_wr_addr_inc2) == fifo_rd_addr_gray_d2) ? 1 : 0;assign full = (bin2gray(fifo_wr_addr_inc1) == fifo_rd_addr_gray_d2) ? 1 : 0;// 空、将空通过rd地址判断assign alempty = (fifo_wr_addr_gray_d2 == bin2gray(fifo_rd_addr_inc1)) ? 1 : 0;assign empty = (fifo_wr_addr_gray_d2 == fifo_rd_addr_gray) ? 1 : 0;endmodule
四、仿真波形
同步fifo配置的仿真波形如下: