Verilog FIFO 设计

在 Verilog 中,FIFO(First-In-First-Out,先入先出)是一种常用的数据结构,用于在数字电路中缓冲数据,特别是在跨时钟域(Clock Domain Crossing, CDC)或数据速率不同的模块之间。FIFO 设计需要考虑深度、宽度、同步/异步时钟、满/空状态以及低功耗优化等因素。以下是关于 Verilog FIFO 设计的详细说明,包括实现方法、代码示例和注意事项。


1. FIFO 的基本概念

  • 定义:FIFO 是一种队列,数据按进入顺序存储和读取,类似于流水线,先进入的数据先被读取。
  • 关键参数
  • 深度(Depth):FIFO 能存储的最大数据项数。
  • 宽度(Width):每个数据项的位宽。
  • 满状态(Full):FIFO 无法再写入数据。
  • 空状态(Empty):FIFO 中无数据可读。
  • 类型
  • 同步 FIFO:读写操作使用同一时钟。
  • 异步 FIFO:读写操作使用不同时钟,需处理跨时钟域问题。
  • 应用场景
  • 数据缓冲:如处理器与外设间的数据传输。
  • 跨时钟域:处理不同频率或相位的时钟域。
  • 低功耗设计:通过 FIFO 管理数据流,减少频繁切换。

2. FIFO 设计关键点

  • 存储结构:通常使用双端口 RAM 或寄存器阵列实现。
  • 指针管理
  • 写指针(wr_ptr):指向下一个写入位置。
  • 读指针(rd_ptr):指向下一个读取位置。
  • 格雷码(Gray Code):异步 FIFO 中用于跨时钟域传递指针,减少亚稳态。
  • 状态标志
  • 满(full)wr_ptrrd_ptr 相差一个循环深度。
  • 空(empty)wr_ptrrd_ptr 相等。
  • 跨时钟域:异步 FIFO 需要同步器处理读/写指针的跨域传递。
  • 低功耗:通过时钟门控或条件读写减少动态功耗。

3. Verilog FIFO 实现

3.1 同步 FIFO

同步 FIFO 使用单一时钟,读写操作同步,设计较简单。

<xaiArtifact artifact_id="3f7d0d21-59c5-49d1-9029-4edb32f17055" artifact_version_id="8e137de0-31c3-416a-9842-f24de6596c89" title="sync_fifo.v" contentType="text/verilog">
module sync_fifo #(
    parameter WIDTH = 8,    // 数据宽度
    parameter DEPTH = 16,   // FIFO 深度
    parameter ADDR_W = 4    // 地址宽度 = ceil(log2(DEPTH))
) (
    input clk, rst_n,       // 时钟和复位
    input wr_en,            // 写使能
    input rd_en,            // 读使能
    input [WIDTH-1:0] data_in, // 写数据
    output reg [WIDTH-1:0] data_out, // 读数据
    output full,            // FIFO 满标志
    output empty            // FIFO 空标志
);
    reg [WIDTH-1:0] mem [0:DEPTH-1]; // 存储器
    reg [ADDR_W:0] wr_ptr, rd_ptr;   // 写指针和读指针(多1位用于满/空判断)
    wire [ADDR_W-1:0] wr_addr, rd_addr;

    // 指针地址(忽略最高位)
    assign wr_addr = wr_ptr[ADDR_W-1:0];
    assign rd_addr = rd_ptr[ADDR_W-1:0];

    // 满和空标志
    assign full = (wr_ptr[ADDR_W-1:0] == rd_ptr[ADDR_W-1:0]) && (wr_ptr[ADDR_W] != rd_ptr[ADDR_W]);
    assign empty = (wr_ptr == rd_ptr);

    // 写操作
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_ptr <= 0;
        end else if (wr_en && !full) begin
            mem[wr_addr] <= data_in;
            wr_ptr <= wr_ptr + 1;
        end
    end

    // 读操作
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rd_ptr <= 0;
            data_out <= 0;
        end else if (rd_en && !empty) begin
            data_out <= mem[rd_addr];
            rd_ptr <= rd_ptr + 1;
        end
    end
endmodule
</xaiArtifact>

说明

  • 存储器:使用 mem 数组存储数据。
  • 指针wr_ptrrd_ptr 记录写/读位置,额外 1 位用于区分满/空。
  • 满/空判断
  • 满:wr_ptrrd_ptr 低位相等,高位不同。
  • 空:wr_ptrrd_ptr 完全相等。
  • 操作:写使能(wr_en)和读使能(rd_en)控制数据写入和读取。

3.2 异步 FIFO

异步 FIFO 使用不同时钟域(写时钟 wr_clk 和读时钟 rd_clk),需要格雷码和同步器处理跨时钟域指针传递。

<xaiArtifact artifact_id="ff8ec2ed-09eb-48cb-8679-91d9d623d10a" artifact_version_id="0f7feed8-708a-4446-b582-fe153b340193" title="async_fifo.v" contentType="text/verilog">
module async_fifo #(
    parameter WIDTH = 8,
    parameter DEPTH = 16,
    parameter ADDR_W = 4
) (
    input wr_clk, wr_rst_n, // 写时钟和复位
    input rd_clk, rd_rst_n, // 读时钟和复位
    input wr_en, rd_en,
    input [WIDTH-1:0] data_in,
    output reg [WIDTH-1:0] data_out,
    output full, empty
);
    reg [WIDTH-1:0] mem [0:DEPTH-1];
    reg [ADDR_W:0] wr_ptr, rd_ptr; // 二进制指针
    reg [ADDR_W:0] wr_ptr_gray, rd_ptr_gray; // 格雷码指针
    reg [ADDR_W:0] wr_ptr_gray_rd, rd_ptr_gray_wr; // 跨时钟域同步后的格雷码

    // 写指针 -> 格雷码
    always @(posedge wr_clk or negedge wr_rst_n) begin
        if (!wr_rst_n) begin
            wr_ptr <= 0;
            wr_ptr_gray <= 0;
        end else if (wr_en && !full) begin
            mem[wr_ptr[ADDR_W-1:0]] <= data_in;
            wr_ptr <= wr_ptr + 1;
            wr_ptr_gray <= (wr_ptr + 1) ^ ((wr_ptr + 1) >> 1); // 转换为格雷码
        end
    end

    // 读指针 -> 格雷码
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            rd_ptr <= 0;
            rd_ptr_gray <= 0;
            data_out <= 0;
        end else if (rd_en && !empty) begin
            data_out <= mem[rd_ptr[ADDR_W-1:0]];
            rd_ptr <= rd_ptr + 1;
            rd_ptr_gray <= (rd_ptr + 1) ^ ((rd_ptr + 1) >> 1);
        end
    end

    // 写指针同步到读时钟域
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n)
            wr_ptr_gray_rd <= 0;
        else
            wr_ptr_gray_rd <= wr_ptr_gray; // 两级同步可加
    end

    // 读指针同步到写时钟域
    always @(posedge wr_clk or negedge wr_rst_n) begin
        if (!wr_rst_n)
            rd_ptr_gray_wr <= 0;
        else
            rd_ptr_gray_wr <= rd_ptr_gray;
    end

    // 满/空标志
    assign full = (wr_ptr_gray == {~rd_ptr_gray_wr[ADDR_W:ADDR_W-1], rd_ptr_gray_wr[ADDR_W-2:0]});
    assign empty = (rd_ptr_gray == wr_ptr_gray_rd);
endmodule
</xaiArtifact>

说明

  • 格雷码:写/读指针转换为格雷码(仅 1 位变化),减少跨时钟域的亚稳态风险。
  • 同步器wr_ptr_gray 同步到读时钟域,rd_ptr_gray 同步到写时钟域。
  • 满/空判断:基于格雷码指针比较,full 检查写指针是否追上读指针,empty 检查指针是否相等。

4. 测试用例

以下是一个同步 FIFO 的测试用例,异步 FIFO 测试类似:

<xaiArtifact artifact_id="a20c2648-21a6-4517-b3a4-9202d616aa72" artifact_version_id="02cc8b6b-550d-4508-b9e9-55893fa1a29b" title="tb_sync_fifo.v" contentType="text/verilog">
module tb_sync_fifo;
    reg clk, rst_n, wr_en, rd_en;
    reg [7:0] data_in;
    wire [7:0] data_out;
    wire full, empty;

    // 实例化 FIFO
    sync_fifo #(.WIDTH(8), .DEPTH(16), .ADDR_W(4)) u_fifo (
        .clk(clk), .rst_n(rst_n), .wr_en(wr_en), .rd_en(rd_en),
        .data_in(data_in), .data_out(data_out), .full(full), .empty(empty)
    );

    // 生成时钟 (10ns 周期)
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    // 测试序列
    initial begin
        rst_n = 0;
        wr_en = 0;
        rd_en = 0;
        data_in = 0;
        #20 rst_n = 1;

        // 写入数据
        repeat (10) begin
            #10 wr_en = 1; data_in = data_in + 1;
        end
        #10 wr_en = 0;

        // 读取数据
        #20 rd_en = 1;
        #100 rd_en = 0;

        #50 $finish;
    end

    // 波形转储
    initial begin
        $dumpfile("fifo.vcd");
        $dumpvars(0, tb_sync_fifo);
    end

    // 监控
    initial begin
        $monitor("Time: %t, wr_en: %b, rd_en: %b, data_in: %h, data_out: %h, full: %b, empty: %b",
                 $time, wr_en, rd_en, data_in, data_out, full, empty);
    end
endmodule
</xaiArtifact>

说明

  • 测试写入 10 个数据,检查满/空标志,然后读取数据,验证 FIFO 行为。
  • 波形文件(fifo.vcd)用于检查时序和状态。

5. 注意事项

  • 同步 vs. 异步
  • 同步 FIFO 简单,适合单一时钟域。
  • 异步 FIFO 复杂,需处理跨时钟域问题,使用格雷码和同步器。
  • 亚稳态
  • 异步 FIFO 中,指针跨时钟域传递需两级或三级同步器。
  • 满/空标志
  • 确保标志逻辑正确,防止误判导致数据丢失或覆盖。
  • 深度和宽度
  • 深度需满足最大缓冲需求,2 的幂次方便于地址管理。
  • 宽度根据数据类型(如 8 位、32 位)设置。
  • 低功耗
  • 结合时钟门控,禁用空闲时的读写操作。
  • 在 UPF 文件中定义 FIFO 的电源域:
    tcl create_power_domain PD_FIFO -elements {u_fifo} set_domain_supply_net PD_FIFO -primary_power_net VDD_FIFO
  • 综合性
  • FIFO 存储器可映射到寄存器或双端口 RAM。
  • 确保时序满足读写时钟的频率要求。
  • FPGA vs. ASIC
  • FPGA 中,可使用 Block RAM 实现 FIFO,调用 IP 核更高效。
  • ASIC 中,需设计专用 RAM 或寄存器阵列。

6. 总结

  • 定义:FIFO 是先入先出缓冲区,用于数据缓冲和跨时钟域。
  • 类型:同步 FIFO(单一时钟)、异步 FIFO(不同时钟)。
  • 关键点
  • 存储器:寄存器或 RAM。
  • 指针:二进制或格雷码,异步 FIFO 需跨时钟域同步。
  • 状态:满/空标志精确判断。
  • 实现
  • 同步 FIFO:简单,单一时钟域。
  • 异步 FIFO:使用格雷码和同步器。
  • 注意事项
  • 防止亚稳态和数据错误。
  • 优化功耗和时序。
  • 结合 FPGA/ASIC 专用资源和 UPF。

如果有具体的 FIFO 设计需求(如深度、宽度、异步时钟频率或低功耗优化),请提供更多细节,我可以进一步优化代码或提供针对性方案!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注